携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情
前言
ListView.build 组件创建的列表是基于Sliver的懒加载创建的,会根据页面需要的数据进行加载和预加载数据,能够有效提升项目性能。但在一些特殊的情况下 ListView.build 组件会失去懒加载的功能,导致页面性能丢失的问题。
ListView.build 简介
listView.builder 组件具备可滚动和无限高度特性,仅对那些实际可见的子级调用构建器,适用于具有大量(或无限)子级的列表视图。
Creates a scrollable, linear array of widgets that are created on demand
This constructor is appropriate for list views with a large (or infinite number of children because the builder is called only for those children) that are actually visible.
ListView.builder(
itemCount: 100,
itemBuilder: (context, index) {
print(index);
return Container(
height: 200,
color: Colors.orange[index % 5 * 100],
child: Center(child: Text('$index')),
);
},
),
/// 控制台输出结果,只输出页面加载可见的数据和预加载部分数据
flutter: init called screen_utils
flutter: 0
flutter: 1
flutter: 2
flutter: 3
flutter: 4
flutter: 5
Reloaded 0 libraries in 368ms.
懒加载失效
在实际的项目开发过程中, listView.builder 组件往往不会单独出现,它会存在在多种嵌套或有大量数据交互的界面里。根据实际需求的不同, listView.builder 可能需要嵌套在其他滚动组件内部(如ListView,SingleChildScrollView等),这样会导致页面出现报错的情况:" Dart Unhandled Exception: Vertical viewport was given unbounded height. ",因为 listView.builder 组件具有无限高度的特性,不可以被套用在另一个无限高度的组件内。为确保页面正常显示,需要修改 listView.builder 两个属性值:
shrinkWrap: true, ///在滑动方向上的高度是否由内容高度决定
physics: const NeverScrollableScrollPhysics(), ///控制用户滚动视图的交互
存在问题:重新运行项目后页面能够正常加载数据,但是通过控制台打印数据能够发现,由于修改了shrinkWrap和 physics这两个属性,在滑动页面内数据会一次性加载完成,ListView.builder会失去懒加载的功能;如果页面存在大量数据交互的话,这将会造成性能的严重损耗。
ListView(
children: [
const FlutterLogo(size: 100), /// 页面可能存在其他组件
ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: 100,
itemBuilder: (context, index) {
print(index);
return Container(
height: 200,
color: Colors.orange[index % 5 * 100],
child: Center(child: Text('$index')),
);
},
),
],
),
/// 控制台输出结果,数据一次性加载完成
flutter: init called screen_utils
flutter: 0
flutter: 1
flutter: 2
flutter: 3
flutter: 4
······
flutter: 96
flutter: 97
flutter: 98
flutter: 99
Reloaded 1 of 2339 libraries in 704ms.
优化方案
在套用多层滚动组件的页面,可以使用Sliver系列组件代替普通组件进行布局。 SliverList 嵌套在 CustomScrollView 组件内可以实现懒加载功能,对页面加载性能进行优化。
/// [CustomScrollView] 允许直接提供 [slivers] 以创建各种滚动效果
CustomScrollView(
slivers: [
/// [SliverToBoxAdapter] 是一个基本的 sliver,通过它可以在 [slivers] 组件内书写普通组件。
const SliverToBoxAdapter(child: FlutterLogo(size: 100)),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
print(index);
return Container(
height: 200,
color: Colors.orange[index % 5 * 100],
child: Center(child: Text('$index')),
);
},
childCount: 100,
),
),
],
)
/// 控制台输出结果,恢复懒加载,只输出页面加载可见的数据和预加载部分数据
flutter: init called screen_utils
flutter: 0
flutter: 1
flutter: 2
flutter: 3
flutter: 4
Reloaded 0 libraries in 200ms.
小结
ListView.build 组件大部分情况下都能够实现我们的需求,但是在部分情况下容易失去懒加载功能,我们可以使用 SliverList 组件代替 ListView.build 进行布局。在Sliver大家族中,除 SliverList 以外,还有着许许多多强大的功能组件,可以实现许多复杂的滑动嵌套布局,有待进一步学习。