关于flutter:重识Flutter-用于解决复杂滑动视窗问题的Slivers-part1

35次阅读

共计 7016 个字符,预计需要花费 18 分钟才能阅读完成。

前言

在日常的开发工作中,仅仅应用 ListViewListView.builder 等这样的滑动组件就能满足大部分的业务需要,在碰到较为简单的滑动页面时,加上 Slivers 系列中的几个罕用组件也简略的实现。这也就导致了一些敌人没有较为残缺的去理解 Slivers 系列。那么在重识 Flutter 这个专栏中,预计有 5 篇 slivers 相干的文章,从组件的应用到其背地的渲染原理,让咱们一起摸索 Slivers 的魅力吧!


视窗是什么?Sliver 是什么?

置信点进这篇文章的敌人,肯定会晓得 ListView 这样的滚动组件,滚动组件会提供一个区块,用于滚动的显示内容,但内容很多时,咱们只能看到可滚动内容中的局部内容。这个就是视窗(ViewPort),也就是列表的可视区域大小。例如一个 ListView 的显示区域高度为 500 像素,它列表项总高度可能远远超过 500 个像素,然而它的 ViewPort 仍为 500 像素。

那么 Sliver 是什么呢?咱们能够通过 ListView.builder(),设置itemCount 为 null,构建一个有限的列表内容,只有当咱们滚动时,才会动静的去创立须要出现在视窗中的内容。这个就是 Sliver,如果一个滚动组件反对Sliver 模型,那么这个组件会将子组件分成很多个 Sliver,只有当 Sliver 呈现在 视窗 中才会构建。

CustomScrollView

ListViewGridView 等组件,在底层实现中都有着对应的 Sliver,如SliverListSliverGrid。Sliver 版本的可滚动组件和非 Sliver 版本的可滚动组件 最大的区别 就是:Sliver 版本的组件不蕴含滚动模型(Scrollable),组件本身不能滚动。

所以,如果想要应用 Sliver 系列的组件,就须要给它们增加滚动模型。Flutter 提供了 CustomScrollView,做为 Sliver 系列组件运行的容器。CustomScrollView 次要的作用就是提供 Viewport 和一个公共的 Scrollable,多个 Sliver 组件共用CustomScrollViewScrollable,就达到了繁多滚动的场景。

CustomScrollView有着许多属性,其中最罕用的便是 slivers,用来传入 Sliver 组件列表。就像这样:

Scaffold(
      body: CustomScrollView(
        slivers: [SliverList(/**/),
          SliverGrid(/**/),
        ],
      ),
    );

有了 CustomScrollView 组件的帮忙,实现简单的滚动成果,如同不是那么艰难。

SliverList

如果须要在一个界面创立多个列表,刚理解 Flutter 的敌人可能会应用 Column 中包裹多个 ListView 去实现,就像这样:

然而这样的成果必定不合乎需要,如果想要让它们一起滚动,或增加一些简单的动画,实现像这样的成果:

那么借助 SliverList 就能很简略的实现。

SliverList 是 Sliver Widget 的一种,作用是将 Widget 排列在一个 List 中,应用 SliverList 须要定义 delegateSliver delegate 是用于形容应用哪种办法对组件进行渲染,通常有两种:staticbuilder。

在 SliverList 中,能够定义两种类型的 delegate:

  • SliverChildListDelegate:获取须要显示的组件列表。此列表中定义的组件将被立刻出现。不会有任何提早加载。
  • SliverChildBuilderDelegate:获取将提早创立的小部件列表。这意味着随着用户滚动,残余的组件才会开始渲染。

能够简略的把 ListView 了解为:CustomScrollView + SliverList + SliverChildListDelegate;把 ListView.Builder 了解为:CustomScrollView + SliverList + SliverChildBuilderDelegate。

在理解了 SliverList 须要定义的 delegate 后,那么应用它就和应用 ListView 一样简略:

CustomScrollView(
        slivers: [
          SliverList(
            delegate: SliverChildListDelegate([
              Container(
                height: 50,
                color: Colors.primaries[0],
              ),
            ]),
          ),
          SliverList(delegate: SliverChildBuilderDelegate((BuildContext ctx, int index) {
              return Container(
                height: 50,
                color: Colors.primaries[index % Colors.primaries.length],
              );
            }, childCount: 5),
          ),
        ],
      ),

