《Flutter 从0到1构建大前端应用》读后感—第5章【动画】

Flutter 《从0到1构建大前端应用》-所有知识点架构

一丶 动画原理及概述

1.Animation

2.Animatable

3.AnimationController

4.Tween

在这里插入图片描述

import 'package:flutter/material.dart';

class TweenAnimation extends StatefulWidget {
  TweenAnimation({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _TweenAnimationState createState() => _TweenAnimationState();
}

class _TweenAnimationState extends State<TweenAnimation>
    with SingleTickerProviderStateMixin {
  Animation<double> animation;          //动画
  AnimationController controller;       //动画管理员

  @override
  void initState() {
    super.initState();
    controller = AnimationController(duration: const Duration(milliseconds: 2000), vsync: this);  //实例化
    animation = Tween(begin: 0.0, end: 300.0).animate(controller)     //实例化
      ..addListener(() {      //级联操作符
        setState(() {
          print(animation.value);
        });
      })
      ..addStatusListener((status) {
        if (status == AnimationStatus.completed) {          //  状态 == 动画状态.已完成
          controller.reverse();                   //  移除-管理员对象
        } else if (status == AnimationStatus.dismissed) {   //  状态 == 动画状态.被解雇.
          controller.forward();                      //  发送-管理员对象
        }
      });
    controller.forward();
  }

  dispose() {             //页面销毁时
    controller.dispose();   //释放资源
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("补间动画"),
        ),
        body: Container(
          margin: EdgeInsets.symmetric(vertical: 10.0),
          height: animation.value,
          width: animation.value,
          child: FlutterLogo(),
        ));
  }
}

5.Tween.animate

6.Curve

在这里插入图片描述

import 'package:flutter/material.dart';

class CurvedAnimationPage extends StatefulWidget {
  CurvedAnimationPage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _CurvedAnimationState createState() => _CurvedAnimationState();
}

class _CurvedAnimationState extends State<CurvedAnimationPage>
    with SingleTickerProviderStateMixin {
  Animation<double> animation;          //动画对象
  AnimationController controller;       //动画管理员对象

  @override
  void initState() {
    super.initState();
    /**
     * milliseconds   毫秒      动画变化时间
     * vsync          垂直同步
     */
    controller = AnimationController(
        duration: const Duration(milliseconds: 2000), vsync: this);
    /**
     * parent         父母
     * curve          曲线,其中Curves.后面跟随着多种动画效果
     * bounceIn       反弹
     */
    animation = CurvedAnimation(parent: controller, curve: Curves.bounceIn)
      ..addStatusListener((status) {           //  ‘..’ 联级操作符,意思是: CurvedAnimation() 返回值后再执行下面的方法
        if (status == AnimationStatus.completed) {    //  状态 == 动画状态.已完成
          controller.reverse();                       //  移除-管理员对象
        } else if (status == AnimationStatus.dismissed) {   //  状态 == 动画状态.被解雇.
          controller.forward();                             //  发送-管理员对象
        }
      });

    /**
     * 如果只是写 :
     *  if (status == AnimationStatus.completed) {    //  状态 == 动画状态.已完成
        controller.reverse();                       //  移除-管理员对象
        那么图标会由小变大,然后就停止在页面上
     */

    /**
     * 如果只是写 :
     * if (status == AnimationStatus.dismissed) {   //  状态 == 动画状态.被解雇.
        controller.forward();                             //  发送-管理员对象
        }
        那么图标会由小变大,在由大变小消失在页面上
     */

    controller.forward();     //  发送-管理员对象
  }

  dispose() {         //页面销毁时
    controller.dispose();   //关闭资源
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedLogo(animation: animation);
  }
}

class AnimatedLogo extends AnimatedWidget {
  // The Tweens are static because they don't change.
  static final _opacityTween = Tween<double>(begin: 0.1, end: 1.0);   //透明补间    开始,截止
  static final _sizeTween = Tween<double>(begin: 0.0, end: 300.0);    //大小补间    开始,截止

  /**
   * 接收参数
   * animation    动画对象
   * listenable   值得一听的
   */
  AnimatedLogo({Key key, Animation<double> animation})
      : super(key: key, listenable: animation);

