Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Keep state in Stepper

Tags:

flutter

I have multiple steps in my Stepper. How do I persist the state of the Step when moving between each Step?

I have tried adding the AutomaticKeepAliveClientMixin, but it still does not keep the state:

class _MyHomePageState extends State<MyHomePage> with AutomaticKeepAliveClientMixin<MyHomePage> {
  int currentStep = 0;
  List<Step> stepList = [
    Step(
      title: Text("Page A"),
      content: Column(
        children: <Widget>[
          Text("Page A"),
          TextField(
            decoration: InputDecoration(
              border: InputBorder.none,
              hintText: "Enter anything"
            ),
          ),
        ],
      )
    ),
    Step(
      title: Text("Page B"),
      content: Text("Page B")
    ),
    Step(
      title: Text("Page C"),
      content: Text("Page C")
    ),
    Step(
      title: Text("Page D"),
      content: Text("Page D")
    ),
    Step(
      title: Text("Page E"),
      content: Text("Page ")
    ),
  ];

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Scaffold(
      body: Stepper(
        steps: stepList,
        type: StepperType.horizontal,
        currentStep: currentStep,
        onStepContinue: nextStep,
        onStepCancel: cancelStep,
      ),
    );
  }

  void nextStep(){
    setState(() {
      if(currentStep < stepList.length - 1)
        currentStep++;
    });
  }

  void cancelStep(){
    setState(() {
      if(currentStep > 0)
      currentStep--;
    });
  }

  @override
  bool get wantKeepAlive => true;
}

If I add anything to the text field, navigate to PageB, then navigate back to PageA, the text field would reset to empty back.

How do I keep state for each Step's "page"?

Edit: Probably should've disclosed this earlier. I have 5 Steps, with each Step containing 8-12 fields consisting of textfields, checkboxes, dropdowns, etc. It's a multi step form. I know you can create a class level TextFieldController to have a "global" variable to keep track of the TextField's state in the Step, but it would mean I need ~50 class level variables, the code would look too convoluted. That is why I was using AutomaticKeepAliveClientMixin, but it doesn't work. Is there a better way to handle this?

like image 391
Farid Avatar asked Apr 24 '26 14:04

Farid


1 Answers

What's happening is your TextField is getting rebuild when you are navigating to it from another Step and so value is getting reset.

Solution:

  1. Convert your List<Step> to get that returns List<Step> like List<Step> get stepList => [
  2. Reason to do this is to make your list able to access global variables.
  3. Create a TextEditingController as global variable: TextEditingController textEditingController = TextEditingController();
  4. Give that controller to your TextField like follows:

      TextField(
        controller: textEditingController,
        decoration: InputDecoration(
          border: InputBorder.none,
          hintText: "Enter anything"
        ),
      ),
    

Now what will happen is that as you have the TextEditingController , whenever your TextField will rebuild it will use the controller to get the value so, whenever you are switching between Steps your TextField value won't reset.

I have edited your code, following is the code with the above mentioned changes:

class _MyHomePageState extends State<MyHomePage> with AutomaticKeepAliveClientMixin<MyHomePage> {
  int currentStep = 0;

  TextEditingController textEditingController = TextEditingController();

  List<Step> get stepList => [
    Step(
      title: Text("Page A"),
      content: Column(
        children: <Widget>[
          Text("Page A"),
          TextField(
            controller: textEditingController,
            decoration: InputDecoration(
              border: InputBorder.none,
              hintText: "Enter anything"
            ),
          ),
        ],
      )
    ),
    Step(
      title: Text("Page B"),
      content: Text("Page B")
    ),
    Step(
      title: Text("Page C"),
      content: Text("Page C")
    ),
    Step(
      title: Text("Page D"),
      content: Text("Page D")
    ),
    Step(
      title: Text("Page E"),
      content: Text("Page ")
    ),
  ];

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Scaffold(
      body: Stepper(
        steps: stepList,
        type: StepperType.horizontal,
        currentStep: currentStep,
        onStepContinue: nextStep,
        onStepCancel: cancelStep,
      ),
    );
  }

  void nextStep(){
    setState(() {
      if(currentStep < stepList.length - 1)
        currentStep++;
    });
  }

  void cancelStep(){
    setState(() {
      if(currentStep > 0)
      currentStep--;
    });
  }

  @override
  bool get wantKeepAlive => true;
}

I hope this helps you, in case of any doubt please comment. In case this answer helps you, please accept and up-vote it.

like image 52
Kalpesh Kundanani Avatar answered Apr 27 '26 18:04

Kalpesh Kundanani