Flutter ListView 不使用 setState() 刷新 UI,尽管 itemCount 和附加列表正确更新

Posted

技术标签:

【中文标题】Flutter ListView 不使用 setState() 刷新 UI,尽管 itemCount 和附加列表正确更新【英文标题】:Flutter ListView doesn't refresh UI with setState(), although itemCount and attached list update correctly 【发布时间】:2020-03-14 03:21:11 【问题描述】:

我看到很多人有非常相似的问题,但我尝试的任何方法都不起作用。

上下文

我有一个收藏的想法列表。每当我单击ideaItem 内的按钮时,它都会从列表中删除。

问题

当我删除任何ideaItem 时,屏幕上的最后一个总是被删除,而不是我点击的那个。我的 FavoriteIdeasListView 似乎正确更新了项目数,这意味着附加的列表有效,但 UI 没有重绘 ideaItems。

我已经尝试过什么

一开始我直接在ideaItem 上有删除功能,我读到我应该做一个VoidCallback 并处理从列表本身删除,所以它会注意到变化。没用

我也尝试过使用 Stream Builder,因此流会通知 ListView 刷新。也没用

我尝试一直调用 SetState 并且它没有重新加载,它只是在 initialState 的开头构建列表。


    class FavoritesList extends StatefulWidget 
      FavoritesList(Key key) : super(key: key);

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

    class _FavoritesListState extends State<FavoritesList> 

      List<Idea> _favorites = [];

      @override
      void initState() 
        super.initState();
        favoritesInitialState();
      

      Future <void> favoritesInitialState() async 
        List<Idea> ideas = await IdeasDB.db.ideas();
        setState(() 
          _favorites = ideas;
        );
      

      @override
      Widget build(BuildContext context) 
        return Scaffold(
            appBar: AppBar(
                title: Text('Favorites')),
            body: Center(
              child: ListView.builder(
                itemCount: _favorites.length,
                itemBuilder: (context, index) 
                  final idea = _favorites[index];
                  return IdeaItem(
                    idea: idea,
                    onFavoriteToggle: () => deleteFromFavorites(idea),
                  );
                ,
              ),
            )
        );
      

      void deleteFromFavorites(Idea idea) async 
        await IdeasDB.db.deleteIdea(idea.url);
        List<Idea> newIdeas = await IdeasDB.db.ideas();
        setState(() 
          this._favorites = newIdeas;
        );
      
    

【问题讨论】:

【参考方案1】:

尝试改变

onFavoriteToggle: () => deleteFromFavorites(index),

然后

  void deleteFromFavorites( int index) async 
    await IdeasDB.db.deleteIdea(idea.url);
    setState(() 
      this._favorites.removeAt(index);
    );
  

看看它是否有效。如果是,deleteIdea() 上可能有错误。

此外,您知道应该删除哪个项目,因此您无需等待从数据库中重新加载列表。 await IdeasDB.db.ideas();

如果您想确保可以使用 Try catch 包装您的代码

  void deleteFromFavorites(int index) async 
    try 
      await IdeasDB.db.deleteIdea(idea.url);
      setState(() 
        this._favorites.removeAt(index);
      );
     catch (_) 
      print('ERROR');
    
  

【讨论】:

解决方案是传递 'key' 参数。我赞成,因为优化解决方案消除了不必要的等待!谢谢!【参考方案2】:

对于为什么会发生这种情况,我只能看到两种可能的选择。

第一个很明显,您的IdeasDB.db.deleteIdea(idea.url); 没有从数据库中删除正确的想法;

第二个不太明显,这是因为您的元素树无法识别您要删除的小部件是什么。它只发生在有状态的小部件上,就像您的 IdeaItem 一样。

然后解决方案是为您的IdeaItem 小部件使用key 属性 像这样:

ListView.builder(
  itemCount: _favorites.length,
  itemBuilder: (context, index) 
    final idea = _favorites[index];
    return IdeaItem(
      key: ValueKey(idea.url) // or UniqueKey()
      idea: idea,
      onFavoriteToggle: () => deleteFromFavorites(idea),
    );
  ,
),

在您的 IdeaItem 小部件中,您必须将该密钥传递给您的父级 Stateful widget,如下例所示:

class IdeaItem extends StatefulWidget 
  final Idea idea;
  Function onFavoriteToggle;

  IdeaItem(Key key, this.idea, this.onFavoriteToggle) : super(key: key);

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

【讨论】:

有效!解决方案是添加密钥,因为我的 IdeaItem 确实也是有状态的!太棒了!

以上是关于Flutter ListView 不使用 setState() 刷新 UI,尽管 itemCount 和附加列表正确更新的主要内容,如果未能解决你的问题,请参考以下文章

flutter listview item滚出屏幕不重置状态

Flutter项目中ListView的滚动不流畅

Flutter—ListView嵌套ListView不显示的问题

Flutter—ListView嵌套ListView不显示的问题

Flutter—ListView嵌套ListView不显示的问题

flutter获取listview可视区域的firstIndex和lastIndex,不破坏原有listview