Progress Bars
Ported from HyperUI’s Progress Bars — see Attribution.
class ProgressBars1Example extends StatelessWidget { const ProgressBars1Example({super.key});
@override Widget build(BuildContext context) { return Style( 'mx-auto max-w-md p-6', children: [ const Style('text-sm font-medium text-gray-900', child: Text('25%')), Style( 'mt-2 h-2 w-full rounded-full bg-gray-200', child: FractionallySizedBox( widthFactor: 0.25, alignment: AlignmentDirectional.centerStart, child: Style('h-full rounded-full bg-blue-600'), ), ), ], ); }}<div role="progressbar" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100" aria-label="Progress"> <p class="text-sm font-medium text-gray-900">25%</p>
<div class="mt-2 h-2 w-full rounded-full bg-gray-200"> <div class="h-full rounded-full bg-blue-600" style="width: 25%"></div> </div></div>With labels and status colors
Section titled “With labels and status colors”class ProgressBars2Example extends StatelessWidget { const ProgressBars2Example({super.key});
@override Widget build(BuildContext context) { return Style( 'mx-auto max-w-md space-y-6 p-6', children: [ Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const Style( 'flex justify-between gap-4', children: [ Style( 'text-sm font-medium text-gray-900', child: Text('Updating'), ), Style( 'text-sm font-medium text-gray-900', child: Text('25%'), ), ], ), Style( 'mt-2 h-2 w-full rounded-full bg-gray-200', child: FractionallySizedBox( widthFactor: 0.25, alignment: AlignmentDirectional.centerStart, child: Style('h-full rounded-full bg-blue-600'), ), ), ], ), Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const Style( 'flex justify-between gap-4', children: [ Style( 'text-sm font-medium text-gray-900', child: Text('Downloading'), ), Style( 'text-sm font-medium text-gray-900', child: Text('100%'), ), ], ), Style( 'mt-2 h-2 w-full rounded-full bg-gray-200', child: FractionallySizedBox( widthFactor: 1, alignment: AlignmentDirectional.centerStart, child: Style('h-full rounded-full bg-green-600'), ), ), ], ), Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const Style( 'flex justify-between gap-4', children: [ Style( 'text-sm font-medium text-gray-900', child: Text('Loading'), ), Style( 'text-sm font-medium text-gray-900', child: Text('50%'), ), ], ), Style( 'mt-2 h-2 w-full rounded-full bg-gray-200', child: FractionallySizedBox( widthFactor: 0.5, alignment: AlignmentDirectional.centerStart, child: Style('h-full rounded-full bg-gray-600'), ), ), ], ), ], ); }}<div role="progressbar" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100" aria-labelledby="UpdatingLabel"> <div class="flex justify-between gap-4"> <span id="UpdatingLabel" class="text-sm font-medium text-gray-900">Updating</span>
<span class="text-sm font-medium text-gray-900">25%</span> </div>
<div class="mt-2 h-2 w-full rounded-full bg-gray-200"> <div class="h-full rounded-full bg-blue-600" style="width: 25%"></div> </div></div>
<div role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" aria-labelledby="DownloadingLabel"> <div class="flex justify-between gap-4"> <span id="DownloadingLabel" class="text-sm font-medium text-gray-900">Downloading</span>
<span class="text-sm font-medium text-gray-900">100%</span> </div>
<div class="mt-2 h-2 w-full rounded-full bg-gray-200"> <div class="h-full rounded-full bg-green-600" style="width: 100%"></div> </div></div>
<div role="progressbar" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100" aria-labelledby="LoadingLabel"> <div class="flex justify-between gap-4"> <span id="LoadingLabel" class="text-sm font-medium text-gray-900">Loading</span>
<span class="text-sm font-medium text-gray-900">50%</span> </div>
<div class="mt-2 h-2 w-full rounded-full bg-gray-200"> <div class="h-full rounded-full bg-gray-600" style="width: 50%"></div> </div></div>Compact with metadata
Section titled “Compact with metadata”class ProgressBars3Example extends StatelessWidget { const ProgressBars3Example({super.key});
@override Widget build(BuildContext context) { return Style( 'mx-auto max-w-md space-y-6 p-6', children: [ Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const Style( 'text-xs font-medium tracking-wide text-gray-700 uppercase', child: Text('Download'), ), Style( 'mt-2 h-1 w-full bg-gray-200', child: FractionallySizedBox( widthFactor: 0.25, alignment: AlignmentDirectional.centerStart, child: Style('h-full bg-blue-600'), ), ), const Style( 'mt-2 text-xs text-gray-700', child: Text('1.2 of 3.8 MB'), ), ], ), Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const Style( 'text-xs font-medium tracking-wide text-gray-700 uppercase', child: Text('File Conversion'), ), Style( 'mt-2 h-1 w-full bg-gray-200', child: FractionallySizedBox( widthFactor: 0.75, alignment: AlignmentDirectional.centerStart, child: Style('h-full bg-blue-600'), ), ), const Style( 'mt-2 text-xs text-gray-700', child: Text('3/4 files done'), ), ], ), Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const Style( 'text-xs font-medium tracking-wide text-gray-700 uppercase', child: Text('File Sync'), ), Style( 'mt-2 h-1 w-full bg-gray-200', child: FractionallySizedBox( widthFactor: 1, alignment: AlignmentDirectional.centerStart, child: Style('h-full bg-green-600'), ), ), const Style( 'mt-2 text-xs text-gray-700', child: Text('Completed'), ), ], ), ], ); }}<div role="progressbar" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100" aria-labelledby="DownloadLabel"> <p id="DownloadLabel" class="text-xs font-medium tracking-wide text-gray-700 uppercase"> Download </p>
<div class="mt-2 h-1 w-full bg-gray-200"> <div class="h-full bg-blue-600" style="width: 25%"></div> </div>
<p class="mt-2 text-xs text-gray-700">1.2 of 3.8 MB</p></div>
<div role="progressbar" aria-valuenow="75" aria-valuemin="0" aria-valuemax="100" aria-labelledby="FileConversionLabel"> <p id="FileConversionLabel" class="text-xs font-medium tracking-wide text-gray-700 uppercase"> File Conversion </p>
<div class="mt-2 h-1 w-full bg-gray-200"> <div class="h-full bg-blue-600" style="width: 75%"></div> </div>
<p class="mt-2 text-xs text-gray-700">3/4 files done</p></div>
<div role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" aria-labelledby="FileSyncLabel"> <p id="FileSyncLabel" class="text-xs font-medium tracking-wide text-gray-700 uppercase"> File Sync </p>
<div class="mt-2 h-1 w-full bg-gray-200"> <div class="h-full bg-green-600" style="width: 100%"></div> </div>
<p class="mt-2 text-xs text-gray-700">Completed</p></div>Circular progress
Section titled “Circular progress”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)); }}
class ProgressBars4Example extends StatelessWidget { const ProgressBars4Example({super.key});
@override Widget build(BuildContext context) { return Style( 'mx-auto flex max-w-3xl flex-wrap gap-8 p-6', children: [ Style( 'text-center', child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [ Style( 'relative mx-auto size-32', children: [ Style( 'size-full', child: Stack( children: [ const Positioned.fill( child: Style( 'text-gray-200', child: CurrentColorIcon(_trackCircleSvg), ), ), Positioned.fill( child: Style( 'origin-center text-blue-600', child: CurrentColorIcon( _arcSvg(endX: 95, endY: 50, largeArcFlag: 0), ), ), ), ], ), ), Style( 'absolute inset-0 grid place-content-center', child: const Style( 'text-xl font-semibold text-gray-900', child: Text('25%'), ), ), ], ), const Style('mt-2 text-sm text-gray-700', child: Text('Loading')), ], ), ), Style( 'text-center', child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [ Style( 'relative mx-auto size-32', children: [ Style( 'size-full', child: Stack( children: [ const Positioned.fill( child: Style( 'text-gray-200', child: CurrentColorIcon(_trackCircleSvg), ), ), Positioned.fill( child: Style( 'origin-center text-green-600', child: CurrentColorIcon( _arcSvg(endX: 5, endY: 50, largeArcFlag: 1), ), ), ), ], ), ), Style( 'absolute inset-0 grid place-content-center', child: const Style( 'text-xl font-semibold text-gray-900', child: Text('75%'), ), ), ], ), const Style( 'mt-2 text-sm text-gray-700', child: Text('Nearly done'), ), ], ), ), ], ); }}
const _trackCircleSvg = '''<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" aria-hidden="true"><circle cx="50" cy="50" r="45" fill="none" stroke="currentColor" stroke-width="8"/></svg>''';
String _arcSvg({ required double endX, required double endY, required int largeArcFlag,}) => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" ' 'aria-hidden="true"><path d="M50 5A45 45 0 $largeArcFlag 1 $endX $endY" ' 'fill="none" stroke="currentColor" stroke-width="8" ' 'stroke-linecap="round"/></svg>';<div class="text-center"> <div class="relative mx-auto size-32" role="progressbar" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100" aria-labelledby="LoadingCircleLabel" > <svg class="size-full" viewBox="0 0 100 100" aria-hidden="true"> <circle cx="50" cy="50" r="45" fill="none" stroke="currentColor" stroke-width="8" class="text-gray-200" />
<circle cx="50" cy="50" r="45" fill="none" stroke="currentColor" stroke-width="8" stroke-dasharray="70.7 282.7" stroke-linecap="round" class="origin-center text-blue-600" style="transform: rotate(-90deg)" /> </svg>
<div class="absolute inset-0 grid place-content-center"> <span class="text-xl font-semibold text-gray-900">25%</span> </div> </div>
<p id="LoadingCircleLabel" class="mt-2 text-sm text-gray-700">Loading</p></div>
<div class="text-center"> <div class="relative mx-auto size-32" role="progressbar" aria-valuenow="75" aria-valuemin="0" aria-valuemax="100" aria-labelledby="NearlyDoneCircleLabel" > <svg class="size-full" viewBox="0 0 100 100" aria-hidden="true"> <circle cx="50" cy="50" r="45" fill="none" stroke="currentColor" stroke-width="8" class="text-gray-200" />
<circle cx="50" cy="50" r="45" fill="none" stroke="currentColor" stroke-width="8" stroke-dasharray="212.1 282.7" stroke-linecap="round" class="origin-center text-green-600" style="transform: rotate(-90deg)" /> </svg>
<div class="absolute inset-0 grid place-content-center"> <span class="text-xl font-semibold text-gray-900">75%</span> </div> </div>
<p id="NearlyDoneCircleLabel" class="mt-2 text-sm text-gray-700">Nearly done</p></div>