如果我删除或添加项目,我想更新我的ListView.现在我只想删除项目,并立即查看项目的删除情况.

我的应用程序比较复杂,所以我编写了一个小示例项目来说明我的问题.

TestItem类包含一些数据条目:

class TestItem {
  static int id = 1;
  bool isFinished = false;
  String text;
  TestItem() {
    text = "Item ${id++}";
  }
}

ItemInfoViewWidgetTestItem的UI表示,如果完成则删除该项(只要复选框更改为true).

class ItemInfoViewWidget extends StatefulWidget {
  TestItem item;
  List<TestItem> items;

  ItemInfoViewWidget(this.items, this.item);

  @override
  _ItemInfoViewWidgetState createState() =>
      _ItemInfoViewWidgetState(this.items, this.item);
}

class _ItemInfoViewWidgetState extends State<ItemInfoViewWidget> {
  TestItem item;
  List<TestItem> items;

  _ItemInfoViewWidgetState(this.items, this.item);

  @override
  Widget build(BuildContext context) {
    return new Card(
      child: new Column(
        children: <Widget>[
          new Text(this.item.text),
          new Checkbox(
              value: this.item.isFinished, onChanged: isFinishedChanged)
        ],
      ),
    );
  }

  void isFinishedChanged(bool value) {
    setState(() {
      this.item.isFinished = value;
      this.items.remove(this.item);
    });
  }
}

ItemViewWidget级建造了ListView.

class ItemViewWidget extends StatefulWidget {
  List<TestItem> items;

  ItemViewWidget(this.items);

  @override
  _ItemViewWidgetState createState() => _ItemViewWidgetState(this.items);
}

class _ItemViewWidgetState extends State<ItemViewWidget> {
  List<TestItem> items;

  _ItemViewWidgetState(this.items);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: new Text('Test'),
      ),
      body: ListView.builder(
          itemCount: this.items.length,
          itemBuilder: (BuildContext context, int index) {
            return new ItemInfoViewWidget(this.items, this.items[index]);
          }),
    );
  }
}

MyApp显示了一个TestItem和一个导航到ItemViewWidget页的按钮.

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  List<TestItem> items = new List<TestItem>();

  _MyHomePageState() {
    for (int i = 0; i < 20; i++) {
      this.items.add(new TestItem());
    }
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text(widget.title),
        ),
        body: new Column(
          children: <Widget>[
            ItemInfoViewWidget(this.items, this.items.first),
            FlatButton(
              child: new Text('Open Detailed View'),
              onPressed: buttonClicked,
            )
          ],
        ));
  }

  void buttonClicked() {
    Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => ItemViewWidget(this.items)),
    );
  }
}

如果我切换第一个项目的复选框,该复选框将标记为已完成(如预期的那样),但它不会从UI中删除,但会从列表中删除.

然后我回到主页,我可以看到第1项也被选中了.

因此,如果我再次转到ItemViewWidget页,我可以观察到选中的项目不再存在. 基于这些观察,我得出结论,我的实现可以工作,但是我的UI没有更新.

如何更改代码才能立即更新UI?

Edit: This is not a duplicate, because

  1. 我不想仅仅为了更新UI而创建我的列表的新实例.
  2. 答案不起作用:我添加了this.items = List.from(this.items);个,但我的应用程序的行为与上面描述的相同.
  3. 我不想通过调用List.from来打破我的引用链,因为我的体系 struct 有一个被几个类引用的列表.如果我打破了链条,我必须自己更新所有的引用.我的架构有问题吗?

推荐答案

我不想仅仅为了更新UI而创建我的列表的新实例.

Flutter 使用不变的对象.不遵守这条规则是违反被动框架的.这是减少bug的自愿要求.

事实是,这种不变性在这里特别防止开发人员做您当前正在做的事情:拥有一个依赖于在类之间共享对象的相同实例的程序;因为多个类可能想要修改它.


真正的问题在于,是您的列表项从列表中删除了删除元素.

问题是,由于是您的项目进行计算,因此不会通知家长列表已更改.因此它不知道应该重新播放.所以视觉上没有任何变化.

要解决此问题,您应该将删除逻辑移动到父级.并确保家长相应地正确呼叫setState.这将转化为向列表项传递回调,删除时将调用该回调.

下面是一个示例:

class MyList extends StatefulWidget {
  @override
  _MyListState createState() => _MyListState();
}

class _MyListState extends State<MyList> {
  List<String> list = List.generate(100, (i) => i.toString());

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: list.length,
      itemBuilder: (context, index) {
        return MyItem(list[index], onDelete: () => removeItem(index));
      },
    );
  }

  void removeItem(int index) {
    setState(() {
      list = List.from(list)
        ..removeAt(index);
    });
  }
}

class MyItem extends StatelessWidget {
  final String title;
  final VoidCallback onDelete;

  MyItem(this.title, {this.onDelete});

  @override
  Widget build(BuildContext context) {
    return ListTile(
      title: Text(this.title),
      onTap: this.onDelete,
    );
  }
}

Dart相关问答推荐

具有级联省道的三元算子的奇异行为

空值判断运算符 - Flutter

将数据库提供程序导入另一个文件提供程序 dart

Asset图像作为Card背景,Flutter 中顶部有文本

如何实现 Iterable

如何在 Flutter DropDown 按钮中搜索

如何使我的对话框可滚动?

在小部件构建期间,如何在Flutter中导航?

更新复选框并从Flutter对话框中返回值

当文本在 Flutter 中溢出时如何使 Text 小部件像选取框一样

单击 TextField 小部件时 Flutter 小部件重建

参数类型Map Function()不能分配给参数类型Map

将函数/方法分配给 Dart 中的变量

Dart 是否支持参数化单元测试?

VS Code 首选项的位置

使用 Dart 解析 URI 以提取查询参数

从 Dart 应用访问 pubspec.yaml 属性

在 Dart 中打开类类型

你如何在 Dart 中打印美元符号 $

extends与 implements与 with