Flutter Stepper Widget Tutorial

The Flutter Stepper widget is a widget that shows the progress sequence through steps.

Stepper is used in forms. It helps us to improve the UX by dividing the UI components into different steps.

Let’s see how we can use Stepper widget inside Flutter.

Preview

How to create a stepper widget in flutter
Stepper Widget

Complete App Code:

The complete code is here, further explanation is available below.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'CodeWithHussain.com',
      theme: ThemeData(
        primarySwatch: Colors.indigo,
      ),
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  int currentStep = 0;
  continueStep() {
    if (currentStep < 2) {
      setState(() {
        currentStep = currentStep + 1; //currentStep+=1;
      });
    }
  }

  cancelStep() {
    if (currentStep > 0) {
      setState(() {
        currentStep = currentStep - 1; //currentStep-=1;
      });
    }
  }

  onStepTapped(int value) {
    setState(() {
      currentStep = value;
    });
  }

  Widget controlBuilders(context, details) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Row(
        children: [
          ElevatedButton(
            onPressed: details.onStepContinue,
            child: const Text('Next'),
          ),
          const SizedBox(width: 10),
          OutlinedButton(
            onPressed: details.onStepCancel,
            child: const Text('Back'),
          ),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stepper(
        elevation: 0, //Horizontal Impact
        // margin: const EdgeInsets.all(20), //vertical impact
        controlsBuilder: controlBuilders,
        type: StepperType.vertical,
        physics: const ScrollPhysics(),
        onStepTapped: onStepTapped,
        onStepContinue: continueStep,
        onStepCancel: cancelStep,
        currentStep: currentStep, //0, 1, 2
        steps: [
          Step(
              title: const Text('Step 1'),
              content: Column(
                children: const [
                  Text('This is the first step.'),
                ],
              ),
              isActive: currentStep >= 0,
              state:
                  currentStep >= 0 ? StepState.complete : StepState.disabled),
          Step(
            title: const Text('Step 2'),
            content: const Text('This is the Second step.'),
            isActive: currentStep >= 0,
            state: currentStep >= 1 ? StepState.complete : StepState.disabled,
          ),
          Step(
            title: const Text('Step 3'),
            content: const Text('This is the Third step.'),
            isActive: currentStep >= 0,
            state: currentStep >= 2 ? StepState.complete : StepState.disabled,
          ),
        ],
      ),
    );
  }
}

Explanation

You can create a stepper in your app by following these steps:

Step 1: Create a Stateful Widget


class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
//Here We create some state widgets
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: //Here We show Stepper
    );
  }
}

First, we create a Stateful widget. We create some state variables for controlling the state of the Stepper.

Step 2: Create a simple Stepper

Scaffold(
      body: Stepper(
        steps: [
          Step(
            title: const Text('Step 1'),
            content: Column(
              children: const [
                Text('This is the first step.'),
              ],
            ),
          ),
          Step(
            title: const Text('Step 2'),
            content: const Text('This is the Second step.'),
          ),
          Step(
            title: const Text('Step 3'),
            content: const Text('This is the Third step.'),
          ),
        ],
      ),
    );

Preview:

Stepper widget without any State
Stepper with disabled buttons

A basic stepper is showing on the screen. But each button is inactive.

Right now Step 1 is visible but we have no way to show Step 2 or Step 3.

Stepper has a few properties:

stepsWe use steps to show a list of Step widgets.
currentStepIndex of the Stepper current index. Start from 0. We can specify the index value here. Adding 1 here will expand the second step. (we create a state variable to dynamically change the index here)
onStepContinueWe can pass a function that will trigger when you click on the continue button.
onStepCancelThe function can be passed here which can be triggered when you click on the cancel button.
onStepTappedWhen you click on the Step tile, this function will execute. It receives the index value of the tile that you tap.
typeHere we can specify StepperType.vertical and StepperType.horizontal to make the stepper vertical and horizontal style respectively.
controlsBuilderThis is special. Here you also pass a function that gets context and details from the framework and returns a Widget that will appear at the bottom of a step. (Use for customizing action buttons).
physicsYou can specify the physics effect when your every step has many widgets and it becomes scrollable.
elevationThe horizontal stepper has elevation. double value can be used for adding elevation (shadow).
Flutter Stepper Widget Properties