  Widget build(BuildContext context) {
    final Animation<double> animation = listenable;   //listenable   值得一听的
    /**
     * Tween对象不存储任何状态。相反,它提供了evaluate(Animation<double> animation)方法将映射函数应用于动画当前值。
     * Animation对象的当前值可以通过value()方法取到。evaluate函数还执行一些其它处理,
     * 例如分别确保在动画值为0.0和1.0时返回开始和结束状态。
     */
    return Scaffold(
        appBar: AppBar(
          title: Text("曲线动画"),
        ),
        body: Center(
          child: Opacity(                 //不透明
              opacity: _opacityTween.evaluate(animation),
              child: Container(
                margin: EdgeInsets.symmetric(vertical: 10.0),   //symmetric   对称的
                height: _sizeTween.evaluate(animation),
                width: _sizeTween.evaluate(animation),
                child: FlutterLogo(),
              )),
        ));
  }
}

二丶 动画的封装与简化

1.AnimatedWidget

2.AnimatedBuilder
在这里插入图片描述

import 'package:flutter/material.dart';

class AnimatedBuilderPage extends StatefulWidget {
  AnimatedBuilderPage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _AnimatedBuilderState createState() => _AnimatedBuilderState();
}

class _AnimatedBuilderState extends State<AnimatedBuilderPage>
    with SingleTickerProviderStateMixin {
  Animation<double> animation;          //动画
  AnimationController controller;       //动画管理员

  @override
  void initState() {      //初始化
    super.initState();
    controller = AnimationController(duration: const Duration(milliseconds: 2000), vsync: this);  //动画管理员
    /**
     * 曲线动画
     * parent       父母
     * controller   管理员
     * curve        曲线
     * Curves       某种曲线函数
     */
    final CurvedAnimation curve = CurvedAnimation(parent: controller, curve: Curves.bounceIn);
    animation = Tween(begin: 0.0, end: 300.0).animate(curve)
      ..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          controller.reverse();
        } else if (status == AnimationStatus.dismissed) {
          controller.forward();
        }
      });
    controller.forward();
  }

  dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return GrowTransition(child: LogoWidget(), animation: animation);
  }
}

class LogoWidget extends StatelessWidget {
  build(BuildContext context) {
    return Container(
      margin: EdgeInsets.symmetric(vertical: 10.0),   //对称
      child: FlutterLogo(),
    );
  }
}

class GrowTransition extends StatelessWidget {
  GrowTransition({this.child, this.animation});

  final Widget child;
  final Animation<double> animation;

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("曲线动画AnimatedBuilder实现"),
      ),
      body: Center(
        child: AnimatedBuilder(     //动画制作器
            animation: animation,   //动画
            builder: (BuildContext context, Widget child) {
              return Container(
                  height: animation.value,
                  width: animation.value,
                  child: child);     // AnimatedBuilder又将这个child作为参数传递给里面的匿名类
            },
            child: child),    //child传给 AnimatedBuilder
      ),
    );
  }
}

三丶 Hero动画

1.基本用法

在这里插入图片描述
在这里插入图片描述
实现原理其实很简单:整个 Hero的运动过程分为三个步骤,即动画开始(t=0.0)、动画进行中、动画结束(t=1.0在动画开始时,Flutter会计算出Hero的位置并复制一份,然后绘制到 Overlay 上。复制的 Hero 和 源Hero的大小是一致的,并且该Hero是在所有路由之上的。在动画实现的过程中,Flutter 会逐渐把源 Hero 移出屏幕。在动画进行中Flutter 依靠 Tween(补间) 来实现,通过createRectTween 属性把 Tween 传给 Hero。 Hero 内部默认使用 MaterialRectArcTween 的曲线路径进行移动动画的操作。 在动画结束时,Flutter 将 Overlay 中的 Hero移除,且完成了 Hero在目标路由上的显示,这时 Overlay 是空白的。

HeroAnimationPage

import 'package:flutter/material.dart';
import 'package:flutter_animations/hero2_animation.dart';

class HeroAnimationPage extends StatefulWidget {
  HeroAnimationPage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _HeroAnimationState createState() => _HeroAnimationState();
}

class _HeroAnimationState extends State<HeroAnimationPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Hero动画"),
      ),
      body: GestureDetector(
        child: Hero(            //Hero动画
          tag: 'hero1',
          child: CustomLogo(    //自定义Logo
            size: 200.0,
          ),
        ),
        onTap: () {
          Navigator.push(
            context,
            MaterialPageRoute(
              builder: (BuildContext context) => Hero2AnimationPage(),    //手势识别点击logo区域时,跳转到 Hero2AnimationPage页面
            ),
          );
        },
      ),
    );
  }
}

