首页 文章

Dart / flutter:如何轻松构建具有多个用户输入字段的表单

提问于
浏览
0

我想构建一个表单,其中我有多个 TextField 小部件,并希望通过传递从这些字段收集的数据,在按下时有一个组合和发送电子邮件的按钮 .

为此,我开始构建 InheritedWidget 以包含 TextField -s,并基于构造函数中传递的操作 - 下面的代码中尚未包含的功能 - 它将返回来自via toString 方法覆盖的不同文本 .

据我所知,只要它是当前Widget树的一部分, InheritedWidget 就会保持它的值(例如,如果我从表单导航它会被破坏并且值丢失) .

这是我使用InheritedWidget构建TextForm的方法:

class TextInheritedWidget extends InheritedWidget {
  const TextInheritedWidget({
    Key key,
    this.text,
    Widget child}) : super(key: key, child: child);

  final String text;

  @override
  bool updateShouldNotify(TextInheritedWidget old) {
    return text != old.text;
  }

  static TextInheritedWidget of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(TextInheritedWidget);
  }

}

class TextInputWidget extends StatefulWidget {
  @override
  createState() => new TextInputWidgetState();
}

class TextInputWidgetState extends State<TextInputWidget> {
  String text;
  TextEditingController textInputController = new TextEditingController();

  @override
  Widget build(BuildContext context) {
    return new TextInheritedWidget(
      text: text,
      child: new TextField(
        controller: textInputController,
        decoration: new InputDecoration(
          hintText: adoptionHintText
        ),
        onChanged: (text) {
          setState(() {
            this.text = textInputController.text;
          });
        },
      ),
    );
  }

  @override
  String toString({DiagnosticLevel minLevel: DiagnosticLevel.debug}) {
    // TODO: implement toString
    return 'Név: ' + text;
  }
}

这是启动电子邮件发送的按钮:

TextInputWidget nameInputWidget = new TextInputWidget();
  TextInheritedWidget inherited = new TextInheritedWidget();

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Örökbefogadás'),
      ),
      body: new Container(
        padding: const EdgeInsets.all(5.0),
        child: new ListView(
          children: <Widget>[
            new Text('Név:', style: infoText16BlackBold,),
            nameInputWidget,
            new FlatButton(onPressed: () {
              launchAdoptionEmail(nameInputWidget.toString(), 'kutya');
            },
                child: new Text('Jelentkezem'))

          ],
        ),
      ),

    );
  }

我的问题是 nameInputWidget.toString() 只返回TextInputWidget(类名),我似乎无法找到访问 TextInputWidgetState.toString() 方法的方法 .

我知道 TextInheritedWidget 正确地保存了 text 值,但我不确定如何通过我的 nameInputWidget 对象访问它 .

不应该 TextInputWidget 能够通过 InheritedWidget 用来确定哪个 Widget 更新并存储值的上下文来访问数据?

2 回答

  • 2

    在Rémi的讲话之后,我想出了一个有效的解决方案,虽然我很确定它不是最好的,不能大规模地遵循,但应该适用于几个领域 .

    解决方案是在一个单独的 State 中处理所有 TextField 小部件,以及电子邮件组合 .

    为了实现相对干净的代码,我们可以使用自定义函数来构建具有适当数据标签的输入字段,该标签接受两个输入参数: StringTextEditingController .

    label 还用于确定 setState() 方法将传递新提交的文本的变量 .

    Widget buildTextInputRow(var label, TextEditingController textEditingController) {
        return new ListView(
          shrinkWrap: true,
          children: <Widget>[
            new Row(
              children: <Widget>[
                new Expanded(
                  child: new Container(
                      padding: const EdgeInsets.only(left: 5.0, top: 2.0, right: 5.0  ),
                      child: new Text(label, style: infoText16BlackBold)),
                ),
              ],
            ),
            new Row(
              children: <Widget>[
                new Expanded(
                  child: new Container(
                      padding: const EdgeInsets.only(left: 5.0, right: 5.0),
                      child: new TextField(
                          controller: textEditingController,
                          decoration: new InputDecoration(hintText: adoptionHintText),
                          onChanged: (String str) {
                            setState(() {
                              switch(label) {
                                case 'Név':
                                  tempName = 'Név: ' + textEditingController.text + '\r\n';
                                  break;
                                case 'Kor':
                                  tempAge = 'Kor: ' + textEditingController.text + '\r\n';
                                  break;
                                case 'Cím':
                                  tempAddress = 'Cím: ' + textEditingController.text + '\r\n';
                                  break;
                                default:
                                  break;
                              }
                            });
                          }
                      )),
                ),
              ],
            )
          ],
        );
      }
    

    问题显然是你需要一个新的TextEditingController和一个新的String来存储你希望用户输入的每个新输入:

    TextEditingController nameInputController = new TextEditingController();
      var tempName;
      TextEditingController ageInputController = new TextEditingController();
      var tempAge;
      TextEditingController addressInputController = new TextEditingController();
      var tempAddress;
    

    如果您有很多字段,这将导致很多额外的行,并且您还必须相应地更新 composeEmail() 方法,并且您拥有的字段越多,您就越有可能忘记一对 .

    var emailBody;
      composeEmail(){
        emailBody = tempName + tempAge + tempAddress;
        return emailBody;
      }
    

    最后,是时候构建表单了:

    @override
      Widget build(BuildContext context) {
        return new Scaffold(
          appBar: new AppBar(
            title: new Text('Örökbefogadás'),
          ),
          body: new ListView(
            children: <Widget>[
              buildTextInputRow('Név', nameInputController),
              buildTextInputRow('Kor', ageInputController),
              buildTextInputRow('Cím', addressInputController),
              new FlatButton(onPressed: () { print(composeEmail()); }, child: new Text('test'))
            ],
          ),
        );
      }
    

    为方便起见,我只是在测试时将电子邮件正文打印到控制台

    I/flutter ( 9637): Név: Zoli
    I/flutter ( 9637): Kor: 28
    I/flutter ( 9637): Cím: Budapest
    

    所有这些都在一个 State 中处理 .

  • 1

    这是不可能的 . Only children of an InheritedWidget can access it's properties

    解决方案是将 InheritedWidget 放在 Button 之上 . 但这意味着你必须重构才能考虑到这一点 .

相关问题