SliverGrid

SliverGridGridView 一样,将组件以一行两个或一行多个的模式排列。它除了和 SliverList 一样须要定义一个失常的 delegate 之外,还须要传入gridDelegate,用于形容每行如何显示组件。就像这样:每行最多 4 个,每个组件的宽度是高度的 1.5 倍。

SliverGrid(
  gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
      crossAxisCount: 4,
      crossAxisSpacing: 5,
      mainAxisSpacing: 3,
      childAspectRatio: 1.5),
  delegate:
      SliverChildBuilderDelegate((BuildContext context, int index) {
    return Container(color: Colors.primaries[index % Colors.primaries.length],
    );
  }, childCount: 20),
)

在 SliverGrid 中能够定义两种 gridDelegate:

  • SliverGridDelegateWithFixedCrossAxisCount:指定一行中有多少列,应用它会主动扩大到屏幕最大宽度。

<!—->

    • crossAxisCount 属性是列数
    • childAspectRatio 属性是宽高比
    • XXXSpacing 属性是指每个 item 之间的边距

<!—->

  • SliverGridDelegateWithMaxCrossAxisExtent: 能够指定列的宽度

<!—->

    • maxCrossAxisExtent 是列中的最大宽度

能够把 GridView.builder 了解为:CustomScrollView + SliverGrid + SliverChildBuilderDelegate + gridDelegate

遇到一些简略的需要,也能够应用缩写组件:

  • SliverGrid.count:SliverGrid + SliverChildListDelegate + SliverGridDelegateWithFixedCrossAxisCount
  • SliverGrid.extent: SliverGrid + SliverChildListDelegate + SliverGridDelegateWithMaxCrossAxisExtent
SliverGrid.count(
  crossAxisCount: 3,
  children: [
    ...List.generate(
      3,
      (index) => Container(color: Colors.primaries[index % Colors.primaries.length],
      ),
    )
  ],
),
SliverGrid.extent(
  maxCrossAxisExtent: 100,
  children: [
    ...List.generate(
      9,
      (index) => Container(color: Colors.primaries[index % Colors.primaries.length],
      ),
    )
  ],
)

SliverGrid 与 SliverList 一起应用即可取得这样的成果:

SliverAppBar

AppBar是大部分应用程序很重要的组件,它位于 App 的顶部,次要管制一些可操作按钮。在 Flutter 中,常在 Scaffold 下应用 AppBar 组件。那么什么是 SliverAppBar 呢?SliverAppBar Widget 是 Flutter 中用于兼容 CustomScrollView 的,它与 AppBar 组件雷同,意味着它具备 AppBar 的所有属性,如:titleactionsleading,但它也有一些额定的参数,如 pinned, floating, snapexpandedHeight 用于自定义 AppBar 的行为。SliverAppBar 通常作为 CustomScrollView slivers 中的第一个组件。

Scaffold(
  body: CustomScrollView(
    slivers: [
      SliverAppBar(title: Text("Hello SliverAppBar & Taxze"),
        actions: <Widget>[IconButton(onPressed: () => null, icon: const Icon(Icons.add))
        ],
      ),
    ],
  ),
)

这就是一个很经典的 Material 应用程序的AppBar

SliverAppBar 属性泛滥,与 AppBar 雷同的属性在本文就不过多介绍,次要解说其特有的属性。

expandedHeight

该属性定义了 AppBar 齐全开展时的大小,高度会随着向下滚动而放大。

SliverAppBar(title: Text("Hello SliverAppBar & Taxze"),
  expandedHeight: 200,
  actions: <Widget>[IconButton(onPressed: () => null, icon: const Icon(Icons.add))
  ],
),
未设置 expandedHeight expandedHeight: 200

pinned

该属性用于确定当用户向下滚动时,AppBar 在屏幕上是否放弃可见。

SliverAppBar(title: Text("Hello SliverAppBar & Taxze"),
  expandedHeight: 200,
  pinned: true,
  actions: <Widget>[IconButton(onPressed: () => null, icon: const Icon(Icons.add))
  ],
),
pinned: true pinned: false

floating

该属性如果设置为 true,则 AppBar 将在用户向上滚动时立刻可见。如果为 false 那么只有当滚动到顶部能力可见。

