【问题标题】:Flutter: keep data in textfield after setstateFlutter:在setstate之后将数据保留在文本字段中
【发布时间】:2019-11-08 09:36:17
【问题描述】:
  1. 我有一个文本字段数组。
  2. 向数组添加新文本字段的按钮。
  3. 如果您将数据添加到文本字段并通过单击添加按钮添加新元素,则添加的数据将消失。
  4. 按钮函数中有一个 setstate,用于查看数组中的新元素。
  5. 如何向数组中添加新元素,将旧数据保留在文本字段中?

start_workout.dart

    import 'package:flutter/material.dart';
import 'package:fultter_ultralifestyle/src/models/models.dart' show SetModel;
import 'package:fultter_ultralifestyle/src/presentation/widgets/dynamic_widget.dart';

class WorkoutStartScreen extends StatefulWidget {
  static const String routeName = "workoutStart";
  @override
  _WorkoutStartScreenState createState() => _WorkoutStartScreenState();
}

class _WorkoutStartScreenState extends State<WorkoutStartScreen> {
  final List<SetModel> sets = [
  ];

  void _addSet() {
    final id = sets.length;
    sets.add(SetModel(id: id, pounds: 0, reps: 0));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Workout tracker"),
        centerTitle: true,
      ),
      body: Container(
        child: Column(
          children: <Widget>[
            Expanded(
              child: ListView.builder(
                  itemCount: sets.length,
                  itemBuilder: (BuildContext context, int index) {
                    return DynamicWidget(
                      set: sets[index],
                      pos: index +1,
                      delete: () {
                        sets.removeAt(index);
                        setState(() {});
                      },
                    );
                  }),
            )
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _addSet(),
        child: Icon(Icons.add),
      ),
    );
  }
}

dynamic_widget.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fultter_ultralifestyle/src/models/models.dart';
import 'package:fultter_ultralifestyle/src/presentation/widgets/text_widget.dart';

class DynamicWidget extends StatelessWidget {
  final TextEditingController poundsController = TextEditingController();
  final TextEditingController repsController = TextEditingController();

  final SetModel set;
  final int pos;
  Function delete;

  DynamicWidget({
    Key key,
    @required this.set,
    @required this.pos,
    @required this.delete,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final Size size = MediaQuery.of(context).size;

    return Container(
      child: Column(
        children: <Widget>[
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Container(
                child: text(
                  caption: "SET $pos",
                ),
              ),
              SizedBox(width: 20.0),
              Container(
                width: size.width / 4,
                child: Column(
                  children: <Widget>[
                    TextField(
                      controller: poundsController,
                      keyboardType: TextInputType.number,
                      inputFormatters: [
                        WhitelistingTextInputFormatter.digitsOnly,
                      ],
                    ),
                    SizedBox(height: 10),
                    text(caption: "pounds".toUpperCase()),
                  ],
                ),
              ),
              SizedBox(
                width: 20,
              ),
              text(caption: "X"),
              SizedBox(
                width: 20,
              ),
              Container(
                width: size.width / 4,
                child: Column(
                  children: <Widget>[
                    TextField(
                      controller: repsController,
                      keyboardType: TextInputType.number,
                      inputFormatters: [
                        WhitelistingTextInputFormatter.digitsOnly
                      ],
                    ),
                    SizedBox(height: 10),
                    text(caption: "reps".toUpperCase()),
                  ],
                ),
              ),
              SizedBox(width: 5.0),
              IconButton(
                icon: Icon(Icons.delete_forever),
                onPressed: delete,
              )
            ],
          ),
        ],
      ),
    );
  }
}

