专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

Flutter动画基础(补间动画)

前言

看过很多文章关于写Flutter动画的,但是大多数就是照着文档抄的文章,也有的就是你转发我的,我转发你的。很多文章没有加入自己对知识的理解,不知道为什么要这做。为此我打算写一篇自己理解的Flutter动画。

动画概述

flutter中动画分为两类:基于tween或基于物理的。

补间(Tween)动画

“介于两者之间”的简称。在补间动画中,定义了开始点和结束点、时间线以及定义转换时间和速度的曲线。然后由框架计算如何从开始点过渡到结束点。

基于物理的动画

在基于物理的动画中,运动被模拟为与真实世界的行为相似。例如,当你掷球时,它在何处落地,取决于抛球速度有多快、球有多重、距离地面有多远。 类似地,将连接在弹簧上的球落下(并弹起)与连接到绳子上的球放下的方式也是不同。

基础概念

1,Animation对象

  • 它是Flutter动画库中的一个核心类,它生成指导动画的值。这个值包括颜色,大小等。

  • 它知道当前动画的状态(开始,停止,反转,完成),但是它不知道屏幕上渲染的UI是什么,也就是说它不关心你的动画对象UI渲染情况。

  • 它是一个抽象类,具体功能实现由 其子类完成。

  • 一个比较常用的Animation类是Animation,它默认产生的值范围是(0.0-1.0)

2,CurvedAnimation

它是Animation的子类, 将动画过程定义为一个非线性曲线,也就是说我们的动画运行的一个路径是怎样的,或者说以一种什么样的形式去表现我们的动画。

3,AnimationController

它是Animation的子类,是一个特殊的Animation对象,在屏幕刷新的每一帧,就会生成一个新的值。默认情况下,AnimationController在给定的时间段内会线性的生成从0.0到1.0的数字。从类名可以知道其意思:动画控制器,它可以控制动画的状态,监听动画的执行状态,监听动画过程中产生的值。

当创建一个AnimationController时,需要传递一个vsync参数,参数的作用就是避免动画的UI不在当前屏幕时消耗不必要的资源。 通过将SingleTickerProviderStateMixin添加到类定义中,可以将stateful对象作为vsync的值。举例:当我们手机接到来电,界面就跳转到接通电话界面,那么来电之前正在运行的动画会暂停。

4,Tween

默认情况下,AnimationController对象产生值的范围是(0.0,1.0)。如果您需要不同的范围或不同的数据类型,则可以使用Tween来配置动画以生成不同的范围或数据类型的值。

概念总结:

  • Animation是动画的核心抽象类,它常用的类对象是Animation

  • CurvedAnimation和AnimationController都是派生自Animation,前者管理动画的表现形式,后者管理动画插值,状态等。

  • 如果默认值满足不了我们需要,此时我们用Tween自定义插值范围

案例

放大动画

1,main.dart,在body里面放了一个执行动画的组件,主要代码在该组件中

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(

        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Flutter动画"),
      ),
      body:Animation1() ,
    );
  }
}

2,Animation1.dart组件文件

import 'package:flutter/material.dart';

///create by:Administrator
///create at:2019-09-28 14:43
///des:

class Animation1 extends StatefulWidget {
  @override
  _Animation1State createState() => _Animation1State();
}

