flutter sliver 多种滚动组合开发指南

视频

https://youtu.be/4mho1kZ_YQU

https://www.bilibili.com/video/BV1WW4y1d7ZC/

前言

有不少同学工作中遇到须要把几个不同滚动行为组件(顶部 appBar、内容固定块、tabBar 切换、tabBarView视图、自适应高度、横向滚动)黏贴成一个组件。

这时候就须要 sliver 出场了,本文将会写一个多种滚动的组合。

业务场景剖析

上面是淘宝、小红书的常见状况。

原文 https://ducafecat.com/blog/flutter-sliver-scroll

知识点 sliver

Sliver 是 Flutter 中用于构建可滚动视图的根本构建块之一。Sliver 是可滚动区域中的一小部分,具备固定的大小和地位,能够依据须要动静加载和卸载。Sliver 通常用于创立高性能、高度灵便的可滚动视图,例如列表、网格、瀑布流等。

在 Flutter 中,有许多不同类型的 Sliver 组件,每个组件都有特定的作用和用处。上面是一些常见的 Sliver 组件:

  • SliverAppBar:一个带有滚动成果的利用栏,能够在向上滚动时暗藏,并在向下滚动时显示。
  • SliverList:将子组件搁置在一个垂直列表中,能够依据须要动静加载和卸载列表项。
  • SliverGrid:将子组件搁置在一个网格中,能够依据须要动静加载和卸载网格项。
  • SliverPadding:为子组件提供填充,以使它们与其余 Sliver 组件的大小和地位保持一致。
  • SliverToBoxAdapter:将一个一般的组件包装成一个 Sliver 组件,以便将其搁置在 CustomScrollView 中。

参考

步骤

第一步:Sliver 横向滚动

lib/page.dart

  Widget _mainView() {    return CustomScrollView(      slivers: [        // 横向滚动        SliverToBoxAdapter(          child: SizedBox(            height: 100,            child: PageView(              children: [                Container(                  color: Colors.yellow,                  child: const Center(child: Text('横向滚动')),                ),                Container(color: Colors.green),                Container(color: Colors.blue),              ],            ),          ),        ),        ...
SliverToBoxAdapter 进行包装能力 slivers 应用。
  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(title: const Text('Sliver Scroll')),      body: _mainView(),    );  }

第二步:固定高度的 tabView

    return CustomScrollView(      slivers: [      ...        // 固定高度内容        SliverToBoxAdapter(          child: Container(            height: 200,            color: Colors.greenAccent,            child: const Center(child: Text('固定高度内容')),          ),        ),
        // tabView 内容        SliverToBoxAdapter(          child: DefaultTabController(            length: 3,            child: Column(              children: [                const TabBar(                  tabs: [                    Tab(text: 'Tab 1'),                    Tab(text: 'Tab 2'),                    Tab(text: 'Tab 3'),                  ],                ),                SizedBox(                  height: 200,                  child: TabBarView(                    children: [                      Container(color: Colors.yellow),                      Container(color: Colors.green),                      Container(color: Colors.blue),                    ],                  ),                ),              ],            ),          ),        ),
外层嵌套 DefaultTabController ,能力让 TabBar、TabBarView 顺利工作。

第三步:自适应高度的 tabView

实现 SliverPersistentHeaderDelegate 抽象类

class _SliverDelegate extends SliverPersistentHeaderDelegate {  _SliverDelegate({    required this.minHeight,    required this.maxHeight,    required this.child,  });  final double minHeight; //最小高度  final double maxHeight; //最大高度  final Widget child;  @override  double get minExtent => minHeight;  @override  double get maxExtent => max(maxHeight, minHeight);  @override  Widget build(      BuildContext context, double shrinkOffset, bool overlapsContent) {    return SizedBox.expand(child: child);  }  @override //是否须要重建  bool shouldRebuild(_SliverDelegate oldDelegate) {    return maxHeight != oldDelegate.maxHeight ||        minHeight != oldDelegate.minHeight ||        child != oldDelegate.child;  }}

编写固定头部 sliver 组件

  Widget _buildPersistentHeader(Widget child,          {double? minHeight, double? maxHeight}) =>      SliverPersistentHeader(          pinned: true,          delegate: _SliverDelegate(            minHeight: minHeight ?? 40.0,            maxHeight: maxHeight ?? 40.0,            child: child,          ));

定义 TabController

class _MyPageViewState extends State<MyPageView> with TickerProviderStateMixin {    ...
混入 TickerProviderStateMixin
  late TabController _tabController;
  @override  void initState() {    _tabController = TabController(length: 3, vsync: this);    super.initState();  }
  @override  void dispose() {    _tabController.dispose(); // 开释内存    super.dispose();  }

退出 slivers

  Widget _mainView() {    return CustomScrollView(      slivers: [        ...                        // TabBar 固定        _buildPersistentHeader(TabBar(          controller: _tabController,          tabs: const [            Tab(text: 'Tab 1'),            Tab(text: 'Tab 2'),            Tab(text: 'Tab 3'),          ],        )),

应用 SliverFillRemaining 来撑开残余空间

        // TabBarView 自适应高度        SliverFillRemaining(          child: TabBarView(            controller: _tabController,            children: [              // 第一个选项卡的内容              ListView.builder(                itemCount: 20,                itemBuilder: (BuildContext context, int index) {                  return ListTile(title: Text('Item $index'));                },              ),              // 第二个选项卡的内容              ListView.builder(                itemCount: 10,                itemBuilder: (BuildContext context, int index) {                  return ListTile(title: Text('Item $index'));                },              ),              // 第三个选项卡的内容              ListView.builder(                itemCount: 5,                itemBuilder: (BuildContext context, int index) {                  return ListTile(title: Text('Item $index'));                },              ),            ],          ),        ),
SliverFillRemaining 是一个能够填充残余空间的 sliver 组件,它能够将子组件搁置在视图区域的残余空间中,并主动调整子组件的大小以填充整个空间。通常状况下,SliverFillRemaining 用于在滚动视图中搁置一个占满整个视图区域的组件,例如底部栏或页脚。

第四步:子 tabBar

还能够退出子 tabBar 组成父子选项切换

        // 子 TabBar 固定        _buildPersistentHeader(TabBar(          controller: _tabController,          tabs: const [            Tab(text: 'subTab 1'),            Tab(text: 'subTab 2'),            Tab(text: 'subTab 3'),          ],        )),

父子 tabBar 两头再加一个固定块,查看滚动成果

        // 固定高度内容        SliverToBoxAdapter(          child: Container(            height: 100,            color: Colors.greenAccent,            child: const Center(child: Text('固定高度内容')),          ),        ),

最初:底部再退出 SliverList

咱们在底部再加一个 list 模块,看看成果。

  Widget _mainView() {    return CustomScrollView(      slivers: [        ...                // 固定高度内容        SliverToBoxAdapter(          child: Container(            height: 200,            color: Colors.greenAccent,            child: const Center(child: Text('固定高度内容')),          ),        ),        // 列表 100 行        SliverList(          delegate: SliverChildBuilderDelegate(            (BuildContext context, int index) {              return ListTile(title: Text('Item $index'));            },            childCount: 100,          ),        ),

代码

https://github.com/ducafecat/flutter_develop_tips/tree/main/flutter_application_sliver_scroll

小结

应用 Sliver 组件后咱们的确把几种滚动给黏贴上了,然而不难发现过于简单的滚动,用户体验方面还是要思考的。

不能只为了沉积性能。

感激浏览本文

如果我有什么错?请在评论中让我晓得。我很乐意改良。


© 猫哥
ducafecat.com

end