Flutter动画详解第二篇之显式动画(Explicit Animations)

目录

前言

一、定义

1.AnimationController

1.常用属性

1. value

2. status

3. duration

2.常用方法

1.forward

2.reverse

3.repeat

4.stop

5. reset

6. animateTo(double target, {Duration? duration, Curve curve = Curves.linear})

7.animateBack(double target, {Duration? duration, Curve curve = Curves.linear})

8. addListener(VoidCallback listener)

10.addStatusListener(AnimationStatusListener listener)

11. removeStatusListener(AnimationStatusListener listener)

        移除状态监听器。

2.Animation

3.Tween

4.Listeners

5.Builders

二、常见的显示动画组件

1.RotationTransition       

2.FadeTransition

3.ScaleTransition

4.SlideTransition

三、自定义显式动画

四、参考文章


前言

    上一篇文章介绍了Flutter中的隐式动画的用法。

    我们会发现隐式动画使用起来非常的方便,我们只需要设置动画的旧值变化之后的值,Flutter会帮助我们完整动画的中间过程。Flutter中隐式动画的实现是全自动的。

     今天我们介绍下Flutter中的显式动画。

一、定义

        在Flutter中,显式动画(Explicit Animations)指的是那些需要你手动控制动画过程的动画。显式动画提供了更多的控制权,但也需要更多的代码和管理。显式动画通常涉及到以下几个核心组件:

  1. AnimationController
  2. Animation
  3. Tween
  4. Listeners
  5. Builders

            核心组件的详解如下:

1.AnimationController

        AnimationController是 Flutter 动画框架的核心部分之一,用于控制动画的播放、停止、前进、倒退等。它提供了丰富的 API 来管理和控制动画的行为。下面是 AnimationController的一些常用 API 及其解释:

1.常用属性

1. value

        当前动画的值。可以是 `double` 类型,表示动画当前的进度。

double currentValue = controller.value;

2. status

        当前动画的状态,是AnimationStatus枚举类型,可能的值有dismissed、forward、reverse 和 completed。

AnimationStatus currentStatus = controller.status;

3. duration

        动画的时长。

controller.duration = Duration(seconds: 2);

4. upperBound和 lowerBound

       动画的范围。

controller.upperBound = 1.0;
controller.lowerBound = 0.0;

2.常用方法

1.forward

        动画正向执行,从lowerBound到upperBound。

 controller.forward();

2.reverse

        动画反向执行,从upperBound到lowerBound

controller.reverse();

3.repeat

        重复执行动画,可以指定周期和是否反向。

 controller.repeat(reverse: true);

4.stop

        停止动画。

controller.stop();

5. reset

        重置动画到 `lowerBound`,并停止动画。

controller.reset();

6. animateTo(double target, {Duration? duration, Curve curve = Curves.linear})

        动画执行到指定值。

   controller.animateTo(0.5, duration: Duration(seconds: 1), curve: Curves.easeInOut);

7.animateBack(double target, {Duration? duration, Curve curve = Curves.linear})

        动画反向执行到指定值。

controller.animateBack(0.0, duration: Duration(seconds: 1), curve: Curves.easeInOut);

8. addListener(VoidCallback listener)

        添加监听器,每次动画值改变时调用。

controller.addListener(() {
     setState(() {
       // 更新UI
     });
   });

9. removeListener(VoidCallback listener)

        移除监听器。

controller.removeListener(listener);

10.addStatusListener(AnimationStatusListener listener)

        添加状态监听器,动画状态改变时调用。

controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        // 动画完成
      }
    });

11. removeStatusListener(AnimationStatusListener listener)
        移除状态监听器。

  controller.removeStatusListener(listener);

2.Animation

        在 Flutter 中,显式动画需要开发者手动管理动画的每个步骤,其中 Animation 类是核心组件之一。Animation 类本身是一个抽象类,它定义了动画的当前值和状态,并且可以被监听以响应动画的变化。通过 Animation 类,开发者可以访问动画的值并将其应用到 UI 元素上。

        在实际做动画的过程中,我们会用到各种各样的Animation。例如我们做缩放动画的时候,Animation的类型为double类型,渐变动画的时候,Animation可以表示颜色的动画,平移动画的时候,Animation可以表示平移的大小。

3.Tween

        定义动画的开始和结束值。常见的有 Tween<double>、ColorTween 等。

4.Listeners

        通过 addListener 和 addStatusListener 可以监听动画的每一帧和动画状态的变化。

5.Builders

        通过 AnimatedBuilder 或 CustomPainter 等将动画值应用到UI上。

