Skip to content
HyperUI for Flutter requires the Style package — this site itself ships no pub package.

Steps

Ported from HyperUI’s Steps — see Attribution.

Includes a dark: variant — toggle the site theme (top right) to preview it.

class CurrentColorIcon extends StatelessWidget
implements StyleReplacedElementWrapper {
const CurrentColorIcon(this.svg, {super.key});
final String svg;
@override
Widget build(BuildContext context) {
final color = IconTheme.of(context).color ?? const Color(0xFF000000);
return SvgPicture.string(svg, theme: SvgTheme(currentColor: color));
}
}
const _detailsSvg =
'''<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M10 6H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V8a2 2 0 00-2-2h-5m-4 0V5a2 2 0 114 0v1m-4 0a2 2 0 104 0m-5 8a2 2 0 100-4 2 2 0 000 4zm0 0c1.306 0 2.417.835 2.83 2M9 14a3.001 3.001 0 00-2.83 2M15 11h3m-3 4h2"/></svg>''';
const _addressSvg =
'''<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"/><path stroke-linecap="round" stroke-linejoin="round" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"/></svg>''';
const _paymentSvg =
'''<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"/></svg>''';
class Steps1Example extends StatelessWidget {
const Steps1Example({super.key});
@override
Widget build(BuildContext context) {
return Style(
'mx-auto max-w-3xl p-6',
child: Stack(
fit: StackFit.passthrough,
children: [
const Positioned(
top: 0,
left: 0,
child: IgnoreBaseline(child: Style('sr-only', child: Text('Steps'))),
),
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: [
const Style(
'overflow-hidden rounded-full bg-gray-200 dark:bg-gray-700',
child: Style('h-2 w-1/2 rounded-full bg-blue-500'),
),
Style(
'mt-4 grid grid-cols-3 text-sm font-medium text-gray-600 dark:text-gray-300',
children: [
Style(
'flex items-center justify-start text-blue-500 sm:gap-1.5',
children: [
const Style(
'sr-only sm:not-sr-only sm:inline',
child: Text('Details'),
),
Style(
'size-6 sm:size-5',
child: CurrentColorIcon(_detailsSvg),
),
],
),
Style(
'flex items-center justify-center text-blue-500 sm:gap-1.5',
children: [
const Style(
'sr-only sm:not-sr-only sm:inline',
child: Text('Address'),
),
Style(
'size-6 sm:size-5',
child: CurrentColorIcon(_addressSvg),
),
],
),
Style(
'flex items-center justify-end sm:gap-1.5',
children: [
const Style(
'sr-only sm:not-sr-only sm:inline',
child: Text('Payment'),
),
Style(
'size-6 sm:size-5',
child: CurrentColorIcon(_paymentSvg),
),
],
),
],
),
],
),
],
),
);
}
}

Includes a dark: variant — toggle the site theme (top right) to preview it.

class Steps2Example extends StatelessWidget {
const Steps2Example({super.key});
@override
Widget build(BuildContext context) {
return Style(
'mx-auto max-w-3xl p-6',
child: Stack(
fit: StackFit.passthrough,
children: [
const Positioned(
top: 0,
left: 0,
child: IgnoreBaseline(child: Style('sr-only', child: Text('Steps'))),
),
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: [
const Style(
'text-xs font-medium text-gray-600 dark:text-gray-300',
child: Text('2/3 - Address'),
),
const Style(
'mt-2 overflow-hidden rounded-full bg-gray-200 dark:bg-gray-700',
child: Style('h-2 w-2/3 rounded-full bg-blue-500'),
),
],
),
],
),
);
}
}

Includes a dark: variant — toggle the site theme (top right) to preview it.

