前语

在上一篇文章中,咱们了解了Flutter中的视窗和Sliver,并经过事例知道了CustomScrollViewSliverListSliverGridSliverAppBar等常用的Sliver系列组件。那么现在让咱们来探索更多常用的Slivers吧!

SliverToBoxAdapter

假如想在CustomScrollView中增加SizedBoxRow这样根据Box协议的组件,你会发现不能直接增加,由于在CustomScrollView中需求运用Sliver协议来完成一些东西,这时也许你会Google:我应该如何将Box协议的组件更改为Sliver协议的组件? 答案是:运用SliverToBoxAdapter即可。

— 在Flutter中,首要有两种布局协议:box协议,和sliver协议。

SliverToBoxAdapter仅仅一个用于包裹Box协议的Sliver组件。假如你想在CustomScrollView中显现一个子项时,经过它会变得十分便利。

Scaffold(
      body: CustomScrollView(
        slivers: [
          SliverToBoxAdapter(
            child: _helloText,
          )
        ],
      ),
);
Widget get _helloText {
    return Column(
      children: [
        ...List.generate(
            10, (index) => Text("Taxze SliverToBoxAdapter $index")),
        ElevatedButton(
            onPressed: () => print("Say Hello!"), child: Text("Hello"))
      ],
    );
}

重识Flutter 在不同的滑动列表场景,请选择合适的Slivers - part2

可是很多朋友第一次运用该组件时,会呈现运用SliverToBoxAdapter去包裹可翻滚组件的情况,例如:

SliverToBoxAdapter(
  child: ListView(
    children: [],
  ),
)

呈现与sliver滑动方向一致的情况时,这个ListView便没有办法正常工作。

SliverPersistentHeader

在上一篇文章中现已知道了SliverAppBar有很多操控特点,但假如想要操控更多的SliverAppBar的行为或自定义AppBar,又或者想要在列表某处固定一个Item,那么能够运用SliverPersistentHeader

SliverPersistentHeader有一个必传参数和两个可选参数:

  • pinned: 默以为false,用于操控item是否固定。例如将自定义的AppBar贴在顶部。
  • floating: 默以为false,用于操控item是否浮动。例如向下滑动时,自定义的AppBar会打开。
  • delegate: 必须传入的参数。该参数需求一个完成扩展抽象类的委托类SliverPersistentHeaderDelegate

现在就来完成一个TaxzePersistentHeaderDelegate,来体验SliverPersistentHeader的强大吧!

重识Flutter 在不同的滑动列表场景,请选择合适的Slivers - part2

能够看到有四个需求完成的办法:

  • build 需求构建烘托的内容,其中shrinkOffset参数的取值为[0,maxExtent],当header在顶部时,值为0
  • maxExtent 打开时组件的高度
  • minExtent 收起时组件的高度
  • shouldRebuild 判别Header是否需求重新构建,通常在父级状态更新时触发
class TaxzePersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
  TaxzePersistentHeaderDelegate({
    required this.minSize,
    required this.maxSize,
  });
  final double minSize;
  final double maxSize;
  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    print(shrinkOffset);
    return Stack(
      fit: StackFit.expand,
      children: [
        ...
      ],
    );
  }
  @override
  bool shouldRebuild(TaxzePersistentHeaderDelegate oldDelegate) =>
      oldDelegate.maxExtent != maxExtent || oldDelegate.minExtent != minExtent;
  @override
  double get maxExtent => maxSize;
  @override
  double get minExtent => minSize;
}

重识Flutter 在不同的滑动列表场景,请选择合适的Slivers - part2

pinned特点和floating特点就不过多介绍了,现在经过一个实例,来看看SliverPersistentHeader能完成什么样的常用功用。效果图:

重识Flutter 在不同的滑动列表场景,请选择合适的Slivers - part2

完成起来也很简略,第一步能够先封装一个通用的HeaderDelegate,便利快速构建 SliverPersistentHeaderDelegate,削减重复代码。

typedef SliverHeaderBuilder = Widget Function(
    BuildContext context, double shrinkOffset, bool overlapsContent);
class TaxzePersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
  TaxzePersistentHeaderDelegate({
    this.minSize = 0,
    required this.maxSize,
    required Widget child,
  })  : builder = ((context, shrinkOffset, overlapsContent) => child),
        assert(minSize <= maxSize && minSize >= 0);
  final double minSize;
  final double maxSize;
  final SliverHeaderBuilder builder;
  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    Widget child = builder(context, shrinkOffset, overlapsContent);
  	//高度充溢父束缚,高度在[minHeight,maxHeight]之间变化
    return SizedBox.expand(
      child: child,
    );
  }
  @override
  bool shouldRebuild(TaxzePersistentHeaderDelegate oldDelegate) =>
      oldDelegate.maxExtent != maxExtent || oldDelegate.minExtent != minExtent;
  @override
  double get maxExtent => maxSize;
  @override
  double get minExtent => minSize;
}

运用起来也很简略:

SliverPersistentHeader(
  pinned: true,
  delegate: TaxzePersistentHeaderDelegate(
    maxSize: 100,
    minSize: 60,
    child: buildItemHeader(1),
  ),
),
Widget buildItemHeader(int i) {
    return Container(
      ...
    );
}