二、常见的显示动画组件

        显示动画都以Transition结尾。常见的显示动画有RotateAnimation动画、FadeTransition、ScaleTransition、SlideTransition、AnimatedIcon。在显示动画中,我们通过AnimatedController控制动画的开始、暂停、重置、跳转、倒播等。

1.RotationTransition       

        RotationTransition主要用来做旋转动画。

        我们以下面的效果为例,看看如何使用RotationTransition动画。

        图1.RotationTransition动画

        我们要做一个不停旋转的FlutterLogo。

        首先我们设置FlutterLogo的大小为60。

        我们要做一个不停旋转的显式动画,因此我们需要再FlutterLogo的外面使用RotationTransition包裹起来。

代码如下:

  @overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('RotateAnimation动画'),),body: Center(child: RotationTransition(turns: controller,child: const FlutterLogo(size: 60,),),),);}

        我们看一下RotatinTransition的定义:

  const RotationTransition({super.key,required Animation<double> turns,super.alignment = Alignment.center,super.filterQuality,super.child,}) : super(animation: turns, onTransform: _handleTurnsMatrix);

        这里必须要传递AnimationController对象。因此我们在定义AnimationController。

// 定义AnimationController
late AnimationController controller;

           为了让程序和手机的刷新频率保持一致,我们的Stateful后面要实现SingleTickerProviderStateMixin。

        代码如下:

class _RotateAnimationDemosState extends State<RotateAnimationDemos> with SingleTickerProviderStateMixin{// 定义AnimationControllerlate AnimationController controller;
}

        然后我们Widget的初始化方法中,初始化我们的AnimationController。

        这里我们设置下动画的时长,vsync参数传this。表示当前app和手机刷新的频率保持一致。

  //初始化 AnimationController@overridevoid initState() {super.initState();//vsync: 让程序和手机的刷新频率统一controller = AnimationController(duration: const Duration(seconds: 3), vsync: this);}

            OK.到这里之后,完整的代码如下:

import 'package:flutter/material.dart';class RotateAnimationDemos extends StatefulWidget {const RotateAnimationDemos({super.key});@overrideState<RotateAnimationDemos> createState() => _RotateAnimationDemosState();
}class _RotateAnimationDemosState extends State<RotateAnimationDemos>with SingleTickerProviderStateMixin {// 定义AnimationControllerlate AnimationController controller;//初始化 AnimationController@overridevoid initState() {super.initState();//vsync: 让程序和手机的刷新频率统一controller =AnimationController(duration: const Duration(seconds: 3), vsync: this);}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('RotateAnimation动画'),),body: Center(child: RotationTransition(turns: controller,child: const FlutterLogo(size: 60,),),),);}
}

        运行代码之后,页面加载出来了,但是FlutterLogo没有转起来。

        因为显式动画是手动控制动画的播放,因此我们还需要手动调用下AnimationController的repeat方法。

  //初始化 AnimationController@overridevoid initState() {super.initState();//vsync: 让程序和手机的刷新频率统一controller = AnimationController(duration: const Duration(seconds: 3), vsync: this)..repeat();}

        再次运行,就发现FlutterLogo旋转起来了。

完整代码如下:

import 'package:flutter/material.dart';class RotateAnimationDemos extends StatefulWidget {const RotateAnimationDemos({super.key});@overrideState<RotateAnimationDemos> createState() => _RotateAnimationDemosState();
}class _RotateAnimationDemosState extends State<RotateAnimationDemos>with SingleTickerProviderStateMixin {// 定义AnimationControllerlate AnimationController controller;//初始化 AnimationController@overridevoid initState() {super.initState();//vsync: 让程序和手机的刷新频率统一controller = AnimationController(duration: const Duration(seconds: 3), vsync: this)..repeat();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('RotateAnimation动画'),),body: Center(child: RotationTransition(turns: controller,child: const FlutterLogo(size: 60,),),),);}
}

        上述调用了AnimationController的repeat方法,AnimationController还提供了forward、reverse、stop、reset等常用方法,它们的含义如下:

  1. forward:动画仅执行一次
  2. reverse:动画倒序执行一次
  3. stop:动画停止
  4. reset:动画重置
  5. repeat:重复的执行动画

        如果感兴趣,可以逐个调用这些方法看看效果。

2.FadeTransition

        FadeTransition用于制作透明度动画。

        FadeTransition和RotationTransition的用法基本差不多。

        下面的例子中,展示了使用FadeTransition制作动画的过程。

        图2.FadeTransition动画

        完整代码如下:

