备案网站,深圳市住房和建设局,跨境电商官方网站建设,产品网络推广的方法BottomSheetBehavior 追踪 BottomSheet系统默认实现效果准备要实现的功能点#xff1a;定义三段式状态#xff1a;BottomSheetBehavoir阀值定义1. 未达到滚动阀值#xff0c;恢复状态2. 达到滚动阀值#xff0c;更新状态 前面倒是有讲过Android原生的BottomSheetBehavior定义三段式状态BottomSheetBehavoir阀值定义1. 未达到滚动阀值恢复状态2. 达到滚动阀值更新状态   前面倒是有讲过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 _halfThreshold  0.5; 
当拖拽结束时如果拖拽偏移量小于此阀值则恢复状态这里有个麻烦的点是需要根据用户拖拽方向来判断是向上还是向下拖动。 方向判断可以在 _handleDragStart 回调时记录初始偏移量startY_handleDragEnd 时计算开始和结束的差值 
/// 偏移量
var offset  updateY-startY ;
/// 当前动画值var value  widget.animationController!.value;late double toValue;late BottomSheetBehavior mode;offset0 为向上滑动反之 向下滑动。接下来需要根据滚动阀值来更新BottomSheet状态。 
1. 未达到滚动阀值恢复状态 
向上滑动恢复BottomSheet状态: Expanded / Half / Peek 
if (value  _maxThreshold) {// 处于Expand状态恢复toValue  _maxThreshold;mode  BottomSheetBehavior.EXPANDED;} else if (value  _halfThreshold  enableHalf) {// 处于Half恢复toValue  _halfThreshold;mode  BottomSheetBehavior.HALF;} else {toValue  peekThreshold;mode  BottomSheetBehavior.PEEK;}向下滑动恢复BottomSheet状态: Expanded / Half / Peek 
if (value  _halfThreshold) {// 处于Expand状态恢复toValue  _maxThreshold;mode  BottomSheetBehavior.EXPANDED;} else if (value  peekThreshold  enableHalf) {// 处于Half恢复toValue  _halfThreshold;mode  BottomSheetBehavior.HALF;} else {toValue  peekThreshold;mode  BottomSheetBehavior.PEEK;}2. 达到滚动阀值更新状态 
向上滑动更新BottomSheet状态: Expanded / Half / Peek 
if (value  _halfThreshold) {toValue  _maxThreshold;mode  BottomSheetBehavior.EXPANDED;} else if (value  peekThreshold) {toValue  enableHalf ? _halfThreshold : _maxThreshold;mode  enableHalf ? BottomSheetBehavior.HALF : BottomSheetBehavior.EXPANDED;} else {toValue  peekThreshold;mode  BottomSheetBehavior.PEEK;}向下滑动更新BottomSheet状态: Half / Peek 
if (value  _halfThreshold) {toValue  enableHalf ? _halfThreshold : peekThreshold;mode  enableHalf ? BottomSheetBehavior.HALF : BottomSheetBehavior.PEEK;
} else {toValue  peekThreshold;mode  BottomSheetBehavior.PEEK;
}以上我们获取到了开始讲到的AnimatedBuilder的 动画值以及变化量在**_handleDragEnd**中可以通过animateTo平滑的过渡BottomSheet状态 
/// 以动画的形式fly
void animateTo(double to) {widget.animationController!.animateTo(to,curve: Curves.linearToEaseOut,duration: animateDuration,);
}另外至于BottomSheet的最新的状态回调最好是在动画结束后再通知给调用者以免更新状态期间build重绘 
Future.delayed(animateDuration, ()  widget.onBehaviorChanged?.call(mode));至此既保留了flutter默认的BottomSheet效果又扩展了三段式当然调用方式和系统BottomSheet一模一样另外还可以像普通Widget一样来使用哦来看看最终的效果吧 项目效果 Demo