【问题讨论】:

    标签: arrays flutter dart textfield setstate


    【解决方案1】:

    我通过在您的文本字段中的文本更改时更新您的SetModel 来解决这个问题。主要部分是动态小部件文本字段中的updatePoundsupdateReps 方法以及onChanged 方法。

    我还确保在构建动态小部件时使用 SetModel 中包含的值初始化控制器。

    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    
    void main() {
      runApp(MaterialApp(
        home: WorkoutStartScreen(),
      ));
    }
    
    class SetModel {
      final int id;
      int pounds;
      int reps;
    
      SetModel({
        this.id,
        this.pounds,
        this.reps,
      });
    
      void updatePounds(int pounds) {
        this.pounds = pounds;
      }
    
      void updateReps(int reps) {
        this.reps = reps;
      }
    }
    
    class WorkoutStartScreen extends StatefulWidget {
      static const String routeName = "workoutStart";
      @override
      _WorkoutStartScreenState createState() => _WorkoutStartScreenState();
    }
    
    class _WorkoutStartScreenState extends State<WorkoutStartScreen> {
      final List<SetModel> sets = [];
    
      void _addSet() {
        final id = sets.length;
        setState(() {
          sets.add(SetModel(id: id, pounds: 0, reps: 0));
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("Workout tracker"),
            centerTitle: true,
          ),
          body: Container(
            child: Column(
              children: <Widget>[
                Expanded(
                  child: ListView.builder(
                    itemCount: sets.length,
                    itemBuilder: (BuildContext context, int index) {
                      return DynamicWidget(
                        set: sets[index],
                        pos: index + 1,
                        delete: () {
                          setState(() {
                            sets.removeAt(index);
                          });
                        },
                      );
                    },
                  ),
                )
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () => _addSet(),
            child: Icon(Icons.add),
          ),
        );
      }
    }
    
    class DynamicWidget extends StatelessWidget {
      final TextEditingController poundsController = TextEditingController();
      final TextEditingController repsController = TextEditingController();
    
      final SetModel set;
      final int pos;
      final Function delete;
    
      DynamicWidget({
        Key key,
        @required this.set,
        @required this.pos,
        @required this.delete,
      }) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        final Size size = MediaQuery.of(context).size;
    
        if (set.pounds != 0) {
          poundsController.text = set.pounds.toString();
        }
    
        if (set.reps != 0) {
          repsController.text = set.reps.toString();
        }
    
        return Container(
          child: Column(
            children: <Widget>[
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Container(
                    child: Text(
                      "SET $pos",
                    ),
                  ),
                  SizedBox(width: 20.0),
                  Container(
                    width: size.width / 4,
                    child: Column(
                      children: <Widget>[
                        TextField(
                          controller: poundsController,
                          onChanged: (String pounds) {
                            set.updatePounds(int.parse(pounds));
                          },
                          keyboardType: TextInputType.number,
                          inputFormatters: [WhitelistingTextInputFormatter.digitsOnly],
                        ),
                        SizedBox(height: 10),
                        Text("pounds".toUpperCase()),
                      ],
                    ),
                  ),
                  SizedBox(
                    width: 20,
                  ),
                  Text("X"),
                  SizedBox(
                    width: 20,
                  ),
                  Container(
                    width: size.width / 4,
                    child: Column(
                      children: <Widget>[
                        TextField(
                          controller: repsController,
                          onChanged: (String reps) {
                            set.updateReps(int.parse(reps));
                          },
                          keyboardType: TextInputType.number,
                          inputFormatters: [WhitelistingTextInputFormatter.digitsOnly],
                        ),
                        SizedBox(height: 10),
                        Text("reps".toUpperCase()),
                      ],
                    ),
                  ),
                  SizedBox(width: 5.0),
                  IconButton(
                    icon: Icon(Icons.delete_forever),
                    onPressed: delete,
                  )
                ],
              ),
            ],
          ),
        );
      }
    }
    

    基本上,每次您从其父级调用setState 时,都会重新绘制动态小部件。由于您从未告诉动态小部件保存任何内容,因此它们会从头开始重绘。这种情况仍在发生,只是现在我们向他们提供了一些初始信息。

    Here 是它工作的视频。

    【讨论】:

    • 是的!如果这是您的解决方案,请随意标记它
    • 我该怎么做?我试图给你一个积极的或类似的东西,但我不知道如何
    【解决方案2】:

    使用控制器!

    TextEditingController password = new TextEditingController();
    
    TextFormField(
                      validator: (value) {
                        if (value.isEmpty) {
                          return "Could not be empty";
                        }
                        return null;
                      },
    
                      enabled: !isLoading,
                      controller: password,
                      obscureText: hidePassword,
                      decoration: InputDecoration(
                        suffix: IconButton(icon: Icon((hidePassword)?Icons.remove_red_eye:Icons.cancel),onPressed: () => setState(() => hidePassword = !hidePassword),),
                          labelText: "PASSWORD",
                          border: OutlineInputBorder(
                              borderRadius: BorderRadius.circular(20))),
                      onFieldSubmitted: (String p) {
                        buttonPressed();
                      },
                    ),
    

    然后使用

    检索它
    var pass = username.text;
    

    显然你可以使用一个控制器数组然后检索它们

    【讨论】:

    • 您好,谢谢,我刚刚更新了问题!你知道我该怎么做吗?
    • DynamicWidget 是您可以控制的吗?因为也许您可以将新的可选控制器参数传递给该小部件!对于文本编辑控制器的数组,您可以在这里看到一些信息:stackoverflow.com/questions/58433524/…
    • 我要再次编辑帖子并粘贴完整代码
    • DynamicWidget 是一个无状态小部件,因此每次您在父级上调用 setstate 时,您的控制器都会保持初始化。如果将DynamicWidget设置为statefull,即使在parent上调用setstate,控制器也不会被重置!
    • 是的,我改变了这一点,但我需要的是当父级使用 setstate 时,每次都会向数组添加一个新元素,在这种情况下是一个新的 DynamicWidget,其中包含输入中的数据。我知道这有点复杂且难以实施。我现在正在尝试用 BLoC 模式来做这件事,而且也很困难。
    【解决方案3】:

    我的技巧是让TextField 具有autofocus: true,onChange(lock) =&gt; lock = true 的属性

    然后在运行 initState 或构建小部件时,我们使用 if (lock == false) 设置数据 setALLYourDataInController

    代码:

    bool lock = false;
    
    Widget build(BuildContext context) {
    ...
    if (lock == false) {
          firstNameController.text = detail?.firstName.toString() ?? '';
    }
    ...
        }
    

    我的TextField

    TextField(
                  autofocus: true,
                  style: kTextFieldTextStyle,
                  decoration: kInputDecoration.copyWith(
                    hintText: 'Yogithesymbian',
                    prefixIcon: Icon(Icons.person_add),
                    suffixIcon: firstNameController.text.isNotEmpty
                        ? IconButton(
                            onPressed: firstNameController.clear,
                            icon: Icon(
                              Icons.clear,
                              color: kPrimaryColorLight,
                            ),
                          )
                        : null,
                  ),
                  onChanged: (unuse) {
                    lock = true; // here is logic
                    // set.updatePounds(int.parse(pounds));
                  },
                  textInputAction: TextInputAction.next,
                  controller: firstNameController,
                ),
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-10-29
      • 2014-05-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多