从列表中删除项目后更新 UI

Posted

技术标签:

【中文标题】从列表中删除项目后更新 UI【英文标题】:Update UI after removing items from List 【发布时间】:2019-01-26 14:23:15 【问题描述】:

如果我删除或添加项目,我想更新我的 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 立即更新成为可能?

编辑:这不是重复的,因为

    我不想为了更新 UI 而创建列表的新实例。 答案无效:我添加了this.items = List.from(this.items);,但我的应用程序的行为与上面已经描述的相同。 我不想通过调用List.from 来破坏我的引用链,因为我的架构有一个被多个类引用的列表。如果我打破链条,我必须自己更新所有参考资料。我的架构有问题吗?

【问题讨论】:

ListView does not refresh whereas attached list does (Flutter)的可能重复 @RémiRousselet 我遵循了答案,但我的解决方案没有任何区别。该项目已删除,但未在 UI 中更新。我也不想创建一个新列表,因为那时我在 ItemViewWidget 中的列表与 MyApp 列表“断开连接”。也可能是我的架构不好,有更好的方法来完成我想做的事情吗? 您的列表实例保持不变。你不应该打电话给list.remove 为什么我不能调用 list.remove?我用调试器查看了我的代码,并比较了 List.from 前后实例的哈希码。之前是 623876470,之后是 812698102。我猜 List.from 创建了一个新实例(或者哈希码不是跟踪实例的正确指标)。 就是这样。您应该故意创建一个新实例,因此 List.from 【参考方案1】:

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

Flutter 使用不可变对象。不遵守这条规则违反了响应式框架。这是减少错误的自愿要求。

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


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

问题是,由于是您的项目进行计算,因此永远不会通知父级列表已更改。因此它不知道它应该重新渲染。所以视觉上没有任何变化。

要解决此问题,您应该将删除逻辑移至父级。并确保家长相应地正确调用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,
    );
  

【讨论】:

所以我评估了您的解决方案,它按原样工作。但是,我将您的 StatelessWidget 修改为 StatefulWidget 并且该解决方案不再有效。我刚刚添加了一个带有两个参数 title 和 onDelete 的构造函数,并将它们也传递给了 State。任何线索这是为什么?如果您需要我的代码,我可以编辑我的答案。 我在您的ItemInfoViewWidget 中看到您实际上将参数传递给State 构造函数。别。见***.com/questions/50818770/…这可能是你的问题 这就是问题所在。

以上是关于从列表中删除项目后更新 UI的主要内容,如果未能解决你的问题,请参考以下文章

删除项目后如何更新列表视图?

LazyColumn 项目从列表中删除后保留在内存中

SwipeCellKit:为啥从列表中删除一个项目,不更新 UITableview?

回收站视图仅在我从房间数据库中删除后重新打开活动时更新

从列表框中删除项目(MS Access VBA)

从列表中删除项目时索引不更新