Flutter - 一个易用且功能强大的粘性头部组件库,适用于任何支持滚动的组件(一)

文章目录

  • 前言
  • 项目地址
  • 主要功能
  • 快速入门
  • 更多用法
    • 粘性量
    • 头部跳转
    • 头部分组
  • 总结
  • 最后
  • 系列文章


前言

开发时遇到需要实现粘性头部效果,类似iOS端的那种。本着有轮子就不造轮子的原则,先在Pub仓库搜索一番,不出所料,果然有大佬已经造好了轮子,心里美滋滋,今天又能早点下班,Demo跑一跑,改一改完事~

事实证明,啥事都不能开心的太早。几个Demo跑完,和我实际想要的有点差距,易用的功能单一,功能强一点的用起来麻烦。没办法‍,只能加班自己搞一个。具体实现原理和遇到的坑后面再慢慢一一道来,先把菜上了,让大家尝尝味道。

项目地址

  • Pub
  • Pub 国内镜像
  • GitHub

主要功能

  • 支持水平或垂直方向滚动的组件
  • 支持反向滚动的组件
  • 允许动态构建头部组件,支持自定义过渡动画
  • 头部组件可以动态改变粘性
  • 支持跳转到指定索引的头部组件
  • 支持头部组件分组
  • 支持无限列表

快速入门

添加依赖:

dependencies:
  easy_sticky_header: ^1.0.5

导入库:

import 'package:easy_sticky_header/easy_sticky_header.dart';

开始使用:

  1. 第一步,用StickyHeader包裹任何可以滚动的组件,例如ListView、GridView、CustomScrollView、SingleChildScrollView等
  2. 第二步,用StickyContainerWidget包裹自定义的头部组件,指定index即可。注意,index不要求连续,但必须唯一且顺序是从小到大的,推荐使用滚动组件提供的index
class Example extends StatelessWidget {
  const Example({super.key});

  
  Widget build(BuildContext context) {
    return StickyHeader(
      child: ListView.builder(
        itemCount: 100,
        itemBuilder: (context, index) {
          // 自定义你的头部组件
          if (index % 3 == 0) {
            return StickyContainerWidget(
              index: index,
              child: Container(
                color: Color.fromRGBO(Random().nextInt(256),
                    Random().nextInt(256), Random().nextInt(256), 1),
                padding: const EdgeInsets.only(left: 16.0),
                alignment: Alignment.centerLeft,
                width: double.infinity,
                height: 50,
                child: Text(
                  'Header #$index',
                  style: const TextStyle(
                    color: Colors.white,
                    fontSize: 16,
                  ),
                ),
              ),
            );
          }
          // 自定义你的Item
          return Container(
            width: double.infinity,
            height: 80,
            color: Colors.white,
          );
        },
      ),
    );
  }
}

效果展示:

Flutter - 一个易用且功能强大的粘性头部组件库,适用于任何支持滚动的组件(一)_第1张图片

更多用法

粘性量

粘性量可以用于实现类似微信通讯录分组头部切换的过渡动画效果。

  • StickyContainerBuilder通过回调动态构建头部,当stickyAmount没有改变时不会重新构建
  • stickyAmount
    • 即将成为粘性头部时,值从0.0 -> 1.0
    • 成为粘性头部,值为1.0
    • 即将不是粘性头部,值从1.0 -> 0.0
    • 其余情况值为0.0
  • 默认不计算stickyAmount,如果使用了StickyContainerBuilder组件会自动开启,其他情况也想开启的话,手动设置useStickyAmount为true。
