这篇会深化view拖拽实例,利用flutter animation、插值器以及animatedbuilder教大家实现带动画的抽屉效果。先来看效果:
通过构思,我们可以设想到实现抽屉的方式就是用stack控件将两个widget叠加显示,用gesturedetector监听手势滑动,动态移动顶层的widget,当监听到手势结束的时候根据手势滑动的距离动态将顶部widget利用动画效果滑动到结束位置即可。
实现底部widget
class downdrawerwidget extends statelesswidget { @override widget build(buildcontext context) { return container(child: center(child: text("底部widget",),),); } }
这个widget太简单了,就不细说了。
实现顶部widget
class updrawerwidget extends statelesswidget { @override widget build(buildcontext context) { return container(child: center(child: text("顶部widget",),),); } }
实现方式和底部是一样的。
实现可以移动的容器
上面两个widget都是单纯用来显示的widget,因此继承了statelesswidget。接下来我们需要根据手势动态移动顶部的widget,因此需要继承statefulwidget。
// 顶部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()方法。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。