skip to Main Content

I am using the following code to create a simple multi-steps question-answer form, but it seems forms can not hold their values and when I go to the second form and come back, the textFormField values are gone.

"Create.dart":

class Create with ChangeNotifier {
  String? title;
  String? description;

  int activeIndex = 0;
  int totalIndex = 6;

  changeStep(int index) {
    activeIndex = index;
    notifyListeners();
  }
}

"handler.dart":

class FormCreateHandler extends StatefulWidget {
  const FormCreateHandler({super.key});
  static const routeName = '/-form-handler';

  @override
  State<FormCreateHandler> createState() =>
      _FormCreateHandlerState();
}

class _FormCreateHandlerState extends State<FormCreateHandler> {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<Create>(
      create: (context) => Create(),
      child: Consumer<Create>(
        builder: (context, modal, child) {
          switch (modal.activeIndex) {
            case 0:
              return const FormQuestion1();
            case 1:
              return const FormQuestion2();
            default:
              return const FormQuestion1();
          }
        },
      ),
    );
  }
}

"form-question1.dart":

class FormQuestion1 extends StatefulWidget {
  const FormQuestion1({super.key});
  static const routeName = '/form-question1';

  @override
  State<FormQuestion1> createState() => _FormQuestion1State();
}

class _FormQuestion1State extends State<FormQuestion1> {
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return Consumer<Create>(builder: (context, modal, child) {
      return Form(
        key: _formKey,
        child: SingleChildScrollView(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              TextFormField(
                decoration: const InputDecoration(
                  labelText: 'title',
                  border: OutlineInputBorder(),
                ),
                validator: (value) {
                  if (value!.isEmpty)
                    return 'Please enter the title of question';
                },
              ),
              TextFormField(
                minLines: 3,
                maxLines: 5,
                decoration: const InputDecoration(
                  labelText: 'description',
                  border: OutlineInputBorder(),
                ),
                validator: (value) {
                  if (value!.isEmpty || value.length < 30) {
                    return 'Description must be longer';
                  }
                },
              ),
              Padding(
                padding: EdgeInsets.all(10),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Expanded(
                      flex: 1,
                      child: ElevatedButton(
                        onPressed: () {
                          if (_formKey.currentState?.validate() ?? false) {
                            _formKey.currentState!.save();
                            modal.changeStep(2);
                          }
                        },
                        child: Text('Back'),
                      ),
                    ),
                    Expanded(
                      flex: 2,
                      child: ElevatedButton(
                        onPressed: null, 
                        child: Text('Submit'),
                      ),
                    ),
                    Expanded(
                      flex: 1,
                      child: ElevatedButton(
                        onPressed: () {
                          if (_formKey.currentState?.validate() ?? false) {
                            _formKey.currentState!.save();
                            modal.changeStep(4);
                          }
                        },
                        child: Text('Next'),
                      ),
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      );
    });
  }
}

"form-question2.dart"

class FormQuestion2 extends StatefulWidget {
  const FormQuestion2({super.key});
  static const routeName = '/form-question2';

  @override
  State<FormQuestion2> createState() => _FormQuestion2State();
}

class _FormQuestion2State extends State<FormQuestion2> {
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return Consumer<Create>(builder: (context, modal, child) {
      return Form(
        key: _formKey,
        child: SingleChildScrollView(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              TextFormField(
                decoration: const InputDecoration(
                  labelText: 'title',
                  border: OutlineInputBorder(),
                ),
                validator: (value) {
                  if (value!.isEmpty)
                    return 'Please enter the title of question';
                },
              ),
              TextFormField(
                minLines: 3,
                maxLines: 5,
                decoration: const InputDecoration(
                  labelText: 'description',
                  border: OutlineInputBorder(),
                ),
                validator: (value) {
                  if (value!.isEmpty || value.length < 30) {
                    return 'Description must be longer';
                  }
                },
              ),
              Padding(
                padding: EdgeInsets.all(10),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Expanded(
                      flex: 1,
                      child: ElevatedButton(
                        onPressed: () {
                          if (_formKey.currentState?.validate() ?? false) {
                            _formKey.currentState!.save();
                            modal.changeStep(2);
                          }
                        },
                        child: Text('Back'),
                      ),
                    ),
                    Expanded(
                      flex: 2,
                      child: ElevatedButton(
                        onPressed: null, 
                        child: Text('Submit'),
                      ),
                    ),
                    Expanded(
                      flex: 1,
                      child: ElevatedButton(
                        onPressed: () {
                          if (_formKey.currentState?.validate() ?? false) {
                            _formKey.currentState!.save();
                            modal.changeStep(4);
                          }
                        },
                        child: Text('Next'),
                      ),
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      );
    });
  }
}

How can I modify this code to be able to hold the state of the forms until the user either presses the Submit button or closes the form?

2

Answers


  1. Chosen as BEST ANSWER

    Based on the other answer by @Aks, I came from a new idea to modify my question forms like below:

    "form-question1.dart":

    class FormQuestion1 extends StatefulWidget {
      const FormQuestion1({super.key});
      static const routeName = '/form-question1';
    
      @override
      State<FormQuestion1> createState() => _FormQuestion1State();
    }
    
    class _FormQuestion1State extends State<FormQuestion1> {
      final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
    
      @override
      Widget build(BuildContext context) {
        return Consumer<Create>(builder: (context, modal, child) {
          return Form(
            key: _formKey,
            child: SingleChildScrollView(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  TextFormField(
                    decoration: const InputDecoration(
                      labelText: 'title',
                      border: OutlineInputBorder(),
                    ),
                    validator: (value) {
                      if (value!.isEmpty)
                        return 'Please enter the title of question';
                    },
    
                initialValue:
                    Provider.of<Create>(context, listen: false).title,
    
                onSaved: (value) {
                  Provider.of<Create>(context, listen: false).title =
                      value;
                },
                  ),
                  TextFormField(
                    minLines: 3,
                    maxLines: 5,
                    decoration: const InputDecoration(
                      labelText: 'description',
                      border: OutlineInputBorder(),
                    ),
                    validator: (value) {
                      if (value!.isEmpty || value.length < 30) {
                        return 'Description must be longer';
                      }
                    },
    
                initialValue:
                    Provider.of<Create>(context, listen: false).title,
    
                onSaved: (value) {
                  Provider.of<Create>(context, listen: false).title =
                      value;
                },
                  ),
                  Padding(
                    padding: EdgeInsets.all(10),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Expanded(
                          flex: 1,
                          child: ElevatedButton(
                            onPressed: () {
                              if (_formKey.currentState?.validate() ?? false) {
                                _formKey.currentState!.save();
                                modal.changeStep(2);
                              }
                            },
                            child: Text('Back'),
                          ),
                        ),
                        Expanded(
                          flex: 2,
                          child: ElevatedButton(
                            onPressed: null, 
                            child: Text('Submit'),
                          ),
                        ),
                        Expanded(
                          flex: 1,
                          child: ElevatedButton(
                            onPressed: () {
                              if (_formKey.currentState?.validate() ?? false) {
                                _formKey.currentState!.save();
                                modal.changeStep(4);
                              }
                            },
                            child: Text('Next'),
                          ),
                        ),
                      ],
                    ),
                  ),
                ],
              ),
            ),
          );
        });
      }
    }
    

    The modifications are adding following initialValue and onSaved() properties to each textFormField:

            initialValue:
                Provider.of<Create>(context, listen: false).title,
    
            onSaved: (value) {
              Provider.of<Create>(context, listen: false).title =
                  value;
            },
    

    But as I am not sure if this is the best way of doing that, this question remains open maybe someone would help me to improve the answer furthermore!


  2. Here’s an example with TextEditingController

    class FromModel extends ChangeNotifier {
    String _text = '';
    
    String get text => _text;
    
    void updateText(String newText) {
    _text = newText;
    notifyListeners();
    }
    }
    

    home_page.dart //with TextEditingController

    class HomePage extends StatefulWidget {
    const HomePage({super.key});
    
    @override
    State<HomePage> createState() => _HomePageState();
    }
    
    class _HomePageState extends State<HomePage> {
    final FromModel myFormModel = FromModel();
    
    TextEditingController controller = TextEditingController();
    
    @override
    void initState() {
    //helpful to show a pre-filled value
    controller.text = myFormModel.text;
    super.initState();
    }
    
    @override
    Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          TextFormField(
            controller: controller,
          ),
          ElevatedButton(
              onPressed: () {
                Navigator.of(context).push(
                  MaterialPageRoute(builder: (context) => NextPage()),
                );
              },
              child: const Text("Go to next page"))
        ],
      ),
    );
    }
    }
    

    When you navigate to next page and come back then the _text state will remain same and when you want to clear it then updateTextValue("")

    home_page.dart //with onChanged(value)

     class HomePage extends StatefulWidget {
     const HomePage({super.key});
    
     @override
    State<HomePage> createState() => _HomePageState();
    }
    
    class _HomePageState extends State<HomePage> {
    final FromModel myFormModel = FromModel();
    
    @override
    Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          //with onChanged()
          ChangeNotifierProvider.value(
            value: myFormModel,
            child: TextFormField(
              onChanged: (newText) {
                myFormModel.updateText(newText);
              },
            ),
          ),
          ElevatedButton(
              onPressed: () {
                Navigator.of(context).push(
                  MaterialPageRoute(builder: (context) => NextPage()),
                );
              },
              child: const Text("Go to next page"))
        ],
      ),
    );
    }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search