Flutter: 维护子状态的有状态小部件

3
我在一个有状态的小部件中使用了ListView.builder,并为每个项目(ImageCard)创建了一个单独的有状态小部件。 在 ImageCard 小部件中,我添加了一个“喜欢”按钮,当我点击它时,它会变成红色(喜欢),灰色(不喜欢)。 问题是,当我向下滚动并返回时,颜色始终为灰色,这意味着没有保存状态! 如何通知父级有状态小部件以保留状态?
@override
  Widget build(BuildContext context) {
    return _buildListView(models, _scrollController);
  }

  Widget _buildListView(
      List<PhotoModel> models, ScrollController scrollController) {
    return Container(
        child: ListView.builder(
            controller: scrollController,
            itemCount: models.length,
            itemBuilder: (context, int index) {
              if (index == models.length - 1) {
                return SpinKitThreeBounce(
                  color: Colors.purple,
                  size: 30.0,
                );
              } else {
                return ImageCard(
                    models[index].regularPhotoUrl,
                    models[index].mediumProfilePhotoUrl,
                    models[index].name,
                    models[index].color);
              }
            }));
  }

子状态组件

class ImageCard extends StatefulWidget {
  final String imageUrl, userProfilePic, userName, color;

  ImageCard(this.imageUrl, this.userProfilePic, this.userName, this.color);

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

class _ImageCardState extends State<ImageCard> {
  bool isLiked = false, isFollowing = false;

  @override
  Widget build(BuildContext context) {
    return new Card( ....

void _onLikedBtnClicked() {
    setState(() {
      if (isLiked)
        isLiked = false;
      else {
        isLiked = true;
      }
    });
  }

那么在ImageCard中,您将isLiked属性保存在哪里?它是指color属性吗? - soupjake
我的代码中显示我将其保存在状态类中。 - M.Ali
如果 isLiked 为 true,我会将 btn 的颜色设置为红色。 - M.Ali
3个回答

4
Flutter会自动处理移出屏幕的小部件,当它们重新出现时,它们将被重新构建而不是恢复。因此,通常的做法是在高级小部件中保存状态,该小部件至少包含完整的业务逻辑方面,并且不会很快被处理掉。然后将状态更改映射到子小部件中。
对于您的特定情况,一个简单的解决方案是:您可以将信息存储在父小部件中,并在父小部件的构建函数中将它们映射到ImageCard上。
向模型添加isliked、isfollowing属性,然后...
class SomeParentState extends State<SomeParent> {
  List<Model> models;

  //.......

  @override
  Widget build(BuildContext context) {
    return _buildListView(models, _scrollController);
  }

  Widget _buildListView(List<PhotoModel> models,
      ScrollController scrollController) {
    return Container(
        child: ListView.builder(
            controller: scrollController,
            itemCount: models.length,
            itemBuilder: (context, int index) {
              if (index == models.length - 1) {
                return SpinKitThreeBounce(
                  color: Colors.purple,
                  size: 30.0,
                );
              } else {
                return ImageCard(
                  models[index].regularPhotoUrl,
                  models[index].mediumProfilePhotoUrl,
                  models[index].name,
                  models[index].color,
                  models[index].isLiked,
                  models[index].isFollowing,
                      () {
                    setState(() {
                      models[index].isLiked = !models[index].isLiked;
                    });
                  },
                      () {
                    setState(() {
                      models[index].isFollowing = !models[index].isFollowing;
                    });
                  },
                );
              }
            }));
  }
}


class ImageCard extends StatelessWidget{

  ImageCard(
      //...,
      this.isLiked,
      this.isFollowing,
      this.likeBtnClickedListener,
      this.followBtnClickedListener,
      )
  //...
  Widget build(BuildContext context){
    return Card(
      //.......
      IconButton(
        onPressed: likeBtnClickedListener,
      ),
      IconButton(
        onPressed: followBtnClickedListener,
      ),
    )
  }
}

这基本上能解决你的问题。无论如何,使用此方法更容易访问和同步子小部件中的数据。

如果您发现仅保持子窗口小部件处于活动状态更容易,您可以阅读AutomaticKeepAliveClientMixin的文档。它将防止Flutter在其移出视野时杀死此小部件。但它有可能导致内存泄漏。


你是正确的!我发现ListView.builder小部件按需创建和销毁项,并且状态在项被销毁时被丢弃,因此将ImageCard设置为无状态小部件是必要的。 - M.Ali

1
为了在 ListView 中保持小部件的状态,您需要 AutomaticKeepAliveAutomaticKeepAliveMixin(用于自定义小部件)。
这将确保在离开屏幕时不会销毁 State 实例。
ListView(
  children: [
    // Not kept alive
    Text('Hello World'),
    // kept alive
    AutomaticKeepAlive(
     child: Text("Hello World"),
    ),
  ]
),

谢谢您的提示,但@First_Strike的解决方案正是我正在寻找的。 - M.Ali

-2
你应该将状态单独保存。你可以创建一个 List<bool>,并为每个列表项设置一个值。你可能想要在某个时候保存或使用数据,那么这种机制就会变得无用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接