美文网首页
Flutter 动画

Flutter 动画

作者: 勇往直前888 | 来源:发表于2019-09-24 15:13 被阅读0次

    动画组件

    有一些现成的动画组件,只要设置时长,对应变化的属性值就可以了,比较方便。

    image.png
    import 'package:flutter/material.dart';
    
    class AnimatedDemo extends StatefulWidget {
      @override
      State<StatefulWidget> createState() {
        return _AnimatedDemoState();
      }
    }
    
    class _AnimatedDemoState extends State<AnimatedDemo> {
      double _padding = 10;
      Alignment _align = Alignment.topRight;
      double _height = 100;
      double _left = 0;
      Color _color = Colors.red;
      TextStyle _style = TextStyle(color: Colors.black);
    
      @override
      Widget build(BuildContext context) {
        final duration = Duration(seconds: 1);
        return Scaffold(
          appBar: AppBar(
            title: Text('动画组件的例子'),
          ),
          body: SingleChildScrollView(
            child: Column(
              children: <Widget>[
                RaisedButton(
                  onPressed: () {
                    setState(() {
                      _padding = 20;
                    });
                  },
                  child: AnimatedPadding(
                    duration: duration,
                    padding: EdgeInsets.all(_padding),
                    child: Text("AnimatedPadding"),
                  ),
                ),
    
                SizedBox(
                  height: 50,
                  child: Stack(
                    children: <Widget>[
                      AnimatedPositioned(
                        duration: duration,
                        left: _left,
                        child: RaisedButton(
                          onPressed: () {
                            setState(() {
                              _left = 100;
                            });
                          },
                          child: Text("AnimatedPositioned"),
                        ),
                      )
                    ],
                  ),
                ),
    
                Container(
                  height: 100,
                  color: Colors.grey,
                  child: AnimatedAlign(
                    duration: duration,
                    alignment: _align,
                    child: RaisedButton(
                      onPressed: () {
                        setState(() {
                          _align = Alignment.center;
                        });
                      },
                      child: Text("AnimatedAlign"),
                    ),
                  ),
                ),
    
                AnimatedContainer(
                  duration: duration,
                  height: _height,
                  color: _color,
                  child: FlatButton(
                    onPressed: () {
                      setState(() {
                        _height = 150;
                        _color = Colors.blue;
                      });
                    },
                    child: Text(
                      "AnimatedContainer",
                      style: TextStyle(color: Colors.white),
                    ),
                  ),
                ),
    
                AnimatedDefaultTextStyle(
                  child: GestureDetector(
                    child: Text("hello world"),
                    onTap: () {
                      setState(() {
                        _style = TextStyle(
                          color: Colors.blue,
                          decorationStyle: TextDecorationStyle.solid,
                          decorationColor: Colors.blue,
                        );
                      });
                    },
                  ),
                  style: _style,
                  duration: duration,
                ),
    
                RaisedButton(onPressed: (){
                  setState(() {
                    _padding = 10;
                    _align = Alignment.topRight;
                    _height = 100;
                    _left = 0;
                    _color = Colors.red;
                    _style = TextStyle(color: Colors.black);
                  });
                }, child: Text('参数还原'),),
    
                RaisedButton(onPressed: (){
                  setState(() {
                    _padding = 20;
                    _align = Alignment.center;
                    _height = 150;
                    _left = 100;
                    _color = Colors.blue;
                    _style = TextStyle(color: Colors.blue,
                      decorationStyle: TextDecorationStyle.solid,
                      decorationColor: Colors.blue,
                    );
                  });
                }, child: Text('一键设置'),),
              ].map((e){
                return Padding(padding: EdgeInsets.all(16), child: e);
              }).toList(),
            ),
          ),
        );
      }
    }
    
    image.png

    过渡动画

    • 采用Navigator.of(context).push(PageRouteBuilder())进行跳转,不能用Navigator.of(context).pushNamed('name');

    • 动画方式有现成的可用,比如FadeTransition就是指“渐隐渐入过渡”

    • 两个页面之间有耦合,页面A中需要引入并创建页面B

    • 页面A;

    import 'package:flutter/material.dart';
    
    import './push_b.dart';
    
    class PushA extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('过渡动画页面A'),
          ),
          body: Center(
            child: Column(
              children: <Widget>[
                RaisedButton(
                  onPressed: () {
                    Navigator.of(context).push(PageRouteBuilder(
                      transitionDuration: Duration(seconds: 1),
                      pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
                        return ScaleTransition(
                          scale: animation,
                          child: PushB(),
                        );
                      },
                    ));
                  },
                  child: Text('Scale方式跳转'),
                ),
    
                RaisedButton(
                  onPressed: () {
                    Navigator.of(context).push(PageRouteBuilder(
                      transitionDuration: Duration(seconds: 1),
                      pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
                        return FadeTransition(
                          opacity: animation,
                          child: PushB(),
                        );
                      },
                    ));
                  },
                  child: Text('Fade方式跳转'),
                ),
    
                RaisedButton(
                  onPressed: () {
                    Navigator.of(context).push(PageRouteBuilder(
                      transitionDuration: Duration(seconds: 1),
                      pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
                        return SizeTransition(
                          sizeFactor: animation,
                          child: PushB(),
                        );
                      },
                    ));
                  },
                  child: Text('Size方式跳转'),
                ),
    
                RaisedButton(
                  onPressed: () {
                    Navigator.of(context).push(PageRouteBuilder(
                      transitionDuration: Duration(seconds: 1),
                      pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
                        return RotationTransition(
                          turns: animation,
                          child: PushB(),
                        );
                      },
                    ));
                  },
                  child: Text('Rotation方式跳转'),
                ),
    
              ].map((e) {
                return Padding(
                  padding: EdgeInsets.all(16),
                  child: e,
                );
              }).toList(),
            ),
          ),
        );
      }
    }
    
    • 页面B
    import 'package:flutter/material.dart';
    
    class PushB extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('过渡动画页面B'),
          ),
          body: Center(
            child: RaisedButton(
              onPressed: () {
                Navigator.of(context).pop();
              },
              child: Text('返回过渡动画页面A'),
            ),
          ),
        );
      }
    }
    
    image.png

    飞行动画

    和过渡动画差不都,只是页面A和页面B都有一个Hero()组件,并且要求tag属性保持一致

    import 'package:flutter/material.dart';
    
    import './hero_b.dart';
    
    class HeroA extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('飞行动画A'),
          ),
          body: Center(
            child: Container(
              width: 300,
              height: 300,
              child: InkWell(
                child: Column(
                  children: <Widget>[
                    Hero(
                      tag: 'icon-60',
                      child: ClipOval(
                        child: Image.asset('image/icon-60.png'),
                      ),
                    ),
                    Padding(
                      padding: EdgeInsets.only(top: 10),
                      child: Text('点击看大图'),
                    ),
                  ],
                ),
                onTap: () {
                  Navigator.of(context).push(PageRouteBuilder(pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
                    return FadeTransition(
                      opacity: animation,
                      child: HeroB(),
                    );
                  }, transitionDuration: Duration(seconds: 1)));
                },
              ),
            ),
          ),
        );
      }
    }
    
    import 'package:flutter/material.dart';
    
    class HeroB extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('看大图'),
          ),
          body: Center(
            child: GestureDetector(
              child: Hero(
                tag: 'icon-60', //唯一标记,前后两个路由页Hero的tag必须相同
                child: Image.asset(
                  'image/icon-60.png',
                  width: 300,
                  height: 300,
                  fit: BoxFit.fill,
                ),
              ),
    
              onTap: () {
                Navigator.of(context).pop();
              },
            ),
          ),
        );
      }
    }
    
    image.png image.png
    • 虽然也是两个页面跳转,但是看起来,就像是在同一个页面一样。

    基本动画

    • 动画需要记忆动画参数,所以要使用StatefulWidget

    • AnimationController设置动画时长,控制动画的执行方式;

    • CurvedAnimation设置动画的轨迹,需要一个AnimationController参数;

    • Tween设置属性的开始和结束的值,调用animate函数转换为动画类型Animation<T>。需要一个参数,类型可以是AnimationController,或者是CurvedAnimation

    • 动画的状态可以通过Animation<T>addStatusListener方法监听。

    import 'package:flutter/material.dart';
    
    class Circle extends StatefulWidget {
      @override
      State<StatefulWidget> createState() {
        return _CircleState();
      }
    }
    
    class _CircleState extends State<Circle> with SingleTickerProviderStateMixin {
    
      AnimationController _controller;
      Animation<double> _withHeight;
    
      @override
      void initState() {
        super.initState();
    
        // 时长
        _controller = AnimationController(
          duration: Duration(seconds: 3),
          vsync: this,
        );
    
        // 动画曲线
        final _curve = CurvedAnimation(parent: _controller, curve: Curves.easeIn);
    
        // 图片宽高从0变到300
        _withHeight = Tween(begin: 0.0, end: 300.0).animate(_curve);
    
        // 状态侦听
        _withHeight.addStatusListener((status) {
          if (status == AnimationStatus.completed) {
            // 动画执行结束时反向执行动画
            _controller.reverse();
          } else if (status == AnimationStatus.dismissed) {
            // 动画反向执行结束时正向执行动画
            _controller.forward();
          }
        });
    
        // 启动动画(正向执行)
        _controller.forward();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('循环动画的例子'),
          ),
          body: SizedBoxAnimation(
              child: Image.asset('image/icon-60.png', fit: BoxFit.fill,),
              animation: _withHeight),
        );
      }
    
      @override
      void dispose() {
        // 路由销毁时需要释放动画资源
        _controller.dispose();
    
        super.dispose();
      }
    }
    
    class SizedBoxAnimation extends StatelessWidget {
      SizedBoxAnimation({@required this.child, @required this.animation});
    
      final Widget child;
      final Animation<double> animation;
    
      @override
      Widget build(BuildContext context) {
        return Center(
          child: AnimatedBuilder(
            child: child,
            animation: animation,
            builder: (BuildContext context, Widget child) {
              return SizedBox(
                width: animation.value, 
                height: animation.value,
                child: child,
              );
            },
          ),
        );
      }
    }
    
    image.png

    例子

    Demo地址

    相关文章

      网友评论

          本文标题:Flutter 动画

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