Flutter Stepper widget in detail

Aakash Pamnani
6 min read2 days ago

--

A Struggling Story: The Complex Form Journey

Meet John, an app developer who is passionate about creating user-friendly interfaces. One day, he faced a daunting challenge: building a multi-step form for a travel booking app. John wanted to guide users through each step of the booking process smoothly, ensuring they didn’t get overwhelmed by a long, single-page form. After trying several approaches, he felt lost and frustrated. Just when he was about to give up, he stumbled upon Flutter’s Stepper widget, which promised to be the perfect solution.

Introduction to Flutter Stepper Widget

The Stepper widget in Flutter is a lifesaver for developers like John. It allows you to create a step-by-step interface, ideal for processes that must be broken down into multiple stages. Each step can contain its content, making complex forms or workflows much more manageable and user-friendly.

Basic Structure of Stepper

Here’s a simple example to get you started:

import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: StepperExample(),
);
}
}

class StepperExample extends StatefulWidget {
@override
_StepperExampleState createState() => _StepperExampleState();
}

class _StepperExampleState extends State<StepperExample> {
int _currentStep = 0;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Stepper Example')),
body: Stepper(
currentStep: _currentStep,
onStepContinue: () {
if (_currentStep < 2) {
setState(() {
_currentStep += 1;
});
}
},
onStepCancel: () {
if (_currentStep > 0) {
setState(() {
_currentStep -= 1;
});
}
},
steps: [
Step(
title: Text('Step 1'),
content: Text('Complete this step first'),
isActive: _currentStep >= 0,
),
Step(
title: Text('Step 2'),
content: Text('Complete this step second'),
isActive: _currentStep >= 1,
),
Step(
title: Text('Step 3'),
content: Text('Complete this step third'),
isActive: _currentStep >= 2,
),
],
),
);
}
}

Parameters of the Stepper Widget

  1. steps: A list of Step widgets that define the content of each step.
  • title: The title of the step.
  • content: The content to display for this step.
  • isActive: Whether this step is active or not.

2. currentStep: An integer that keeps track of the current step.

3. onStepContinue: A callback is triggered when the “continue” button is pressed.

4. onStepCancel: A callback is triggered when the “cancel” button is pressed.

5. onStepTapped: A callback is triggered when a step is tapped.

6. controlsBuilder: A custom builder for the controls (buttons) at the bottom of each step.

7. type: Defines the type of the stepper, either horizontal or vertical. Possible values are StepperType.horizontal and StepperType.vertical.

Detailed Example with All Parameters

Let’s dive into a more detailed example that showcases all parameters:


import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: DetailedStepperExample(),
);
}
}

class DetailedStepperExample extends StatefulWidget {
@override
_DetailedStepperExampleState createState() => _DetailedStepperExampleState();
}

class _DetailedStepperExampleState extends State<DetailedStepperExample> {
int _currentStep = 0;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Detailed Stepper Example')),
body: Stepper(
type: StepperType.vertical,
currentStep: _currentStep,
onStepTapped: (step) {
setState(() {
_currentStep = step;
});
},
onStepContinue: () {
if (_currentStep < 3) {
setState(() {
_currentStep += 1;
});
}
},
onStepCancel: () {
if (_currentStep > 0) {
setState(() {
_currentStep -= 1;
});
}
},
steps: [
Step(
title: Text('Step 1: Personal Info'),
content: Column(
children: [
TextFormField(
decoration: InputDecoration(labelText: 'Name'),
),
TextFormField(
decoration: InputDecoration(labelText: 'Email'),
),
],
),
isActive: _currentStep >= 0,
),
Step(
title: Text('Step 2: Address'),
content: Column(
children: [
TextFormField(
decoration: InputDecoration(labelText: 'Address'),
),
TextFormField(
decoration: InputDecoration(labelText: 'City'),
),
],
),
isActive: _currentStep >= 1,
),
Step(
title: Text('Step 3: Payment Info'),
content: Column(
children: [
TextFormField(
decoration: InputDecoration(labelText: 'Credit Card Number'),
),
TextFormField(
decoration: InputDecoration(labelText: 'Expiry Date'),
),
],
),
isActive: _currentStep >= 2,
),
Step(
title: Text('Step 4: Review & Submit'),
content: Text('Review your details and submit'),
isActive: _currentStep >= 3,
),
],
controlsBuilder: (BuildContext context, ControlsDetails details) {
return Row(
children: <Widget>[
ElevatedButton(
onPressed: details.onStepContinue,
child: Text('NEXT'),
),
SizedBox(width: 8),
TextButton(
onPressed: details.onStepCancel,
child: Text('BACK'),
),
],
);
},
),
);
}
}

