Flutter实现抽屉动画

这篇会深化View拖拽实例,利用Flutter Animation、插值器以及AnimatedBuilder教你们实现带动画的抽屉效果。先来看效果:html

经过构思,咱们能够设想到实现抽屉的方式就是用Stack控件将两个Widget叠加显示,用GestureDetector监听手势滑动,动态移动顶层的Widget,当监听到手势结束的时候根据手势滑动的距离动态将顶部Widget利用动画效果滑动到结束位置便可。java

实现底部Widget

class DownDrawerWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Container(child: Center(child: Text("底部Widget",),),); } } 

这个Widget太简单了,就不细说了。web

实现顶部Widget

class UpDrawerWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Container(child: Center(child: Text("顶部Widget",),),); } } 

实现方式和底部是同样的。less

实现能够移动的容器

上面两个Widget都是单纯用来显示的Widget,所以继承了StatelessWidget。接下来咱们须要根据手势动态移动顶部的Widget,所以须要继承StatefulWidget。ide

// 顶部Widget class HomePageWidget extends StatefulWidget { @override State<StatefulWidget> createState() => HomePageState(); } class HomePageState extends State<HomePageWidget> with SingleTickerProviderStateMixin { @override void initState() {...} @override void dispose() {...} @override Widget build(BuildContext context) {...} void _onViewDragDown(DragDownDetails callback) {...} void _onViewDrag(DragUpdateDetails callback) {...} void _onViewDragUp(DragEndDetails callback) {...} } 

初始化状态initState()

这个方法是在Widget初始化的时候系统的回调函数,咱们须要在该函数中初始化动画函数

AnimationController controller;
@override void initState() { // 初始化动画控制器,这里限定动画时常为200毫秒 controller = new AnimationController(vsync: this, duration: const Duration(milliseconds: 200)); // vsync对象会绑定动画的定时器到一个可视的widget,因此当widget不显示时,动画定时器将会暂停,当widget再次显示时,动画定时器从新恢复执行,这样就能够避免动画相关UI不在当前屏幕时消耗资源。 // 当使用vsync: this的时候,State对象必须with SingleTickerProviderStateMixin或TickerProviderStateMixin;TickerProviderStateMixin适用于多AnimationController的状况。 // 设置动画曲线,就是动画插值器 // 经过这个连接能够了解更多差值器,https://docs.flutter.io/flutter/animation/Curves-class.html,咱们这里使用带回弹效果的bounceOut。 CurvedAnimation curve = new CurvedAnimation(parent: controller, curve: Curves.bounceOut); // 增长动画监听,当手势结束的时候经过动态计算到达目标位置的距离实现动画效果。curve.value为当前动画的值,取值范围0~1。 curve.addListener(() { double animValue = curve.value; double offset = dragUpDownX - dragDownX; double toPosition; // 右滑 if (offset > 0) { if (offset > maxDragX / 5) { // 打开 toPosition = maxDragX; isOpenState = true; } else { if (isOpenState) { toPosition = maxDragX; isOpenState = true; } else { toPosition = 0.0; isOpenState = false; } } } else { if (offset < (-maxDragX / 2.0)) { // 关 toPosition = 0.0; isOpenState = false; } else { if (isOpenState) { toPosition = maxDragX; isOpenState = true; } else { toPosition = 0.0; isOpenState = false; } } } dragOffset = (toPosition - dragUpDownX) * animValue + dragUpDownX; // 刷新位置 setState(() {}); }); } 

结束Widget dispose()

当Widget不可用将被回收的时候,系统会回调dispose()方法,咱们在这里回收动画。动画

@override void dispose() { controller.dispose(); } 

记录按下的位置

double dragDownX = 0.0; void _onViewDragDown(DragDownDetails callback) { dragDownX = callback.globalPosition.dx; } 

拖动的时候刷新View的位置

/** * 最大可拖动位置 */ final double maxDragX = 230.0; double dragOffset = 0.0; void _onViewDrag(DragUpdateDetails callback) { double tmpOffset = callback.globalPosition.dx - dragDownX; if (tmpOffset < 0) { tmpOffset += maxDragX; } // 边缘检测 if (tmpOffset < 0) { tmpOffset = 0.0; } else if (tmpOffset >= maxDragX) { tmpOffset = maxDragX; } // 刷新 if (dragOffset != tmpOffset) { dragOffset = tmpOffset; setState(() {}); } } 

离手的时候记录位置并执行动画

/** * 脱手时候的位置 */ double dragUpDownX = 0.0; void _onViewDragUp(DragEndDetails callback) { dragUpDownX = dragOffset; // 执行动画,每次都从第0帧开始执行 controller.forward(from: 0.0); } 

支持移动的Widget

@override Widget build(BuildContext context) { return Transform.translate( offset: Offset(dragOffset, 0.0), child: Container( child: GestureDetector( onHorizontalDragDown: _onViewDragDown, onVerticalDragDown: _onViewDragDown, onHorizontalDragUpdate: _onViewDrag, onVerticalDragUpdate: _onViewDrag, onHorizontalDragEnd: _onViewDragUp, onVerticalDragEnd: _onViewDragUp, child: Container( child: new UpDrawerWidget(), ),),),);} 

Flutter动画

总结一下,想在Flutter中实现动画,须要先建立一个AnimationController控制器;若是有特殊的插值要求,再建立一个插值器,调用controller.forward()方法执行动画,经过addListener()的回调改变对应数值以后调用setState(() {})方法刷新位置便可。
Flutter API还提供AnimatedBuilder用来简化实现动画的复杂性,让咱们不用手动调用addListener()方法。ui

相关文章
相关标签/搜索