Listview.builder 插入新数据后未更新

Listview.builder not updating after inserting new data

我的列表生成器有问题。 当按下“标签”文本旁边的“+”按钮时,我试图添加更多 TextFormFields,我从 firebase 获取标签列表,然后在单独的 TextFormField 中显示该列表中的每个标签,但是当我尝试使用“+”按钮添加一个新的 TextFormField,没有任何反应,我检查列表是否发生变化,确实它发生了变化,但没有任何反应,我期望的是在红色方块中获得一个新的 TextFormField。

代码:

import 'package:flutter/material.dart';
import '../database/firestoreHandler.dart';
import '../models/todo2.dart';
import '../widgets/dialogs.dart';

class TodoEdit extends StatefulWidget {
  String? doctitle;
  String? doctdescription;
  String? docimage;
  String? docid;
  List? doctags;

  TodoEdit({Key? key, this.doctitle, this.doctdescription, this.docimage, this.docid,this.doctags}) : super(key: key);


  @override
  _TodoEditState createState() => _TodoEditState();

}
// -----------------------------my widget------------
Widget tagForm(controller){
  return TextFormField(
    controller: controller,
    style: TextStyle(color: Colors.white),
    decoration: InputDecoration(
      labelText: "Tag",
      labelStyle: TextStyle(color: Colors.white60),
      fillColor: Colors.black,
      filled: true,
    ),
  );

}
//---------------------------------------------------
class _TodoEditState extends State<TodoEdit> {
  final _formKey = GlobalKey<FormState>();
  final tcontroller = TextEditingController();
  final dcontroller = TextEditingController();
  final icontroller = TextEditingController();
//--------------------add widget to list----------------
  void _addformWidget(list,controller) {
    setState(() {
      list.add(tagForm(controller));
    });
  }
//------------------------------------------------

  @override
  void initState() {

    super.initState();
    tcontroller.text = widget.doctitle.toString();
    dcontroller.text = widget.doctdescription.toString();
    icontroller.text = widget.docimage.toString();
    
  }

  @override
  Widget build(BuildContext context) {
//----------I add the tags to the list view for the first time-----
    var textEditingControllers = <TextEditingController>[];
    var textformFields = <Widget>[];
    widget.doctags?.forEach((element) {
      var textEditingController = new TextEditingController(text: element);
      textEditingControllers.add(textEditingController);
      //return textformFields.add(tagForm(textEditingController)
      return _addformWidget(textformFields, textEditingController);
      //);
    });
//------------------------------------------
    return Scaffold(
      backgroundColor: Colors.grey[900],
      appBar: AppBar(
        actions: [
          IconButton(onPressed: (){
            showDialog(
              barrierDismissible: false,
              context: context,
              builder: (context) {
                return AlertDialog(
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(20),
                  ),
                  title: Text('Delete TODO'),
                  actions: [
                    TextButton(
                      child: Text('Cancel'),
                      onPressed: () {
                        Navigator.pop(context);
                      },
                    ),
                    TextButton(
                      child: Text('Delete'),
                      onPressed: () {
                        deleteData(widget.docid.toString(), context);
                        setState(() {
                          showSnackBar(context, 'todo "${widget.doctitle}" successfully deleted!');
                        });
                      },
                    ),
                  ],
                );
              },
            );
          },
              icon: Icon(Icons.delete))
        ],
        backgroundColor: Colors.grey[900],
        title: Text("${widget.doctitle}"),
      ),
      body: Container(
        child: SafeArea(
        child:  Form(
          key: _formKey,
          child: Column(
            children: [
              SizedBox(height: 10),
              TextFormField(
                controller: tcontroller,
                style: TextStyle(color: Colors.white),
                decoration: InputDecoration(
                  labelText: "Title",
                  labelStyle: TextStyle(color: Colors.white60),
                  fillColor: Colors.black,
                  filled: true,
                ),
              ),
              SizedBox(height: 10),
              TextFormField(
                controller: dcontroller,
                style: TextStyle(color: Colors.white),
                decoration: InputDecoration(
                  labelText: "Description",
                  labelStyle: TextStyle(color: Colors.white60),
                  fillColor: Colors.black,
                  filled: true,
                ),
              ),
              SizedBox(height: 10),
              TextFormField(
                controller: icontroller,
                style: TextStyle(color: Colors.white),
                decoration: InputDecoration(
                  labelText: "Image url",
                  labelStyle: TextStyle(color: Colors.white60),
                  fillColor: Colors.black,
                  filled: true,
                ),
              ),
              SizedBox(height: 10),
              Row(children: [
                Text("Tags:", style:TextStyle(color: Colors.white)),
//-----------------------here I try to add the new text form field-----------
                IconButton(onPressed: (){
                  var textEditingController = new TextEditingController(text: "tag");
                  textEditingControllers.add(textEditingController);
                  _addformWidget(textformFields,textEditingController);
                  print(textformFields.length);
                },
                  icon: Icon(Icons.add,color: Colors.white,),
                )
              ],),//------------------------

              /*SingleChildScrollView(
                child: new Column(
                children: textformFields,
                )
              ),*/
//--------------------------------here I build my list--------------
              Expanded(
                  child: SizedBox(
                    height: 200.0,
                    child: ListView.builder(
                        itemCount: textformFields.length,
                        itemBuilder: (context,index) {
                          return textformFields[index];
                        }),
                  )
              ),
            ],
          ),
        ),
        ),
        ),
//--------------------------------------------------
        floatingActionButton: FloatingActionButton(
          onPressed: (){
            if(tcontroller == '' && dcontroller == '' && icontroller == ''){
              print("not valid");
            }else{
              var todo = Todo2(
                title: tcontroller.text,
                description: dcontroller.text,
                image: icontroller.text,
                //tags: tagcontroller.text,
              );
              updateData(todo, widget.docid.toString(),context);
              setState(() {
                showSnackBar(context, 'todo ${widget.doctitle} successfully updated!');
              });
            }
          },
          child: Icon(Icons.update),
    ),
    );
  }
}

