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


    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),
                  ],
                ),
              ),
            ),
            ...
    • 1

    SliverToBoxAdapter 进行包装才能 slivers 使用。

      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: const Text('Sliver Scroll')),
          body: _mainView(),
        );
      }
    • 1

    第二步:固定高度的 tabView

        return CustomScrollView(
          slivers: [
          ...
            // 固定高度内容
            SliverToBoxAdapter(
              child: Container(
                height: 200,
                color: Colors.greenAccent,
                child: const Center(child: Text('固定高度内容')),
              ),
            ),
    • 1
            // 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),
                        ],
                      ),
                    ),
                  ],
                ),
              ),
            ),
    • 1

    外层嵌套 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;
      }
    }

    • 1

    编写固定头部 sliver 组件

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

    定义 TabController

    class _MyPageViewState extends State<MyPageViewwith TickerProviderStateMixin {
     ...
    • 1

    混入 TickerProviderStateMixin

      late TabController _tabController;
    • 1
      @override
      void initState() {
        _tabController = TabController(length: 3, vsync: this);
        super.initState();
      }
    • 1
      @override
      void dispose() {
        _tabController.dispose(); // 释放内存
        super.dispose();
      }
    • 1

    加入 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'),
              ],
            )),
    • 1

    使用 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'));
                    },
                  ),
                ],
              ),
            ),
    • 1

    SliverFillRemaining 是一个可以填充剩余空间的 sliver 组件,它可以将子组件放置在视图区域的剩余空间中,并自动调整子组件的大小以填充整个空间。通常情况下,SliverFillRemaining 用于在滚动视图中放置一个占满整个视图区域的组件,例如底部栏或页脚。

    第四步:子 tabBar

    还可以加入子 tabBar 组成父子选项切换

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

    父子 tabBar 中间再加一个固定块,查看滚动效果

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

    最后:底部再加入 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,
              ),
            ),
    • 1

    代码

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

    小结

    使用 Sliver 组件后我们确实把几种滚动给黏贴上了,但是不难发现过于复杂的滚动,用户体验方面还是要考虑的。

    不能只为了堆积功能。

    感谢阅读本文

    如果我有什么错?请在评论中让我知道。我很乐意改进。


    © 猫哥 ducafecat.com

    end

  • 相关阅读:
    java 中 object 转 list
    C++11 - 6 - 可变参数模板
    ER模型与维度模型
    【树莓派 picamera】
    数据结构PT1——线性表/链表
    Autosar工具-Davinci Developer
    计算机网络408考研 2020
    使用SQL语句进行各表的定义查看、重命名、修改和删除操作。
    Vite 中怎么添加全局 scss 文件
    三维模型体积计算及其注意事项
  • 原文地址:https://blog.csdn.net/weixin_42320543/article/details/136242889