关于flutter列表的性能优化,你必须要了解的
Posted 早起的年轻人-坚果の博客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于flutter列表的性能优化,你必须要了解的相关的知识,希望对你有一定的参考价值。
这里是坚果前端小课堂,大家喜欢的话,可以关注我的公众号“坚果前端,”,或者加我好友,获取更多精彩内容
嵌套列表 - ShrinkWrap 与 Slivers
使用 ShrinkWrap 的列表列表
下面是一些使用ListView
对象呈现列表列表的代码,内部列表的shrinkWrap
值设置为 true。shrinkWrap
强行评估整个内部列表,允许它请求有限的高度,而不是通常的ListView
对象高度,即无穷大!
下面是基本的代码结构:
ListView(
// Setting `shrinkWrap` to `true` here is both unnecessary and expensive.
children: <Widget>[
ListView.builder(
itemCount: list1Children.length,
itemBuilder: (BuildContext context, int index)
return list1Children[index];
,
// This forces the `ListView` to build all of its children up front,
// negating much of the benefit of using `ListView.builder`.
shrinkWrap: true,
),
ListView.builder(
itemCount: list2Children.length,
itemBuilder: (BuildContext context, int index)
return list2Children[index];
,
// This forces the `ListView` to build all of its children up front,
// negating much of the benefit of using `ListView.builder`.
shrinkWrap: true,
),
...
],
)
注意:观察外部
ListView
没有将其shrinkWrap
值设置为true
。只有内部列表需要设置shrinkWrap
。
另请注意:虽然
ListView.builder
(默认情况下)有效地构建其子项,为您节省构建屏幕外小部件的不必要成本,但设置shrinkWrap
为true
覆盖此默认行为!
import 'package:flutter/material.dart';
import 'dart:math' as math;
void main()
runApp(ShrinkWrApp());
class ShrinkWrApp extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'ShrinkWrap vs Slivers',
home: Scaffold(
appBar: AppBar(
title: const Text("ShrinkWrap, Street Rat, I don't, Buy that!"),
),
body: const ShrinkWrapSlivers(),
),
);
class ShrinkWrapSlivers extends StatefulWidget
const ShrinkWrapSlivers(
Key? key,
) : super(key: key);
@override
_ShrinkWrapSliversState createState() => _ShrinkWrapSliversState();
class _ShrinkWrapSliversState extends State<ShrinkWrapSlivers>
List<ListView> innerLists = [];
final numLists = 15;
final numberOfItemsPerList = 100;
@override
void initState()
super.initState();
for (int i = 0; i < numLists; i++)
final _innerList = <ColorRow>[];
for (int j = 0; j < numberOfItemsPerList; j++)
_innerList.add(const ColorRow());
innerLists.add(
ListView.builder(
itemCount: numberOfItemsPerList,
itemBuilder: (BuildContext context, int index) => _innerList[index],
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
),
);
@override
Widget build(BuildContext context)
return ListView.builder(
itemCount: numLists,
itemBuilder: (context, index) => innerLists[index]);
@immutable
class ColorRow extends StatefulWidget
const ColorRow(Key? key) : super(key: key);
@override
State createState() => ColorRowState();
class ColorRowState extends State<ColorRow>
Color? color;
@override
void initState()
super.initState();
color = randomColor();
@override
Widget build(BuildContext context)
print('Building ColorRowState');
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
randomColor(),
randomColor(),
],
),
),
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(height: 50, width: 50, color: Colors.white),
),
Flexible(
child: Column(
children: const <Widget>[
Padding(
padding: EdgeInsets.all(8),
child: Text('这里是 坚果前端小课堂!',
style: TextStyle(color: Colors.white)),
),
],
),
),
],
),
);
Color randomColor() =>
Color((math.Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0);
一切都建立起来!
当您滚动浏览此 UI 并注意该ColorBarState.build
方法的调用方式时,会出现可怕的部分 。每个内部列表包含 100 个元素,因此当 UI 加载时,您会立即看到 100 个“Building ColorBarState”的实例打印到控制台,
更糟糕的是,一旦向下滚动大约一百行,就会再生成一百行。😱😱😱
而且你滑动的快的时候列表会抖动!
重新构建嵌套列表
要了解如何使您的用户免受卡顿威胁,请等待我的第二节,下一节将使用 Slivers 而不是 ListViews 重建相同的 UI。
使用 Slivers 的列表列表
下面的代码构建了与之前相同的 UI,但这次它使用Slivers
而不是收缩包装ListView
对象。本页的其余部分将引导您逐步完成更改。
如何将嵌套列表迁移到 Slivers
第1步
首先,将最外面的 ListView 更改为SliverList
.
// Before
@override
Widget build(BuildContext context)
return ListView.builder(
itemCount: numberOfLists,
itemBuilder: (context, index) => innerLists[index],
);
变成:
// After
@override
Widget build(BuildContext context)
return CustomScrollView(slivers: innerLists);
第2步
其次,将内部列表的类型从List<ListView>
更改为 List<SliverList>
。
// Before
List<ListView> innerLists = [];
变成:
// After
List<SliverList> innerLists = [];
第 3 步
现在是时候重建内部列表了。的SliverList
类是比原始略有不同ListView
的类,与主要差异是的外观delegate
。
原始版本ListView
对所有内容都使用对象,不知道内部构建器构造函数将被shrinkWrap
.
// Before
@override
void initState()
super.initState();
for (int i = 0; i < numberOfLists; i++)
final _innerList = <ColorRow>[];
for (int j = 0; j < numberOfItemsPerList; j++)
_innerList.add(const ColorRow());
innerLists.add(
ListView.builder(
itemCount: numberOfItemsPerList,
itemBuilder: (BuildContext context, int index) => _innerList[index],
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
),
);
更改后,ListView
对象被替换为SliverList
对象,每个对象都使用一个SliverChildBuilderDelegate
来提供高效的按需构建。
// After
@override
void initState()
super.initState();
for (int i = 0; i < numLists; i++)
final _innerList = <ColorRow>[];
for (int j = 0; j < numberOfItemsPerList; j++)
_innerList.add(const ColorRow());
innerLists.add(
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) => _innerList[index],
childCount: numberOfItemsPerList,
),
),
);
完整代码:
import 'package:flutter/material.dart';
import 'dart:math' as math;
void main()
runApp(SliversApp());
class SliversApp extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'ShrinkWrap vs Slivers',
home: Scaffold(
appBar: AppBar(
title: const Text("Revenge of the Slivers"),
),
body: const ShrinkWrapSlivers(),
),
);
class ShrinkWrapSlivers extends StatefulWidget
const ShrinkWrapSlivers(
Key? key,
) : super(key: key);
@override
_ShrinkWrapSliversState createState() => _ShrinkWrapSliversState();
class _ShrinkWrapSliversState extends State<ShrinkWrapSlivers>
List<SliverList> innerLists = [];
final numLists = 15;
final numberOfItemsPerList = 100;
@override
void initState()
super.initState();
for (int i = 0; i < numLists; i++)
final _innerList = <ColorRow>[];
for (int j = 0; j < numberOfItemsPerList; j++)
_innerList.add(const ColorRow());
innerLists.add(
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) => _innerList[index],
childCount: numberOfItemsPerList,
),
),
);
@override
Widget build(BuildContext context)
return CustomScrollView(slivers: innerLists);
@immutable
class ColorRow extends StatefulWidget
const ColorRow(Key? key) : super(key: key);
@override
State createState() => ColorRowState();
class ColorRowState extends State<ColorRow>
Color? color;
@override
void initState()
super.initState();
color = randomColor();
@override
Widget build(BuildContext context)
print('Building ColorRowState');
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
randomColor(),
randomColor(),
],
),
),
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(height: 50, width: 50, color: Colors.white),
),
Flexible(
child: Column(
children: const <Widget>[
Padding(
padding: EdgeInsets.all(8),
child: Text('这里是坚果前端小课堂!',
style: TextStyle(color: Colors.white)),
),
],
),
),
],
),
);
Color randomColor() =>
Color((math.Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0);
Lazy building!
上面的代码已经应用了这些更改。运行应用程序并注意 Flutter 不再需要立即渲染 100 个 ColorRow 小部件。当您滚动时,会动态构建更多小部件,正如您所期望的那样。更好的是,一直滚动到下一个列表也不会产生任何特殊费用。
Flutter 会根据需要重新构建小部件,而且很快。
这节课对你来说怎么样,可以的话,支持一下吧
你快速的滑动的时候会发现,这个时候的列表没有抖动!
以上是关于关于flutter列表的性能优化,你必须要了解的的主要内容,如果未能解决你的问题,请参考以下文章