首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >每次调用setState()时,Flutter ListView都会跳到顶部

每次调用setState()时,Flutter ListView都会跳到顶部
EN

Stack Overflow用户
提问于 2019-05-16 05:42:25
回答 3查看 1.8K关注 0票数 10

我有一个客户的ListView,其中用户能够选中/取消选中每个客户,以将他们标记为“收藏”。但是,每次点击ListItem时,ListView都会跳回到屏幕顶部。

如何防止这种情况发生?我只希望复选框刷新,而不是整个屏幕,并防止屏幕每次都跳到顶部。

代码语言:javascript
运行
复制
@override
Widget build(BuildContext context) {
  customers = getAllCustomers();

  return Scaffold(
     appBar: _getAppBar(),
     body: customers != null
          ? FutureBuilder(
              future: getFavouriteCustomers(), // contains IDs of favourite customers
              builder: (context, snapshot) {
                if (snapshot.connectionState == ConnectionState.done) {
                  if (snapshot.hasData) {
                    List<String> favouriteCustomersList = snapshot.data;

                    return ListView.builder(
                        itemCount: customers?.length ?? 0,
                        itemBuilder: (BuildContext context, int index) {
                          customer c = customers?.elementAt(index);

                          if (favouriteCustomersList.contains(c.id)) {
                            c.isSelected = true;
                          }

                          return ListTile(
                            title: Text(c.name),
                            trailing: Checkbox(
                                value: c.isFavourite,
                                onChanged: (newValue) {}),
                            onTap: () {
                              if (c.isSelected) {
                                setState(() {
                                  c.setFavourite(false);
                                });
                              } else {
                                setState(() {
                                  c.setFavourite(true);
                                }
                              }
                            },
                          );
                        });
                  }
                } else {
                  return CircularProgressIndicator();
                }
              })
          : Center(
              child: CircularProgressIndicator(),
            );
);

}

EN

回答 3

Stack Overflow用户

发布于 2021-03-20 16:55:46

  1. 确保您在StatefulWidget.

中执行此操作

  1. 在状态类(而不是小部件类)中创建一个ScrollController as字段:

代码语言:javascript
运行
复制
  _controller = ScrollController(keepScrollOffset: true);

这样,控制器将保留在状态中,而不会在重建时重新创建。

  1. 将其作为

传递给构建器

代码语言:javascript
运行
复制
  controller: _controller,

  1. 记住在状态被销毁时释放控制器:

代码语言:javascript
运行
复制
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

正如Benno提到的那样,确保在重新构建时没有任何带有密钥的小部件在小部件树上向上移动:How to prevent Flutter app from scrolling to top after calling setState?

票数 1
EN

Stack Overflow用户

发布于 2021-05-19 02:29:09

我不确定您是否已经找到了答案,但我通常通过使用两个StatefulWidgets拆分您的构建方法来解决问题。这里发生的事情是,每次调用setState()时,整个小部件都会重新构建,因为整个state类的状态都发生了变化。这意味着build方法被调用,强制scaffold与其中的所有小部件树一起重新创建。由于小部件被重建,它将假定它的原始位置是偏移量为0。我建议创建另一个有状态小部件来返回ListTile对象。这将破坏build方法,在每个ListTile上执行的操作都是在它自己的build方法中执行的。这不会强制重新创建你的脚手架。

我没有你的完整代码,所以不能建议完整的工作解决方案,但像下面这样的东西可能会有所帮助。

代码语言:javascript
运行
复制
class SomeStatefulWidget extends StatefulWidget {

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

class SomeStatefulWidgetState extends State<SomeStatefulWidget> {
@override
Widget build(BuildContext context) {
  customers = getAllCustomers();

  return Scaffold(
     appBar: _getAppBar(),
     body: customers != null
          ? FutureBuilder(
              future: getFavouriteCustomers(), // contains IDs of favourite customers
              builder: (context, snapshot) {
                if (snapshot.connectionState == ConnectionState.done) {
                  if (snapshot.hasData) {
                    List<String> favouriteCustomersList = snapshot.data;

                    return ListView.builder(
                        itemCount: customers?.length ?? 0,
                        itemBuilder: (BuildContext context, int index) {
                          customer c = customers?.elementAt(index);

                          if (favouriteCustomersList.contains(c.id)) {
                            c.isSelected = true;
                          }

                          return MyListTile(c);

                        });
                  }
                } else {
                  return CircularProgressIndicator();
                }
              })
          : Center(
              child: CircularProgressIndicator(),
            );
);
}


class MyListTile extends StatefulWidget {

  customer c;

  @override
  _MyListTileState createState(this.c) => _MyListTileState();

}

class _MyListTileState extends State<MyListTile> {

  @override
  Widget build(BuildContext context) {

    var c = widget.c;

    return ListTile(
      title: Text(c.name),
      trailing: Checkbox(
          value: c.isFavourite,
          onChanged: (newValue) {}),
      onTap: () {
        if (c.isSelected) {
          setState(() {
            c.setFavourite(false);
          });
        } else {
          setState(() {
            c.setFavourite(true);
          }
        }
      },
    );
  }
}

通过将小部件一分为二,setState()现在将只通过调用MyListTileState类的build方法来构建MyListTile对象。您可以有效地将ListTile封装到自己的有状态类MyListTile中。

我希望这对你有用。如上所述,我没有你的完整代码,因此做了一些包装器类来使这个例子变得清晰。另外,我还没有编译这段代码,也没有在这里输入所有内容,所以可能会有一些编译时错误。但这解释了跳到顶部的概念、原因和解决方案。

票数 0
EN

Stack Overflow用户

发布于 2021-05-24 13:47:42

与使用setState()更新UI不同,您可以尝试将listTile包装在StreamBuilder中,并为其提供一个流,该流在每次点击复选框时发送数据。一旦StreamBuilder接收到来自流的数据,它将只更新单元格,而不是整个列表,最终您将能够避免列表的顶部滚动。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56158020

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档