flutter以中心旋转控件
2019-05-10 本文已影响0人
叶落清秋
1.gif
其实就实现这么一个旋转动画,一开始想到的是用AnimatedContainer的transform属性来实现这个动画,结果发现不行,三角形的旋转是以左上角为中心的。
效果就不演示了,随便试试就知道了
看下为什么以中心旋转,有什么办法使中心旋转
- transform 通过矩阵变换实现,矩阵的变换是以canvas的(0,0)点作为起始点的,所以需要图像中心变换的话,需要canvas先移动到(-width*0.5,-height*0.5),将图像中心点移动到(0,0)位置,进行变换,然后再移动回来
显然我们在AnimatedContainer里获取不到canvas,所以这方案实现不了 - 分析源码
@override
Widget build(BuildContext context) {
return Container(
child: widget.child,
alignment: _alignment?.evaluate(animation),
padding: _padding?.evaluate(animation),
decoration: _decoration?.evaluate(animation),
foregroundDecoration: _foregroundDecoration?.evaluate(animation),
constraints: _constraints?.evaluate(animation),
margin: _margin?.evaluate(animation),
transform: _transform?.evaluate(animation),
);
}
在AnimatedContainer的build中找到了Container,显然是通过Animation改变Container的属性,来实现动画效果的
继续追踪Container的transform
@override
Widget build(BuildContext context) {
...
if (transform != null)
current = Transform(transform: transform, child: current);
return current;
}
build中找到了使用了Transform
const Transform({
Key key,
@required this.transform,
this.origin,
this.alignment,
this.transformHitTests = true,
Widget child,
}) : assert(transform != null),
super(key: key, child: child);
Transform中有一个alignment,这个可以移动到中心点,但是Container未设置,所以默认使用左上角为起始点
既然AnimatedContainer实现不了,那就自定义一个
我们使用Transform控件,使用其Transform.rotate构造函数
Transform.rotate({
Key key,
@required double angle,
this.origin,
this.alignment = Alignment.center, //已经设置为中心了
this.transformHitTests = true,
Widget child,
})
但是这个没动画效果,所以我们需要加上动画
import 'dart:math';
import 'package:flutter/material.dart';
/*
* 中心处旋转动画,AnimatedContainer针对的是左上角的旋转,注意角度传递的是π
* */
class RotateContainer extends StatefulWidget {
final double endAngle; // 注意这个角度,π为180°
final bool rotated;
final Widget child;
@override
_RotateContainerState createState() => _RotateContainerState();
RotateContainer({this.endAngle, this.child, this.rotated = false});
}
class _RotateContainerState extends State<RotateContainer>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> _animation;
double angle = 0;
@override
void initState() {
_controller =
AnimationController(vsync: this, duration: Duration(milliseconds: duration));
_animation =
Tween(begin: 0.0, end: widget.endAngle).animate(_controller)
..addListener(() {
setState(() {
angle = _animation.value;
});
});
super.initState();
}
@override
void didUpdateWidget(RotateContainer oldWidget) {
if (oldWidget.rotated == widget.rotated) return; //防止多余刷新
if (!widget.rotated) {
_controller.reverse();
} else {
_controller.forward();
}
super.didUpdateWidget(oldWidget);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Transform.rotate(
angle: angle,
child: widget.child,
);
}
}
附上其他代码:
const int duration = 400;
class TagsButton extends StatelessWidget {
final String text;
final bool rotated;
final VoidCallback onPressed;
TagsButton({this.text, this.onPressed, this.rotated = false});
@override
Widget build(BuildContext context) {
return InkWell(
child: Container(
padding: EdgeInsets.all(10.0),
child: Row(
children: <Widget>[
Text(text),
RotateContainer(
endAngle: pi,
rotated: rotated,
child: CustomPaint( //自己画个三角形,当然也可以使用Icon
painter: TrianglePainter(Colors.grey),
child: SizedBox(
width: 12,
height: 12,
),
),
),
],
),
),
onTap: onPressed,
);
}
}
/*
* 画个三角形
* */
class TrianglePainter extends CustomPainter {
Color color;
Paint _paint;
Path _path;
double angle;
TrianglePainter(this.color) {
_paint = Paint()
..strokeWidth = 1.0
..color = color
..isAntiAlias = true;
_path = Path();
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
@override
void paint(Canvas canvas, Size size) {
final baseX = size.width * 0.5;
final baseY = size.height * 0.5;
//三角形
_path.moveTo(baseX - 0.86 * baseX, 0.5 * baseY);
_path.lineTo(baseX, 1.5 * baseY);
_path.lineTo(baseX + 0.86 * baseX, 0.5 * baseY);
canvas.drawPath(_path, _paint);
}
}
使用
bool tagsMenuOpen = false;
@override
Widget build(BuildContext context) {
return Center(
child: Container(
width: 80,
child: TagsButton(
text: "筛选",
rotated: tagsMenuOpen,
onPressed: () {
tagsMenuOpen = !tagsMenuOpen;
setState(() {});
},
),
),
);
}