Flutter Animation 动画

news2025/1/18 12:01:35

前言 :

在Flutter 中,做动画离不开这么一个类,那就是

Animation

这个类如往常一样,也是一个抽象类。

abstract class Animation<T> extends Listenable implements ValueListenable<T>

整个animation.dart 文件只有两百多行代码,其中包含了大量的注释。

这句话应该就可以看出Tween 的重要性了。

一 Animation 

了解一下这个类中的方法吧。

1 addListener

每当动画的值改变的时候,动画就会通知所有通过addListener 添加的监听器

2 addStatusListener

简而言之,就是动画的状态发生改变的时候,会通知所有通过addStatusListener 添加的监听器 。

  • 当动画的状态发生变化时,会通知所有通过 addStatusListener 添加的监听器。

  • 通常情况下,动画会从 dismissed 状态开始,表示它处于变化区间的开始点。

  • 举例来说,从 0.0 到 1.0 的动画在 dismissed 状态时的值应该是 0.0。

  • 动画进行的下一状态可能是 forward(比如从 0.0 到 1.0)或者 reverse(比如从 1.0 到 0.0)。

  • 最终,如果动画到达其区间的结束点(比如 1.0),则动画会变成 completed 状态。

3 removeListener

4 removeStatusListener

二  AnimationController

AnimationController 这个是继承Anamation的,里面有几个属性比较重要


值得注意的是 vsync 参数是必传的,Flutter的渲染闭环,每次渲染一帧画面之前都需要一个vsync信号,所以需要将SingleTickerProviderStateMixin 混入到State 类中才能初始化AnimationController

  AnimationController({
    double? value,
    this.duration,
    this.reverseDuration,
    this.debugLabel,
    this.lowerBound = 0.0,
    this.upperBound = 1.0,
    this.animationBehavior = AnimationBehavior.normal,
    required TickerProvider vsync,
  }) : assert(lowerBound != null),
       assert(upperBound != null),
       assert(upperBound >= lowerBound),
       assert(vsync != null),
       _direction = _AnimationDirection.forward {
    _ticker = vsync.createTicker(_tick);
    _internalSetValue(value ?? lowerBound);
  }

三  CurvedAnimation

这个类可以将AnimationController 和 Curve 结合起来,生成一个新的Animation 对象,有什么用呢?这个地方可以设置动画的速率。

它也是继承与Animation 

  CurvedAnimation({
    required this.parent,
    required this.curve,
    this.reverseCurve,
  }) : assert(parent != null),
       assert(curve != null) {
    _updateCurveDirection(parent.status);
    parent.addStatusListener(_updateCurveDirection);
  }

这个Curve 类型的对象有一些常量,可以直接调用

四 Tween

Tween 动画,又称为补间动画,有两个关键参数,begin 和 end

这玩意有什么用呢?

它主要是弥补 AnimationController 动画值只能为 double 类型的不足,,所以需要不同类型的变化值,那么就可以使用 Tween 。

类型多种多样

ColorTween  ,TextStyleTween,DecorationTween

 _colorAnimation =
        ColorTween(begin: primaryColor[100], end: primaryColor[900])
            .animate(_animationController!)
          ..addListener(() {
            setState(() {});
          })
          ..addStatusListener((status) {
            if (status == AnimationStatus.completed) {
              _animationController!.reverse();
            } else if (status == AnimationStatus.dismissed) {
              _animationController!.forward();
            }
          });
 //字体样式动画
    _textStyleAnimation = TextStyleTween(
            begin: TextStyle(color: Colors.orange, fontSize: 15),
            end: TextStyle(color: Colors.redAccent, fontSize: 30))
        .animate(_animationController!)
      ..addListener(() {
        setState(() {});
      });
  _sizeAnimation =
        Tween(begin: Size(50, 50), end: Size(200, 200)).animate(_controller!)
          ..addListener(() {
            setState(() {});
          })
          ..addStatusListener((status) {
            if (status == AnimationStatus.completed) {
              _controller!.reverse();
            }
          });


  _demo2() {
    return Container(
      width: _sizeAnimation!.value.width,
      height: _sizeAnimation!.value.height,
      color: Colors.blueAccent,
    );
  }

五  使用

上面把该介绍的都介绍了,那么如何使用呢 ?

就是如下方式使用

import 'package:flutter/material.dart';

class StudyAnimationPage extends StatefulWidget {
  const StudyAnimationPage({super.key});

  @override
  State<StudyAnimationPage> createState() => _StudyAnimationPageState();
}

