Flutter ListView嵌套GridView.builder无法懒加载问题咨询
问题根因
GridView无法懒加载的核心原因是你配置了shrinkWrap: true + 非独立滚动物理特性:
开启shrinkWrap后,GridView会预先计算所有子项的总高度来适配外层ListView的滚动容器,不会按需渲染可见范围内的子项,必然会一次性构建全部网格元素。你单独使用GridView.builder时没有开启shrinkWrap,所以懒加载表现正常。
解决方案1(最优,性能最佳):改用CustomScrollView + Sliver组件,完全兼容ValueListenableBuilder
你认为Sliver无法配合ValueListenableBuilder实现自动刷新是认知误区,ValueListenableBuilder可以包裹任何Sliver组件,改造后代码示例如下:
CustomScrollView( slivers: <Widget>[ // 原有ListView中除了GridView之外的其他组件,用SliverToBoxAdapter包裹即可 SliverToBoxAdapter( child: /* 其他非网格内容 */, ), // 第一个网格组件 ValueListenableBuilder<List<MyObject>?>( valueListenable: xyz, builder: (BuildContext context, List<MyObject>? myList, Widget? child) { return SliverGrid.builder( itemCount: myList?.length ?? 0, gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( childAspectRatio: uiConfig.getPosterAspectRatio(2 / 3.5), maxCrossAxisExtent: uiConfig.getPosterSize(), crossAxisSpacing: 5, mainAxisSpacing: 10, ), itemBuilder: (BuildContext context, int index) { final item = myList![index]; return CustomWidget(item); }, ); }, ), // 第二个网格组件同理配置即可 ValueListenableBuilder<List<MyObject>?>( valueListenable: xyz2, builder: (context, myList2, child) { return SliverGrid.builder( itemCount: myList2?.length ?? 0, gridDelegate: /* 对应网格配置 */, itemBuilder: (context, index) => CustomWidget2(myList2![index]), ); }, ) ], )
该方案完全保留ValueListenableBuilder的自动刷新能力,所有子项都走CustomScrollView的统一懒加载逻辑,不会一次性构建全部元素,性能最优。
解决方案2(临时兼容,不改动原有ListView结构)
如果你坚持要保留外层ListView的嵌套结构,可以给GridView设置固定可视高度,关闭shrinkWrap,示例代码如下:
ListView( children: <Widget>[ SizedBox( // 设置GridView在页面中的固定展示高度,可根据业务需求调整 height: 500, child: ValueListenableBuilder( builder: (BuildContext context, List<MyObject>? myList, Widget? child) { return GridView.builder( itemCount: myList?.length ?? 0, // 移除shrinkWrap: true配置 // 保留默认滚动物理特性,允许GridView内部独立滚动 gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( childAspectRatio: uiConfig.getPosterAspectRatio(2 / 3.5), maxCrossAxisExtent: uiConfig.getPosterSize(), crossAxisSpacing: 5, mainAxisSpacing: 10, ), itemBuilder: (BuildContext context, int index) { final item = myList![index]; return CustomWidget(item); }, ); }, valueListenable: xyz, ), ), buildGridView2(), ], )
该方案的缺点是会出现嵌套滚动,用户需要滚动完GridView内部内容才能继续滚动外层ListView,使用体验不如方案1。
内容的提问来源于stack exchange,提问作者Mihai
相关产品推荐
相关产品推荐