class CurrentColorIcon extends StatelessWidget
implements StyleReplacedElementWrapper {
const CurrentColorIcon(this.svg, {super.key});
final String svg;
@override
Widget build(BuildContext context) {
final color = IconTheme.of(context).color ?? const Color(0xFF000000);
return SvgPicture.string(svg, theme: SvgTheme(currentColor: color));
}
}
const _checkCircleSvg =
'''<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/></svg>''';
const _detailsSvg =
'''<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M10 6H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V8a2 2 0 00-2-2h-5m-4 0V5a2 2 0 114 0v1m-4 0a2 2 0 104 0m-5 8a2 2 0 100-4 2 2 0 000 4zm0 0c1.306 0 2.417.835 2.83 2M9 14a3.001 3.001 0 00-2.83 2M15 11h3m-3 4h2"/></svg>''';
const _addressSvg =
'''<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"/><path stroke-linecap="round" stroke-linejoin="round" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"/></svg>''';
const _paymentSvg =
'''<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"/></svg>''';
class Steps3Example extends StatelessWidget {
const Steps3Example({super.key});
@override
Widget build(BuildContext context) {
return Style(
'mx-auto max-w-3xl p-6',
child: Stack(
fit: StackFit.passthrough,
children: [
const Positioned(
top: 0,
left: 0,
child: IgnoreBaseline(child: Style('sr-only', child: Text('Steps'))),
),
Style(
'after:mt-4 after:block after:h-1 after:w-full after:rounded-lg after:bg-gray-200 dark:after:bg-gray-700',
child: Style(
'grid grid-cols-3 text-sm font-medium text-gray-600 dark:text-gray-300',
children: [
Style(
'relative flex justify-start text-blue-500',
children: [
Style(
'absolute start-0 -bottom-7 rounded-full bg-blue-500 text-white dark:text-gray-900',
child: Style(
'size-5',
child: CurrentColorIcon(_checkCircleSvg),
),
),
const Style(
'sr-only sm:not-sr-only sm:block',
child: Text('Details'),
),
Style(
'size-6 sm:hidden',
child: CurrentColorIcon(_detailsSvg),
),
],
),
Style(
'relative flex justify-center text-blue-500',
children: [
Style(
'absolute -bottom-7 left-1/2 -translate-x-1/2 rounded-full bg-blue-500 text-white dark:text-gray-900',
child: Style(
'size-5',
child: CurrentColorIcon(_checkCircleSvg),
),
),
const Style(
'sr-only sm:not-sr-only sm:block',
child: Text('Address'),
),
Style(
'mx-auto size-6 sm:hidden',
child: CurrentColorIcon(_addressSvg),
),
],
),
Style(
'relative flex justify-end',
children: [
Style(
'absolute end-0 -bottom-7 rounded-full bg-gray-500 text-white dark:bg-gray-400 dark:text-gray-900',
child: Style(
'size-5',
child: CurrentColorIcon(_checkCircleSvg),
),
),
const Style(
'sr-only sm:not-sr-only sm:block',
child: Text('Payment'),
),
Style(
'size-6 sm:hidden',
child: CurrentColorIcon(_paymentSvg),
),
],
),
],
),
),
],
),
);
}
}

Grouped with title and description, with highlighted current step

Section titled “Grouped with title and description, with highlighted current step”

Includes a dark: variant — toggle the site theme (top right) to preview it.