class _StudyAnimationPageState extends State<StudyAnimationPage>
    with SingleTickerProviderStateMixin {
  // 控制器
  AnimationController? _controller;
  CurvedAnimation? _curvedAnimation;
  // 动画
  Animation? _colorAnimation;
  // size 动画
  Animation<Size>? _sizeAnimation;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    // 这个this 必须在方法里面写才不报错
    //1  初始化控制器
    _controller =
        AnimationController(vsync: this, duration: Duration(seconds: 2));
    // 2 这里可以设置执行动画的速率
    _curvedAnimation =
        CurvedAnimation(parent: _controller!, curve: Curves.easeInOutCirc);

    // 3 动画
    _colorAnimation =
        ColorTween(begin: Colors.redAccent, end: Colors.blueAccent)
            .animate(_controller!)
          ..addListener(() {
            setState(() {});
          })
          ..addStatusListener((status) {
            if (status == AnimationStatus.completed) {
              _controller!.reverse();
            }
          });
    _sizeAnimation =
        Tween(begin: Size(50, 50), end: Size(200, 200)).animate(_controller!)
          ..addListener(() {
            setState(() {});
          })
          ..addStatusListener((status) {
            if (status == AnimationStatus.completed) {
              _controller!.reverse();
            }
          });
  }

  @override
  Widget build(BuildContext context) {
    print("build方法被调用了哈哈哈");
    return Scaffold(
      appBar: AppBar(
        title: Text("data"),
      ),
      body: Container(
        width: double.infinity,
        height: double.infinity,
        child: Column(
          children: [_demo1(), _demo2()],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          _controller!.forward();
        },
        child: Icon(Icons.open_in_browser),
      ),
    );
  }

  _demo1() {
    return Container(
      color: _colorAnimation!.value,
      width: 100,
      height: 100,
    );
  }

  _demo2() {
    return Container(
      width: _sizeAnimation!.value.width,
      height: _sizeAnimation!.value.height,
      color: Colors.blueAccent,
    );
  }
}

以上的代码还有一个问题,因为监听动画的值不停的改变,那么setState就会不停的执行,略显繁重,所以就可以 使用AnimatedWidget

六  AnimatedWidget

创建一个Widget 继承与AnimatedWidget ,然后Widget 的build 就不会随着动画值的改变一直执行,而是执行我们自己创建的Widget 中的build

class MyAnimatedBox extends AnimatedWidget {
  // 这里传递 一个animation 过来 传递给父类
  MyAnimatedBox(Animation anim) : super(listenable: anim);

  @override
  Widget build(BuildContext context) {
    Animation sizeAnimation = listenable as Animation;
    // TODO: implement build
    return Container(
      width: sizeAnimation.value.width,
      height: sizeAnimation.value.height,
      color: Colors.blueAccent,
    );
  }
}

调用

_demo2() {

return MyAnimatedBox(_sizeAnimation!);

}

当然还有一些问题:

1 每一次使用都要创建一个类,这有点麻烦

2 如果我们构建的Widget 有子类,那么子类依然会重复的build

so 怎么解决呢 ??

七  AnimatedBuilder

1  不用显式的去添加帧监听器,然后再调用setState() 了,这个好处和AnimatedWidget是一样的。

2 更好的性能:因为动画每一帧需要构建的 widget 的范围缩小了,如果没有builder,setState()将会在父组件上下文中调用,这将会导致父组件的build方法重新调用;而有了builder之后,只会导致动画widget自身的build重新调用,避免不必要的rebuild。

  _demo3() {
    return AnimatedBuilder(
      animation: _sizeAnimation!,
      // child: child,
      builder: (BuildContext context, Widget? child) {
        return Container(
          width: _sizeAnimation!.value.width,
          height: _sizeAnimation!.value.height,
          color: Colors.blueAccent,
        );
      },
    );
  }

八  交织动画

什么事交织动画,顾名思义,就是多个动画交织在一起。通过多个Tween 生成多个Animation 对象。

// 动画

Animation? _colorAnimation;

// size 动画

Animation<Size>? _sizeAnimation;

// 透明度动画

Animation<double>? _opacityAnimation;

// 旋转动画

Animation<double>? _rotationAnim;

// 3 动画

_colorAnimation =

ColorTween(begin: Colors.redAccent, end: Colors.blueAccent)

.animate(_controller!)

..addListener(() {

setState(() {});

});

// ..addStatusListener((status) {

// if (status == AnimationStatus.completed) {

// _controller!.reverse();