import 'package:flutter/material.dart';class FadeAnimationDemos extends StatefulWidget {const FadeAnimationDemos({super.key});@overrideState<FadeAnimationDemos> createState() => _FadeAnimationDemosState();
}class _FadeAnimationDemosState extends State<FadeAnimationDemos> with SingleTickerProviderStateMixin{// 定义AnimationControllerlate AnimationController controller;//初始化 AnimationController@overridevoid initState() {super.initState();//vsync: 让程序和手机的刷新频率统一controller = AnimationController(duration: const Duration(seconds: 1),vsync: this,lowerBound: 0.1,upperBound: 1.0,)..repeat();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('FadeTransition动画'),),body: Center(child:FadeTransition(opacity: controller,child: const FlutterLogo(size: 200,),),),);}
}

3.ScaleTransition

        ScaleTransition用于制作缩放动画,依然可以使用AnimationController更加精准的控制动画的细节。

        图3.缩放动画

        完整代码如下:

import 'package:flutter/material.dart';class ScaleTransitionDemos extends StatefulWidget {const ScaleTransitionDemos({super.key});@overrideState<ScaleTransitionDemos> createState() => _ScaleTransitionDemosState();
}class _ScaleTransitionDemosState extends State<ScaleTransitionDemos> with SingleTickerProviderStateMixin{// 定义AnimationControllerlate AnimationController controller;//初始化 AnimationController@overridevoid initState() {super.initState();//vsync: 让程序和手机的刷新频率统一controller = AnimationController(duration: const Duration(seconds: 1),vsync: this,lowerBound: 0.1,upperBound: 1.0,)..repeat();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('ScaleTransition动画'),),body: Center(child:ScaleTransition(scale: controller,child: Container(width: 100,height: 100,color: Colors.deepPurpleAccent,),),),);}
}

4.SlideTransition

        SlideTransition用于做平移动画。

        下图是使用SlideTransition制作的平移动画。

图4.SlideTransition动画

        完整代码如下:

import 'package:flutter/material.dart';class SlideTransitionDemos extends StatefulWidget {const SlideTransitionDemos({super.key});@overrideState<SlideTransitionDemos> createState() => _SlideTransitionDemosState();
}class _SlideTransitionDemosState extends State<SlideTransitionDemos> with SingleTickerProviderStateMixin{// 定义AnimationControllerlate AnimationController controller;//初始化 AnimationController@overridevoid initState() {super.initState();//vsync: 让程序和手机的刷新频率统一controller = AnimationController(duration: const Duration(seconds: 1),vsync: this,lowerBound: 0.1,upperBound: 1.0,)..repeat();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('SlideTransition动画'),),body: Center(child:SlideTransition(position: controller.drive(Tween(begin: const Offset(0, 0), end: const Offset(0.5, 0))),child: const FlutterLogo(size: 200,),),),);}
}

        我们还可以使用AnimationController的drive方法修改动画的初始值。

        例如在下面的缩放动画代码中,开始的时候,长度和宽度都是从0.5倍开始,2倍结束。

        部分代码如下:

Center(child:ScaleTransition(scale: controller.drive(Tween(begin: 0.5, end: 2.0)),child: Container(width: 100,height: 100,color: Colors.deepPurpleAccent,),),)

        还可以调用AnimationController的chain方法叠加动画的曲线。

三、自定义显式动画

        这里我们看一下如何自定义显式动画。

        例如我们这里有一个透明度为0.9的Container组件,代码如下:

import 'package:flutter/material.dart';class CustomExplicitPage extends StatefulWidget {const CustomExplicitPage({super.key});@overrideState<CustomExplicitPage> createState() => _CustomExplicitPageState();
}class _CustomExplicitPageState extends State<CustomExplicitPage> with SingleTickerProviderStateMixin {late AnimationController _controller;@overridevoid initState() {super.initState();_controller = AnimationController(vsync: this);}@overridevoid dispose() {_controller.dispose();super.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('自定义显示动画'),),body: Center(child: Opacity(opacity: 0.9,child: Container(alignment: Alignment.center,color: Colors.deepPurpleAccent,width: 250,height: 250,child: const Text('Hi',style: TextStyle(fontSize: 24),),),),),);}
}

        我们看下如何使用AnimatedBuilder实现自定义显式动画。

        首先我们把需要制作动画的Widget的外层包裹一个AnimatedBuilder。builder函数内部返回要操作的Widget,把AnimatedController传给AnimatedBuilder的animation属性。

        部分代码如下:

Center(child: AnimatedBuilder(animation: _controller,builder: (BuildContext context, Widget? child) {return Opacity(opacity: 0.9,child: Container(alignment: Alignment.center,color: Colors.deepPurpleAccent,width: 250,height: 250,child: const Text('Hi',style: TextStyle(fontSize: 24),),),);},),)

             然后调用AnimationController的repeat方法,一个自定义的显式动画就实现了。

        完整代码如下:

import 'package:flutter/material.dart';class CustomExplicitPage extends StatefulWidget {const CustomExplicitPage({super.key});@overrideState<CustomExplicitPage> createState() => _CustomExplicitPageState();
}class _CustomExplicitPageState extends State<CustomExplicitPage> with SingleTickerProviderStateMixin {late AnimationController _controller;@overridevoid initState() {super.initState();_controller = AnimationController(vsync: this,duration: const Duration(seconds: 2))..repeat();}@overridevoid dispose() {_controller.dispose();super.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('自定义显示动画'),),body: Center(child: AnimatedBuilder(animation: _controller,builder: (BuildContext context, Widget? child) {return Opacity(opacity: _controller.value,child: Container(alignment: Alignment.center,color: Colors.deepPurpleAccent,width: 250,height: 250,child: const Text('Hi',style: TextStyle(fontSize: 24),),),);},),),);}
}

        完整的效果如下:

        图5.自定义显式动画

四、参考文章

1.教程 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter

    

        

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

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

相关文章

C# 智慧大棚nmodbus4

窗体 &#xff1a;图表&#xff08;chart&#xff09;&#xff1a; 下载第三方&#xff1a; nmodbus4:可以实现串口直连&#xff0c;需要创建串口对象设置串口参数配置Serialport 如果需要把串口数据表通过tcp进行网口传递 需要创建tcpclient对象 ModbusSerialMaster master; /…

ConcurrentHashMap第1讲——哪些地方做了并发控制

我们知道在多线程环境下&#xff0c;HashMap在初始化桶数组、put桶、插入链表以及树化等阶段都有线程安全问题&#xff0c;在jdk1.5之前我们通常用HashTable或Collections.synchronizedMap包装过的HashMap来保证线程安全&#xff0c;不过它们在执行任何操作时都需要锁住整个has…

UNIX中sigaction和sigevent有啥区别

sigaction和sigevent在UNIX和类UNIX系统&#xff08;如Linux&#xff09;的信号处理机制中扮演着不同的角色。 sigaction 功能&#xff1a; sigaction是一个用于查询或设置信号处理方式的函数。它允许进程为特定的信号指定一个信号处理函数&#xff0c;以及设置信号处理时的其…

MyPostMan 迭代文档管理、自动化接口闭环测试工具(自动化测试篇)

MyPostMan 是一款类似 PostMan 的接口请求软件&#xff0c;按照 项目&#xff08;微服务&#xff09;、目录来管理我们的接口&#xff0c;基于迭代来管理我们的接口文档&#xff0c;文档可以导出和通过 url 实时分享&#xff0c;按照迭代编写自动化测试用例&#xff0c;在不同环…

kubernetes--Istio(四)

一、可观测性 Istio 为网格内所有的服务通信生成详细的遥测数据。这种遥测技术提供了服务行为的可观测性&#xff0c; 使运维人员能够排查故障、维护和优化应用程序&#xff0c;而不会给服务的开发人员带来任何额外的负担。 通过 Istio&#xff0c;运维人员可以全面了解到受监…

【React】创建React项目:使用 create-react-app 创建 React 应用

在本文中&#xff0c;我们将介绍如何使用 create-react-app 创建一个名为 react-basic 的 React 应用。以下步骤将帮助你快速搭建一个新的 React 项目。 1. 确保已安装 Node.js 和 npm 在开始之前&#xff0c;确保你的系统上已经安装了 Node.js 和 npm&#xff08;Node 包管理…

Burp安全扫描Web应用

一、浏览器设置代理 如下图所示&#xff0c;点击火狐浏览器的“扩展和主题”&#xff0c;搜索“代理”。 如下图所示&#xff0c;选择搜索到的第一个代理&#xff08;选择任何一个都可以&#xff09;。 如上图所示&#xff0c;第一个点击后&#xff0c;进入如下页面&#xff0…

在 Electron 中,主进程和渲染进程之间有多种通信方式

在 Electron 中&#xff0c;主进程和渲染进程之间有多种通信方式。以下列出了其中几种&#xff1a; 1. ipcMain 和 ipcRenderer&#xff1a;使用主进程和渲染进程之间的 Electron 网络协议 (ipc) 模块来发送事件和消息。这是一种双向通信的方式&#xff0c;可以实现消息的传递…