class CustomLogo extends StatelessWidget {
  final double size;

  CustomLogo({this.size = 200.0});

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.black26,
      width: size,
      height: size,
      child: Center(
        child: FlutterLogo(
          size: 150.0,
        ),
      ),
    );
  }
}

Hero2AnimationPage

import 'package:flutter/material.dart';

class Hero2AnimationPage extends StatefulWidget {
  Hero2AnimationPage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _Hero2AnimationState createState() => _Hero2AnimationState();
}

class _Hero2AnimationState extends State<Hero2AnimationPage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Hero动画"),
      ),
      body: Center(
        child: Hero(
          tag: 'hero1',
          child: CustomLogo(      //自定义Logo
            size: 300.0,
          ),
        ),
      ),
    );
  }
}

class CustomLogo extends StatelessWidget {
  final double size;

  CustomLogo({this.size = 200.0});

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.black26,
      width: size,
      height: size,
      child: Center(
        child: FlutterLogo(
          size: 250.0,
        ),
      ),
    );
  }
}

2.实现原理

四丶 交错动画

在这里插入图片描述
交错动画的运动步骤
在这里插入图片描述

import 'package:flutter/material.dart';

import 'dart:async';
import 'package:flutter/scheduler.dart' show timeDilation;

class StaggerAnimation extends StatelessWidget {
  StaggerAnimation({Key key, this.controller}):

        opacity = Tween<double>(        //不透明
          begin: 0.0,
          end: 1.0,
        ).animate(
          CurvedAnimation(              //曲线动画
            parent: controller,
            curve: Interval(            //间隔
              0.0,
              0.100,
              curve: Curves.ease,       //由ease函数曲线效果变化,Curves有多个丰富的动画效果
            ),
          ),
        ),
        width = Tween<double>(          //宽度变化
          begin: 50.0,
          end: 150.0,
        ).animate(
          CurvedAnimation(
            parent: controller,
            curve: Interval(
              0.125,
              0.250,
              curve: Curves.ease,
            ),
          ),
        ),
        height = Tween<double>(begin: 50.0, end: 150.0).animate(      //高度变化
          CurvedAnimation(
            parent: controller,
            curve: Interval(
              0.250,
              0.375,
              curve: Curves.ease,
            ),
          ),
        ),
        padding = EdgeInsetsTween(                  //内边距变化
          begin: const EdgeInsets.only(bottom: 16.0),
          end: const EdgeInsets.only(bottom: 75.0),
        ).animate(
          CurvedAnimation(
            parent: controller,
            curve: Interval(
              0.250,
              0.375,
              curve: Curves.ease,
            ),
          ),
        ),
        borderRadius = BorderRadiusTween(         //圆角变化
          begin: BorderRadius.circular(4.0),
          end: BorderRadius.circular(75.0),
        ).animate(
          CurvedAnimation(
            parent: controller,
            curve: Interval(
              0.375,
              0.500,
              curve: Curves.ease,
            ),
          ),
        ),
        color = ColorTween(                     //颜色变化
          begin: Colors.indigo[100],
          end: Colors.orange[400],
        ).animate(
          CurvedAnimation(
            parent: controller,
            curve: Interval(
              0.500,
              0.750,
              curve: Curves.ease,
            ),
          ),
        ),
        super(key: key);

  final Animation<double> controller;         //动画管理员
  final Animation<double> opacity;            //不透明
  final Animation<double> width;              //宽度
  final Animation<double> height;             //高度
  final Animation<EdgeInsets> padding;        //内边距
  final Animation<BorderRadius> borderRadius; //圆角
  final Animation<Color> color;               //颜色

