美文网首页FlutterFlutter
flutter以中心旋转控件

flutter以中心旋转控件

作者: 叶落清秋 | 来源:发表于2019-05-10 10:59 被阅读0次
    1.gif

    其实就实现这么一个旋转动画,一开始想到的是用AnimatedContainer的transform属性来实现这个动画,结果发现不行,三角形的旋转是以左上角为中心的。
    效果就不演示了,随便试试就知道了
    看下为什么以中心旋转,有什么办法使中心旋转

    1. transform 通过矩阵变换实现,矩阵的变换是以canvas的(0,0)点作为起始点的,所以需要图像中心变换的话,需要canvas先移动到(-width*0.5,-height*0.5),将图像中心点移动到(0,0)位置,进行变换,然后再移动回来
      显然我们在AnimatedContainer里获取不到canvas,所以这方案实现不了
    2. 分析源码
    @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(() {});
              },
            ),
          ),
        );
      }
    

    相关文章

      网友评论

        本文标题:flutter以中心旋转控件

        本文链接:https://www.haomeiwen.com/subject/tajooqtx.html