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


前言

最近工作繁忙,下班后都不想开电脑😂,现在工作告一段落了,终于可以抽出时间来继续写这个系列。项目实现请看这篇文章,今天来讲讲项目实现后的第二个棘手问题,粘性头部无法像头部组件一样滑动。

项目地址

问题描述

话不多说,先上图看看问题是个什么情况:

screenshot1

当Header成为粘性头部后,无法滑动。

问题分析

Header组件的本质是滚动组件的Item,所以Header组件不需要进行额外处理就能响应滑动事件。Header成为粘性头部后,会在Header组件上方显示粘性头部组件(关于这方面的具体详情请看项目实现),这时候尝试滑动的其实是粘性头部组件,而粘性头部组件没有做额外处理,所以无法响应滑动事件。

解决方案

问题已经明确,那我们开始着手加上滑动事件处理。Flutter中要添加各种手势事件监听回调,那GestureDetector组件肯定是少不了的。

GestureDetector构造方法中一堆手势事件回调就不一一展示了,这里主要用到了onVerticalDragUpdate(垂直方向滑动中)和onVerticalDragEnd(垂直方向滑动结束)这两个回调。

滑动粘性头部组件时,只需要在onVerticalDragUpdate回调中同步改变滚动组件的滚动位置,便能实现像头部组件一样的滑动效果。

onVerticalDragUpdate回调处理源码:

void _onVerticalDragUpdate(DragUpdateDetails details) {
  // currentPixels: 当前滚动位置
  // details.delta.dy: 滑动变化量
  widget.controller.scrollPosition?.jumpTo(widget.controller.currentPixels + details.delta.dy);
}

滑动结束时,如果有一定的滑动速度,那么滑动不会马上停止,如果触及到边界,不同的平台还会有不同的效果,例如iOS平台是回弹效果。看到这,我猜你已经联想到了滚动组件的physics属性,不设置这个属性的情况下,Android平台默认使用ClampingScrollPhysics,iOS平台默认使用BouncingScrollPhysics

那这些跟onVerticalDragEnd的回调处理又有什么关系呢?如果只考虑将滑动速度慢慢减小直到滑动停止,那么通过AnimationController创建动画就能实现,但是这有以下两个问题:

  1. AnimationController默认创建的动画是线性动画,虽然可以通过CurvedAnimation转为非线性动画,但是这个动画曲线不一定和滚动组件的动画曲线一致,导致用户体验不一致
  2. 滚动位置超出边界时,不同的平台或设置的physics属性不同会有不同的处理方式,简单的动画曲线无法满足该需求

要解决以上两个问题,最好的办法是模仿滚动组件的滑动事件处理。对于滚动组件,滑动结束后的动画是由Simulation对象决定的,而Simulation是通过createBallisticSimulation方法创建的,createBallisticSimulationScrollPhysics类方法,ClampingScrollPhysicsBouncingScrollPhysics都是ScrollPhysics的子类。

现在首先是要获取到ScrollPhysics对象,这个对于已有项目来说很简单,只需要通过scrollPosition就能获取。创建Simulation对象的源码如下:

// 手指离开屏幕时的滑动速度
var velocity = details.velocity.pixelsPerSecond;
// 创建Simulation
var simulation = scrollPosition.physics.createBallisticSimulation(scrollPosition, velocity);

Simulation对象有了,那该怎么变成动画呢?这里就需要前面提到的AnimationController,通过AnimationControlleranimateWith方法将Simulation对象转为动画曲线。通过监听动画改变,不断调整滚动组件的滚动位置实现动画效果。监听动画改变的源码如下:

_animationController.addListener(() {
  widget.controller.scrollPosition?.jumpTo(_animationController.value);
});

onVerticalDragEnd回调处理源码:

void _onVerticalDragEnd(DragEndDetails details) {
  var scrollPosition = widget.controller.scrollPosition;
  if (scrollPosition != null) {
    // 手指离开屏幕时的滑动速度
    var velocity = details.velocity.pixelsPerSecond;
    // 创建Simulation
    var simulation = scrollPosition.physics
        .createBallisticSimulation(scrollPosition, velocity);
    if (simulation != null) {
      _animationController.animateWith(simulation);
    }
  }
}

最终效果展示:

screenshot2

总结

这个系列写到这基本算是告一段落了,项目实现中更细节的功能,例如支持水平滚动组件、反向滚动组件等就不再赘述,欢迎查看项目源码。

关于项目的后续计划,短期内会对项目进行一次重构,这是为了提升项目的扩展性,同时会完善一些文档说明,以及支持普通Item的跳转。项目已经趋于稳定,所以长期来看不会有特别大的改动,只会不断完善一些常见实例,例如微信通讯录、城市列表等。当然,如果你发现现有功能满足不了你的需求,欢迎评论留言提issue

后面会不定期更新新版本使用说明以及常见实例介绍等文章,如果你有兴趣了解的话,欢迎查看后续文章。

最后

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

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


系列文章

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值