【问题标题】:Flutter | Use Listview.builder inside ListView - Make whole page scrollable颤振 |在 ListView 中使用 Listview.builder - 使整个页面可滚动
【发布时间】:2019-09-23 06:27:38
【问题描述】:

我想建立一个类似 instagram 的个人资料页面,其中当前用户的所有帖子都列在一个提要中。在此之上,应显示有关用户的一些信息(头像、关注者数量、帖子数量、个人简介等)。 用户应该能够滚动整个页面。 目前页面只能滚动到我的 ListView.builder 中 sizedBox-widget 的高度。就这样:

ProfilePage类的构建方法如下:

Widget build(BuildContext context) {

    return SafeArea(
      child: Scaffold(    
        appBar: AppBar(),
        body: ListView(    
          children: <Widget>[
           Padding(
                    padding: const EdgeInsets.only(left: 25.0, top: 30.0),
                    child: Text(_user.displayName,
                        style: TextStyle(
                            color: Colors.black,
                            fontWeight: FontWeight.bold,
                            fontSize: 20.0)),
                  ),
                  ...(all the other information for the "header-part")

                  postImagesWidget(),

            ])));}

postImagesWidget 中,我使用 ListView.builder 构建提要:

Widget postImagesWidget(){
return FutureBuilder(
            future: _future,
            builder:
                ((context, AsyncSnapshot<List<DocumentSnapshot>> snapshot) {
              if (snapshot.hasData) {
                if (snapshot.connectionState == ConnectionState.done) {
                  return SizedBox(
                    height: 600,
                      child: ListView.builder(
                          physics: const NeverScrollableScrollPhysics(),

                          //shrinkWrap: true,
                          itemCount: snapshot.data.length,
                          itemBuilder: ((context, index) => ListItem(
                              list: snapshot.data,
                              index: index,
                              user: _user))));
                } else {
                  return Center(
                    child: CircularProgressIndicator(),
                  );
                }
              } else {
                return Center(
                  child: CircularProgressIndicator(),
                );
              }
            }),
          );

}

我必须添加一个 sizedBox-Widget 以避免此错误:

抛出了另一个异常:RenderBox 未布置:RenderViewport#50ccf NEEDS-PAINT

这个错误的描述说我应该将收缩包装添加到我的 ListView.builder 中,但是在出现这个错误的一些滚动之后,这会使我的整个应用程序崩溃:

断言失败:第 470 行 pos 12: 'child.hasSize': is not true.

如何使整个页面可滚动,而不是两个部分(页眉和提要)分开滚动?

我知道我在 ListView 中使用 ListView.builder...

最好的问候。

【问题讨论】:

    标签: listview dart flutter


    【解决方案1】:

    我个人不会在 Flutter 中将大小组件用于列表,仅当您希望布局的一部分在特定区域中可滚动时。

    但在您的情况下,我看到您希望整个页面都可以滚动,因此您有一个起点,您可以从该起点设计页面的其余部分。 (整个页面应该是可滚动的,所以我理解这个页面的顶部小部件应该是可滚动的小部件,如 SingleChildScrollView、CustomScrollView、ListView 等)

    现在您的方法没有错,但您需要知道 SizedBox 的最终高度,您可以根据您的列表计算出该高度。 (太难了)

    为了解决您的问题,我使用了带有 SliverList 小部件的 CustomScrollView。

    return SafeArea(
            child: Scaffold(
                appBar: AppBar(),
                body: CustomScrollView(
                  slivers: <Widget>[
                    SliverList(
                      delegate: SliverChildListDelegate(
                        [
                          Padding(
                            padding: const EdgeInsets.only(left: 25.0, top: 30.0),
                            child: Text(displayName,
                                style: TextStyle(
                                    color: Colors.black, fontWeight: FontWeight.bold, fontSize: 20.0)),
                          ),
                        ],
                      ),
                    ),
                    FutureBuilder(
                      future: ApiProvider.getPictures(),
                      builder: ((context, AsyncSnapshot snapshot) {
                        if (snapshot.hasData) {
                          if (snapshot.connectionState == ConnectionState.done) {
                            return SliverList(
                                delegate: SliverChildBuilderDelegate(
                                    (context, index) => Container(
                                          margin: EdgeInsets.symmetric(vertical: 10),
                                          child: Column(
                                            crossAxisAlignment: CrossAxisAlignment.start,
                                            children: <Widget>[
                                              Text(
                                                displayName,
                                                style: TextStyle(
                                                    fontSize: 17, fontWeight: FontWeight.bold),
                                              ),
                                              Text(snapshot.data[index].location),
                                              Image(
                                                image: NetworkImage(snapshot.data[index].pictureUrl),
                                                width: MediaQuery.of(context).size.width,
                                              )
                                            ],
                                          ),
                                        ),
                                    childCount: snapshot.data.length));
                          } else {
                            return SliverFillRemaining(
                              child: Center(
                                child: CircularProgressIndicator(),
                              ),
                            );
                          }
                        } else {
                          return SliverFillRemaining(
                            child: Center(
                              child: CircularProgressIndicator(),
                            ),
                          );
                        }
                      }),
                    ),
                  ],
                )));

    您的问题还有其他解决方案,但我相信这是性能最佳的解决方案。

    如果您需要帮助,请随时寻求帮助。

    【讨论】:

    • 感谢您的建议!我选择了 Marcel 的解决方案,但您能否解释一下,为什么您认为您的解决方案会更高效?
    • 在第二个 SliverList 中,您可以看到我使用了 SliverChildBuilderDelegate,它在项目滚动到屏幕时创建项目,类似于 ListView.builder。我使用它是因为您可能要显示很多图片,因此如果您不需要它,则无需创建图像小部件。我个人在我的项目中使用了类似于 Marcel 解决方案的代码,但是从内存和性能方面来说,映射列表并将其显示在列中可能不是最有效的方式。
    【解决方案2】:

    内部ListView 不是手头工作的正确小部件。您想要做的是在彼此上方显示多个小部件,Column 旨在执行此操作。 因此,不要使用内部ListView,而是考虑使用Column,如下所示:

    Column(
      children: snapshot.data.map((data) {
        return ListItem(
          data: data,
          user: _user,
        );
      }).toList(),
    )
    

    请注意,我更新了 ListItem 小部件以接受具体数据部分而不是列表和索引。

    现在您可能想知道Column 的性能是否不如ListView.builder,但事实并非如此。那是因为 ListView.builder 本身没有滚动,所以它没有懒惰地布局它的孩子。相反,外部ListView 将决定是否应显示所有内部ListView

    从长远来看,您可能希望延迟初始化小部件(尤其是当您的用户拥有数千张图片时)。这是a StackOverflow answer,介绍如何通过从 BLoC 提供多个 Futures 来做到这一点。

    【讨论】:

    • 很好的解释,谢谢!我尝试了您的解决方案并且它正在工作,直到我向上滚动并再次到达页面的标题部分。它引发以下错误:“indexOf(child)> index”:不正确。任何想法?我用谷歌搜索了它,但没有找到任何东西。
    • 解决了这个问题。它与 3 个 Streambuilders 相关,以获得帖子、关注者和追随者的数量。
    猜你喜欢
    • 1970-01-01
    • 2021-12-22
    • 2020-08-22
    • 2020-03-09
    • 1970-01-01
    • 2022-01-14
    • 2019-03-05
    • 1970-01-01
    • 2021-05-21
    相关资源
    最近更新 更多