// }

// });

_sizeAnimation =

Tween(begin: Size(50, 50), end: Size(200, 200)).animate(_controller!);

_opacityAnimation = Tween(begin: 0.3, end: 1.0).animate(_controller!);

_rotationAnim = Tween(begin: 0.0, end: 2 * pi).animate(_controller!);

  /**
   * 多个动画交织在一起
   * 1 大小变化的动画
   * 2 颜色变化的动画
   * 3 透明度变化的动画
   * 4 旋转的动画
   * 
  */
  _demo4() {
    return Opacity(
      opacity: _opacityAnimation!.value,
      child: Transform(
        alignment: Alignment.center,
        transform: Matrix4.rotationZ(_rotationAnim!.value),
        child: Container(
          color: _colorAnimation!.value,
          width: _sizeAnimation!.value.width,
          height: _sizeAnimation!.value.height,
        ),
      ),
    );
  }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/435110.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Windows和IDEA安装Scala

一、Windows安装Scala 前提&#xff1a;Windows已经安装好JDK1.8 第一步&#xff0c;下载对应的 Scala 安装文件 scala-2.12.11.zip (尚硅谷资料里有。直接获取&#xff09; 第二步&#xff0c;解压scala-2.12.11.zip 注意自己解压的目录&#xff0c;我这里解压到D盘java文…

3、ThingsBoard使用jar包单机部署

1、概述 前面一节我讲了如何初始化数据库表结构以及默认的数据。这一节我将讲解如何使用jar包部署。 2、部署 2.1、修改thingsboard.yml配置 上一节我已经讲解了thingsboard.yml中的基础配置,基础的组件配置如何redis、kafka、Cassandra、pg等大家都知道,关键的地方是在于…

Zimbra 远程代码执行漏洞(CVE-2019-9670)漏洞分析

Zimbra 远程代码执行漏洞(CVE-2019-9670)漏洞分析 漏洞简介 Zimbra是著名的开源系统&#xff0c;提供了一套开源协同办公套件包括WebMail&#xff0c;日历&#xff0c;通信录&#xff0c;Web文档管理和创作。一体化地提供了邮件收发、文件共享、协同办公、即时聊天等一系列解决…

主 存储器

主存储器 概述 实际上在主存储器运作时&#xff0c;根据MAR中的地址访问某个存储单元时&#xff0c;还需经过地址译码、驱动等电路才能找到所需的访问单元。读出时需经过读出放大器&#xff0c;才能将被选中单元的存储字送到MDR。写入时&#xff0c;MDR中的数据也必须经过写入…

课程简介:.Net Core从零学习搭建权限管理系统

课程简介目录 &#x1f680;前言一、课程背景二、课程目的三、系统功能四、系统技术架构五、课程特点六、课程适合人员七、课程规划的章节八、最后 &#x1f680;前言 本文是《.Net Core从零学习搭建权限管理系统》教程专栏的导航站&#xff08;点击链接&#xff0c;跳转到专栏…

【运动规划算法】路径规划中常用的插值方法

文章目录 简介一、线性插值二、三次样条插值三、B样条插值四、贝塞尔曲线插值总结 简介 常见用于处理路径平滑的插值算法主要包括线性插值、三次样条插值、B样条插值和贝塞尔曲线插值等&#xff0c;下面分别介绍它们的优缺点和使用场景。 一、线性插值 线性插值是最简单的插值…

【主流Chat模型的申请入口和方法】

主流Chat模型的申请入口和方法 一、申请New Bing二、申请内测文心一言三、申请内测Claude四、谷歌家的Bard五、Adobe Firefly六、GitHub Copilot chat七、通义千问八、360智脑 一、申请New Bing 注册一个 outlook 邮箱&#xff0c;很简单&#xff0c;2分钟就可搞定&#xff5e…

操作系统(2.7)--进程

目录 一、进程的引入 1.进程的两个基本属性 2.程序并发执行所需付出的时空开销 3.线程---作为调度和分派的基本单位 二、线程(轻型进程)与进程(重型进程)的比较 1&#xff09;调度的基本单位 2&#xff09;并发性 3&#xff09;拥有资源 4&#xff09;独立性 5&#…

离线数仓的数仓分层

数据仓库分层的作用 数据结构化更清晰&#xff1a; 对于不同层级的数据&#xff0c;他们作用域不相同&#xff0c;每一个数据分层都有它的作用域&#xff0c;这样我们在使用表的时候能更方便地定位和理解。 数据血缘追踪&#xff1a; 提供给外界使用的是一张业务表&#xf…