opencv—常用函数学习_“干货“_11

目录 二九、图像累加 将输入图像累加到累加图像中 (accumulate) 将输入图像加权累加到累加图像中 (accumulateWeighted) 将输入图像的平方累加到累加图像中 (accumulateSquare) 将两个输入图像的乘积累加到累加图像中 (accumulateProduct) 解释 三十、随机数与添加噪声 …

【Access、Trunk和Hybrid】

概述 Access类型的端口只能属于1个VLAN&#xff0c;一般用于连接计算机的端口&#xff1b;Trunk类型的端口可以允许多个VLAN通过&#xff0c;可以接收和发送多个VLAN的报文&#xff0c;一般用于交换机之间连接的端口&#xff1b;Hybrid类型的端口可以允许多个VLAN通过&#xf…

自己编写一个谷歌浏览器插件, 模拟某音直播间自动发消息

闲来没事&#xff0c; 做个插件玩一玩&#xff0c;于是一顿学习。 按照浏览器插件规范&#xff0c;一顿代码编写&#xff0c; 搞了一个简单的插件。仅做学习。 可以实现在直播间自动发消息。 定时轮发。 实现原理&#xff1a; 利用谷歌popub.js 发送消息。 在content-script.…

leetcode-49. 字母异位词分组

题目描述 给你一个字符串数组&#xff0c;请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。 字母异位词 是由重新排列源单词的所有字母得到的一个新单词。 示例 1: 输入: strs ["eat", "tea", "tan", "ate", "n…

g2plot如何实现面积图和折线图的动态切换?

一开始的时候显示的是面积图&#xff1a; 当我点击折线图的时候&#xff0c;要变成折线图&#xff1a; 当我再点击面积图的时候&#xff0c;还要变回面积图&#xff1a; 要实现这个功能&#xff0c;得知道g2plot几个重要的API。 参考文档如下&#xff1a;https://g2plot…

【python】网络通信socket

一、什么是socket socket网络通信是一种基于TCP/IP协议的通信方式&#xff0c;通过套接字&#xff08;Socket&#xff09;实现不同主机间的通信。它基于客户端-服务器架构&#xff0c;客户端和服务器通过Socket进行连接、通信和数据交换。在网络中&#xff0c;进程之间如何通信…

防火墙之双机热备篇

为什么要在防火墙上配置双机热备技术呢&#xff1f; 相信大家都知道&#xff0c;为了提高可靠性&#xff0c;避免单点故障 肯定有聪明的小伙伴会想到那为什么不直接多配置两台防火墙&#xff0c;然后再将他们进行线路冗余&#xff0c;不就完成备份了吗&#xff1f; 答案是不…

JDK、JRE、JVM的区别java的基本数据类型

说一说JDK、JRE、JVM的区别在哪&#xff1f; JDK&#xff1a; Java Delopment kit是java工具包&#xff0c;包含了编译器javac&#xff0c;调试器&#xff08;jdb&#xff09;以及其他用于开发和调试java程序的工具。JDK是开发人员在开发java应用程序时候所需要的的基本工具。…

海外社媒矩阵为何会被关联?如何IP隔离?

在当今的数字时代&#xff0c;社交媒体已经成为人们日常生活中不可或缺的一部分。通过社交媒体&#xff0c;人们可以与朋友互动&#xff0c;分享生活&#xff0c;甚至进行业务推广和营销。然而&#xff0c;社交媒体账号关联问题逐渐受到广泛关注。社交媒体账号为何会关联&#…

问题清除指南|成功解决pipmatplotlib因为ConnectTimeoutError更新失败问题

前言&#xff1a;跑baseline需要升级matplotlib和pip&#xff0c;在此记录一个错误和一个「别致」的解决方案。 北京时间 14:00 左右&#xff0c;在终端环境中运行命令python -m pip install --upgrade pip&#xff0c;报错&#xff1a; 多次尝试&#xff0c;未果。 隔天上午 0…

Elasticsearch 企业级实战 01:Painless 脚本如何调试?

在企业级应用中&#xff0c;Elasticsearch 常常被用来处理复杂的数据查询和操作。 Painless 是 Elasticsearch 的内置脚本语言&#xff0c;虽然强大&#xff0c;但调试起来并不容易。 本文将详细介绍如何在实战中有效调试 Painless 脚本&#xff0c;以提高开发和运维效率。 本文…