Flutter开发(十五):Flutter动画基础

1.哪些类型动画

2.添加动画

3.为动画添加监听器

4.AnimatedWidget 与 AnimatedBuilder

5.Hero动画

1.哪些类型动画

大概分为两类:基于 tween 和基于物理。

tween 动画就是补间动画,定义开始点和结束点、时间线和定义转化时间和速度的曲线。

基于物理的动画:就是模拟真实世界。比如扔个球,会做抛物线,然后弹了几下。

2.添加动画

几个常见类:

Animation : 动画库核心,生成指导动画的值。

CurvedAnimation : Animation 子类,将过程抽象为一个非线性曲线。

AnimationController : Animation 子类,管理 Animation。

Tween : 在正在执行动画的对象所使用的数据范围之间生成值。比如红到绿,它可以自动生成过程。

动画的几个状态方法:

forward()        启动动画

reverse({double from})        倒放动画

reset()        重置动画

stop({bool canceled = true})        停止动画

动画使用大概步骤:

① 创建 AnimationController ,指定动画时间;

② 创建 Tween ,传入区间大小变化,传入控制器 AnimationController ;

③ 添加动画变化监听器,类型是 double 类型,每回调一次就会用 setState() 刷新一次页面;

④ 添加动画状态监听器,来监听动画的状态:开始和停止等,利用 setState() 刷新页面;

⑤ 添加一个图片,动态设置它的大小;

示例代码如下,大概就是一个图标从宽高为0两秒内变成宽高为300的过程:

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyApp>
    with SingleTickerProviderStateMixin {
  late Animation<double> animation;
  late AnimationController controller;
  //设置了一个初始状态。
  late AnimationStatus animationStatus = AnimationStatus.reverse;
  late double animationValue = 0;

  @override
  void initState() {
    super.initState();
    // ① 创建 AnimationController ,指定动画时间;
    // 其中 vsync: this 表示该动画被别的页面遮挡,防止消耗不必要的资源。
    controller =
        AnimationController(duration: const Duration(seconds: 2), vsync: this);
    // ② 创建 Tween ,传入区间大小变化,传入控制器 AnimationController ;
    animation = Tween<double>(begin: 0, end: 300).animate(controller)
    // ③ 添加动画变化监听器,类型是 double 类型,每回调一次就会用 setState() 刷新一次页面;
      ..addListener(() {
        setState(() {
          animationValue = animation.value;
        });
      })
      // ④ 添加动画状态监听器,来监听动画的状态:开始和停止等,利用 setState() 刷新页面;
      ..addStatusListener((AnimationStatus status) {
        setState(() {
          animationStatus = status;
        });
      });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        margin: EdgeInsets.only(top: 100),
        child: Column(
          children: [
            //点击监听
            GestureDetector(
              onTap: () {
                //重置动画
                controller.reset();
                //动画开始
                controller.forward();
              },
              child: Text(
                'Start',
                textDirection: TextDirection.ltr,
              ),
            ),
            Text('State:' + animationStatus.toString(),
                textDirection: TextDirection.ltr),
            Text('Value:' + animationValue.toString(),
                textDirection: TextDirection.ltr),
            // ⑤ 添加一个图片,动态设置它的大小;
            Container(
              height: animation.value,
              width: animation.value,
              child: FlutterLogo(),
            )
          ],
        ));
  }

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

3.为动画添加监听器

上面代码中已经添加了两个监听:

..addListener        动画的值发生变化被调用

..addStatusListener        动画状态发生变化被调用

4.AnimatedWidget 与 AnimatedBuilder

上面代码中,必须得添加动画的变化监听,然后不停调用 setState ,才能显示动画。AnimatedWidget 可以简化这一个操作,AnimatedWidget 很常用,它的作用就是简化动画。

简化完成的代码:

import 'package:flutter/material.dart';
import 'package:flutter/animation.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyApp>
    with SingleTickerProviderStateMixin {
  late Animation<double> animation;
  late AnimationController controller;

  @override
  void initState() {
    super.initState();
    // ① 创建 AnimationController ,指定动画时间; 其中 vsync: this 表示该动画被别的页面遮挡,防止消耗不必要的资源。
    controller =
        AnimationController(duration: const Duration(seconds: 2), vsync: this);
    // ② 创建 Tween ,传入区间大小变化,传入控制器 AnimationController ;
    animation = Tween<double>(begin: 0, end: 300).animate(controller);
    controller.forward();
  }

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

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

class AnimatedLogo extends AnimatedWidget {
   AnimatedLogo({Key? key, required Animation<double> animation})
      : super(key: key, listenable: animation);

  @override
  Widget build(BuildContext context) {
    final Animation<double> animation = listenable as Animation<double>;
    return new Center(
        child: new Container(
          margin: new EdgeInsets.symmetric(vertical: 10.0),
          height: animation.value,
          width: animation.value,
          child: new FlutterLogo(),
        )
    );
  }
}

AnimatedBuilder 是用于构建动画的通用 widget,作用是拆分动画,可以将动画和 widget 进行分离。

将上面的动画拆分为三块:显示图片,定义动画,渲染动画效果。

示例代码:

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyApp>
    with SingleTickerProviderStateMixin {
  late Animation<double> animation;
  late AnimationController controller;

  @override
  void initState() {
    super.initState();
    // ① 创建 AnimationController ,指定动画时间; 其中 vsync: this 表示该动画被别的页面遮挡,防止消耗不必要的资源。
    controller =
        AnimationController(duration: const Duration(seconds: 2), vsync: this);
    // ② 创建 Tween ,传入区间大小变化,传入控制器 AnimationController ;
    animation = Tween<double>(begin: 0, end: 300).animate(controller);
    controller.forward();
  }

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

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

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

  final Widget child;
  final Animation<double> animation;

  @override
  Widget build(BuildContext context){
    return Center(
      child: AnimatedBuilder(
        animation: animation,
        builder: (context, child) {
          return Container(
            height: animation.value,
            width: animation.value,
            child: child,
          );
        },
        child: child,
      ),
    );
  }
}

class LogoWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Container(
      margin: EdgeInsets.symmetric(vertical: 10),
      child: FlutterLogo(),
    );
  }
}

5.Hero动画

从第一个页面跳转到第二个页面,并且两个页面都有这个图标,可以用这个动画来实现。实现一个飞入的感觉。

效果可以看官网:Flutter Hero动画 - Flutter中文网

示例代码,下面代码我把 timeDilation = 5.0; 注释了,加上这个报错,不知道为什么,报错信息是

Error: Setter not found: 'timeDilation'.
    timeDilation = 5.0;

有知道的小伙伴告诉我一下原因吧~ 

下面是完整代码:


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


void main() {
  runApp(MaterialApp(home: HeroAnimation()));
}

class PhotoHero extends StatelessWidget {
  const PhotoHero({ Key? key,required this.photo,required this.onTap, required this.width }) : super(key: key);

  final String photo;
  final VoidCallback onTap;
  final double width;

  Widget build(BuildContext context) {
    return new SizedBox(
      width: width,
      child: new Hero(
        tag: photo,
        child: new Material(
          color: Colors.transparent,
          child: new InkWell(
            onTap: onTap,
            child: new Image.asset(
              photo,
              fit: BoxFit.contain,
            ),
          ),
        ),
      ),
    );
  }
}

class HeroAnimation extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // timeDilation = 5.0;

    return new Scaffold(
      appBar: new AppBar(
        title: const Text('Basic Hero Animation'),
      ),
      body: new Center(
        child: new PhotoHero(
          photo: 'assets/images/my_icon.jpg',
          width: 300.0,
          onTap: () {
            Navigator.of(context).push(new MaterialPageRoute<void>(
                builder: (BuildContext context) {
                  return new Scaffold(
                    appBar: new AppBar(
                      title: const Text('Flippers Page'),
                    ),
                    body: new Container(
                      // The blue background emphasizes that it's a new route.
                      color: Colors.lightBlueAccent,
                      padding: const EdgeInsets.all(16.0),
                      alignment: Alignment.topLeft,
                      child: new PhotoHero(
                        photo: 'assets/images/my_icon.jpg',
                        width: 100.0,
                        onTap: () {
                          Navigator.of(context).pop();
                        },
                      ),
                    ),
                  );
                }
            ));
          },
        ),
      ),
    );
  }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值