感谢任何帮助!

由于您在构建函数中保留了表单字段和控制器的列表,因此当您对它们调用 setState 时不会重建小部件。

而是将它们与其他状态变量一起移动。

  final _formKey = GlobalKey<FormState>();
  final tcontroller = TextEditingController();
  final dcontroller = TextEditingController();
  final icontroller = TextEditingController();
  
  final textEditingControllers = <TextEditingController>[];
  final textformFields = <Widget>[];

现在您可以更改 _addformWidget 函数以直接使用列表而不将其作为参数。

  void _addformWidget(TextEditingController controller) {
    setState(() {
      textformFields.add(tagForm(controller));
    });
  }

然后在initState函数中初始化它们。

  @override
  void initState() {

    super.initState();
    tcontroller.text = widget.doctitle.toString();
    dcontroller.text = widget.doctdescription.toString();
    icontroller.text = widget.docimage.toString();
    
    
    widget.doctags?.forEach((element) {
      final textEditingController = new TextEditingController(text: element);
      textEditingControllers.add(textEditingController);
      //return textformFields.add(tagForm(textEditingController)
      _addformWidget(textEditingController);
      //);
    });
    
  }

理想情况下,这应该可以解决您的问题。如果不是,请告诉我,如果是,您可以单击复选标记进行确认。