SliverAppBar(title: Text("Hello SliverAppBar & Taxze"),
  expandedHeight: 200,
  floating: true,
  actions: <Widget>[IconButton(onPressed: () => null, icon: const Icon(Icons.add))
  ],
),
floating: true floating: false

snap

该属性如果设置为 true,那么用户向上滚动一点,即可见残缺的 AppBar。应用该属性须要将 floating 设置为 true。

SliverAppBar(title: Text("Hello SliverAppBar & Taxze"),
  expandedHeight: 200,
  floating: true,
  snap: true,
  actions: <Widget>[IconButton(onPressed: () => null, icon: const Icon(Icons.add))
  ],
),
snap: true floating: true

在效果图中能显著看出 snap 和 floating 的显著区别。

flexibleSpace

该属性用于给 AppBar 提供 backgroundcollapseMode,还有能够随用户滚动而扭转地位的title

SliverAppBar(
  expandedHeight: 200,
  flexibleSpace: FlexibleSpaceBar(title: Text("First FlexibleSpace",style: TextStyle(color: Colors.red),),
    background: Image.network(
        "https://p3-passport.byteimg.com/img/user-avatar/af5f7ee5f0c449f25fc0b32c050bf100~180x180.awebp",
        fit: BoxFit.cover),
  ),
  actions: <Widget>[IconButton(onPressed: () => null, icon: const Icon(Icons.add))
  ],
),

当用户向上滚动时,就会失去视差成果,这是因为 collapseMode,它有三种模式:parallax , pin , nonecollapseMode 默认为CollapseMode.parallax,如果将其设置为pin,那么你将不会失去视差成果,只有简略的淡入淡出。

flexibleSpace: FlexibleSpaceBar(title: Text("First FlexibleSpace",style: TextStyle(color: Colors.black),),
  collapseMode: CollapseMode.pin,
  background: Image.network(
      "https://p3-passport.byteimg.com/img/user-avatar/af5f7ee5f0c449f25fc0b32c050bf100~180x180.awebp",
      fit: BoxFit.cover),
),
CollapseMode.parallax CollapseMode.pin

stretch

应用该属性前,须要先设置 CustomScrollViewphysics,给它一个弹性成果,在滚动到内容止境时依然运行滚动。stretch属性设置为 true 时,会让 FlexibleSpaceBar 与内部组件同步滚动。

SliverAppBar(
  expandedHeight: 200,
  pinned: true,
  stretch: true,
  flexibleSpace: FlexibleSpaceBar(title: Text("First FlexibleSpace",style: TextStyle(color: Colors.black),),
    // collapseMode: CollapseMode.pin,
    background: Image.network(
        "https://p3-passport.byteimg.com/img/user-avatar/af5f7ee5f0c449f25fc0b32c050bf100~180x180.awebp",
        fit: BoxFit.cover),
  ),
  actions: <Widget>[IconButton(onPressed: () => null, icon: const Icon(Icons.add))
  ],
),
stretch: true stretch: false

stretchModes

stretch 属性设置为 true 时,此时会触发 FlexibleSpaceBar 容器放大导致的背景图片变动的一个动画成果 ->stretchModesstretchModes有三种属性:

  • zoomBackground 默认成果,放大背景图片
  • blurBackground 含糊背景图片
  • fadeTitle 淡化 title
flexibleSpace: FlexibleSpaceBar(title: Text("First FlexibleSpace",style: TextStyle(color: Colors.black),),
  // collapseMode: CollapseMode.pin,
  background: Image.network(
      "https://p3-passport.byteimg.com/img/user-avatar/af5f7ee5f0c449f25fc0b32c050bf100~180x180.awebp",
      fit: BoxFit.cover),
  stretchModes: [
    // StretchMode.fadeTitle,
    // StretchMode.blurBackground,
    StretchMode.zoomBackground
  ],
),
zoomBackground blurBackground fadeTitle

对于我

Hello,我是 Taxze,如果您感觉文章对您有价值,心愿您能给我的文章点个❤️,有问题须要分割我的话:我在这里,也能够通过掘金的新的私信性能分割到我。如果您感觉文章还差了那么点货色,也请通过 关注 督促我写出更好的文章~ 万一哪天我提高了呢?😝

正文完
 0