  // This function is called each time the controller "ticks" a new frame.
  // When it runs, all of the animation's values will have been
  // updated to reflect the controller's current value.
  Widget _buildAnimation(BuildContext context, Widget child) {
    return Container(
      padding: padding.value,     //动态变化
      alignment: Alignment.bottomCenter,
      child: Opacity(
        opacity: opacity.value,
        child: Container(
          width: width.value,
          height: height.value,
          decoration: BoxDecoration(
            color: color.value,
            border: Border.all(
              color: Colors.indigo[300],
              width: 3.0,
            ),
            borderRadius: borderRadius.value,
          ),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      builder: _buildAnimation,   //建造者
      animation: controller,      //动画
    );
  }
}

class StaggerDemo extends StatefulWidget {      //页面入口
  @override
  _StaggerDemoState createState() => _StaggerDemoState();
}

class _StaggerDemoState extends State<StaggerDemo>
    with TickerProviderStateMixin {
  AnimationController _controller;          //动画管理员对象

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(       //创建
        duration: const Duration(milliseconds: 2000), vsync: this);
  }

  @override
  void dispose() {      //销毁
    _controller.dispose();  //释放资源
    super.dispose();
  }

  Future<void> _playAnimation() async {     //异步开启
    try {
      await _controller.forward().orCancel; //开启
      await _controller.reverse().orCancel; //移除
    } on TickerCanceled {
      // the animation got canceled, probably because we were disposed
    }
  }

  @override
  Widget build(BuildContext context) {
    timeDilation = 10.0; // 1.0 is normal animation speed.
    return Scaffold(
      appBar: AppBar(
        title: const Text('Staggered Animation'),
      ),
      body: GestureDetector(
        behavior: HitTestBehavior.opaque,   //行为:命中测试行为.不透明
        onTap: () {               //点击后开启动画
          _playAnimation();
        },
        child: Center(
          child: Container(
            width: 300.0,
            height: 300.0,
            decoration: BoxDecoration(
              color: Colors.black.withOpacity(0.1),
              border: Border.all(
                color: Colors.black.withOpacity(0.5),
              ),
            ),
            child: StaggerAnimation(controller: _controller.view),    //StaggerAnimation 抽离的动画组件
          ),
        ),
      ),
    );
  }
}

五丶 动画示例

1.自定义加载动画

2.实现动画效果

import 'package:flutter/material.dart';

class SpinKitRipple extends StatefulWidget {
  const SpinKitRipple({
    Key key,
    this.color,
    this.size = 50.0,
    this.borderWidth = 6.0,
    this.itemBuilder,
    this.duration = const Duration(milliseconds: 1800),
  })  : assert(color != null),
        assert(size != null),
        assert(borderWidth != null),
        super(key: key);

  final Color color;            //颜色
  final double size;            //大小
  final double borderWidth;          //边界宽度
  final IndexedWidgetBuilder itemBuilder;    //项目建造者
  final Duration duration;    //持续时间

  @override
  _SpinKitRippleState createState() => _SpinKitRippleState();
}

class _SpinKitRippleState extends State<SpinKitRipple>
    with TickerProviderStateMixin {
  AnimationController _controller;        //动画管理员
  Animation<double> _animation1, _animation2;   //两个水波纹圈圈动画对象

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: widget.duration)
      ..repeat();   //repeat  重复

    _animation1 = Tween(begin: 0.0, end: 1.0).animate(      //动画对象1
      CurvedAnimation(
        parent: _controller,
        curve: const Interval(0.0, 0.75, curve: Curves.linear),
      ),
    )..addListener(() => setState(() => <String, void>{}));

    _animation2 = Tween(begin: 0.0, end: 1.0).animate(        //动画对象2
      CurvedAnimation(
        parent: _controller,
        curve: const Interval(0.25, 1.0, curve: Curves.linear),
      ),
    )..addListener(() => setState(() => <String, void>{}));
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Stack(
        children: <Widget>[
          Opacity(      //不透明
            opacity: 1.0 - _animation1.value,   //透明度动态变化
            child: Transform.scale(   //转变规模
              scale: _animation1.value,   //规模
              child: _itemBuilder(0),     //项目建造者
            ),
          ),
          Opacity(
            opacity: 1.0 - _animation2.value,
            child: Transform.scale(
              scale: _animation2.value,
              child: _itemBuilder(1),
            ),
          ),
        ],
      ),
    );
  }

  Widget _itemBuilder(int index) {
    return SizedBox.fromSize(
      size: Size.square(widget.size),
      child: widget.itemBuilder != null
          ? widget.itemBuilder(context, index)
          : DecoratedBox(
              decoration: BoxDecoration(
                shape: BoxShape.circle,
                border:
                    Border.all(color: widget.color, width: widget.borderWidth),
              ),
            ),
    );
  }
}

3.Dialog 加载框

import 'package:flutter/material.dart';
import 'package:flutter_animations/spin_kit_ripple.dart';

