当表单滚动到屏幕外时,每个表单字段的状态会丢失吗?

Posted

技术标签:

【中文标题】当表单滚动到屏幕外时,每个表单字段的状态会丢失吗?【英文标题】:Losing state of each form field when the form is scrolled off screen? 【发布时间】:2017-11-21 09:08:11 【问题描述】:

当表单滚动离开屏幕时,如何保存每个表单字段的状态?当我向上滚动时,我会丢失在 TextFormFields 中输入的值,并且字段不在屏幕上。

我听说过将其保存在控制器中然后分配给 TextFormField 的初始值的建议,如代码中所示,但它对我不起作用。我错过了什么?

TextEditingController _controller = new TextEditingController();

@override
  Widget build(BuildContext context) 
    return new Scaffold(
      key: scaffoldKey,
      appBar: new AppBar(
        title: new Text('Enter Vehicle'),
      ),
      body: new Padding(
          padding: const EdgeInsets.all(16.0),
          child: new Form(
              key: formKey,
              child: new ListView(children: <Widget>[
                new TextFormField(
                  decoration: new InputDecoration(labelText: 'Vehicle Number'),
                  controller: _controller,
                  initialValue: _controller.text,
                ),
                new TextFormField(
                  decoration: new InputDecoration(labelText: 'Tag'),
                ),
                new TextFormField(
                  decoration: new InputDecoration(labelText: 'Make'),
                ),
                new TextFormField(
                  decoration: new InputDecoration(labelText: 'Model'),
                ),
                new TextFormField(
                  decoration: new InputDecoration(labelText: 'Year'),
                ),
                new TextFormField(
                  decoration: new InputDecoration(labelText: 'Vehicle Type'),
                ),
                new TextFormField(
                  decoration: new InputDecoration(labelText: 'Location'),
                ),
                new TextFormField(
                  decoration: new InputDecoration(labelText: 'Fuel'),
                ),
                new TextFormField(
                  decoration: new InputDecoration(labelText: 'Other'),
                ),
                new TextFormField(
                  decoration: new InputDecoration(labelText: 'Your password'),
                  validator: (val) =>
                  val.length < 6 ? 'Password too short.' : null,
                  onSaved: (val) => _password = val,
                  obscureText: true,
                ),
                new RaisedButton(
                  onPressed: _submit,
                  child: new Text('Login'),
                ),
              ],)
          )
      ),
    );
  

【问题讨论】:

【参考方案1】:

有两种解决方案:

    防止物品回收 在父窗口小部件中保存状态

因此,虽然从 TextFormField 派生的 FormField 是有状态的,但将其放入任何描述的列表中都会有重建它的风险。

选项 1:

List 更改为Column 并将其包装在ListSingleChildListView 中,它既快速又笨拙,但对于许多项目可能存在一些性能问题

List(
   children: [...]
)

注意。如果您有一个 listview.builder,您将需要将其映射出来,而不是使用该构建器,或者转到选项 2。

SingleChildScrollView(
   child: Column(
       children: [...]
   )
)

选项 2:

为每个字段创建一个控制器并将其保存在父窗口小部件中,或者将数据保存到地图并设置其初始数据。您可以解决它,但无论哪种方式,您都需要将其保存在父小部件中。

【讨论】:

【参考方案2】:

在使用 TextFormField 时,您不需要以这种方式使用 initialValue。事实上,如果_controller.text 为空,它会给出一个断言错误。

要让 TextFormFields 正常工作,请确保:

您在每个字段上使用不同的 TextEditingController。 您正在处理完成后的 TextEditingController。 您的表单位于 StatefulWidget 中。

工作示例:

class MyForm extends StatefulWidget 
  @override
  _MyFormState createState() => _MyFormState();


class _MyFormState extends State<MyForm> 
  TextEditingController _vehicleNumber;

  @override
  void initState() 
    super.initState();
    _vehicleNumber = new TextEditingController();
  

  @override
  void dispose() 
    super.dispose();
    _vehicleNumber.dispose();
  

  @override
  Widget build(BuildContext context) 
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Enter Vehicle'),
      ),
      body: new Padding(
          padding: const EdgeInsets.all(16.0),
          child: new Form(
              child: new ListView(
            children: <Widget>[
              new TextFormField(
                decoration: new InputDecoration(labelText: 'Vehicle Number'),
                controller: _vehicleNumber,
              ),
              // All the other fields here
              // All should have its own controller
            ],
          ))),
    );
  

【讨论】:

【参考方案3】:

您需要为每个TextFormField 指定一个专用的TextEditingController,我认为您不需要在字段上使用initialValue 属性。

看看这个答案https://***.com/a/45242235/6414732,这可能也会有所帮助。

【讨论】:

这看起来很痛苦。 使用表单字段而是制作自定义“react-form”会更容易。 或者也许不要使用 ListView,一旦它们看不见,它就会从小部件树中删除元素。只要列表不是数百个字段,这应该不是问题。 其实我已经试过了。我在每个 TextFormField 上都有一个 TextEditingController。在 build() 中,我将每个字段的控制器分配给相关的字段值,然后将每个字段值分配给相关的 TextFormField 的初始值。然而,即便如此,除非调用 setState 来保持值,否则它仍然不起作用。感觉不对。我知道他们有开放的票来改进表单的处理方式。这比我想象的要大得多。在某些时候可能会出现更好的解决方案。与此同时,我将使用我的 hacky 解决方案。【参考方案4】:
  new Container(
                  padding: const EdgeInsets.only(
                      left: 20.0, right: 20.0, top: 0.0, bottom: 0.0),
                  child: new EnsureVisibleWhenFocused(
                    focusNode: _focusNode_nooftickets,
                    child: new TextFormField(
                      controller: _ticketscontroller,
                      initialValue: _ticketscontroller.text,
                      focusNode: _focusNode_nooftickets,
                      keyboardType: TextInputType.number,
                      inputFormatters: <TextInputFormatter>[
                        WhitelistingTextInputFormatter.digitsOnly
                      ],
                      validator: validatenooftickets,
                      onSaved: (String value) 
                        ticketmode.nooftickets = value;
                      ,
                    ),
                  ),
                ),

【讨论】:

以上是关于当表单滚动到屏幕外时,每个表单字段的状态会丢失吗?的主要内容,如果未能解决你的问题,请参考以下文章

表单执行时会重建 Imagepicker,丢失所选图像

Gridview 中的小部件在离屏时丢失状态

当表单包含大量输入时,表单数据被截断/丢失?

表视图单元格仅在屏幕外时自动布局

添加到 HTML 表单而不会丢失 Javascript 中的当前表单输入信息

代号一屏缩水[重复]