程序员开发实例大全宝库

网站首页 > 编程文章 正文

Flutter实现抽屉动画(flutter 抽奖转盘)

zazugpt 2024-08-25 17:26:15 编程文章 15 ℃ 0 评论

这篇会深化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()方法。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表