这篇文章主要讲解了“Flutter Flow如何实现滑动显隐层”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Flutter Flow如何实现滑动显隐层”吧!
思路其实非常简单,监听横向滑动的手势事件,根据偏移量让上层组件进行偏移。当放手时,根据偏移量是否达到宽度的一半,使用动画进行移出或者关闭。
偏移的实现方式有很多,但需要自由地进行布局和矩阵变换、透明度,并且需要支持动画的变化,Flow 组件是一个非常不错的选择。 Flow 组件可以通过代理类对子组件进行自定义布局,灵活性极强;如果是 CustomPaint
是 绘制之王 可以绘制万物,那么 Flow
就是 布局之王,可以摆放万物。
另外,在滑动过程中需要注意限制偏移量,使偏移量在 0~size.width
之内;当放手时,通过动画控制器来驱动动画,使用补间让偏移量运动到 0
(打开) 或 size.width
(关闭) 。当关闭时,在右下角展示一个按钮用于点击展开:
Flow 组件布局最重要的是实现 FlowDelegate
,在其中的 paintChildren
方法中实现布局的逻辑。和 CustomPainter
类似,FlowDelegate
的实现类也可以通过 super
构造为 repaint
入参设置可监听对象。可监听对象的变化会触发 paintChildren
重新绘制:
SwipeFlowDelegate
实现类再构造时传入可监听对象 offsetX
,在绘制索引为 1
的孩子时,通过 Matrix4
进行偏移。这样只要在手势水平滑动中,更新 offsetX 值即可。另外,可以根据 offsetX.value
是否达到 size.width
知道是否是关闭状态,如果已经关闭,绘制按钮。
class SwipeFlowDelegate extends FlowDelegate { final ValueListenable<double> offsetX; SwipeFlowDelegate(this.offsetX) : super(repaint: offsetX); @override void paintChildren(FlowPaintingContext context) { Size size = context.size; context.paintChild(0); Matrix4 offsetM4 = Matrix4.translationValues(offsetX.value, 0, 0); context.paintChild(1, transform: offsetM4); // 偏移量对于父级尺寸 if (offsetX.value == size.width) { Matrix4 m1 = Matrix4.translationValues(size.width / 2 - 30, size.height / 2 - 30, 0); context.paintChild(2, transform: m1); Matrix4 m2 = Matrix4.translationValues(size.width / 2 - 30, -(size.height / 2 - 50), 0); context.paintChild(3, transform: m2); } } @override bool shouldRepaint(covariant SwipeFlowDelegate oldDelegate) { return oldDelegate.offsetX.value != offsetX.value; } }
从这里可以看出,FlowDelegate
的最大优势是可以自定义孩子的绘制与否,还可以在绘制时通过 Matrix4
对孩子进行矩阵变换,还有可选参数可以控制透明度。接下来使用 Flow
组件时,提供 SwipeFlowDelegate
,并在 children 列表中依次放入子组件。其中前两个组件由外界传入,分别是底组件和上层组件,这样组件的布局就完成了,接下来监听事件,更新 factor
即可:
final ValueNotifier<double> factor = ValueNotifier(0); Flow( delegate: SwipeFlowDelegate(factor), children: [ widget.content, widget.overflow, GestureDetector( onTap: open, child: const Icon(Icons.menu_open_outlined, color: Colors.white)), GestureDetector( onTap: () { Navigator.of(context).pop(); }, child: const Icon(Icons.close, color: Colors.white)) ], )
这里手势的处理是非常简单的,通过 GestureDetector
监听水平拖拽事件。在 onHorizontalDragUpdate
中根据拖拽的偏移量更新 factor
的值,其中通过 .clamp(0, widget.width)
可以限制偏移量的取值区间。
@override Widget build(BuildContext context) { return GestureDetector( behavior: HitTestBehavior.opaque, onHorizontalDragUpdate: _onHorizontalDragUpdate, onHorizontalDragEnd: _onHorizontalDragEnd, child: SizedBox( height: MediaQuery.of(context).size.height, width: widget.width, child: Flow( delegate:// 同上,略... ); } void _onHorizontalDragUpdate(DragUpdateDetails details) { double cur = factor.value + details.delta.dx; factor.value = cur.clamp(0, widget.width); } void _onHorizontalDragEnd(DragEndDetails details) { if (factor.value > widget.width / 2) { close(); } else { open(); } }
最后在 _onHorizontalDragEnd
回调中,根据当前偏移量是否大于一般宽度,决定关闭还是打开。期间过程使用动画进行偏移量的过渡变化。
动画的使用,主要是通过 AnimationController
动画控制器来驱动数值的变化;在放手时 Tween
创建补间动画器,监听动画器数值的变化更新偏移量。这样偏移量就可以在指定时间内,在两个值之间渐变,从而产生动画效果。比如抬手时,open
方法是让偏移量从当前位置变化到 0
:
class _ScrollHideWrapperState extends State<ScrollHideWrapper> with SingleTickerProviderStateMixin { late AnimationController _ctrl; final ValueNotifier<double> factor = ValueNotifier(0); @override void initState() { super.initState(); _ctrl = AnimationController( duration: const Duration(milliseconds: 200), vsync: this, ); } @override Widget build(BuildContext context) { // 略同... } // 动画关闭 Future<void> close() async { Animation<double> anim = Tween<double>(begin: factor.value, end: widget.width).animate(_ctrl); anim.addListener(() => factor.value = anim.value); await _ctrl.forward(from: 0); } // 动画打开 Future<void> open() async { Animation<double> anim = Tween<double>(begin: factor.value, end: 0).animate(_ctrl); anim.addListener(() => factor.value = anim.value); await _ctrl.forward(from: 0); } }
如果想让动画的变化非匀速,可以使用 Curve 来控制动画曲线。这样,基于 Flow 实现的自定义布局,就可以根据手势和动画,完成特定的交互功能。从这里可以看出 Flow 自定义布局的灵活性非常强,很多疑难杂症,都可以使用它来完成。
感谢各位的阅读,以上就是“Flutter Flow如何实现滑动显隐层”的内容了,经过本文的学习后,相信大家对Flutter Flow如何实现滑动显隐层这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。