关于flutter列表的性能优化,你必须要了解的

简介: 这里是坚果前端小课堂,大家喜欢的话,可以关注我的公众号“坚果前端,”,或者加我好友,获取更多精彩内容嵌套列表 - ShrinkWrap 与 Slivers

这里是坚果前端小课堂,大家喜欢的话,可以关注我的公众号“坚果前端,”,或者加我好友,获取更多精彩内容

嵌套列表 - ShrinkWrap 与 Slivers

image.png

使用 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”的实例打印到控制台,


更糟糕的是,一旦向下滚动大约一百行,就会再生成一百行。😱😱😱


image.png


而且你滑动的快的时候列表会抖动!


重新构建嵌套列表

要了解如何使您的用户免受卡顿威胁,请等待我的第二节,下一节将使用 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更改为 List

// 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 会根据需要重新构建小部件,而且很快。


这节课对你来说怎么样,可以的话,支持一下吧


你快速的滑动的时候会发现,这个时候的列表没有抖动!


相关文章
|
5天前
|
缓存
使用Riverpod在Flutter中创建Todo列表
学习如何使用Riverpod在Flutter中构建一个功能完整的Todo列表应用。通过Consumer组件、ConsumerStatefulWidget类、ref.read方法和provider build重写,了解Riverpod的状态管理和更新状态机制。
270 6
使用Riverpod在Flutter中创建Todo列表
|
9月前
|
Dart 监控 算法
Flutter性能优化分析
使用工具来分析Flutter的性能瓶颈
315 0
|
10月前
|
Android开发
Flutter一天一控件之ListTile(列表的实现)
Flutter一天一控件之ListTile(列表的实现)
|
iOS开发 容器
重识Flutter 在不同的滑动列表场景,请选择合适的Slivers - part2
在Flutter中,碰到复杂的、不同的滑动业务场景,若是选择了一个合适的Slivers组件,那么我认为问题会变得简单!
重识Flutter 在不同的滑动列表场景,请选择合适的Slivers - part2
|
测试技术 容器
Flutter Web网站之Markdown展示与博客列表
Flutter Web网站之Markdown展示与博客列表
173 0
Flutter Web网站之Markdown展示与博客列表
Flutter之 动态列表
Flutter之 动态列表
102 0
Flutter之 动态列表
Flutter之 横向列表、自定义组件
Flutter之 横向列表、自定义组件
155 0
Flutter之 横向列表、自定义组件
|
Dart IDE 开发工具
Flutter 图文并茂列表实现
Flutter使用 ListView 完成列表的构建,界面实现的关键工作实际是布局子元素的拆分。剩下的实现方式存在多种,看各人喜好。但是,需要注意避免过多嵌套导致代码不好维护,并需要提高复用性。
806 2
Flutter 图文并茂列表实现
|
前端开发
Flutter 之列表下拉刷新和上拉加载
在实际的 App 中,下拉刷新和上滑加载更多是非常常见的交互形式。在 Flutter 中,有 flutter_easyrefresh开源插件用于实现下拉刷新和上滑加载更多。本篇介绍了有状态组件和 flutter_easyrefresh 的基本应用,同时使用模拟的方式完成了异步数据加载。
605 0
Flutter 之列表下拉刷新和上拉加载
|
前端开发 JavaScript API
Flutter(六)之豆瓣电影列表
学习完列表渲染后,我打算做一个综合一点的练习小项目:豆瓣Top电影排行列表;
188 0
Flutter(六)之豆瓣电影列表