1.让动画使用起来就像使用widget。
2.可自定义动画。
3.内置平移动画。
演示:
代码:
import 'dart:math';
import 'package:flutter/cupertino.dart';
class AnimateView extends StatefulWidget {
///子Widget
final Widget child;
///动画自定义
final IAnimate? animate;
///是否需要每次刷新时都执行动画
final bool isNeedFlashEveryTime;
const AnimateView({
super.key,
required this.child,
this.animate,
this.isNeedFlashEveryTime = false,
});
@override
State<StatefulWidget> createState() => _AnimateViewState();
}
class _AnimateViewState extends State<AnimateView>
with TickerProviderStateMixin {
late IAnimate animate;
late AnimationController controller;
late Animation animation;
@override
void initState() {
super.initState();
animate = widget.animate ??
TranslationAnimate(
angle: TranslationAnimateDirection.rightToLeft.angle);
animate.init();
controller = animate.getAnimationController(this);
animation = animate.getAnimation(controller, this);
//启动动画(正向执行)
controller.forward();
}
@override
void didUpdateWidget(covariant AnimateView oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.isNeedFlashEveryTime) {
animate = widget.animate ??
TranslationAnimate(
angle: TranslationAnimateDirection.rightToLeft.angle);
animate.init();
controller = animate.getAnimationController(this);
animation = animate.getAnimation(controller, this);
//启动动画(正向执行)
controller.forward();
}
}
@override
Widget build(BuildContext context) {
return animate.animate(context, widget.child, animation, controller);
}
}
///动画抽象类。
///实现该类,定制自己的动画。
abstract class IAnimate {
///初始化
void init();
///获取AnimationController
AnimationController getAnimationController(TickerProvider provider);
///获取Animation
Animation getAnimation(
AnimationController controller, State<StatefulWidget> state);
///定制自己的动画,每一个item都会调用到animate,
///[widget] 执行动画之后的widget
///[index] 列表的item的index
Widget animate(
BuildContext context,
Widget widget,
Animation animation,
AnimationController controller,
);
}
///平移动画
class TranslationAnimate extends IAnimate {
///动画执行的总长度
static double gap = 1000.0;
///动画执行角度
final int angle;
///动画执行时长,毫秒(ms)
final int duration;
///进入动画还是出去动画
final TranslationAnimateType type;
///界面的宽,动画需要根据你需要操作的界面宽进行计算,不传代表屏幕宽
final double? width;
///界面的高,动画需要根据你需要操作的界面高进行计算,不传代表屏幕高
final double? height;
TranslationAnimate({
this.angle = 0,
this.duration = 500,
this.type = TranslationAnimateType.translateIn,
this.width,
this.height,
});
@override
Widget animate(BuildContext context, Widget widget, Animation animation,
AnimationController controller) {
final size = MediaQuery.of(context).size;
double width = this.width ?? size.width;
double height = this.height ?? size.height;
double left = 0;
double top = 0;
///范围0.0->1000.0
double animateValue = animation.value;
int positiveAngle = angle;
if (angle < 0) {
int tempAngle = angle % 360;
positiveAngle = 360 - tempAngle;
}
positiveAngle = positiveAngle % 360;
///范围0->1
double rate = animateValue / gap;
if (type == TranslationAnimateType.translateIn) {
rate = rate - 1;
}
if (positiveAngle >= 0 && positiveAngle <= 45 ||
positiveAngle >= 135 && positiveAngle <= 225 ||
positiveAngle > 315 && positiveAngle <= 360) {
///移出距离以宽度为准
left = rate * width;
if (positiveAngle > 90 && positiveAngle < 270) {
left = -left;
}
double tanValue = tan(positiveAngle * pi / 180);
top = rate * width * tanValue;
} else if (positiveAngle == 90) {
top = rate * height;
left = 0;
} else if (positiveAngle == 270) {
top = -rate * height;
left = 0;
} else {
///移出距离以高度为准
top = rate * height;
if (positiveAngle > 180 && positiveAngle < 360) {
top = -top;
}
double tanValue = tan(positiveAngle * pi / 180);
if (tanValue == 0) {
left = 0;
} else {
left = rate * height / tanValue;
}
}
//print("angle=$positiveAngle");
//print("left=$left");
//print("top=$top");
return Container(
transform: Matrix4.translationValues(left, top, 0),
child: widget,
);
}
@override
Animation getAnimation(
AnimationController controller, State<StatefulWidget> state) {
return Tween(begin: 0.0, end: gap).animate(controller)
..addListener(() {
if (state.mounted) {
state.setState(() {});
}
});
}
@override
AnimationController getAnimationController(TickerProvider provider) {
return AnimationController(
duration: Duration(milliseconds: duration), vsync: provider);
}
@override
void init() {}
}
///平移动画执行方向枚举
enum TranslationAnimateDirection {
///从下往上
bottomToTop(90),
///从上往下
topToBottom(270),
///从左往右
leftToRight(0),
///从右往左
rightToLeft(180);
///动画执行方向度数
final int angle;
const TranslationAnimateDirection(this.angle);
}
///位移动画类型
enum TranslationAnimateType {
///出去动画
translateOut,
///进入动画
translateIn
}