写在前面
适用于对flutter动画有一定了解的同学,本案例是为了模拟一个角色对象根据不同状态变换不同效果
先看下效果图
实现&代码
定义状态&动画
enum CellStatus { none, mot, pen, def }
- non: none/常规状态,无动画
- mot: motion/行动状态,背景闪烁
- pen: pending/待选状态,抖动
- def: defense/防守状态,尖角变圆角
定义角色对象
class Cell {
CellStatus status;
Notify1 onStatusChanged;
}
本例中只包含两个属性
- status: CellStatus,表示当前状态
- onStatusChanged: Notify1
,表示状态变化的回调
这里的Notify1是自定义的一个带一个泛型参数的方法对象
当需要变化状态时,比如转换到行动状态
cell.onStatusChanged?.call(CellStatus.mot)
其实更常见的办法是让Cell继承于ChangeNotifier,然后通过addListener来添加监听变化,相比于onStatusChanged: Notify1
,后者只针对单一属性,具有指向性。
页面布局
上方是4个按钮,模拟状态变化,下方是展示角色对象的CellWidget
class CellApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
/// 这里创建角色对象
Cell cell = Cell();
var children = [
Spacer(),
RaisedButton(onPressed: () => cell.onStatusChanged?.call(CellStatus.none), child: Text("None")),
Spacer(),
RaisedButton(onPressed: () => cell.onStatusChanged?.call(CellStatus.mot), child: Text("Mot")),
Spacer(),
RaisedButton(onPressed: () => cell.onStatusChanged?.call(CellStatus.pen), child: Text("Pen")),
Spacer(),
RaisedButton(onPressed: () => cell.onStatusChanged?.call(CellStatus.def), child: Text("Def")),
Spacer(),
];
return Scaffold(
body: SafeArea(
child: Column(children: [
Expanded(child: Center(child: Row(children: children, mainAxisSize: MainAxisSize.min))),
Expanded(child: Container(alignment: Alignment.topCenter, child: CellWidget(cell)))
]),
));
}
}
CellWidget
这是一个需要应用动画的widget,传入Cell对象。其State中加入AnimationController
class CellWidget extends StatefulWidget {
final Cell cell;
CellWidget(this.cell);
@override
_CellWidgetState createState() => _CellWidgetState();
}
class _CellWidgetState extends State with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: Duration(milliseconds: 500));
_controller.addListener(() => setState(() {}));
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
...
}
监听状态变化
就是为cell.onStatusChanged赋值
@override
void initState() {
super.initState();
...
widget.cell.onStatusChanged = onStatusChanged;
}
void onStatusChanged(CellStatus newStatus) {
...
}
处理状态变化
其实就是创建根据状态创建Animation
- 当为motion时,创建背景闪烁动画,使用ColorTween创建开始和结束的颜色,并调用controller.repeat(reverse: true)来实现重复
- 当为pending时,抖动动画是通过四向随机改变widget位置来实现的,所以只需在每次重绘时通过Random#nextInt()来随机方向即可,此处不用创建任何动画,只需让controller重复即可
- 当为defense时,尖角变圆角,使用Tween创建开始和结束时圆角的半径即可,不需重复
- 而none状态,需要回归正常,由于正常值是预定义的,所以此处仅仅需要关闭动画
void onStatusChanged(CellStatus newStatus) {
print("cell status change from ${widget.cell.status.toString()} to ${newStatus.toString()}");
widget.cell.status = newStatus;
setState(() {});
_controller.reset();
_controller.duration = Duration(milliseconds: 200);
if (widget.cell.status == CellStatus.mot) {
_animation = ColorTween(begin: Colors.white, end: Colors.red)
.animate(CurvedAnimation(parent: _controller, curve: Curves.easeInCubic));
_controller.repeat(reverse: true);
} else if (widget.cell.status == CellStatus.none) {
_controller.stop();
} else if (widget.cell.status == CellStatus.pen) {
_controller.repeat(reverse: true);
} else if (widget.cell.status == CellStatus.def) {
_animation = Tween(begin: 0.0, end: 16.0).animate(_controller);
_controller.duration = Duration(milliseconds: 800);
_controller.forward();
}
}
build不同状态下的widget
正常情况下,应该是一个Container,宽高为64.0,居中一个Text,decoration为白底,边框为尖角
由于抖动动画需要改变位置,所以外层应当加Transform.translate(offset, widget)
@override
Widget build(BuildContext context) {
var color = Colors.white;
var offset = Offset.zero;
var radius = 0.0;
if (widget.cell.status == CellStatus.mot) {
color = _animation.value;
} else if (widget.cell.status == CellStatus.pen) {
var random = new Random().nextInt(4);
/// left:0 right:1 top:2 bottom:3
double x = 0, y = 0;
switch (random) {
case 0: x = -2; break;
case 1: x = 2; break;
case 2: y = -2; break;
case 3: y = 2; break;
}
offset = offset.translate(x, y);
} else if (widget.cell.status == CellStatus.def) {
radius = _animation.value;
}
var decoration = BoxDecoration(color: color, border: Border.all(color: Colors.blue), borderRadius: BorderRadius.circular(radius));
var container = Container(width: 64.0, height: 64.0, decoration: decoration, alignment: Alignment.center, child: Text("Cell"));
return Transform.translate(offset: offset, child: container);
}
最后
本人热爱编程,热爱游戏,热爱flutter,一直想用flutter写一个RPG类型的游戏,有喜欢flutter开发游戏的朋友可以加我好友互相交流哈!