Material Design:为你的 Android 应用提供精美的 UI 体验

Material Design&#xff1a;为你的 Android 应用提供精美的 UI 体验 介绍 Material Design 概念&#xff1a;介绍 Material Design 是 Google 推出的一种设计语言&#xff0c;用于创建现代、美观、直观且一致的用户界面。解释 Material Design 的基本原则&#xff0c;包括材料…

9、MachO简介

一、MachO文件 MachO其实是Mach Object文件格式的缩写,是Mac以及iOS上可执行文件的格式,类似于windows上的PE格式(Portable Executable), linux上的elf格式(Executable and Linking Format) 二、MachO文件结构 Mach-O为Mach Object文件格式的缩写,它是一种用于可执行文件、目…

fileclude(文件包含漏洞及php://input、php://filter的使用)

先介绍一些知识 1、文件包含漏洞 和SQL注入等攻击方式一样&#xff0c;文件包含漏洞也是一种注入型漏洞&#xff0c;其本质就是输入一段用户能够控制的脚本或者代码&#xff0c;并让服务端执行。 什么叫包含呢&#xff1f;以PHP为例&#xff0c;我们常常把可重复使用的函数写…

Pytorch实现FCN图像语义分割网络

针对图像的语义分割网络&#xff0c;本节将介绍PyTorch中已经预训练好网络的使用方式&#xff0c;然后使用VOC2012数据集训练一个FCN语义分割网络。 一、使用预训练好的语义分割网络 PyTorch提供了已预训练好的图像语义分割网络&#xff0c;已经预训练好的可供使用的网络模型…

Java 对象的创建过程面试总结

Java对象创建的过程 Java对象创建的过程主要分为五个步骤&#xff0c;下面我将详细介绍这五个步骤。 Step1:类加载检查 虚拟机遇到一条new指令时&#xff0c;首先会去检查这个指令的参数是否能在class文件中的常量池中定位到这个类的符号引用&#xff0c;并且会检查这个符号…

unplugin-vue-components 源码原理分析

unplugin-vue-components 是一款按需自动导入Vue组件的库。支持 Vue2 和 Vue3&#xff0c;同时支持组件和指令。使用此插件库后&#xff0c;不再需要手动导入组件&#xff0c;插件会自动识别按需导入组件以及对应样式&#xff0c;我们只需要像全局组件那样使用即可。 当然上面…

深入谈谈内存压缩那些事!

1. 技术背景 说到压缩这个词&#xff0c;我们并不陌生&#xff0c;应该都能想到是降低占用空间&#xff0c;使同样的空间可以存放更多的东西&#xff0c;类似于我们平时常用的文件压缩,内存压缩同样也是为了节省内存。 尽管当前android手机6GB&#xff0c;8GB甚至12GB的机器都…

两个月15斤以上的健康减脂减重法,与饥饿、运动等无关的自我实验的验证方法(第六篇完结,无收费内容)...

阅读本文前请先看前五篇内容&#xff0c;本文有部分修改&#xff0c;但是涉及前五篇的内容会大量隐藏&#xff0c;只保留关联修改部分&#xff0c;链接如下&#xff1a; 两个月15斤以上的健康减脂减重法&#xff0c;与饥饿、运动等无关的自我实验的验证方法&#xff08;第五篇&…

OpenCascade安装编译

重新编译OpenCascade&#xff0c;在漫长的等待过程中&#xff0c;记录一下编译的流程 下载安装 OpenCascade官网中提供了直接安装的二进制版本&#xff0c;如果只是简单的使用需求可以直接下载安装&#xff0c;二进制版本使用VC 2017 64 bit编译 官网地址 源码编译 源码编…

Docker容器:docker基础

目录 一、docker容器简介 1、什么是容器 2、容器的优点 3、什么是docker容器 4、docker的logo及设计宗旨 5、docker与虚拟机对比 6、docker容器2个重要技术 7、docker三大核心概念 二、docker的安装及管理 1、安装docker 2、配置docker加速器 3、docker镜像相关基础…

你为什么从上一家公司离职?程序员这样回答最机智

想必每一位跳槽的程序员伙伴&#xff0c;在面试时都会被问到这个问题吧&#xff1a; “为什么从上一家公司离职&#xff1f;” 可能不少人跳槽的原因都是钱少事多离家远&#xff0c;加班干到十二点&#xff0c;同事之间还内卷&#xff0c;但是这些原因在面试的时候都能说吗&a…