In your application, you might have a sequence of actions that the user has to perform consequently. For example, completing an order needs a sequence of steps including selecting items from cart, entering shipping info, payment, and order confirmation. Therefore, you’d want to display the user’s progression in a beautiful and organized way. Here comes the time to think of using Stepper. It’s super useful when you have forms where one step requires the completion of another.
In this article, we’ll together go through a Flutter Stepper example in depth. we’ll explore it and learn how to implement it in Flutter apps.
Table of Contents
Brief Overview
Stepper is a material widget that’s used to show progress for a sequence of steps. It can also be used when you want to divide a process into sections to ease the steps for users. It’s commonly used when sequential steps require the completion of each other such as registration and ordering processes.
Stepper constructor contains useful properties and functions which control its appearance and behavior. The table below shows Flutter Stepper constructor:
steps | takes a list of Step widgets where each Step has its own title, subtitle, content, and icon. |
physics | specifies the physics of the Stepper |
type | determines whether the stepper is horizontal or vertical |
currentStep | takes an integer value that represents the index of the current step |
onStepTapped | determines what happen when the user tap a step |
onStepContinue | determines what happen when the user clicks the button Continue |
onStepCancel | determines what happen when the user clicks the buttton Cancel |
controlsBuilder | creates custom controls for Continue and Cancel controls. |
elevation | specifies the elevation (shadow) of the stepper when its in horizontal form |
margin | specifies the margin for the vertical stepper |
Flutter Stepper Widget Example
Let’s start implementing the Stepper example by creating a new file stepper.dart
. Inside this file, we’ll create a stateful widget that I called StepperEx
. Inside the build method, replace the returned Widget Container with Scaffold widget. Then, create an app bar just to enhance the appearance of the UI (you can skip this step).
appBar: AppBar(
title: const Text('Stepper Example'),
centerTitle: true,
),
After that, create the Stepper widget in the body of the Scaffold
body: Stepper()
Next, we’ll create two variables that we’ll use to control the Stepper. One variable is of type integer to keep track of the current step (_index) and another variable of type bool to indicate if the steps are completed (_complete)
int _index = 0;
bool _complete = false;
Now, we need to define the property currentStep
and assign the variable _index
to it to keep track of the current active step
currentStep: _index,
Create The Steps
For the purpose of keeping the code tidy and organized, let’s create the steps separately by declaring a list of type Step and create the required steps. For this example, we’ll be creating 3 steps.
List<Step> createSteps() => [
//steps
];
Let’s create the first step to enter the account details (Email and password). the property isActive
is to determine when this step is active so it becomes highlighted with the theme color. In this case, we’ll be using the variable _index
to indicate the step active state. The property state
is to indicate the state of the step to determine its styling (icon) based on its current state. When the user clicks Next, the step will be forward to the next step and the icon will change to complete shape.
Step(
isActive: _index >= 0,
title: Text('Account'),
subtitle: Text('Enter you Email and Password'),
state: _index > 0 ? StepState.complete : StepState.indexed,
content: Column(
children: [
TextFormField(
decoration: InputDecoration(labelText: 'Email'),
),
TextFormField(
decoration: InputDecoration(labelText: 'Password'),
),
],
)
),
Next, we’ll create the second step which will be used to enter the phone number
Step(
title: Text('Phone'),
subtitle: Text('Enter your phone number'),
isActive: _index >= 1,
state: _index > 0 ? StepState.complete : StepState.indexed,
content: Column(
children: [
TextFormField(
decoration: InputDecoration(labelText: 'Phone'),
),
],
)
),
Finally, we’ll create the third step to enter the address details
Step(
title: Text('Address'),
subtitle: Text('Enter your address'),
isActive: _index >= 2,
state: _index > 0 ? StepState.complete : StepState.indexed,
content: Column(
children: [
TextFormField(
decoration: InputDecoration(labelText: 'Address'),
),
TextFormField(
decoration: InputDecoration(labelText: 'Postcode'),
),
],
)
),
Now, let’s assign the list we created to the stepper steps property
steps: createSteps(),
Impement onStepContinue and onStepCancel methods
These methods determines what happens when the user clicks on the buttons Next and Cancel. Everytime the user clicks one of these buttons, we need to update the _index
variable to match the current active step.
Let’s first implement the onStepCancel method. When the user clicks on the Cancel button, we need to decrease the index by 1 as long as the index is larger than 0. It’s necessary to add this condition to prevent any error if the user clicks the cancel button in the first step.
onStepCancel: () {
if(_index > 0) {
setState(() {
_index -= 1;
});
}
},
Next, let’s implement the onStepContinue method. When the user clicks on the Next button, we need to increase the index by 1 as long as the index is less than the number of total steps. For this reason, we need to create a variable called _isFinalStep which is used to check if the current step is the last step within the Stepper. If it’s the last step, then we need to set _complete
to true to indicate that the steps are completed. Otherwise, we increase the _index
by 1
onStepContinue: () {
final isLastStep = _index == createSteps().length - 1;
if (isLastStep) {
print('Completed all steps');
setState(() {
_complete = true;
});
} else {
setState(() {
_index += 1;
});
}
},
Finally, let’s implement the onStepTapped method which returns the current index in the Stepper. We’ll use the returned index value and assign it to the _index
variable that we defined.
onStepTapped: (int index) {
setState(() {
_index = index;
});
}
Flutter Stepper Custom Button
By default, Flutter Stepper has two control buttons which are Next and Cancel. However, you can create custom controls using the property controlsBuiler
. We can use this properties to create controls with special text and design to meet our requirements. For example, you can choose to hide the cancel button for the first step and define a special button for the last step. In addition, we’ll hide the back button for the first step.
controlsBuilder: (BuildContext context, ControlsDetails details) {
return Container(
margin: EdgeInsets.only(top: 40),
child:Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: details.onStepContinue,
child: _index < createSteps().length ? Text('NEXT') : Text('Finish'),
),
),
SizedBox(width: 20,),
if(_index != 0)
Expanded(
child: ElevatedButton(
onPressed: details.onStepCancel,
child: Text('BACK'),
style: ElevatedButton.styleFrom(primary: Colors.grey.shade400)
),
),
],
),
);
},
Flutter Stepper Horizontal Example
As a matter of fact, the default orientation for Flutter Stepper is vertical layout. At the same time, you can change this orientation using the property type
. To achieve that, we’ll add a floatingActionButton
to the scaffold to enable switching the Stepper type when the user clicks on it. Note that this is only to demonstrate the way to do that and is not a necessary step for real applications.
Let’s first define a StepperType
variable called _type
with the value StepperType.vertical
assigned to it.
StepperType _type = StepperType.vertical;
Then, let’s add the floatingActionButton
to the Scaffold widget. inside the onPressed
function, we’ll check if the value of _type
is vertical and change it to horizontal and vice versa. Unfortunately, horizontal orientation may cause a render overflow and Flutter doesn’t have a solution for this issue. It seems like the only way to fix this is by building a custom horizontal Stepper.
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
_type == StepperType.vertical ? _type = StepperType.horizontal :_type = StepperType.vertical ;
});
},
child: Text('Switch'),
),
Changing the Stepper Color
Flutter Stepper inherits the color of the app theme color by default. However, you can attempt to change this by wrapping the Stepper with a Theme
widget and assign its color separately. So without further ado, let’s do it
Simply, wrap the Stepper widget with Theme
widget then assign the following values to it.
Theme(
data: Theme.of(context).copyWith(
colorScheme: ColorScheme.light(primary: Colors.red.shade600)
),
Action After Completing the Steps
In this example, we’ll use the _complete
variable to indicate if the steps are completed. if it’s true, we’ll display a differnt widget with a success message; otherwise we’ll keep the stepper. Alternatively, you can display a dialog box or direct the user to another screen. To achieve this, we’ll check the value of variable at the beginning when the body of the Scaffold is built
body: _complete?
Container(
child: Center(
child: Text(
'Success!',
style: TextStyle(
fontSize: 50,
color: Colors.pink.shade500
),
),
),
)
:Theme(// rest of code)
Output

Conclusion
This brings an end to Flutter Stepper example tutorial where we learnt all about the Stepper widget. You can get the source code from here.
As mentioned above, Stepper widget is useful in different scenarios when the user needs to perform a sequnce of steps.
like & share my facebook page. Subscribe to newsletter if you find this article helpful. Thank you for your time, I hope you enjoyed this tutorial!
Wanna learn more about Flutter? I have category called Flutter that’s ripe for you to explore. You can learn about Flutter widgets, layouts, onboarding screen, tabbar, inkwell and gesture detector, and practice with a bunch of real examples.
Also, don’t forget to like and share my facebook page, and subscribe to my blog to be one of the firsts to know about my newest posts!