【Flutter】动画介绍隐式动画

🔥 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏:Flutter学习
🌠 首发时间:2024年5月28日
🦋 欢迎关注🖱点赞👍收藏🌟留言🐾

目录

  • 动画基本原理
  • Flutter动画简介
  • 隐式动画
    • curve
    • AnimatedContainer
    • AnimatedPadding
    • AnimatedPositioned
    • AnimatedOpacity
    • AnimatedDefaultTextStyle
    • AnimatedSwitcher
    • 修改 AnimatedSwitcher 的动画效果
    • 子组件相同的AnimatedSwitcher

动画基本原理

在任何系统的UI框架中,动画实现的原理都是相同的,即:在一段时间内,快速地多次改变UI外观;由于人眼会产生视觉暂留,所以最终看到的就是一个“连续”的动画,这和电影的原理是一样的。

我们将UI的一次改变称为一个动画帧,对应一次屏幕刷新,而决定动画流畅度的一个重要指标就是帧率FPS(Frame Per Second),即每秒的动画帧数。很明显,帧率越高则动画就会越流畅!一般情况下,对于人眼来说,动画帧率超过16 FPS,就基本能看了,超过 32 FPS就会感觉相对平滑,而超过 32FPS,大多数人基本上就感受不到差别了。

由于动画的每一帧都是要改变UI输出,所以在一个时间段内连续的改变UI输出是比较耗资源的,对设备的软硬件系统要求都较高,所以在UI系统中,动画的平均帧率是重要的性能指标,而在Flutter中,理想情况下是可以实现 60FPS 的,这和原生应用能达到的帧率是基本是持平的。

Flutter动画简介

FLutter 中的动画主要分为:隐式动画、显式动画、自定义隐式动画、自定义显式动画和 Hero 动画。

隐式动画

通过几行代码就可以实现隐式动画,由于隐式动画背后的实现原理和繁琐的操作细节都被隐去了,所以叫隐式动画,FLutter 中提供的 AnimatedContainerAnimatedPaddingAnimatedPositioned
AnimatedOpacityAnimatedDefaultTextStyleAnimatedSwitcher 都属于隐式动画。

隐式动画中可以通过 duration 来配置动画时长,通过 curve (曲线)来配置动画过程,这两个属性在上述组件中都是存在的。

curve

Flutter 中,Curves 类提供了一系列预定义的曲线,用于控制动画的速度变化。以下是一些常用的 Curves 组件的值及简单解释:

  1. Curves.linear

    • 线性曲线,动画以恒定的速度进行,没有加速或减速。
  2. Curves.decelerate

    • 减速曲线,动画开始时速度较快,然后逐渐减速。
  3. Curves.ease

    • 标准的加速减速曲线,动画开始和结束时速度较慢,中间时速度较快。
  4. Curves.easeIn

    • 加速曲线,动画开始时速度较慢,然后逐渐加速。
  5. Curves.easeOut

    • 减速曲线,动画开始时速度较快,然后逐渐减速。
  6. Curves.easeInOut

    • 加速减速曲线,动画开始和结束时速度较慢,中间时速度较快,类似于Curves.ease
  7. Curves.fastOutSlowIn

    • 快出慢入曲线,动画开始时速度较快,然后逐渐减速到结束。
  8. Curves.bounceIn

    • 弹簧效果曲线,动画开始时速度为0,然后加速进入动画,到达最大速度后反弹一次。
  9. Curves.elasticIn

    • 弹性效果曲线,动画开始时速度为0,然后加速进入动画,到达最大速度后会有一些超过目标值的回弹效果。

这些是常用的一些Curves组件的值,通过选择合适的曲线,可以控制动画的速度变化,从而实现不同的动画效果。

AnimatedContainer

AnimatedContainer 的属性和 Container 属性基本是一样的,不同的地方是,当 AnimatedContainer 属性改变的时候会触发动画。

下面的程序中,我们通过浮动按钮来改变 AnimatedContainertransform 的值,以触发动画。

