从动态创建的列表中动态删除小部件 flutter

afa*_*rre 2 android dart flutter flutter-layout

我按照这个示例从列表中删除所需的元素,并且效果很好。然后我做了一些更改,因为列表的大小不是预定义的(在示例中又称为 5),而是通过按钮进行更改。在根据我的需要调整代码后,我发现了两个问题:

  1. +每次我点击添加新元素时,元素哈希代码都会发生变化。这是正确的,因为它正在重新绘制列表,还是不正确?
  2. 删除一个元素时,除了更改所有其他元素的哈希码外,仅删除最后一个元素,而不删除所选元素。

这是执行过程:

在此输入图像描述

这是代码:

import 'package:flutter/material.dart';

class StreamBuilderIssue extends StatefulWidget {
  @override
  _StreamBuilderIssueState createState() => _StreamBuilderIssueState();
}

class _StreamBuilderIssueState extends State<StreamBuilderIssue> {
  List<ServiceCard> serviceCardList;
  int numElements = 1; //dynamic number of elements

  void removeServiceCard(index) {
    setState(() {
      serviceCardList.remove(index);
      numElements--;
    });
  }


  @override
  Widget build(BuildContext context) {
    serviceCardList = List.generate(numElements, (index) => ServiceCard(removeServiceCard, index: index));
    return Scaffold(
      body: ListView(
        children: <Widget>[
          ...serviceCardList,
          new FlatButton(
            onPressed: (){
              setState(() {
                numElements++;
              });
            },
            child: new Icon(Icons.add),
          ),
        ],
      ),
    );
  }
}

class ServiceCard extends StatelessWidget {
  final int index;
  final Function(ServiceCard) removeServiceCard;

  const ServiceCard(this.removeServiceCard, {Key key, @required this.index})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Text(
            'Index: ${index.toString()} Hashcode: ${this.hashCode.toString()}'),
        RaisedButton(
          color: Colors.accents.elementAt(3 * index),
          onPressed: () {
            removeServiceCard(this);
          },
          child: Text('Delete me'),
        ),
      ],
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

如何解决此问题并正确删除所选元素?

有人可以解释 ServiceCard 类的构造函数吗?我不明白语法。

编辑:同样的问题适应了我的代码并使用有状态类,而不是无状态类:

import 'package:flutter/material.dart';

class CreateForms extends StatefulWidget{
  @override
  _CreateForms createState() => _CreateForms();

}

class _CreateForms extends State<CreateForms>{
  final _formKey = GlobalKey<FormState>();
  List<Question> _questions = [];
  final myController = TextEditingController();

  @override
  void dispose() {
    // Clean up the controller when the widget is disposed.
    myController.dispose();
    super.dispose();
  }

  void removeQuestion(index) {
    print("Removing " + index.hashCode.toString());
    setState(() {
      _questions.remove(index);
    });
  }

  void addServiceCard() {
    setState(() {
      Question question = Question(removeQuestion, _questions.length);
      _questions.add(question);
      print("Adding " + question.hashCode.toString());
    });
  }

  @override
  void initState() {
    addServiceCard(); //Initialize with 1 item
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    for (var i = 0; i < _questions.length; i++){
      print("Iterating on scaffold over: " + _questions[i].hashCode.toString());
    }
    return Scaffold(
      key: _formKey,
      resizeToAvoidBottomPadding: true,
      backgroundColor: Colors.white,
      body: Container(
        padding: new EdgeInsets.all(20.0),
        width: double.infinity,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            SizedBox(height: 20),
            _displayTitle(),
            new Expanded(
              // height: 300,
              child: new ListView(
                children: <Widget>[
                  ..._questions,
                ],
                scrollDirection: Axis.vertical,
              ),
            ),
            Row(
              children: <Widget>[
                new FlatButton(
                  onPressed: () => addServiceCard(),
                  child: new Icon(Icons.ac_unit),
                ),
              ],
            ),
            const Divider(
              color: Colors.black,
              height: 20,
              thickness: 1,
              indent: 0,
              endIndent: 0,
            ),
            Row(
              children: <Widget>[
                new Expanded(child: FlatButton(
                  onPressed: () {
                    //return to prev window
                    Navigator.pop(context);
                  },
                  padding: EdgeInsets.symmetric(vertical: 10),
                  child: Text('Cancel', style: TextStyle(fontSize: 20.0)),
                ), flex: 2),
                new Expanded(child: RaisedButton(
                  child: Text('Create form', style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold)),
                  onPressed: () {
                    //FormModel form = FormModel(myController.text, _questions);
                    //String json = jsonEncode(form);
                    //print(json);

                    //_formKey.currentState.save();
                    //Navigator.push(context, MaterialPageRoute(builder: (context) => Result(model: this.model)));
                  },
                ), flex: 3)
              ],
            )
          ],
        ),
      ),
    );
  }

  _displayTitle(){
    return TextField(
      controller: myController,
      maxLines: null,
      autofocus: true,
      decoration: InputDecoration(
          border: InputBorder.none,
          hintText: 'Form title'
      ),
      style: TextStyle(
          color: Colors.black,
          fontSize: 20,
          fontWeight: FontWeight.bold
      ),
    );
  }
}

