CoUI Stepper#
ํ๋ง๋๋ก (๋น๊ฐ๋ฐ์์ฉ)#
์ด ์คํฌ์ "์ฌ๋ฌ ๋จ๊ณ๋ก ๋๋ ํ๋ฉด"์ ๋ง๋๋ ๋๊ตฌ์ ๋๋ค. ๋ง์น ํ๋ฐฐ ๋ฐฐ์ก ์กฐํ์์ "์ฃผ๋ฌธ โ ๊ฒฐ์ โ ๋ฐฐ์ก โ ์๋ฃ"์ฒ๋ผ ์ง๊ธ ์ด๋๊น์ง ์๋์ง ๋๊ทธ๋ ๋ฒํธ์ ์ฐ๊ฒฐ์ ์ผ๋ก ๋ณด์ฌ์ฃผ๋ ๊ทธ ํ์๋ฅผ ๋ง๋ค์ด ์ค๋๋ค. ๊ฐ์ ์ ์ฐจ๋ ๊ฒฐ์ ๊ณผ์ ์ฒ๋ผ ํ ๋ฒ์ ๋ค ํ๊ธฐ ๋ถ๋ด์ค๋ฌ์ด ์ผ์ ์์ ๋จ๊ณ๋ก ์ชผ๊ฐ ์๋ดํ ๋ ์๋๋ค.
๋ฌด์์ยท์ธ์ (๋น๊ฐ๋ฐ์์ฉ)#
- ๋ฌด์์ ํด์ฃผ๋์: ์ฌ๋ฌ ๋จ๊ณ๋ก ์งํ๋๋ ํ๋ฉด(๋จ๊ณ ํ์์ค)์ ๋ง๋ค์ด ์ค๋๋ค. ๊ฐ๋ก/์ธ๋ก ๋ฐฐ์น, ๋จ๊ณ ํด๋ฆญ์ผ๋ก ์ด๋, ์ยทํฌ๊ธฐ ๋ฑ ๋ชจ์ ๊พธ๋ฏธ๊ธฐ๋ฅผ ์ง์ํฉ๋๋ค. ์๋ฃํ ๋จ๊ณ๋ ์ฒดํฌ ํ์๋ก, ์์ง ์ ํ ๋จ๊ณ๋ ๋ฒํธ๋ก ๋ณด์ฌ์ค๋๋ค.
- ์ธ์ ์๋ํ๋์: ํ์๊ฐ์ ๋ง๋ฒ์ฌ, ๊ฒฐ์ (์ฅ๋ฐ๊ตฌ๋ โ ๋ฐฐ์ก โ ๊ฒฐ์ โ ์๋ฃ) ๊ฐ์ ์ฌ๋ฌ ๋จ๊ณ ํผ, ๋จ๊ณ๋ณ ์๋ด ํ๋ฆ์ ๋ง๋ค ๋ ํ์ฑํ๋ฉ๋๋ค.
- ์ด๋์ ์ฐ๋์: Flutter ์ฑ๊ณผ ์น(Jaspr) ์์ชฝ์์ ๋์ผํ๊ฒ ์ฌ์ฉํ ์ ์์ต๋๋ค.
ํต์ฌ ์ฉ์ด (๋น๊ฐ๋ฐ์์ฉ)#
| ์ฉ์ด | ์ฌ์ด ์ค๋ช |
|---|---|
| Stepper(์คํ ํผ) | ์งํ ๋จ๊ณ๋ฅผ ๋ฒํธ์ ์ฐ๊ฒฐ์ ์ผ๋ก ๋ณด์ฌ์ฃผ๋ ๋จ๊ณ ํ์์ค |
| step(์คํ ) | ์ ์ฒด ๊ณผ์ ์ค ํ๋์ ๋จ๊ณ (์: "๊ฒฐ์ ") |
| indicator(์ธ๋์ผ์ดํฐ) | ๊ฐ ๋จ๊ณ๋ฅผ ๋ํ๋ด๋ ๋๊ทธ๋ ๋ฒํธ(๋๋ ์ฒดํฌ) ํ์ |
| connector(์ปค๋ฅํฐ) | ๋จ๊ณ์ ๋จ๊ณ ์ฌ์ด๋ฅผ ์๋ ์ฐ๊ฒฐ์ |
| label(๋ ์ด๋ธ) | ๋จ๊ณ ์ด๋ฆ ๊ธ์ (์: "๋ฐฐ์ก") |
| description(์ค๋ช ) | ๋จ๊ณ ์๋ ๋ถ๋ ๋ถ๊ฐ ์ค๋ช ๊ธ์ (์ธ๋ก ๋ฐฐ์น์์๋ง ํ์) |
| orientation(๋ฐฉํฅ) | ๋จ๊ณ๋ฅผ ๊ฐ๋ก๋ก ๋์ด๋์์ง ์ธ๋ก๋ก ์์์ง ๊ฒฐ์ |
| horizontal/vertical | ๊ฐ๋ก ๋ฐฐ์น / ์ธ๋ก ๋ฐฐ์น |
| style(์คํ์ผ) | ์ยทํฌ๊ธฐยท๋๊ป ๋ฑ ๋จ๊ณ ํ์์ค์ ๊ฒ๋ชจ์ ์ค์ |
| Legacy(๋ ๊ฑฐ์) | ๋ ์ด์ ์ฐ๋ฉด ์ ๋๋ ์๋ ๋ฐฉ์ (์ ๋ฐฉ์์ผ๋ก ๊ต์ฒด ํ์) |
Overview#
Stepper displays a multi-step process with circular indicators, connector lines, and step labels. Completed steps render a
LucideIcons.check glyph; the current and pending steps render their 1-based number. It supports horizontal and vertical layouts, click-to-navigate via
onStepChanged, and full chrome customisation through a single stepperStyle slot.
The canonical API (coui v0.127.15) is Stepper on both platforms, sharing CoreStepperContract
from coui_core. Behaviour fields (steps, currentStep, orientation,
onStepChanged) stay flat on the widget; every chrome/dimensional/nested-slot override flows through the single
stepperStyle: CoreStepperStyle(...) slot, with a project-level base in CoreStepperTheme.
Resolve chain (identical on both platforms):
design-system default
โ CoreStepperTheme.style // project-wide (coreComponentTheme.stepper)
โ widget.stepperStyle // per-instance
Variants & Sizes#
The stepper is a layout/structural component: it has no variant enum and no size enum. There is no
CoreStepperVariant and no CoreComponentSize parameter on any stepper class. The only semantic enum is the orientation โ full set:
| Value | Description |
|---|---|
CoreStepperOrientation.horizontal |
Horizontal layout (default). Indicator above label, connectors run between steps. |
CoreStepperOrientation.vertical |
Vertical layout. Indicator column on the left, label + optional description on the right, vertical connectors. |
Indicator size, connector thickness, colours, and typography all go through CoreStepperStyle
โ see the style slot table below.
Flutter (coui_flutter)#
Import#
import 'package:coui_flutter/coui_flutter.dart';
Note: Flutter's Material library also exports a class named Stepper. When a file imports both
package:flutter/material.dart and the coui barrel, hide the Material one: import 'package:flutter/material.dart' hide Stepper;.
Horizontal Stepper (default)#
Stepper(
currentStep: _currentStep,
steps: const [
CoreCoStepItem(label: 'Account'),
CoreCoStepItem(label: 'Details'),
CoreCoStepItem(label: 'Confirm'),
],
onStepChanged: (index) => setState(() => _currentStep = index),
)
Vertical Stepper#
Stepper(
currentStep: _currentStep,
orientation: CoreStepperOrientation.vertical,
steps: const [
CoreCoStepItem(label: 'Create Account', description: 'Name and email'),
CoreCoStepItem(label: 'Set Up Profile'),
CoreCoStepItem(label: 'Complete'),
],
onStepChanged: (index) => setState(() => _currentStep = index),
)
Stepper Parameters (Flutter)#
| Parameter | Type | Default | Description |
|---|---|---|---|
steps |
List<CoreCoStepItem> |
required | List of steps to display |
currentStep |
int |
0 |
Index of the currently active step |
orientation |
CoreStepperOrientation |
.horizontal |
Layout orientation |
onStepChanged |
ValueChanged<int>? |
null |
Called with the tapped step index when an indicator is tapped.
null
disables navigation (cursor stays basic)
|
stepperStyle |
CoreStepperStyle? |
null |
Per-instance chrome and nested slot overrides |
CoreCoStepItem Parameters#
| Parameter | Type | Default | Description |
|---|---|---|---|
label | String | required | Step label text |
description |
String? |
null |
Optional description โ rendered only in the vertical layout |
Rendering Rules (source-verified)#
-
Completed steps (
index < currentStep): indicator shows theLucideIcons.checkicon over the active fill. -
Current step (
index == currentStep): indicator shows its number over the active fill; label renders bold (unlesslabelStylesets an explicitfontWeight). -
Pending steps (
index > currentStep): indicator shows its number over the inactive fill. - Connector segments before the current step use the active colour, the rest the inactive colour.
descriptionrenders only in the vertical layout, below the label.
Style Customisation#
All chrome flows through the single stepperStyle: CoreStepperStyle(...) slot:
Stepper(
currentStep: _currentStep,
steps: const [
CoreCoStepItem(label: 'Step 1'),
CoreCoStepItem(label: 'Step 2'),
CoreCoStepItem(label: 'Step 3'),
],
stepperStyle: const CoreStepperStyle(
indicatorActiveColor: CoreColor.token(CoreColors.secondary),
connectorActiveColor: CoreColor.token(CoreColors.secondary),
indicatorSize: 36.0,
connectorThickness: 2.0,
labelStyle: CoreTextStyle(fontWeight: CoreFontWeight.semiBold),
),
onStepChanged: (index) => setState(() => _currentStep = index),
)
CoreStepperStyle Fields (all 17, source-verified)#
| Field | Type | Description |
|---|---|---|
indicatorActiveColor |
CoreColor? |
Indicator fill for the current and completed steps |
indicatorInactiveColor |
CoreColor? |
Indicator fill for pending steps |
indicatorBorderColor |
CoreColor? |
Indicator border stroke colour (no border painted when both border fields are null) |
indicatorBorderWidth |
double? |
Indicator border stroke width (logical px; falls back to
CoreStrokeWidth.stroke1
when only the colour is set)
|
indicatorSize |
double? |
Indicator circle diameter (logical px; explicit override is used as-is, un-scaled) |
connectorActiveColor |
CoreColor? |
Connector colour for completed segments |
connectorInactiveColor |
CoreColor? |
Connector colour for pending segments |
connectorThickness |
double? |
Connector line thickness (logical px) |
indicatorContentGapStyle |
CoreGapStyle? |
Gap between indicator column and step content (vertical layout) |
stepPadding |
CoreEdgeInsets? |
Padding below each non-final step (vertical layout) |
connectorMargin | double? | Margin around connectors |
labelGapStyle |
CoreGapStyle? |
Gap between indicator and label (horizontal layout) |
connectorMinExtent |
double? |
Minimum connector length in the vertical layout (logical px) |
iconSizeRatio |
double? |
Check icon size relative to indicator diameter |
labelStyle |
CoreTextStyle? |
Step label typography override |
descriptionStyle |
CoreTextStyle? |
Step description typography override |
indicatorIconStyle |
CoreIconStyle? |
Completed-step check icon style override |
CoreStepperStyle also provides merge(other) and copyWith(...); nested
labelStyle / descriptionStyle / indicatorIconStyle / gap slots merge recursively.
Design-system Defaults (source-verified statics on CoreStepperStyle)#
| Static constant | Value |
|---|---|
defaultIndicatorSize |
CoreSpace.space32 (32.0, pre-scaling) |
defaultConnectorThickness | CoreStrokeWidth.stroke2 (2.0) |
defaultIndicatorContentGapStyle |
CoreGapStyle(size: CoreSpace.space12) |
defaultStepPadding |
CoreEdgeInsets.only(bottom: CoreSpace.space24) |
defaultConnectorMargin | CoreSpace.space4 (4.0) |
defaultLabelGapStyle | CoreGapStyle(size: CoreSpace.space4) |
defaultConnectorMinExtent | CoreSpace.space8 (8.0) |
defaultIconSizeRatio |
0.5 (check icon = 50% of indicator diameter) |
defaultIndicatorActiveColor |
CoreColor.token(CoreColors.primary) |
defaultIndicatorInactiveColor |
CoreColor.token(CoreColors.surfaceContainer) |
defaultConnectorActiveColor |
CoreColor.token(CoreColors.primary) |
defaultConnectorInactiveColor |
CoreColor.token(CoreColors.surfaceContainer) |
defaultIndicatorIconColor |
CoreColor.token(CoreColors.onPrimary) |
defaultIndicatorNumberActiveStyle |
bodySmall role, bold, onPrimary |
defaultIndicatorNumberInactiveStyle |
bodySmall role, bold, onSurface |
defaultLabelVerticalStyle |
titleSmall role, onSurface |
defaultLabelHorizontalStyle |
labelSmall role, onSurface |
defaultDescriptionStyle |
bodySmall role, onSurfaceVariant |
Project-level Theme#
Apply a shared style across all Stepper instances via CoreStepperTheme (the
stepper slot on CoreComponentTheme):
// In your theme configuration:
coreComponentTheme: CoreComponentTheme(
stepper: CoreStepperTheme(
style: CoreStepperStyle(
indicatorActiveColor: CoreColor.token(CoreColors.primary),
connectorThickness: 2.0,
),
),
),
Multi-step Form Pattern#
import 'package:coui_flutter/coui_flutter.dart';
import 'package:flutter/widgets.dart';
class CheckoutStepper extends StatefulWidget {
const CheckoutStepper({super.key});
@override
State<CheckoutStepper> createState() => _CheckoutStepperState();
}
class _CheckoutStepperState extends State<CheckoutStepper> {
int _currentStep = 0;
static const _steps = [
CoreCoStepItem(label: 'Cart'),
CoreCoStepItem(label: 'Shipping'),
CoreCoStepItem(label: 'Payment'),
CoreCoStepItem(label: 'Done'),
];
@override
Widget build(BuildContext context) {
return Column(
children: [
Stepper(
currentStep: _currentStep,
steps: _steps,
onStepChanged: (index) => setState(() => _currentStep = index),
),
Expanded(child: _buildStepContent(_currentStep)),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (_currentStep > 0)
Button(
onPressed: () => setState(() => _currentStep--),
child: const Text('Back'),
),
Button(
onPressed: _currentStep < _steps.length - 1
? () => setState(() => _currentStep++)
: null,
child: Text(_currentStep < _steps.length - 1 ? 'Next' : 'Complete'),
),
],
),
],
);
}
Widget _buildStepContent(int step) {
// Swap in the form body for each step here.
return Center(child: Text('Step ${step + 1} content'));
}
}
Web (coui_web / Jaspr)#
Import#
import 'package:coui_web/coui_web.dart';
Horizontal Stepper#
Stepper(
currentStep: currentStep,
steps: const [
CoreCoStepItem(label: 'Account'),
CoreCoStepItem(label: 'Details'),
CoreCoStepItem(label: 'Confirm'),
],
onStepChanged: (index) => setState(() => currentStep = index),
)
Vertical Stepper#
Stepper(
currentStep: currentStep,
orientation: CoreStepperOrientation.vertical,
steps: const [
CoreCoStepItem(label: 'Create Account', description: 'Name and email'),
CoreCoStepItem(label: 'Set Up Profile'),
CoreCoStepItem(label: 'Complete'),
],
onStepChanged: (index) => setState(() => currentStep = index),
)
Stepper Parameters (Web)#
The behaviour and style parameters are identical to Flutter (steps, currentStep,
orientation, onStepChanged, stepperStyle). In addition, the Web
Stepper extends UiComponent (rendered as a <div>) and accepts the standard Web chrome parameters:
| Extra parameter | Type | Description |
|---|---|---|
id |
String? |
DOM id on the root <div> |
classes | String? | Extra CSS classes on the root |
css |
Styles? |
Inline styles combined with the resolved root styles |
attributes |
Map<String, String>? |
Extra HTML attributes |
eventHandlers |
Map<String, List<UiEventHandler>>? |
Extra DOM event handlers |
The Web Stepper also provides a copyWith(...) method covering all of the above plus the behaviour fields.
Web Accessibility Behaviour (source-verified)#
When onStepChanged is non-null, each indicator <div> gets role="button"
and tabindex="0", and a click DOM event that calls onStepChanged(index). Only the
click event is wired in v0.127.15 (no keydown handler).
Flutter โ Web Differences#
| Aspect | Flutter | Web (Jaspr) |
|---|---|---|
| Import | package:coui_flutter/coui_flutter.dart |
package:coui_web/coui_web.dart |
| Widget class | Stepper (extends StatelessWidget) |
Stepper (extends UiComponent, renders <div>) |
| Shared contract | CoreStepperContract |
CoreStepperContract (identical) |
| Step item type | CoreCoStepItem |
CoreCoStepItem (identical) |
| Orientation enum | CoreStepperOrientation |
CoreStepperOrientation (identical) |
| Callback | onStepChanged: ValueChanged<int>? |
onStepChanged
โ same name; contract type
CoreValueChanged<int>
(=
void Function(int)
)
|
| Style slot / theme | stepperStyle: CoreStepperStyle?, CoreStepperTheme |
identical |
| Completed-step icon | Icon(LucideIcons.check, iconStyle: โฆ) |
Icon(LucideIcons.check, iconStyle: โฆ) (Web Icon component) |
| Click affordance | MouseRegion + GestureDetector.onTap |
role="button" + tabindex="0" + click event |
| Extra chrome params | none |
id
,
classes
,
css
,
attributes
,
eventHandlers
; plus
copyWith(...)
|
| Resolver output | ResolvedStepper with paint-ready Color / TextStyle |
ResolvedStepper
with
WebElementChrome
lists (Tailwind-style class names + inline CSS variables)
|
| Scaling |
design defaults multiplied by
theme.scaling
; explicit
indicatorSize
override used as-is
|
no scaling step; values emitted as CSS px |
The Stepper public API (parameter names, types, and every CoreStepperStyle field) is identical between Flutter and Web; the resolver internals differ but are transparent to callers.
v0.127 Migration (Legacy)#
v0.127.0 removed the Co prefix from widget classes โ a hard break with no aliases. โ marks legacy identifiers that no longer exist and must not appear in new code.
Core* classes (CoreCoStepItem, CoreStepperOrientation, CoreStepperStyle,
CoreStepperTheme, CoreGapStyle, CoreTextStyle, CoreIconStyle, โฆ) are unchanged.
| Legacy (โ removed in v0.127) | v0.127 Canonical |
|---|---|
โ CoStepper | Stepper |
โ CoButton | Button |
โ CoGap | Gap |
โ CoIcon | Icon |
โ CoDivider | Divider |
โ CoLucideIcons | LucideIcons |
The even older shadcn-style stepper API (a controller-based Stepper with StepperController,
Step, StepVariant, StepSize) was already removed before v0.127 and does not exist in v0.127.15 โ the bare name
Stepper now refers exclusively to the unified contract-based widget documented above.