import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});State<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {bool flag = true;Widget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(child: const Icon(Icons.refresh),onPressed: () {setState(() {flag = !flag;});},),appBar: AppBar(title: const Text('AnimatedContainer'),),body: Center(child: AnimatedContainer(duration: const Duration(seconds: 1, milliseconds: 100),width: 200,height: 200,transform: flag? Matrix4.translationValues(0, 0, 0): Matrix4.translationValues(-100, 0, 0),color: Colors.blue,),),);}
}

AnimatedPadding

import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});State<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {bool flag = true;Widget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(child: const Icon(Icons.refresh),onPressed: () {setState(() {flag = !flag;});},),appBar: AppBar(title: const Text('AnimatedPadding'),),body: AnimatedPadding(curve: Curves.linear,padding: EdgeInsets.fromLTRB(10, flag ? 10 : 200, 0, 0),duration: const Duration(seconds: 2),child: Container(width: 100,height: 100,color: Colors.blue,),),);}
}

AnimatedPositioned

import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});State<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {bool flag = true;Widget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(child: const Icon(Icons.refresh),onPressed: () {setState(() {flag = !flag;});},),appBar: AppBar(title: const Text('AnimatedPositioned'),),body: Stack(children: [ListView(children: const [ListTile(title: Text("我是一个列表"),),ListTile(title: Text("我是一个列表"),),ListTile(title: Text("我是一个列表"),),ListTile(title: Text("我是一个列表"),),ListTile(title: Text("我是一个列表"),),ListTile(title: Text("我是一个列表"),),],),AnimatedPositioned(curve: Curves.linear,right: flag ? 10 : 300,top: flag ? 10 : 560,duration: const Duration(seconds: 1, milliseconds: 500),child: Container(width: 60,height: 60,color: Colors.blue,)),],),);}
}

AnimatedOpacity

import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});State<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {bool flag = true;Widget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(child: const Icon(Icons.refresh),onPressed: () {setState(() {flag = !flag;});},),appBar: AppBar(title: const Text('AnimatedOpacity'),),body: Center(child: AnimatedOpacity(opacity: flag ? 0.2 : 1,duration: const Duration(seconds: 1),curve: Curves.easeIn,child: Container(width: 200,height: 200,color: Colors.red,),),),);}
}

AnimatedDefaultTextStyle

import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});State<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {bool flag = true;Widget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(child: const Icon(Icons.refresh),onPressed: () {setState(() {flag = !flag;});},),appBar: AppBar(title: const Text('AnimatedDefaultTextStyle'),),body: Center(child: Container(alignment: Alignment.center,width: 300,height: 300,color: Colors.blue,child: AnimatedDefaultTextStyle(duration: const Duration(seconds: 1),style: TextStyle(fontSize: flag ? 20 : 50, color: Colors.white),child: const Text("你好Flutter"),),),),);}
}

AnimatedSwitcher

Flutter 中的 AnimatedSwitcher 是一个用于在切换子元素时执行动画效果的预置组件。它允许你在切换子元素时使用自定义的过渡效果,从而实现平滑的切换体验。

AnimatedSwitcher 通常用于在切换应用程序界面的不同部分或内容时提供动画效果。例如,当用户点击按钮切换页面或者切换视图时,可以使用 AnimatedSwitcher 来实现页面间的过渡动画。

在使用 AnimatedSwitcher 时,你需要提供新旧子元素,并指定一个过渡效果。当新的子元素被提供时,AnimatedSwitcher 将会在旧的子元素和新的子元素之间执行过渡动画。

要使用 AnimatedSwitcher,通常需要指定以下几个关键属性:

  • child:当前的子元素,即将要显示的子元素。
  • duration:动画的持续时间,用于控制过渡动画的速度。
  • transitionBuilder:一个回调函数,用于定义子元素切换时的过渡效果。该回调函数接受当前子元素和动画对象,并返回一个 Widget,用于实现过渡效果。

通过这些属性,你可以轻松地在 Flutter 应用程序中添加动画效果,从而提升用户体验并增加应用的交互性。AnimatedSwitcherFlutter 框架中 Material 组件库的一部分,因此可以与其他 Material 风格的组件无缝集成。