class Question extends StatefulWidget {
  final int index;
  final Function(Question) removeQuestion;

  const Question(this.removeQuestion, this.index);

  void remove(){
    print("Called remove on " + this.hashCode.toString());

    removeQuestion(this);
  }

  @override
  _Question createState() => new _Question(index, remove);
}

class _Question extends State<Question> {
  final int questionIndex;
  final Function() remove;
  _Question(this.questionIndex, this.remove);
  int _numOptions = 1;

  @override
  Widget build(BuildContext context) {
    List<Widget> _options = new List.generate(_numOptions, (int i) => new Options(_numOptions));
    return Container(
      color: Colors.accents.elementAt(3 * questionIndex),
      child: Column(
        children: <Widget>[
          Row(
            children: <Widget>[
              Flexible(
                flex: 2,
                child: new TextFormField(
                  maxLines: null,
                  decoration: InputDecoration(
                    border: InputBorder.none,
                    hintText: 'Question title',
                    isDense: true,
                    prefixIcon:Text((questionIndex + 1).toString() + ". "),
                    prefixIconConstraints: BoxConstraints(minWidth: 0, minHeight: 0),
                  ),
                ),
              ),
              new IconButton(
                icon: new Icon(Icons.delete),
                onPressed: (){
                  print("delete pressed");
                  remove();
                }
              ),
            ],
          ),
          new ListView(
            physics: NeverScrollableScrollPhysics(),
            children: _options,
            scrollDirection: Axis.vertical,
            shrinkWrap: true,
          ),
          Row(
            children: <Widget>[
              new FlatButton(
                onPressed: (){
                  setState(() {
                    _numOptions++;
                  });
                },
                child: new Icon(Icons.add),
              ),
              new FlatButton(
                onPressed: (){
                  setState(() {
                    _numOptions--;
                  });
                },
                child: new Icon(Icons.remove),
              ),
            ],
          )
        ],
      ),
    );
  }
}


class Options extends StatefulWidget {
  int _numOptions;

  Options(int numOptions){
    this._numOptions = numOptions;
  }

  @override
  State<StatefulWidget> createState() => new _Options(_numOptions);
}

class _Options extends State<Options> {
  int _numOptions;
  _Options(int numOptions){
    this._numOptions = numOptions;
  }

  @override
  Widget build(BuildContext context) {
    return Padding(padding: EdgeInsets.only(left: 15),
      child: new TextFormField(
        maxLines: null,
        decoration: InputDecoration(
          border: InputBorder.none,
          hintText: 'Option',
          isDense: true,
          prefixIcon:Text(_numOptions.toString() + ". "),
          prefixIconConstraints: BoxConstraints(minWidth: 0, minHeight: 0),
        ),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

Gab*_*che 6

它会更改哈希值,因为您在构建方法中定义列表,基本上每次小部件重建自身时都会调用该方法(因此每次您调用 setState() ),并且每次重新定义列表时它都会随机化新的哈希码。

我改变了你的代码,这应该可以解决你的问题,并且看起来应该更干净一些:

class StreamBuilderIssue extends StatefulWidget {
  @override
  _StreamBuilderIssueState createState() => _StreamBuilderIssueState();
}

class _StreamBuilderIssueState extends State<StreamBuilderIssue> {
  
  List<ServiceCard> serviceCardList = []; //Define the dynamic list

  void removeServiceCard(index) {
    setState(() {
      serviceCardList.remove(index);
    });
  }

  void addServiceCard() {
    setState(() {
    serviceCardList.add(ServiceCard(removeServiceCard, index: serviceCardList.length));
    });
  }


  @override
  void initState() {
    addServiceCard(); //Initialize with 1 item
    super.initState();
  }



  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView(
        children: <Widget>[
          ...serviceCardList,
          FlatButton(
            onPressed: () => addServiceCard(),
            child: new Icon(Icons.add),
          ),
        ],
      ),
    );
  }
}

class ServiceCard extends StatelessWidget {
  final int index;
  final Function(ServiceCard) removeServiceCard;

  const ServiceCard(this.removeServiceCard, {Key key, @required this.index})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Text(
            'Index: ${index.toString()} Hashcode: ${this.hashCode.toString()}'),
        RaisedButton(
          color: Colors.accents.elementAt(3 * index),
          onPressed: () {
            removeServiceCard(this);
          },
          child: Text('Delete me'),
        ),
      ],
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 完美,工作正常。只需编辑帖子,使其包含“serviceCardList.length”而不是“serviceCardList.length()”,以便将来的用户查看此内容。谢谢 :) (2认同)