class ProgressDialog extends StatelessWidget {
  //子布局
  final Widget child;

  //加载中是否显示
  final bool isLoading;

  //进度提醒内容
  final String message;

  //加载中动画
  final Widget progress;

  // 加载框背景透明度
  final double alpha;

  // 字体颜色
  final Color textColor;

  ProgressDialog(
      {Key key,
      @required this.isLoading,
      this.message,
      this.progress = const SpinKitRipple(color: Colors.white, size: 60.0),
      this.alpha = 0.5,
      this.textColor = Colors.white,
      @required this.child})
      : assert(child != null),
        assert(isLoading != null),
        super(key: key);

  @override
  Widget build(BuildContext context) {
    List<Widget> widgetList = [];
    widgetList.add(child);
    if (isLoading) {
      Widget layoutProgress;
      if (message == null) {
        layoutProgress = Center(
          child: progress,
        );
      } else {
        layoutProgress = Center(
          child: Container(
            padding: const EdgeInsets.all(20.0),
            decoration: BoxDecoration(
              color: Colors.black87,
              borderRadius: BorderRadius.circular(4.0),
            ),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                progress,
                Container(
                  padding: const EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 0),
                  child: Text(
                    message,
                    style: TextStyle(color: textColor, fontSize: 16.0),
                  ),
                )
              ],
            ),
          ),
        );
      }
      widgetList.add(Opacity(
        opacity: alpha,     //不透明
        child: new ModalBarrier(color: Colors.black87),
      ));
      widgetList.add(layoutProgress);
    }
    return Stack(
      children: widgetList,
    );
  }
}

4.测试加载框效果
在这里插入图片描述

import 'package:flutter/material.dart';
import 'package:flutter_animations/progress_dialog.dart';

class LoadingPage extends StatefulWidget {
  LoadingPage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _LoadingState createState() => _LoadingState();
}

class _LoadingState extends State<LoadingPage> {
  bool _loading = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("加载动画"),
      ),
      body: ProgressDialog(
        isLoading: _loading,
        message: '正在加载...',
        alpha: 0.35,
        child: Center(
          child: RaisedButton(
            onPressed: () => _onRefresh(),
            child: Text('显示加载动画'),
          ),
        ),
      ),
    );
  }

  Future<Null> _onRefresh() async {
    setState(() {
      _loading = !_loading;
    });
    // 模拟耗时操作
    await Future.delayed(Duration(seconds: 5), () {
      setState(() {
        _loading = !_loading;
      });
    });
  }
}

六丶 实现淡入淡出效果

在这里插入图片描述

import 'package:flutter/material.dart';

class Fade extends StatefulWidget {
  Fade({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _FadeState createState() => _FadeState();
}

class _FadeState extends State<Fade> {
  bool _visible = true;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("渐入淡出动画"),
      ),
      body: Center(
        child: AnimatedOpacity(
            opacity: _visible ? 1.0 : 0.0,
            duration: Duration(milliseconds: 500),
            child: FlutterLogo(
              size: 100.0,
            )),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            _visible = !_visible;
          });
        },
        tooltip: "Toggole Opacity",
        child: Icon(Icons.flip),
      ),
    );
  }
}

七丶 实现小红心动画

点击产生动画
在这里插入图片描述

import 'package:flutter/material.dart';

class AnimatedSwitcherDemo extends StatefulWidget {
  AnimatedSwitcherDemo({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _AnimatedSwitcherState createState() => _AnimatedSwitcherState();
}

class _AnimatedSwitcherState extends State<AnimatedSwitcherDemo>
    with SingleTickerProviderStateMixin {
  IconData _actionIcon = Icons.favorite_border;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('点击中间的❤️'),
        actions: <Widget>[],
      ),
      body: Center(
        child: AnimatedSwitcher(
          transitionBuilder: (child, anim) {
            return ScaleTransition(child: child, scale: anim);
          },
          duration: Duration(milliseconds: 350),
          child: IconButton(
            iconSize: 100,
            key: ValueKey(_actionIcon),
            icon: Icon(
              _actionIcon,
              color: Colors.pink,
            ),
            onPressed: () {
              setState(
                () {
                  if (_actionIcon == Icons.favorite_border)
                    _actionIcon = Icons.favorite;
                  else
                    _actionIcon = Icons.favorite_border;
                },
              );
            },
          ),
        ),
      ),
    );
  }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王睿丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值