class _Animation1State extends State<Animation1>
    with SingleTickerProviderStateMixin {
  Animation animation;
  AnimationController animationController;

  @override
  void initState() {
    super.initState();
    animationController = AnimationController(vsync: this, duration: Duration(milliseconds: 2000));//创建动画控制器
    animation = Tween(begin: 10.0, end: 200.0).animate(animationController); //自定义插值范围
    animation.addListener((){   //设置插值监听器,通过setState()重新渲染UI
      setState(() {

      });
    });
    animationController.forward(); //启动动画

  }

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

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

3,说明:

  • 整个动画效果就是让Container长宽从10.0放大到200.0,动画时长为2秒。

  • 通过Tween创建了自定义的插值,范围是(10.0,200.0),也就是Container在动画执行过程中改变值的范围。然后通过animate()方法传入动画控制器对象animationController,获取到Animation对象,前面我们说过,这个对象对动画有控制权,我们需要通过该对象知道动画的状态和插值情况

  • 通过设置插值监听器,调用setState()方法重新渲染UI。
  • 通过animationController(动画控制器)启动动画。

AnimatedWidget简化放大动画

  • AnimatedWidget类允许您从setState()调用中的动画代码中分离出widget代码。AnimatedWidget不需要维护一个State对象来保存动画。

  • AnimatedWidget是一个抽象类继承自StateFulWidget。

1,Animation2.dart组件文件

import 'package:flutter/material.dart';

///create by:Administrator
///create at:2019-09-28 14:43
///des:

class Animation2 extends StatefulWidget {
  @override
  _Animation2tate createState() => _Animation2tate();
}

class _Animation2tate extends State<Animation2>
    with SingleTickerProviderStateMixin {
  Animation animation;
  AnimationController animationController;

  @override
  void initState() {
    super.initState();
    animationController = AnimationController(vsync: this, duration: Duration(milliseconds: 2000));//创建动画控制器
    animation = Tween(begin: 10.0, end: 200.0).animate(animationController); //自定义插值范围
    animationController.forward(); //启动动画
  }

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

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

//需要执行动画的组件封装在这里
class CustomWidget extends AnimatedWidget{
  CustomWidget({Key key, Animation<double> animation})
      : super(key: key, listenable: animation);

  @override
  Widget build(BuildContext context) {
    final Animation<double> animation = listenable;
    return Container(
      width: animation.value,
      height: animation.value,
      color: Colors.redAccent,
    );
  }

}

2,说明:

我们把动画组件中的child换成了CustomWidget,去掉了动画的监听和状态的刷新。该替换组件是继承自AnimatedWidget的,它接收一个Animation对象。AnimatedWidget(基类)中会自动调用addListener()和setState()方法,完成UI的刷新,此时动画执行与之前一样。

为什么说是简化了操作呢,我们可以这么理解,原先我们需要自己监听动画执行管理状态,此时我们只需要定义出动画规则,把规则交给AnimatedWidget就行了,其他的我们不用管了,让它自己去完成动画的状态管理与UI刷新。

另外,我们这样写的话,达到了动画复用的效果,从这个层面上来看也可以理解为简化了动画。

感觉从整体上面来看,这个简化有点牵强附会,我暂时只能这样理解了,读者有更好的理解可以下方留言,谢谢

AnimatedBuilder 重构动画

前面两种动画实现中,我们的插值都侵入到了组件的内部,看下面代码。

 return Container(
      width: animation.value,
      height: animation.value,
      color: Colors.redAccent,
    );

如果此时我们要更换这个组件怎么办,比如我们想把动画对象(需要执行动画的组件)换成一张图片,又或者是其他的,那我们不得不去修改这个动画对象,是不是很麻烦,而且复用程度很低。那么我们有没有什么办法把动画对象,动画规则都给分离开来,各司其职。此时就用到了AnimatedBuilder。它通过匿名构造器按照动画插值重新构建UI,自动管理动画状态与刷新。

另外,这种刷新是局部的,它只针对于动画对象做的刷新,而不是应用到整个页面,所以我认为这种刷新比上面两种节省资源。

Animation3.dart组件文件

import 'package:flutter/material.dart';

///create by:Administrator
///create at:2019-09-28 17:29
///des:

class Animation3 extends StatefulWidget {
  @override
  _Animation3State createState() => _Animation3State();
}

class _Animation3State extends State<Animation3> with SingleTickerProviderStateMixin{
  Animation animation;
  AnimationController controller;

  initState() {
    super.initState();
    controller = new AnimationController(duration: const Duration(milliseconds: 2000), vsync: this); //动画控制器
    animation = new Tween(begin: 0.0, end: 300.0).animate(controller);  //自定义插值范围
    controller.forward();//启动动画
  }

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

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

//通过AnimatedBuilder实现动画规则与动画对象分离
class GrowBuild extends StatelessWidget {
  GrowBuild({this.child, this.animation});

  final Widget child;
  final Animation<double> animation;

  Widget build(BuildContext context) {
    return new Center(
      child: new AnimatedBuilder(
          animation: animation,
          builder: (BuildContext context, Widget child) {  //匿名构造器根据动画插值重新构建child,然后重新渲染
            return new Container(
                height: animation.value, width: animation.value, child: child);
          },
          child: child),  //将重新渲染后的child回显
    );
  }
}

//动画对象
class MyContainer extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    return Container(
      width: 100.0,
      height: 200.0,
      color: Colors.redAccent,
    );
  }

}

结束语:

以上是个人对Flutter动画基础的一点见解,不一定全对。如有错误,烦请指正。

另外,读者在有些地方有自己的见解可以留言,互相探讨,谢谢。

文章永久链接:https://tech.souyunku.com/43155

未经允许不得转载:搜云库技术团队 » Flutter动画基础(补间动画)

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们