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

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

视频

youtu.be/4mho1kZ_YQU

www.bilibili.com/video/BV1WW...

前言

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

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

业务场景分析

下面是淘宝、小红书的常见情况。

原文 ducafecat.com/blog/flutte...

知识点 sliver

Sliver 是 Flutter 中用于构建可滚动视图的基本构建块之一。Sliver 是可滚动区域中的一小部分,具有固定的大小和位置,可以根据需要动态加载和卸载。Sliver 通常用于创建高性能、高度灵活的可滚动视图,例如列表、网格、瀑布流等。

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

  • SliverAppBar:一个带有滚动效果的应用栏,可以在向上滚动时隐藏,并在向下滚动时显示。
  • SliverList:将子组件放置在一个垂直列表中,可以根据需要动态加载和卸载列表项。
  • SliverGrid:将子组件放置在一个网格中,可以根据需要动态加载和卸载网格项。
  • SliverPadding:为子组件提供填充,以使它们与其他 Sliver 组件的大小和位置保持一致。
  • SliverToBoxAdapter:将一个普通的组件包装成一个 Sliver 组件,以便将其放置在 CustomScrollView 中。

参考

步骤

第一步:Sliver 横向滚动

lib/page.dart

less 复制代码
   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 使用。

less 复制代码
   @override
   Widget build(BuildContext context) {
     return Scaffold(
       appBar: AppBar(title: const Text('Sliver Scroll')),
       body: _mainView(),
     );
   }

第二步:固定高度的 tabView

less 复制代码
     return CustomScrollView(
       slivers: [
       ...
         // 固定高度内容
         SliverToBoxAdapter(
           child: Container(
             height: 200,
             color: Colors.greenAccent,
             child: const Center(child: Text('固定高度内容')),
           ),
         ),
less 复制代码
         // 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 抽象类

dart 复制代码
 ​
 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 组件

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

定义 TabController

scala 复制代码
 class _MyPageViewState extends State<MyPageView> with TickerProviderStateMixin {
   ...

混入 TickerProviderStateMixin

ini 复制代码
   late TabController _tabController;
scss 复制代码
  @override
  void initState() {
    _tabController = TabController(length: 3, vsync: this);
    super.initState();
  }
scss 复制代码
  @override
  void dispose() {
    _tabController.dispose(); // 释放内存
    super.dispose();
  }

加入 slivers

less 复制代码
  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 来撑开剩余空间

less 复制代码
        // 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 组成父子选项切换

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

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

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

最后:底部再加入 SliverList

我们在底部再加一个 list 模块,看看效果。

less 复制代码
  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,
          ),
        ),

代码

github.com/ducafecat/f...

小结

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

不能只为了堆积功能。

感谢阅读本文

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


© 猫哥 ducafecat.com

end

相关推荐
jhonjson16 小时前
Flutter开发之flutter_local_notifications
flutter·macos·cocoa
iFlyCai17 小时前
23种设计模式的Flutter实现第一篇创建型模式(一)
flutter·设计模式·dart
恋猫de小郭19 小时前
Flutter 小技巧之 OverlayPortal 实现自限性和可共享的页面图层
flutter
A_cot1 天前
Vue.js:构建现代 Web 应用的强大框架
前端·javascript·vue.js·flutter·html·web·js
B.-1 天前
在 Flutter 应用中调用后端接口的方法
android·flutter·http·ios·https
️ 邪神1 天前
【Android、IOS、Flutter、鸿蒙、ReactNative 】约束布局
android·flutter·ios·鸿蒙·reactnative
pinkrecall20121 天前
flutter调试
flutter
jhonjson1 天前
在Flutter中,禁止侧滑的方法
前端·javascript·flutter
️ 邪神2 天前
【Android、IOS、Flutter、鸿蒙、ReactNative 】文本点击事件
flutter·ios·鸿蒙·reactnative·anroid
️ 邪神2 天前
【Android、IOS、Flutter、鸿蒙、ReactNative 】文本Text显示
flutter·ios·鸿蒙·reactnative·anroid