Step 3: Create State and Methods For Stepper

Let’s see what we can do inside the Stateful widget.

currentStep variable

  int currentStep = 0;

This variable will make sure what we should display on the screen.

continueStep Method

  continueStep() {
    if (currentStep < 2) {
      setState(() {
        currentStep = currentStep + 1; //currentStep+=1;
      });
    }
  }

This method will be executed when the user press the continue button of the Stepper.

Also here we specify the logic that this button only performs its function if its index is less than the length of the Stepper.

When it runs its currentStep value will be increased by 1.

cancelStep Method

  cancelStep() {
    if (currentStep > 0) {
      setState(() {
        currentStep = currentStep - 1; //currentStep-=1;
      });
    }
  }

This method will execute when the user will press the cancel button of the Stepper.

Here we decrease the currentStep by 1 only when the index is greater than 0.

onStepTapped(int value) Method

  onStepTapped(int value) {
    setState(() {
      currentStep = value;
    });
  }

This method will receive the index integer value from the Stepper.

We simply assign that value to the currentStep and update the state.

controlBuilders(context, details) Method

  Widget controlBuilders(context, details) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Row(
        children: [
          ElevatedButton(
            onPressed: details.onStepContinue,
            child: const Text('Next'),
          ),
          const SizedBox(width: 10),
          OutlinedButton(
            onPressed: details.onStepCancel,
            child: const Text('Back'),
          ),
        ],
      ),
    );
  }

The method is used to replace the continue and Cancel buttons with custom-designed Next and Back buttons.

Note: Make sure you add the Row widget surrounding these buttons otherwise border position will be disturbed.

Step 4: Assign Methods to the Stepper

Scaffold(
      body: Stepper(
        elevation: 0, //Horizontal Impact
        // margin: const EdgeInsets.all(20), //vertical impact
        controlsBuilder: controlBuilders,
        type: StepperType.vertical,
        physics: const ScrollPhysics(),
        onStepTapped: onStepTapped,
        onStepContinue: continueStep,
        onStepCancel: cancelStep,
        currentStep: currentStep, //0, 1, 2
        steps: [
          /Here we specify the steps
        ],
      ),
    );

We just assign the all defined method to the stepper widget.

Step 5: Change State of Each Step

Step(
    title: const Text('Step 1'),
    content: Column(
      children: const [
        Text('This is the first step.'),
      ],
    ),
    isActive: currentStep >= 0,
    state:
        currentStep >= 0 ? StepState.complete : StepState.disabled),
Step(
  title: const Text('Step 2'),
  content: const Text('This is the Second step.'),
  isActive: currentStep >= 0,
  state: currentStep >= 1 ? StepState.complete : StepState.disabled,
),
Step(
  title: const Text('Step 3'),
  content: const Text('This is the Third step.'),
  isActive: currentStep >= 0,
  state: currentStep >= 2 ? StepState.complete : StepState.disabled,
),

Step widget has two most important properties:

isActive: true and false value (or condition that return true or false) can be specified

state: will receive a StepState value which is an enum value that you can give here.

StepState.indexedDisplay index
StepState.editingDisplay pencil icon
StepState.completeDisplay a tick icon
StepState.disabledDisabled step (can’t be tapped)
StepState.errorIndicate error
StepState for Stepper

Note: You can see the complete code at the top. For more detail, you can also visit official docs.

Conclusion

In this article, you learn how you can create a Stepper widget in Flutter with an example.

See more:

I hope you like this flutter tutorial. Thanks!

Hussain Humdani

Hussain Humdani

while ( ! ( succeed = try() ) );