dependencies:
toggle_rotate: $lastVersion
一、描述
目标: 让一个组件点击时执行旋转,再点击旋转回去。
最简使用 | 时长、曲线、方向 | 可含一切组件 | 旋转角度 |
---|---|---|---|
1.所有属性:
名称 | 类型 | 功能 | 备注 | 默认 |
---|---|---|---|---|
rad | double | 旋转角度 | 弧度制 | pi / 2 |
durationMs | int | 动画时长 | 毫秒 | 200 |
curve | Curve | 动画曲线 | - | Curves.fastOutSlowIn |
clockwise | bool | 是否顺时针旋转 | - | true |
onTap | Function | 点击事件 | @required | null |
child | Widget | 子组件 | @required | null |
2.最简使用:
ToggleRotate(
child: Icon(Icons.arrow_upward,size: 60,color: Colors.orangeAccent),
onTap: () {}, //点击事件
),
3.指定时长和曲线和方向
ToggleRotate(
curve: Curves.decelerate,
durationMs: 400,//动画时长
clockwise: false, //是否是顺时针
child: Icon(Icons.arrow_upward,size: 60,color: Colors.orangeAccent),
onTap: () {},
),
4.可使一切组件进行旋转切换
ToggleRotate(
curve: Curves.decelerate,
durationMs: 400,
child: Image(width:60,height: 60,image: AssetImage("assets/images/icon_28.jpg")),
onTap: () {},
)
5.可使旋转的角度
ToggleRotate(
rad: pi / 4,
curve: Curves.linear,
child: Image(width:60,height: 60,image: AssetImage("assets/images/icon_28.jpg")),
onTap: () {},
)
二、实现原理
点击时进行一些动画效果比较好看,顺便抽离成一个组件分享一下
这个小组件是一个动画的经典案例,所以分析一下具体实现还是很有意义的
1.自定义组件
开始分析一下是否有状态。很明显,我们需要在点击时让组件旋转
组件有是否旋转是一个状态量,旋转过程中的角度也是状态量
可以说想要实现动画,基本上是基于StatefulWidget的,先写出一个基本的组件
由于需要动画,要with SingleTickerProviderStateMixin
library toggle_rotate;
import 'dart:math';
import 'package:flutter/material.dart';
class ToggleRotate extends StatefulWidget {
final Widget child;
final Function onTap;
final double rad;
final int durationMs;
final bool clockwise;
final Curve curve;
ToggleRotate(
{this.child,
@required this.onTap,
this.rad = pi / 2,
this.clockwise = true,
this.durationMs = 200,
this.curve = Curves.fastOutSlowIn});
@override
_ToggleRotateState createState() => _ToggleRotateState();
}
class _ToggleRotateState extends State<ToggleRotate> with SingleTickerProviderStateMixin {
@override
void initState() {
super.initState();
}
@override
void dispose()
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container();
}
}
2.动画器的创建和销毁
状态量有旋转的弧
_rad
、是否已旋转_rotated
。
动画器AnimationController负责让数字在0.0~1.0之间均匀变化
通过CurvedAnimation来让数字变化率为曲线
核心就是确定每次更新状态时弧度的大小。 通过addListener可以在动画器每次刷新时进行监听
通过addStatusListener对动画的状态进行监听,如果完成_rotated置反
class _ToggleRotateState extends State<ToggleRotate>
with SingleTickerProviderStateMixin {
double _rad = 0;
bool _rotated = false;
AnimationController _controller;
Animation _rotate;
@override
void initState() {
_controller = AnimationController(
duration: Duration(milliseconds: widget.durationMs), vsync: this)
..addListener(() => setState(() =>
_rad = (_rotated ? (1 - _rotate.value) : _rotate.value) * widget.rad))
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
_rotated = !_rotated;
}
});
_rotate = CurvedAnimation(parent: _controller, curve: widget.curve);
super.initState();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
3.通过Transform实现变换
在点击时先重设控制器,然后再执行。否则第二次是不会动的
在这里只用onTap回调点击事件,暴露给外界处理。
clockwise决定是否是顺时针旋转,这样该组件就完成了。
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
_controller.reset();
_controller.forward();
widget.onTap();
},
child: Transform(
transform: Matrix4.rotationZ(widget.clockwise ? _rad : -_rad),
alignment: Alignment.center,
child: widget.child,
),
);
}
麻雀虽小五脏俱全,这个小组件虽然就60行左右的代码,但包含很多知识点。
如果你想要一个组件在点击时不那么古板,欢迎使用
尾声
另外本人有一个Flutter微信交流群,欢迎小伙伴加入,共同探讨Flutter的问题,期待与你的交流与切磋。
@张风捷特烈 2019.02.23 未允禁转
我的公众号:编程之王
联系我--邮箱:1981462002@qq.com --微信:zdl1994328
~ END ~