class CurrentColorIcon extends StatelessWidget
implements StyleReplacedElementWrapper {
const CurrentColorIcon(this.svg, {super.key});
final String svg;
@override
Widget build(BuildContext context) {
final color = IconTheme.of(context).color ?? const Color(0xFF000000);
return SvgPicture.string(svg, theme: SvgTheme(currentColor: color));
}
}
const _detailsSvg =
'''<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M10 6H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V8a2 2 0 00-2-2h-5m-4 0V5a2 2 0 114 0v1m-4 0a2 2 0 104 0m-5 8a2 2 0 100-4 2 2 0 000 4zm0 0c1.306 0 2.417.835 2.83 2M9 14a3.001 3.001 0 00-2.83 2M15 11h3m-3 4h2"/></svg>''';
const _addressSvg =
'''<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"/><path stroke-linecap="round" stroke-linejoin="round" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"/></svg>''';
const _paymentSvg =
'''<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"/></svg>''';
class Steps4Example extends StatelessWidget {
const Steps4Example({super.key});
@override
Widget build(BuildContext context) {
return Style(
'mx-auto max-w-3xl p-6',
child: Stack(
fit: StackFit.passthrough,
children: [
const Positioned(
top: 0,
left: 0,
child: IgnoreBaseline(
child: Style('sr-only', child: Text('Steps')),
),
),
Style(
'grid grid-cols-1 divide-x divide-gray-100 overflow-hidden rounded-lg border border-gray-100 text-sm text-gray-600 sm:grid-cols-3 dark:divide-gray-700 dark:border-gray-700 dark:text-gray-300',
children: [
Style(
'flex items-center justify-center gap-2 p-4',
children: [
Style(
'size-7 shrink-0',
child: CurrentColorIcon(_detailsSvg),
),
Style(
'leading-none',
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
const Style(
'block font-medium text-gray-900 dark:text-white',
child: Text('Details'),
),
const Style(
'mt-1',
display: DisplayType.inline,
textScaleDefault: 0.8,
child: Text('Some info about you.'),
),
],
),
),
],
),
Style(
'relative flex items-center justify-center gap-2 bg-gray-50 p-4 dark:bg-gray-800',
children: [
const Style(
'absolute top-1/2 -left-2 hidden size-4 -translate-y-1/2 rotate-45 border border-gray-100 sm:block ltr:border-s-0 ltr:border-b-0 ltr:bg-white rtl:border-e-0 rtl:border-t-0 rtl:bg-gray-50 dark:border-gray-700 dark:ltr:bg-gray-900 dark:rtl:bg-gray-800',
),
const Style(
'absolute top-1/2 -right-2 hidden size-4 -translate-y-1/2 rotate-45 border border-gray-100 sm:block ltr:border-s-0 ltr:border-b-0 ltr:bg-gray-50 rtl:border-e-0 rtl:border-t-0 rtl:bg-white dark:border-gray-700 dark:ltr:bg-gray-800 dark:rtl:bg-gray-900',
),
Style(
'size-7 shrink-0',
child: CurrentColorIcon(_addressSvg),
),
Style(
'leading-none',
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
const Style(
'block font-medium text-gray-900 dark:text-white',
child: Text('Address'),
),
const Style(
'mt-1',
display: DisplayType.inline,
textScaleDefault: 0.8,
child: Text('Where we sending it?'),
),
],
),
),
],
),
Style(
'flex items-center justify-center gap-2 p-4',
children: [
Style(
'size-7 shrink-0',
child: CurrentColorIcon(_paymentSvg),
),
Style(
'leading-none',
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
const Style(
'block font-medium text-gray-900 dark:text-white',
child: Text('Payment'),
),
const Style(
'mt-1',
display: DisplayType.inline,
textScaleDefault: 0.8,
child: Text('Show us the money.'),
),
],
),
),
],
),
],
),
],
),
);
}
}

Timeline with title, with highlighted current step

Section titled “Timeline with title, with highlighted current step”

Includes a dark: variant — toggle the site theme (top right) to preview it.

class Steps5Example extends StatelessWidget {
const Steps5Example({super.key});
@override
Widget build(BuildContext context) {
return Style(
'mx-auto max-w-3xl p-6',
child: Stack(
fit: StackFit.passthrough,
children: [
const Positioned(
top: 0,
left: 0,
child: IgnoreBaseline(child: Style('sr-only', child: Text('Steps'))),
),
Style(
'relative after:absolute after:inset-x-0 after:top-1/2 after:block after:h-0.5 after:-translate-y-1/2 after:rounded-lg after:bg-gray-200 dark:after:bg-gray-700',
child: Style(
'relative z-10 flex justify-between text-sm font-medium text-gray-600 dark:text-gray-300',
children: [
Style(
'flex items-center gap-2 bg-white p-2 dark:bg-gray-900',
children: [
const Style(
'size-6 rounded-full bg-gray-100 text-center text-[10px]/6 font-bold dark:bg-gray-800',
display: DisplayType.inline,
child: Text('1'),
),
const Style('hidden sm:block', child: Text('Details')),
],
),
Style(
'flex items-center gap-2 bg-white p-2 dark:bg-gray-900',
children: [
const Style(
'size-6 rounded-full bg-blue-500 text-center text-[10px]/6 font-bold text-white',
display: DisplayType.inline,
child: Text('2'),
),
const Style('hidden sm:block', child: Text('Address')),
],
),
Style(
'flex items-center gap-2 bg-white p-2 dark:bg-gray-900',
children: [
const Style(
'size-6 rounded-full bg-gray-100 text-center text-[10px]/6 font-bold dark:bg-gray-800',
display: DisplayType.inline,
child: Text('3'),
),
const Style('hidden sm:block', child: Text('Payment')),
],
),
],
),
),
],
),
);
}
}