在flutter中动态加载表单字段的最佳方法是什么?

时间:2019-04-01 21:42:15

标签: dart flutter flutter-layout

我的TextFormFields不能正常工作,当我单击它们并且键盘没有完全显示时,导致页面重建。

我有一个包含2个下拉菜单和多个文本字段的表单。下拉菜单确定显示哪些文本字段。我通过执行以下操作来确定这一点:

Form(
                    key: _formKey,
                    child: Flex(
                      direction: Axis.vertical,
                      children: <Widget>[
                        //category field
                        new FormField<String>(
                            validator: this.validateField,
                            builder: (FormFieldState<String> state) {
                              return new DropdownButtonHideUnderline(
                                  child: DropdownButton<String>(
                                hint: new Text('Select Category'),
                                value: exercise.categoryName,
                                items: categoryNameList.map((String value) {
                                  return new DropdownMenuItem<String>(
                                    value: value,
                                    child: new Text(value),
                                  );
                                }).toList(),
                                onChanged: (String val) {
                                  setState(() {
                                    //save category name to the exercise object to be saved
                                    exercise.categoryName = val;
                                    state.didChange(val);
                                    // reset exercise dropdown when switching categories
                                    exercise = new Exercise(
                                        categoryName: val, cardioImperial: -1);
                                    // get index of the selected category to be able to load the correct
                                    // exercise list
                                    _exerciseSelectedTextIndex =
                                        categoryNameList.indexOf(val);
                                    _exerciseSelectedList =
                                        categoryList[_exerciseSelectedTextIndex]
                                            .exerciseNames;
                                  });
                                },
                              ));
                            }),

                        // only load the exercise list once the category is selected
                        _exerciseSelectedList != null
                            ? Container(
                                child:
                                    Flex(direction: Axis.vertical, children: <
                                        Widget>[
                                new FormField(
                                    validator: this.validateField,
                                    builder: (FormFieldState<String> state) {
                                      return new DropdownButtonHideUnderline(
                                          child: DropdownButton<String>(
                                        hint: new Text('Select Exercise'),
                                        value: exercise.name,
                                        items: _exerciseSelectedList
                                            .map((String value) {
                                          return new DropdownMenuItem<String>(
                                            value: value,
                                            child: new Text(value),
                                          );
                                        }).toList(),
                                        onChanged: (String val) {
                                          setState(() {
                                            exercise.name = val;
                                            state.didChange(val);
                                          });
                                        },
                                      ));
                                    }),
                              ]))
                            : Container(),

                        //if cardio load time/length else load reps/quantity
                        _exerciseSelectedList != null
                            ? categoryList[_exerciseSelectedTextIndex].cardio ==
                                    true
                                ? Flex(direction: Axis.vertical, children: <
                                    Widget>[
                                    Row(
                                        mainAxisAlignment:
                                            MainAxisAlignment.center,
                                        children: <Widget>[
                                          SizedBox(
                                              width: 75.0,
                                              child: TextFormField(
                                                validator: this.validateField,
                                                onSaved: (value) => exercise
                                                    .cardioLength = value,
                                                keyboardType:
                                                    TextInputType.number,
                                                decoration: InputDecoration(
                                                  hintText: 'Length',
                                                ),
                                              )),
                                          new Radio(
                                            groupValue: exercise.cardioImperial,
                                            value: 1,
                                            onChanged: (value) => setState(() {
                                                  exercise.cardioImperial =
                                                      value;
                                                }),
                                          ),
                                          new Text('Miles'),
                                          new Radio(
                                            groupValue: exercise.cardioImperial,
                                            value: 2,
                                            onChanged: (value) => setState(() {
                                                  exercise.cardioImperial =
                                                      value;
                                                }),
                                          ),
                                          new Text('KM')
                                        ]),

                                    // time fields
                                    Row(
                                        mainAxisAlignment:
                                            MainAxisAlignment.center,
                                        children: <Widget>[
                                          Padding(
                                            padding: const EdgeInsets.all(8.0),
                                            child: SizedBox(
                                              width: 30.0,
                                              child: TextFormField(
                                                validator: this.validateField,
                                                onSaved: (value) =>
                                                    exercise.hh = value,
                                                keyboardType:
                                                    TextInputType.number,
                                                decoration: InputDecoration(
                                                  hintText: 'HH',
                                                ),
                                              ),
                                            ),
                                          ),
                                          Padding(
                                            padding: const EdgeInsets.all(8.0),
                                            child: SizedBox(
                                              width: 30.0,
                                              child: TextFormField(
                                                validator: this.validateField,
                                                onSaved: (value) =>
                                                    exercise.mm = value,
                                                keyboardType:
                                                    TextInputType.number,
                                                decoration: InputDecoration(
                                                  hintText: 'MM',
                                                ),
                                              ),
                                            ),
                                          ),
                                          Padding(
                                            padding: const EdgeInsets.all(8.0),
                                            child: SizedBox(
                                              width: 30.0,
                                              child: TextFormField(
                                                validator: this.validateField,
                                                onSaved: (value) =>
                                                    exercise.ss = value,
                                                keyboardType:
                                                    TextInputType.number,
                                                decoration: InputDecoration(
                                                  hintText: 'SS',
                                                ),
                                              ),
                                            ),
                                          )
                                        ])
                                  ])

                                // if not cardio, load quantity/reps
                                : Flex(
                                    direction: Axis.vertical,
                                    children: <Widget>[
                                        SizedBox(
                                          width: 75.0,
                                          child: TextFormField(
                                            onSaved: (value) =>
                                                exercise.quantity = value,
                                            keyboardType: TextInputType.number,
                                            decoration: InputDecoration(
                                              hintText: 'Weight',
                                            ),
                                          ),
                                        ),

                                        // reps field
                                        Padding(
                                          padding: const EdgeInsets.all(8.0),
                                          child: SizedBox(
                                            width: 50.0,
                                            child: TextFormField(
                                              onSaved: (value) =>
                                                  exercise.reps = value,
                                              keyboardType:
                                                  TextInputType.number,
                                              decoration: InputDecoration(
                                                hintText: 'Reps',
                                              ),
                                            ),
                                          ),
                                        ),
                                      ])
                            : Container(),
                      ],
                    ))

我希望当我选择Cardio(cardio == true)时,可以单击可用的TextFormFields并可以输入数据。显示正确的字段,但是当我单击任何文本表单字段时,整个页面都会重建。有没有更好的方法来构造此页面以实现预期的行为?

1 个答案:

答案 0 :(得分:1)

问题源于包含我的表单的Stateful小部件中的ScaffoldState和FormState。

我的应用程序的流程如下:

主页->第1页->第2页(带有表单)

最初,ScaffoldState和FormState驻留在Page2上。我保持相同的流程,但将ScaffoldState和FormState移至Page1,然后将它们传递给Page2,以供Form使用。现在,当我单击它时,TextFormField不会引起重建,并且保存功能可以按预期工作。