import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});State<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {bool flag = true;Widget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(child: const Icon(Icons.refresh),onPressed: () {setState(() {flag = !flag;});},),appBar: AppBar(title: const Text('AnimatedSwitcher'),),body: Center(child: Container(alignment: Alignment.center,width: 300,height: 220,color: Colors.orange,child: AnimatedSwitcher(//当子元素改变的时候会触发动画duration: const Duration(seconds: 1),child: flag? const CircularProgressIndicator(): Image.network("https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/5.jpg",fit: BoxFit.cover,))),),);}
}

Flutter 中的 CircularProgressIndicator 是一个用于显示循环进度的预置组件。它通常用于在应用程序加载数据、执行长时间任务或执行任何需要显示进度的操作时显示一个圆形的进度条。

CircularProgressIndicator 可以以不同的方式进行定制,包括更改颜色、大小和动画。它是 Flutter 框架中 Material 组件库的一部分,因此在使用 MaterialAppScaffoldMaterial 风格的组件时很常见。

你可以通过简单的代码来创建一个 CircularProgressIndicator,然后将其放置在应用程序的合适位置。在加载数据或执行任务时,CircularProgressIndicator 将显示一个圆形进度条,直到任务完成为止。

修改 AnimatedSwitcher 的动画效果

通过 AnimatedSwitcher 中的 transitionBuilder 参数可以修改其动画效果。

import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});State<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {bool flag = true;Widget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(child: const Icon(Icons.refresh),onPressed: () {setState(() {flag = !flag;});},),appBar: AppBar(title: const Text('AnimatedSwitcher'),),body: Center(child: Container(alignment: Alignment.center,width: 300,height: 220,color: Colors.orange,child: AnimatedSwitcher(transitionBuilder: ((child, animation) {//可以改变动画效果return ScaleTransition(scale: animation,child: FadeTransition(opacity: animation,child: child,),);}),duration: const Duration(seconds: 1),//当子元素改变的时候会触发动画child: flag? const CircularProgressIndicator(): Image.network("https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/5.jpg",fit: BoxFit.cover,))),),);}
}

在上面的代码中,transitionBuilder 参数是用于定义 AnimatedSwitcher 在切换子元素时所使用的过渡效果的回调函数。这个回调函数接受两个参数:

  1. child:当前子元素(即将要切换的元素)。
  2. animation:表示当前切换的动画。在这里,animation 是一个 Animation<double> 类型的对象,它描述了从0到1的动画值。

transitionBuilder 回调函数应该返回一个 Widget,这个 Widget 将作为子元素在切换时的动画效果。在上面的代码中,回调函数使用了两种过渡效果:

  1. ScaleTransition:它通过对子元素进行缩放来实现动画效果,缩放的比例由动画值 animation 控制,从而实现子元素的逐渐放大或缩小的效果。
  2. FadeTransition:它通过改变子元素的不透明度来实现淡入淡出的效果,不透明度的变化由动画值 animation 控制,从而实现子元素的逐渐显现或消失的效果。

通过结合使用 ScaleTransitionFadeTransition,可以实现更加丰富的过渡效果,使切换子元素时更加平滑和动态。

子组件相同的AnimatedSwitcher

我们知道,AnimatedSwitcher 只有在其子元素发生改变时才会触发动画,那么当子元素不变时,比如子元素为一个 Text 组件,我们只修改里面的内容,不改变组件类型,在这种情况下又该如何触发动画呢?

很简单,我们只要设置一下子元素的 key 值为 UniqueKey(),就可以让 AnimatedSwitcher 认为子元素已经改变。

import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return const MaterialApp(home: MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});State<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {bool flag = true;Widget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(child: const Icon(Icons.refresh),onPressed: () {setState(() {flag = !flag;});},),appBar: AppBar(title: const Text('AnimatedSwitcher'),),body: Center(child: Container(alignment: Alignment.center,width: 300,height: 220,color: Colors.blue,child: AnimatedSwitcher(transitionBuilder: ((child, animation) {//可以改变动画效果return ScaleTransition(scale: animation,child: FadeTransition(opacity: animation,child: child,),);}),duration: const Duration(seconds: 1),//当子元素改变的时候会触发动画child: Text(key: UniqueKey(),flag ? "凡所过往" : "皆为序章",style: Theme.of(context).textTheme.headlineLarge,)),)),);}
}

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

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

相关文章

SpringMvc-restful设计风格

Restful 1、入门1.1 简介1.2 实例 1、入门 1.1 简介 RESTFul是什么 RESTFul是WEB服务接口的一种设计风格。 RESTFul定义了一组约束条件和规范&#xff0c;可以让WEB服务接口更加简洁、易于理解、易于扩展、安全可靠。 1.2 实例 web.xml <?xml version"1.0"…

5、xss-labs之level6

一、level6-----大小写绕过 1、测试分析 测试了之前用过的payload&#xff0c;发现都不行&#xff0c;并且level4使用的Java伪协议也不行&#xff0c;可以得出<>、script、onclick都被过滤 2、构造payload 因为href被过滤&#xff0c;可以试一下大写HREF 初试payload…

没人愿意和我们最好的工程师一起工作

几年前&#xff0c;有一位技术非常好的工程师&#xff08;我们叫他“乔恩”&#xff09;为我工作。 他的代码写得很好&#xff0c;代码审查&#xff08;PRs&#xff09;也完成得很快。从技术角度来看&#xff0c;他是个出色的工程师。 但是我们从其他工程师那里得到了一些关于…

Python实现解码二进制数据以匹配给定的C++结构体

要在Python中实现解码二进制数据以匹配给定的C结构体Ytest&#xff0c;你需要了解每个字段在结构体中的偏移量&#xff08;由于结构体内存对齐&#xff0c;这些偏移量可能与字段的顺序和大小不完全对应&#xff09;。不过&#xff0c;在没有指定内存对齐的情况下&#xff0c;我…

使用nvm管理node多版本(安装、卸载nvm,配置环境变量,更换npm淘宝镜像)淘宝的镜像域名更换

最近 使用nvm 管理 node 的时候发现nvm install node版本号 总是失败。 nvm install 20.12.2Error retrieving "http://npm.taobao.org/mirrors/node/latest/SHASUMS256.txt": HTTP Status 404查看原因&#xff0c;因为淘宝的镜像域名更换&#xff0c;由于 npm.taob…

2020职称继续教育--发挥好“显著优势”,坚持和完善生态文明制度体系,促进人与自然和谐共生

单选题&#xff08;共7题&#xff0c;每题5分&#xff09; 1、我国生态脆弱区广布&#xff0c;适宜生存的空间不足&#xff08;&#xff09;。 B、三分之一 2、按照传统文化的说法&#xff0c;制度体系与治理体系&#xff0c;是“体”和“用”的关系&#xff0c;强调&#xff…

BI系统:数据驱动的决策利器,引领企业走向智能化

在当今这个数据驱动的时代&#xff0c;商业智能&#xff08;BI&#xff09;系统已成为企业不可或缺的工具。BI系统不仅提高了数据处理和分析的效率&#xff0c;更重要的是&#xff0c;它能够帮助企业做出更加明智和精准的决策。在数聚多年的从业经验来看&#xff0c;BI系统的重…

基于直接二元搜索的片上偏振分束器设计 (Nature Photonics, 9, 6, (2015))案例复现

时间—2024.6.08 腾讯会议 智能算法驱动的光子学设计与应用

Dream

好像很多人梦寐以求的都是别人已经拥有的&#xff0c;多少人奋斗一生的目标&#xff0c;却只是别人的起点&#xff0c;人生而自由&#xff0c;只是不在枷锁之中&#xff0c;生活中没有人不遗憾&#xff0c;只是没有人喊疼&#xff0c;时间不会重来&#xff0c;已经过去了就让它…

内存泄露问题? 怎么解决

内存泄漏在Android应用开发中是一个常见问题&#xff0c;它发生在对象不再被使用时&#xff0c;但仍然被引用&#xff0c;导致垃圾回收器无法释放其占用的内存。这会逐渐消耗应用可用内存&#xff0c;最终可能导致应用运行缓慢、崩溃或被系统终止。以下是一些常见的内存泄漏场景…

vue3 使用vant

使用前提&#xff1a; vite创建的vue3项目 vanthttps://vant-ui.github.io/vant/#/zh-CN/home npm i vant 引入样式&#xff1a; main.js import vant/lib/index.css vant封装 import { showLoadingToast,closeToast,showDialog,showConfirmDialog } from vant;export func…

Typora图床配置优化(PicGo-Core(command line) 插件 + gitee)

Typora图床配置优化&#xff08;PicGo-Core(command line) 插件 gitee&#xff09; 前言 在日常使用Typora编写markdown笔记时&#xff0c;经常需要插入图片来帮助理解和整理逻辑。然而&#xff0c;由于图片保存在本地&#xff0c;上传到网上时经常出现图片不见或错误警告的…

育菁桌面式数控机床助力教育装备

桌面式数控机床是一种小型化的数控机床&#xff0c;它通常具有紧凑的设计和较小的体积&#xff0c;可以放置在桌面上进行操作。 这种车床结合了数控技术&#xff0c;通过计算机编程来控制机床的运动和加工过程&#xff0c;以实现高精度、高效率的工件加工。 桌面式数控车床是一…

如何部署一套高可用性的医院信息管理系统?基于华为云、SpringBoot、Vue及Jenkins、Gitlab的CI/CD流程

目录 一、项目背景 二、项目架构 三、项目部署流程 1、前端部署 2、后端部署 3、监控与运维 四、项目过程 一、项目背景 随着医疗信息化程度的不断加深&#xff0c;医院信息管理系统的稳定性和可用性成为了医疗机构日常运营的关键。在这个数字化时代&am…

选择快充时代下的理想充电器与电压诱骗芯片PW6606

随着科技的不断进步&#xff0c;我们的电子设备对于充电速度和效率的要求越来越高。在快充技术迅猛发展的今天&#xff0c;了解不同类型的充电器及其对应的快充协议&#xff0c;以及如何选择适合的电压诱骗芯片&#xff0c;对于提升充电体验和保障设备安全显得尤为重要。 一、快…

生信网络学院|05月31日《SOLIDWORKS Manage 产品周期管理》

课程主题&#xff1a;SOLIDWORKS Manage 产品周期管理 课程时间&#xff1a;2024年05月31日 14:00-14:30 主讲人&#xff1a;付舰 生信科技 PLM实施顾问 1、SOLIDWORKS Manage介绍 2、周期流程管理 3、产品项目管理 4、项目会议管理 5、项目问题管理 安装腾讯会议客户端…

Android 13 VSYNC重学习

Android 13 VSYNC重学习 引言 学无止境&#xff0c;一个字干就完事&#xff01; 源码参考基于Android 13 aosp&#xff01; 一. Android VSync模块开胃菜 在开始正式的分析之前&#xff0c;我们先简单对Android的Vsync模块简单介绍下,如下图所示&#xff0c;其中: HW_VSync是…

【Java面试】一、Redis篇(上)

文章目录 0、准备1、缓存穿透&#xff1a;不存在的key2、缓存击穿&#xff1a;热点key过期3、缓存雪崩&#xff1a;大批key同时过期4、双写一致性4.1 要求高一致性4.2 允许一定的一致延迟 5、面试 0、准备 Redis相关概览&#xff1a; 以简历上所列的项目为切入点&#xff0c;展…

Steamdeck使用Windows系统游玩雪地奔驰时闪退问题解决方法

我非常喜欢雪地奔驰这款游戏&#xff0c;买sd的一部分也是为了它。可在我打开这个游戏时&#xff0c;游戏发生闪退问题。查阅了网络各个途径&#xff0c;基本没有解决方法。因此我自己分析终于解决该问题。以下是我解决问题的思路&#xff0c;仅供记录参考&#xff1a; 游戏在崩…

2024提升数字思维能力加快企业数字化转型(74页PPT)

方案介绍&#xff1a; 本报告的价值在于为企业提供了一套系统的提升数字思维能力、加快数字化转型的理论框架和实践指南。通过本报告的学习和应用&#xff0c;企业可以更加清晰地认识到数字化转型的重要性和紧迫性&#xff0c;明确自身在数字化转型中的优势和不足&#xff0c;并…