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

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<MyPageViewwith 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/704597.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软硬协同设计下的飞天盘古,是如何降低存储系统开销的?

云布道师 经过十几年的技术演进&#xff0c;阿里巴巴已经实现了统一存储的目标——即以“飞天盘古”系统作为统一底座&#xff0c;通过标准化、服务化和开放化的方式建立了完整的存储产品和服务体系&#xff0c;服务广大内部和外部客户。 “万古乾坤心上辟&#xff0c;于令日…

正大国际:董宇辉最治愈的六句话

关于努力 努力不是为了证明自己多优秀&#xff0c; 而是在意外和不可控的因素来临时&#xff0c; 那些平常所努力积淀的涵养和能力&#xff0c; 可以成为抗衡一切风雨的底气。 关于焦虑 焦虑是对的。 焦虑是因为你想做得更好&#xff0c; 说明你追求高&#xff0c; 说明你眼界…

基于51单片机的智能监护与健康检测[proteus仿真]

基于51单片机的自行车测速系统设计[proteus仿真] 个人健康检测系统这个题目算是课程设计和毕业设计中常见的题目了&#xff0c;本期是一个基于51单片机的智能监护与健康检测 需要的源文件和程序的小伙伴可以关注公众号【阿目分享嵌入式】&#xff0c;赞赏任意文章 2&#xff…

CrossOver24破解版下载安装与激活

在 Mac 上运行Windows 软件&#xff0c;CrossOver Mac 可以轻松地从 Dock 本地启动 Windows 应用程序&#xff0c;并将 Mac 操作系统功能&#xff08;如跨平台复制和粘贴以及共享文件系统&#xff09;集成到您的 Windows 程序中。 CrossOver 产品特性 无需重启 CrossOver 可以…

LeetCode69. x 的平方根(C++)

LeetCode69. x 的平方根 题目链接代码 题目链接 https://leetcode.cn/problems/sqrtx/description/ 代码 class Solution { public:int mySqrt(int x) {int right x, left 0, ans -1;while(left < right){long long mid left (right - left) / 2;if(mid * mid <…

openssl3.2 - crypto-mdebug被弃用后, 内存泄漏检查的替代方法

文章目录 openssl3.2 - crypto-mdebug被弃用后, 内存泄漏检查的替代方法概述笔记查看特性列表openssl3.2编译脚本 - 加入enable-crypto-mdebug看看有没有替代内存诊断的方法?main.cppmy_openSSL_lib.hmy_openSSL_lib.c备注备注这招不行啊显势调用默认上下文也不行END openssl3…

Laravel03 路由到控制器与连接数据库

Laravel03 路由到控制器与连接数据库 1. 路由到控制器2. 连接数据库 1. 路由到控制器 如下图一些简单的逻辑处理可以放在web.php中&#xff0c;也就是路由的闭包函数里面。但是大的项目&#xff0c;我们肯定不能这么写。 为什么保证业务清晰好管理&#xff0c;都应该吧业务逻辑…

Amazon Generative AI | 基于 Amazon 扩散模型原理的代码实践之采样篇

以前通过论文介绍 Amazon 生成式 AI 和大语言模型&#xff08;LLMs&#xff09;的主要原理之外&#xff0c;在代码实践环节主要还是局限于是引入预训练模型、在预训练模型基础上做微调、使用 API 等等。很多开发人员觉得还不过瘾&#xff0c;希望内容可以更加深入。因此&#x…

python 进程笔记二(通讯) (概念+示例代码)

1、为什么要掌握进程间通信 Python代码效率由于受制于GIL全局锁限制&#xff0c;多线程不能利用多核CPU来加速&#xff0c;而多进程方式却可以绕过GIL限制, 发挥多CPU加速的优势&#xff0c;达到提高程序的性能的目的。 然而进程间通信却是不得不考虑的问题。 进程不同于线程&a…

react useMemo 用法

1&#xff0c;useCallback 的功能完全可以由 useMemo 所取代&#xff0c;如果你想通过使用 useMemo 返回一个记忆函数也是完全可以的。 usecallback(fn,inputs)is equivalent to useMemo(()> fn, inputs). 区别是:useCallback不会执行第一个参数函数&#xff0c;而是将它返…

Java Swing游戏开发学习2

跟随大佬教程继续&#xff0c;图片资源&#xff0c;视频简介有下载链接。 这个文章是看视频教程写的&#xff0c;不算原创。有条件的可以去油管搜索RyiSnow&#xff0c;是一个游戏开发视频制作up主&#xff0c;讲解的非常基础&#xff0c;可以边看边实践&#xff0c;增加对Java…

JavaWeb个人学习

1:RequestParam(defaultValue "默认的值") 这个可以在一个参数的前面写上 要是前端不传值进来的话 这个形参就是你定义的默认值 2: slf4j 对应的是日志的输出 log.info("参数是 {}", detail); 3: 分页插件 PageHelper 用法: 准备工作: 引入依赖 …

【MySQL】学习和总结联合查询

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-OPj5g6evbkm5ol0U {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

向导式堆栈管理器Dockge

经过申诉&#xff0c;目前博客的几个域名都恢复了&#xff0c;时间也延长到了 2033 年&#xff0c;后面还会不会出问题&#xff0c;老苏就不知道了 什么是 Dockge ? Dockge 是一款时髦的、易于使用的、响应式的、自托管的 docker-compose.yaml 向导式堆栈管理器&#xff0c;可…

单目与双目相机标定(一)

下面是几种常见的方法来获得三维点云重建的坐标系&#xff1a; 外部标定方法&#xff1a;在采集点云数据之前&#xff0c;通过使用已知尺寸和形状的校准物体在场景中放置特定的标记点或标定板。通过捕捉这些已知的标记点&#xff0c;可以建立一个参考坐标系&#xff0c;所有的点…

【Java程序设计】【C00317】基于Springboot的智慧社区居家养老健康管理系统(有论文)

基于Springboot的智慧社区居家养老健康管理系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的智慧社区居家养老健康管理系统设计与实现&#xff0c;本系统有管理员、社区工作人员、医生以及家属四种角色权限 管…

Atcoder ABC341 D - Only one of two

Only one of two&#xff08;只有两个中的一个&#xff09; 时间限制&#xff1a;2s 内存限制&#xff1a;1024MB 【原题地址】 所有图片源自Atcoder&#xff0c;题目译文源自脚本Atcoder Better! 点击此处跳转至原题 【问题描述】 【输入格式】 【输出格式】 【样例1】 …

C语言--- 操作符详解(上)

目录 一.操作符的分类 1.算术操作符&#xff1a; - * / % 1. 和 - 2. * 3./ 4.% 2.移位操作符 3.位操作符 4.赋值操作符 1.连续赋值 2.复合赋值 5.单目操作符 1.和-- &#xff08;1&#xff09;前置 &#xff08;2&#xff09;后置 &#xff08;3&#xff09;前置…

用html编写的简易新闻页面

用html编写的简易新闻页面 相关代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document<…

【MySQL】表的约束 -- 详解

表中一定要有各种约束&#xff0c;通过约束让我们在未来插入数据库表中的数据是符合预期的。约束本质是通过技术手段倒逼程序员插入正确的数据&#xff0c;反过来站在 MySQL 的角度&#xff0c;凡是插入进来的数据都是符合数据约束的。约束的最终目标&#xff1a;保证数据的完整…