Bonus

You can use step state to mark the status of the step. There are 5 values of state [indexed, editing, complete, disabled, wrong, error]. You can use this enum to change the display behavior of each step.

Customizations

Customizing Step Icons

You can replace the default numeric indicators with custom icons:

 stepIconBuilder: (step, state) {
return Icon(state == StepState.indexed ? Icons.edit : Icons.close);
},

Step(
title: Text('Step 2'),
content: Text('Complete this step second'),
isActive: _currentStep >= 1,
state: _currentStep > 1 ? StepState.complete : StepState.indexed,

),

Customizing Colors and Styles

Customize the appearance of the Stepper by modifying the theme:

Stepper(
steps: _steps,
currentStep: _currentStep,
onStepContinue: _nextStep,
onStepCancel: _previousStep,
controlsBuilder: (BuildContext context, ControlsDetails details) {
return Row(
children: [
ElevatedButton(
onPressed: details.onStepContinue,
child: Text('NEXT'),
style: ElevatedButton.styleFrom(
primary: Colors.blue,
onPrimary: Colors.white,
),
),
SizedBox(width: 8),
TextButton(
onPressed: details.onStepCancel,
child: Text('BACK'),
style: TextButton.styleFrom(
primary: Colors.grey,
),
),
],
);
},
),

Adding Form Validation

Implement form validation to ensure data integrity before proceeding to the next step:

class _StepperExampleState extends State<StepperExample> {
int _currentStep = 0;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();

bool _validateForm() {
return _formKey.currentState?.validate() ?? false;
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Stepper Example with Validation')),
body: Form(
key: _formKey,
child: Stepper(
currentStep: _currentStep,
onStepContinue: () {
if (_validateForm()) {
if (_currentStep < 2) {
setState(() {
_currentStep += 1;
});
}
}
},
onStepCancel: () {
if (_currentStep > 0) {
setState(() {
_currentStep -= 1;
});
}
},
steps: [
Step(
title: Text('Step 1'),
content: TextFormField(
decoration: InputDecoration(labelText: 'Enter some text'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
isActive: _currentStep >= 0,
),
Step(
title: Text('Step 2'),
content: Text('Complete this step second'),
isActive: _currentStep >= 1,
),
Step(
title: Text('Step 3'),
content: Text('Complete this step third'),
isActive: _currentStep >= 2,
),
],
),
),
);
}
}

Real-World Use Cases

E-Commerce Checkout

Using the Stepper widget, you can create a streamlined checkout process:

Step(
title: Text('Billing Information'),
content: BillingForm(),
isActive: _currentStep >= 0,
),
Step(
title: Text('Shipping Address'),
content: ShippingForm(),
isActive: _currentStep >= 1,
),
Step(
title: Text('Payment Method'),
content: PaymentForm(),
isActive: _currentStep >= 2,
),
Step(
title: Text('Review Order'),
content: OrderReview(),
isActive: _currentStep >= 3,
),

Conclusion

In summary, the Flutter Stepper widget is a versatile and powerful tool that can transform complex processes into intuitive, step-by-step workflows. By leveraging its various parameters and customizations, you can create seamless user experiences for forms, checkouts, onboarding processes, and more. So, dive in and experiment with the Stepper widget to elevate your app’s usability and design!

I hope you found this article enjoyable! If you appreciate the information provided, you have the option to support me by Buying Me A Coffee! Your gesture would be greatly appreciated!

Follow Me

https://www.linkedin.com/in/aakashpamnani/

Thank you for taking the time to read this article. If you enjoyed it, feel free to explore more of my articles and consider following me for future updates. Your support is greatly appreciated!

--

--

Aakash Pamnani

Flutter, Java, developer. | Trying to explain concepts in the most straightforward way possible.