class Example extends StatelessWidget {
  const Example({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return StickyHeader(
      child: ListView.builder(
        itemCount: 100,
        itemBuilder: (context, index) {
          // 自定义你的头部组件
          if (index % 3 == 0) {
            return StickyContainerBuilder(
              index: index,
              builder: (context, stickyAmount) => Container(
              	// 根据粘性量动态改变颜色,实现过渡效果
                color: Color.fromRGBO(
                    155 + (100 * stickyAmount).toInt(), 105, 0, 1.0),
                padding: const EdgeInsets.only(left: 16.0),
                alignment: Alignment.centerLeft,
                width: double.infinity,
                height: min(100, 50 + 5.0 * index),
                child: Text(
                  'Header #$index  stickyAmount:${stickyAmount.toStringAsFixed(2)}',
                  style: const TextStyle(
                    color: Colors.white,
                    fontSize: 16,
                  ),
                ),
              ),
            );
          }
          // 自定义你的Item
          return Column(
            children: <Widget>[
              Container(
                width: double.infinity,
                height: 80,
                color: Colors.white,
                padding: const EdgeInsets.only(left: 16),
                alignment: Alignment.centerLeft,
                child: Text(
                  'Item #$index',
                  style: const TextStyle(
                    color: Colors.black,
                    fontSize: 16,
                  ),
                ),
              ),
              Divider(
                height: 1.0,
                thickness: 1.0,
                color: Colors.grey.shade200,
                indent: 16.0,
              ),
            ],
          );
        },
      ),
    );
  }
}

效果展示:

Flutter - 一个易用且功能强大的粘性头部组件库,适用于任何支持滚动的组件(一)_第2张图片

头部跳转

众所周知,在Flutter中,不依赖第三方库跳转到列表指定Item不是一件容易的事(如果全部Item高度一致,当我没说)。在实现这个库的过程中,因为有缓存,所以可以很简单地跳转已经在缓存中的头部,但是局限性很大,对于还未构建过的头部组件,就无能为力了。

这么核心的一个功能,如果都没实现,那用起来是真难受。现在从1.0.3版本开始,通过查找的方式已经实现任意头部跳转,原理后面再说,先看效果展示:

Flutter - 一个易用且功能强大的粘性头部组件库,适用于任何支持滚动的组件(一)_第3张图片

完整源码详见示例项目-Example5。

核心源码:

void animateTo(
  // 跳转目标的头部索引
  int index, {
  // 偏移量,有时需要设置一点点偏移量让头部组件成为粘性头部(详见示例源码)
  double offset = 0.0,
  // 滚动速度,单位:像素/毫秒,设为1.0即一秒滚动1000像素
  double velocity = 1.0,
  // 可以直接跳转时使用的动画参数
  Duration? duration,
  Curve? curve,
  // 通过查找跳转时使用的动画参数
  Duration? findingStartDuration,
  Curve? findingStartCurve,
  Duration? findingEndDuration,
  Curve? findingEndCurve,
}) 

有了头部跳转这一核心功能后,配合字母索引条(列),就可以轻松实现通讯录、城市列表等功能啦。

头部分组

头部分组指多个头部可以组成一个组,粘性头部的构建内容由父头部提供。说的可能不是很清楚,但这个使用效果,诸位应该都见过,先看效果展示:

Flutter - 一个易用且功能强大的粘性头部组件库,适用于任何支持滚动的组件(一)_第4张图片
完整源码详见示例项目-Example6。

紫色背景的父头部:由TabBar组件组成。当子头部成为粘性头部时,切换指示器。点击tab时,跳转到对应的子头部。
绿色背景的子头部:只需要指定父头部索引,通过索引关联父头部。

核心源码:

// ParentStickyContainerBuilder用于构建父头部,如果你非要拿去构建子头部,那也不是不行‍♂️
// 注意:StickyContainerWidget和StickyContainerBuilder也可以构建父头部,只是没有ParentStickyContainerBuilder那么方便快捷
ParentStickyContainerBuilder({
  Key? key,
  required this.index,
  this.visible = true,
  this.pixels,
  this.performancePriority = true,
  // 更新回调,如果父头部组件不需要一直重新构建,可以在回调中返回false(参考示例源码)
  this.onUpdate,
  // 通过当前粘性子头部信息构建父头部组件
  required this.builder,
})

// StickyContainerWidget构建子头部,StickyContainerBuilder也可以
StickyContainerWidget({
  Key? key,
  required this.index,
  this.visible = true,
  this.pixels,
  this.performancePriority = true,
  // 指定父头部索引
  this.parentIndex,
  // 是否重叠父头部,一般来说都是不重叠的,使用默认值即可
  this.overlapParent = false,
  required Widget child,
})

总结

看到这,诸位对这个项目应该有所了解了,使用起来还是很容易的。如果想自定义类似StickyContainerBuilder这样动态构建的头部,请参考sticky_container_widget里面的实现方式。

更多使用例子请参考示例项目,诸多应用场景都有例子可参考,目前也还在不断更新中,欢迎留言补充使用场景。

如果你有兴趣了解项目的实现原理和遇到的问题,欢迎查看后续文章。

最后

如果这个项目对你有所帮助,点赞加星安排一下~

如果你发现了bug或想要新功能,欢迎在issue留言讨论,同时也欢迎你加入到这个项目中,为这项目出一份力。


系列文章

Flutter - 一个易用且功能强大的粘性头部组件库,适用于任何支持滚动的组件(一)
Flutter - 一个易用且功能强大的粘性头部组件库,适用于任何支持滚动的组件(二)
Flutter - 一个易用且功能强大的粘性头部组件库,适用于任何支持滚动的组件(三)
Flutter - 一个易用且功能强大的粘性头部组件库,适用于任何支持滚动的组件(四)
Flutter - 一个易用且功能强大的粘性头部组件库,适用于任何支持滚动的组件(五)

你可能感兴趣的:(flutter,android,ios)