awesome_stepper logo

awesome_stepper

Developer Challenge pub package

The beautiful Flutter stepper for multi-step forms, onboarding, checkout and wizard flows: an animated step progress indicator, sliding pages, form validation per step, a controller, and a side-by-side layout on tablets and foldables. Material 3, dark mode, no dependencies.

🏆 Award-winning: awesome_stepper won 1st place in the Autumn Flutter Package Development Challenge (2021), organized by Levent Kantaroğlu. Version 1.0 is a full redesign.

Flutter stepper with progress indicator, numbered steps and form validation

Live demo: monsware.com/awesome-stepper

Features of this Flutter stepper widget

  • 4 header styles: the classic progress circle, a progress bar, dots, numbered steps — plus a vertical stepper layout.
  • Standalone step progress indicator: AwesomeStepIndicator without pages.
  • Material 3: colors come from your theme; light and dark mode out of the box.
  • Smooth motion: pages slide and fade, the title cross-fades, the progress animates.
  • Validation: canContinue per step (sync or async) keeps the user on an invalid step and marks it red.
  • Step states: completed ✓, error !, locked 🔒 (disabled steps are skipped).
  • Controller: next(), back(), jumpTo(), listen to currentStep.
  • Large screens & foldables: from 840 px the circle and numbered headers become a side list; on foldables (Galaxy Z Fold, Pixel Fold, iPhone Duo) the split follows the fold.
  • Accessible: screen reader announces "Step 2 of 5: Payment", 48 px buttons, RTL, arrow keys, swipe, haptics.
  • No dependencies besides Flutter.

Install

dependencies:
  awesome_stepper: ^1.1.0

Usage: a multi-step form in Flutter

import 'package:awesome_stepper/awesome_stepper.dart';

AwesomeStepper(
  onCompleted: () => print('done'),
  steps: [
    AwesomeStepperItem(
      label: 'Address',
      subtitle: 'Where should we deliver?',
      content: AddressForm(key: formKey),
      canContinue: () => formKey.currentState!.validate(),
    ),
    const AwesomeStepperItem(label: 'Payment', content: PaymentPage()),
    const AwesomeStepperItem(label: 'Review', content: ReviewPage()),
  ],
)

Header styles

AwesomeStepper(headerType: AwesomeStepperHeader.numbered, steps: steps)
// circle (default) · bar · dots · numbered

Controller

final controller = AwesomeStepperController();

AwesomeStepper(controller: controller, steps: steps);

await controller.next();   // runs canContinue first
await controller.jumpTo(3);
controller.addListener(() => print(controller.currentStep));

Custom controls

AwesomeStepper(
  steps: steps,
  controlBuilder: (onNext, onBack) => Row(
    mainAxisAlignment: MainAxisAlignment.spaceBetween,
    children: [
      IconButton(onPressed: onBack, icon: const Icon(Icons.arrow_back)),
      IconButton(onPressed: onNext, icon: const Icon(Icons.arrow_forward)),
    ],
  ),
)

Vertical stepper

AwesomeStepper(
  layout: AwesomeStepperLayout.vertical,
  lineStyle: AwesomeStepperLineStyle.dashed,
  steps: steps, // content must size itself: Column, not ListView
)

Optional steps, completed screen, restoration

AwesomeStepper(
  restorationId: 'checkout',          // survives app restarts
  completedBuilder: (_) => const ThankYouPage(),
  steps: [
    AwesomeStepperItem(label: 'Extras', optional: true, content: extras), // Skip button
    ...
  ],
)

Just the indicator: AwesomeStepIndicator

AwesomeStepIndicator(
  type: AwesomeStepperHeader.bar,      // circle · bar · dots · numbered
  currentStep: 1,                      // 0-based
  labels: const ['Cart', 'Address', 'Payment'],
)

Coming from step_progress_indicator? StepProgressIndicator(totalSteps: 5, currentStep: 2) becomes AwesomeStepIndicator(totalSteps: 5, currentStep: 1) — currentStep is the 0-based index of the active step. You get labels, numbered steps, animations, dark mode and accessibility for free.

Theme for the whole app

ThemeData(extensions: const [
  AwesomeStepperTheme(accentColor: Colors.teal, buttonHeight: 52),
])

Translations

AwesomeStepper(
  nextLabel: 'Weiter',
  backLabel: 'Zurück',
  finishLabel: 'Fertig',
  semanticsLabel: (step, total, label) => 'Schritt $step von $total: $label',
  steps: steps,
)

Options

Parameter Default
headerType circle circle, bar, dots, numbered
headerColor / progressColor theme header background / accent
headerStyle / progressStyle theme title / counter text styles
headerHeight / controllerHeight 72 (88 numbered) / 48
contentAnimationDuration 300 ms page slide
headerAnimationDuration / progressBarAnimationDuration 250 ms / 1 s
swipeEnabled / keyboardEnabled / hapticsEnabled true
sideBySideMinWidth 840 null = never side by side
sideListWidth 300 when there is no fold
contentMaxWidth 720 page, buttons and header column on wide windows
layout horizontal horizontal, vertical
transition slide slide, fade, scale, none
lineStyle solid solid, dotted, dashed
skipLabel Skip shown on optional steps
allowHeaderJump true jump forward from the header
completedBuilder — screen after the last step
restorationId — keep the step across app restarts

Upgrading from 0.x

Existing code keeps working. The default look is now Material 3 (theme colors instead of white / red / green); pass headerColor and progressColor to keep your colors. AwesomeStepperItem is const, onStepChanged is ValueChanged<int>.

Keywords

Flutter stepper · step progress indicator · vertical stepper · horizontal stepper · wizard · multi-step form · onboarding flow · checkout flow · form validation · progress bar · Material 3 stepper.

Issues and ideas: GitHub.

About the author

Made with ❤️ by Muhammed Şamil Demir, Flutter & iOS developer and founder of Monsware. Free and open source (see LICENSE).

If this package saves you time, a ⭐ on GitHub and a 👍 on pub.dev help a lot.

Libraries

awesome_stepper
A customizable stepper with an animated progress header.