额外的知识点:SliverPersistentHeader 组件的规划初衷首要是为了完成 SliverAppBar,所以 SliverAppBar原理其实就像是这样:

CustomScrollView(
  slivers: [
    SliverToBoxAdapter(),
    //避免SliverPersistentHeader成为最顶层的Sliver,以至于无法上拉改写
    SliverPersistentHeader(
      delegate:XXXDelegate(),
      //固定在顶部
      pinned: true, 
    )
  ],
),

SliverFixedExtentList

SliverFixedExtentListSliverList用法一样,唯一的区别是SliverFixedExtentList是固定子组件的高度的,所以假如你确认了子组件的高度,那么请挑选SliverFixedExtentList,由于它无需计算子组件的布局尺度,愈加高效!而且假如想要跳转到很远的间隔时,在加载组件之前就知道尺度是十分重要的! 例如:每个item固定为100px,现在要往下翻滚2000px,那么只需求跳转20个item,只需求加载第21个item即可。假如子组件的尺度是不固定的,那么假如想要跳转到2000px的地方,就不得不逐一烘托中间的组件,才干知道2000px的位置。

CustomScrollView(
  slivers: [
    SliverFixedExtentList(
    	//固定尺度
      itemExtent: 100,
      delegate: SliverChildBuilderDelegate((ctx, index) {
        return Container(
          alignment: Alignment.center,
          color: Colors.primaries[index % Colors.primaries.length],
          child: Text(
            "$index",
            style: const TextStyle(color: Colors.white, fontSize: 18),
          ),
        );
      }),
    )
  ],
),

SliverPrototypeExtentList

知道了子组件的尺度就能运用SliverFixedExtentList然后提高功用,可是在真实的事务中,子组件固定尺度的场景较少,一般都是呈现在设置页,想要在其他的场景下固定子组件的尺度是十分麻烦的,可是,有了SliverPrototypeExtentList就简略多了!

为什么说运用它会变得简略呢?在SliverPrototypeExtentList中,有一个prototypeItem特点,能够传入一个组件,可是这个组件不会被烘托到屏幕上,该组件的作用是供给一个尺度,在SliverPrototypeExtentList中的组件尺度都会被设置成该组件的尺度。

SliverPrototypeExtentList(
  prototypeItem: const Text(""),
  delegate: SliverChildBuilderDelegate((ctx, index) {
    return Text("$index");
  }),
)

SliverFillViewport

学习这个组件之前,让咱们回想一下PageView组件,PageView每一个子组件都会占有整个父束缚,然后能够滑动切换。而在Sliver中,就有SliverFillViewport,它也是PageView的底层原理。在PageViewbuild函数中就能够看到SliverFillViewport

重识Flutter 在不同的滑动列表场景,请选择合适的Slivers - part2

SliverFillViewport(
  delegate: SliverChildListDelegate([
    Container(
      color: Colors.red,
    ),
    Container(
      color: Colors.blue,
    ),
    Container(
      color: Colors.green,
    ),
  ]),
),

重识Flutter 在不同的滑动列表场景,请选择合适的Slivers - part2

SliverFillRemaining

SliverFillRemainingSliverFillViewport很类似,SliverFillViewport是生成的每一个item都占满全屏,而SliverFillRemaining是会主动填充溢整个视图。SliverFillRemaining有两个特点:

  • hasScrollBody 默以为true,该输入用于判别内容是否能够翻滚。
  • fillOverscroll 允许在 iOS 上看到列表过度翻滚时的拉伸行为。
CustomScrollView(
  slivers: [
    SliverList(delegate: SliverChildBuilderDelegate((ctx, index) {
      return Container(
        height: 50,
        margin: EdgeInsets.all(10),
        color: Colors.red,
      );
    },childCount: 5)),
    SliverFillRemaining(
      hasScrollBody: true,
      child: FlutterLogo(),
    )
  ],
),
hasScrollBody:true hasScrollBody:false
重识Flutter 在不同的滑动列表场景,请选择合适的Slivers - part2
重识Flutter 在不同的滑动列表场景,请选择合适的Slivers - part2

SliverPadding

假如想在CustomScrollView中增加Padding时,在没有了解过SliverPadding之前,也许你会想运用SliverToBoxAdapter去包裹一层Padding,可是其实不必那么麻烦,Slivers中就有SliverPadding这样的组件帮助完成需求。运用它也很简略,只需求在需求PaddingSliver组件外报上一层即可。

SliverPadding(
  padding: EdgeInsets.all(20.0),
  sliver: SliverList(
    delegate: SliverChildBuilderDelegate((ctx, index) {
      return Container(
        color: Colors.primaries[index % Colors.primaries.length],
        height: 50,
      );
    },childCount: 10),
  ),
)

重识Flutter 在不同的滑动列表场景,请选择合适的Slivers - part2

— 在2020 年 9 月 29 日,修正了之前运用SliverPersistentHeader外面包裹SliverPadding导致SliverPersistentHeaderpinned 特点失效的问题,有爱好的能够看下这个。

关于我

Hello,我是Taxze,假如您觉得文章对您有价值,希望您能给我的文章点个❤️,有问题需求联系我的话:我在这里,也能够经过的新的私信功用联系到我。假如您觉得文章还差了那么点东西,也请经过重视催促我写出更好的文章~万一哪天我前进了呢?