BottomSheetBehavior
前面倒是有讲过Android原生的BottomSheetBehavior,使用场景还是蛮多的,最近在用Flutter做一款地图App,有用到BottomSheet的功能,但是 Flutter 自带的BottomSheet有点拉,只能显示和隐藏销毁,不支持折叠为最小高度状态也不支持三段式拖动,那就自己撸一个吧:
追踪 BottomSheet
既然是基于系统的BottomSheet ,不妨来看看sdk的实现方式,正常来讲,显示一个BottomSheet,可以通过showBottomSheet 来触发,或者给Scaffold配置bottomSheet属性,查看源码可以看到Scaffold.of(context).showBottomSheet,内部是创建了一个_StandardBottomSheet,继续追踪发现Widget其实是通过AnimatedBuilder来实现内容高度的扩展,其内部维护了一个BottomSheet。
简单阅读下BottomSheet源码,重点就在于 GestureDetector 的垂直方向上的手势回调 onVerticalDragUpdate 、以及onVerticalDragEnd,拖动位置更新、惯性滑动以及销毁,核心都在这了。
系统默认实现效果
- 拖拽速度大于某一个像素阀值时,销毁。
- 拖拽位置小于总高度的一半时,销毁。
保留这一份默认效果,对于想使用默认效果的同学,不做任何额外配置即可。
准备要实现的功能点:
- 三段式: 基于SDK的BottomSheet ,可扩展为完全展开、中间状态、折叠状态;
- 阻尼、惯性滑动: 支持配置最小滑动偏移量;
- 保持状态,支持Peek状态: 以最小高度显示BottomSheet;
- 打破 showBottomSheet 限制: 兼容系统默认的弹出方式,亦可当作正常的Widget使用,脱离showBottomSheet。
定义三段式状态:BottomSheetBehavoir
- EXPANDED 完全展开
- HALF 中间状态,介于EXPANDED与PEEK之间
- PEEK 以一个最小高度展示
- HIDDEN 完全隐藏,即销毁,系统默认效果
开启三段式,我们还需要配置一个约束条件,即BottomSheet的最大高度和最小高度 BoxConstraints:
- 最小高度
HALF模式下
如果提供的 Constraints minHeight 小于最大高度的一半,则取后者,防止位置错乱!
var peekThreshold = enableHalf
? min(_childHeight / 2, constraints.minHeight) / _childHeight
: constraints.minHeight / _childHeight;
阀值定义
- 拖拽滚动阀值,大于此值,才允许滑动
const double _offsetThreshold = 32.0; - 展开时最大高度 阀值
const double _maxThreshold = 1.0; - 中间状态阀值
const double _halfThres