//// 从 build 中删除 textEditingControllerstextformFields 列表。并在顶部声明它。

 @override
  Widget build(BuildContext context) {
//----------I add the tags to the list view for the first time-----
    var textEditingControllers = <TextEditingController>[];
    var textformFields = <Widget>[];

//// 如下使用

class _TodoEditState extends State<TodoEdit> {
  final _formKey = GlobalKey<FormState>();
  final tcontroller = TextEditingController();
  final dcontroller = TextEditingController();
  final icontroller = TextEditingController();

  var textEditingControllers = <TextEditingController>[]; //<---------
  var textformFields = <Widget>[];

////// 完整代码

class TodoEdit extends StatefulWidget {
  String? doctitle;
  String? doctdescription;
  String? docimage;
  String? docid;
  List? doctags;

  TodoEdit(
      {Key? key,
        this.doctitle,
        this.doctdescription,
        this.docimage,
        this.docid,
        this.doctags})
      : super(key: key);

  @override
  _TodoEditState createState() => _TodoEditState();
}

class _TodoEditState extends State<TodoEdit> {
  final _formKey = GlobalKey<FormState>();
  final tcontroller = TextEditingController();
  final dcontroller = TextEditingController();
  final icontroller = TextEditingController();

  var textEditingControllers = <TextEditingController>[];
  var textformFields = <Widget>[];

  @override
  void initState() {
    widget.doctags?.forEach((element) {
      var textEditingController = TextEditingController(text: element);
      textEditingControllers.add(textEditingController);
      //return textformFields.add(tagForm(textEditingController)
      return _addformWidget(textformFields, textEditingController);
      //);
    });
    super.initState();
    tcontroller.text = widget.doctitle.toString();
    dcontroller.text = widget.doctdescription.toString();
    icontroller.text = widget.docimage.toString();
  }

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      backgroundColor: Colors.grey[900],
      appBar: AppBar(
        actions: [
          IconButton(
              onPressed: () {
                showDialog(
                  barrierDismissible: false,
                  context: context,
                  builder: (context) {
                    return AlertDialog(
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(20),
                      ),
                      title: Text('Delete TODO'),
                      actions: [
                        TextButton(
                          child: Text('Cancel'),
                          onPressed: () {
                            Navigator.pop(context);
                          },
                        ),
                        TextButton(
                          child: Text('Delete'),
                          onPressed: () {
                            deleteData(widget.docid.toString(), context);
                            setState(() {
                              showSnackBar(context,
                                  'todo "${widget.doctitle}" successfully deleted!');
                            });
                          },
                        ),
                      ],
                    );
                  },
                );
              },
              icon: Icon(Icons.delete))
        ],
        backgroundColor: Colors.grey[900],
        title: Text("${widget.doctitle}"),
      ),
      body: Container(
        child: SafeArea(
          child: Form(
            key: _formKey,
            child: Column(
              children: [
                SizedBox(height: 10),
                TextFormField(
                  controller: tcontroller,
                  style: TextStyle(color: Colors.white),
                  decoration: InputDecoration(
                    labelText: "Title",
                    labelStyle: TextStyle(color: Colors.white60),
                    fillColor: Colors.black,
                    filled: true,
                  ),
                ),
                SizedBox(height: 10),
                TextFormField(
                  controller: dcontroller,
                  style: TextStyle(color: Colors.white),
                  decoration: InputDecoration(
                    labelText: "Description",
                    labelStyle: TextStyle(color: Colors.white60),
                    fillColor: Colors.black,
                    filled: true,
                  ),
                ),
                SizedBox(height: 10),
                TextFormField(
                  controller: icontroller,
                  style: TextStyle(color: Colors.white),
                  decoration: InputDecoration(
                    labelText: "Image url",
                    labelStyle: TextStyle(color: Colors.white60),
                    fillColor: Colors.black,
                    filled: true,
                  ),
                ),
                SizedBox(height: 10),
                Row(
                  children: [
                    Text("Tags:", style: TextStyle(color: Colors.white)),
//-----------------------here I try to add the new text form field-----------
                    IconButton(
                      onPressed: () {
                        var textEditingController =
                        new TextEditingController(text: "tag");
                        textEditingControllers.add(textEditingController);
                        _addformWidget(textformFields, textEditingController);
                        print(textformFields.length);
                      },
                      icon: Icon(
                        Icons.add,
                        color: Colors.white,
                      ),
                    )
                  ],
                ), //------------------------

                /*SingleChildScrollView(
                child: new Column(
                children: textformFields,
                )
              ),*/
//--------------------------------here I build my list--------------
                Expanded(
                    child: SizedBox(
                      height: 200.0,
                      child: ListView.builder(
                          itemCount: textformFields.length,
                          itemBuilder: (context, index) {
                            return textformFields[index];
                          }),
                    )),
              ],
            ),
          ),
        ),
      ),
//--------------------------------------------------
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          if (tcontroller == '' && dcontroller == '' && icontroller == '') {
            print("not valid");
          } else {
            var todo = Todo2(
              title: tcontroller.text,
              description: dcontroller.text,
              image: icontroller.text,
              //tags: tagcontroller.text,
            );
            updateData(todo, widget.docid.toString(), context);
            setState(() {
              showSnackBar(
                  context, 'todo ${widget.doctitle} successfully updated!');
            });
          }
        },
        child: Icon(Icons.update),
      ),
    );
  }

  //--------------------add widget to list----------------
  void _addformWidget(list, controller) {
    setState(() {
      list.add(tagForm(controller));
    });
  }

  // -----------------------------my widget------------
  Widget tagForm(controller) {
    return TextFormField(
      controller: controller,
      style: TextStyle(color: Colors.white),
      decoration: InputDecoration(
        labelText: "Tag",
        labelStyle: TextStyle(color: Colors.white60),
        fillColor: Colors.black,
        filled: true,
      ),
    );
  }
}