在Flutter中,使用Stateless widget中的TextFormField非常困难。

19

我正在尝试在无状态小部件中使用TextFormField和ScopedModel处理其文本,但遇到以下各种问题。

  1. 我尝试为该字段使用Controller,但每次输入一些文本并在键盘上按“完成”后,文本都会被清除。不知道为什么。

  2. 如果我删除Controller,则文本将保留在该字段中,但会出现新问题,即如何从该字段获取文本。我通过使用callback onFieldSubmitted来解决这个问题。

  3. 结果发现,只有在我们点击键盘上的Done按钮时才会调用 onFieldSubmitted 回调函数。如果我在字段中输入文本,而不是点击ok,然后点击另一个字段,则回调将不会被调用,并且我将无法追踪用户在字段中输入了什么。

有任何解决方案吗?

附上问题的示例代码。

  class LoginPageStateless extends StatelessWidget {

  final loginUsernameController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomPadding: true,
      body: ScopedModelDescendant<AccountModel>(
        builder: (context, child, model) {
          return Form(
            //key: _formKey,
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                TextFormField(
                  style: TextStyle(fontSize: 15.0),
                  decoration: InputDecoration(
                    labelText: 'Email id',
                    hintText: 'johndoe@ipropal.com',
                  ),
                  controller: loginUsernameController,
                  onFieldSubmitted: model.updateLoginUsernameText,
                ),
                TextFormField(
                  style: TextStyle(fontSize: 15.0),
                  decoration: InputDecoration(
                    labelText: 'Password',
                  ),
                  controller: loginUsernameController,
                  onFieldSubmitted: model.updateLoginUsernameText,
                  obscureText: true,
                ),
              ],
            ),
          );
        },
      ),
    );
  }
}
4个回答

39

您不能也不应该使用Stateless小部件来存储长期变量。

问题在于,这正是您正在尝试的。 TextEditingController 是一个类实例,应该在渲染之间保留。但是通过将它存储在StatelessWidget 中,您基本上是在每次更新后重新创建它。

您应该将部件转换为Stateful。并将控制器移到State 部分。


1
如果我将控制器移动到作用域模型中会怎样? - Amit Bhandari
但是,在有状态的小部件中使用作用域模型是否有意义? - Amit Bhandari
我应该在处理表单字段时完全放弃作用域模型的想法吗? - Amit Bhandari
2
Stateful小部件和作用域模型混合使用没有任何问题。动画和控制器非常适合Stateful小部件。 - Rémi Rousselet
1
在有状态的小部件中使用控制器更有意义,因为这是它的预期目的。为该特定小部件保留状态。如果您不在小部件之外使用此控制器,则无需将其放入ScopedModel中,因为ScopedModel通常用于共享变量。 - ThinkDigital
显示剩余3条评论

3

我之前从未使用过TextFormField,我总是使用TextField()因为它简单且灵活。当我使用Redux和无状态小部件时,遇到了类似的问题,因为我在顶层存储中只有一个数据源。所以,我不得不在ViewModel内创建一些回调函数,然后将该回调分配给文本字段回调onChanged,该回调接受一个字符串参数。

CustomTextWidgetWrapper(
    onChangedCallback: viewModel.onChanged
),

在小部件包装的 TextField 中,我执行以下操作(不提供更多细节):
new TextField(
    controller: myController, // no use practically now
    onChanged: onChangedCallback,

在视图模型中,我获取字符串并将其调度到中央存储中以供其他小部件重用,比如一个按钮,它获取数据并发送到服务器。
static ViewModel fromStore(Store<AppState> store) {
    return new ViewModel(
        onChanged: (String textFieldText) {
            // I call dispatch or an API here if I want
            store.dispatch(new CallAPI(params: textFieldText);

1

你不能使用无状态小部件来存储长期变量。

假设您有一个基本行为,例如当用户点击任何地方时键盘将关闭,在无状态小部件中这是不可能的,因为每次键盘打开或关闭时它都会重新构建并创建文本控制器的新实例,

因此,在这种情况下,必须使用有状态小部件,并将控制器放在状态类中。


-1

将textEditingController声明更改为静态:

static final loginUsernameController = TextEditingController();

1
这不是一个好的解决方案,因为再次访问该特定屏幕时不会清除上一次会话的文本。 - Chetan Goyal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接