Flutter开发进阶之动画

Flutter开发进阶之动画

在Flutter中,动画是至关重要的一个部分,它能够为应用程序提供更加丰富和生动的用户体验,Flutter中的动画系统是UI框架的核心功能之一,也是开发者学习Flutter框架的重要部分,由于动画原理在所有程序中都是相同的,即视觉暂留,因此理解这一原理对于更好地使用Flutter的动画系统至关重要;
动画在应用程序中具有许多用途,例如引导用户注意力、增强视觉效果、提供更好的交互体验等,通过精心设计的动画,应用程序可以变得更加有趣、吸引人,并提高用户的参与度和满意度;
Flutter开发进阶之动画
Flutter开发中动画可以有多种分类,其中可以通过是否模拟现实世界中物理行为物理动画和补间动画。

物理动画

物理动画模拟现实世界中的物理行为,如重力、弹性、摩擦力等;
Flutter的AnimatedPhysics组件提供了这种能力,可以为AnimatedPhysics组件提供一个AccelerationSpec和DecelerationSpec,以定义动画的加速和减速行为。

  Widget build(BuildContext context) {  return AnimatedPhysics(  mass: 100.0,  acceleration: 9.8, // 重力加速度  deceleration: 0.5, // 阻尼系数,决定物体下落后的减速程度  child: Container(  width: 200.0,  height: 200.0,  color: Colors.red,  ),  animationId: _animationId, // 动画ID,用于标识这个动画,用于后续的动画控制操作,例如暂停、恢复等。  );  }  

补间动画

补间动画是更传统的动画方式,它允许你定义动画的开始和结束状态,以及中间过渡状态;
Flutter的AnimatedBuilder和AnimatedCrossFade组件提供了这种能力,还可以通过Tween来构建。

  Widget build(BuildContext context) {  return AnimatedBuilder(  animation: _animationController,  builder: (context, child) {  if (_isFirst) {  return Container(color: Colors.red, child: child);  } else {  return Container(color: Colors.green, child: child);  }  },  child: AnimatedCrossFade(  duration: Duration(seconds: 1),  firstChild: Container(color: Colors.red), // 第一个显示的子组件,初始为红色容器  secondChild: Container(color: Colors.green), // 第二个显示的子组件,初始为绿色容器  crossfadeState: _isFirst ? 1 : 0, // 控制动画状态,true表示第二个子组件显示,false表示第一个子组件显示  firstChildBuilder: (BuildContext context, child, bool visible) { // 可选,用于定制第一个子组件的动画效果,这里我们保持默认行为,不进行定制。  return visible ? child : null;  },  secondChildBuilder: (BuildContext context, child, bool visible) { // 可选,用于定制第二个子组件的动画效果,这里我们保持默认行为,不进行定制。  return visible ? child : null;  },  ),  animationId: _animationController.id, // 动画ID,用于标识这个动画,用于后续的动画控制操作,例如暂停、恢复等。这里我们使用了和之前示例中一样的ID。  );  }  

还可以根据动画的触发方式(隐式和显式)分为隐式动画和显式动画。

隐式动画

隐式动画是Flutter框架自动处理的动画,通常可以使用对应的组件(AnimatedOpacity、AnimatedContainer、AnimatedPadding、AnimatedPositioned、AnimatedSwitcher等)来实现,通过简单的属性设置来实现动画效果,而不需要手动创建和管理动画控制器。

  
Widget build(BuildContext context) {  return Column(  mainAxisAlignment: MainAxisAlignment.center,  children: [  // 示例1:使用AnimatedOpacity创建动画  AnimatedOpacity(  value: _animationValue, // 动画值范围:0.0(完全透明)到1.0(完全不透明)  duration: _controller.duration, // 使用AnimationController的持续时间,确保动画同步。  child: Container(  width: 100,  height: 100,  color: Colors.red,  ),  ),  // 示例2:使用AnimatedContainer创建动画  AnimatedContainer(  duration: _controller.duration, // 使用AnimationController的持续时间,确保动画同步。  curve: Curves.bounceInOut, // 动画曲线  child: Container(  width: 100,  height: 50,  color: Colors.green,  ),  ),  // 其他使用动画效果的子项...  ],  );  
}

显式动画

显式动画是开发者需要手动创建和管理的动画,在Flutter中,这通常通过使用Animated组件(例如AnimatedPositioned,AnimatedBuilder等)以及AnimationController类来完成;
需要明确指定动画的起始状态、结束状态以及过渡的时间和曲线。

class MyAnimatedWidget extends StatefulWidget {    _MyAnimatedWidgetState createState() => _MyAnimatedWidgetState();  
}  class _MyAnimatedWidgetState extends State<MyAnimatedWidget> with SingleTickerProviderStateMixin {  AnimationController _controller;  Tween<double> _tween = Tween<double>(  begin: 0.0,  end: 200.0,  ).animate(_controller);    void initState() {  super.initState();  _controller = AnimationController(  vsync: this,  duration: Duration(seconds: 2), // 动画持续时间  );  }    Widget build(BuildContext context) {  return Column(  mainAxisAlignment: MainAxisAlignment.center,  children: [  AnimatedPositioned(  animation: _controller, // 将动画控制器绑定到AnimatedPositioned上  duration: _controller.duration, // 使用AnimationController的持续时间,确保动画同步。  x: 0, // x位置保持不变,仅y位置进行动画处理。  y: _tween.value, // 使用Tween的值作为y位置。随着动画的进行,y位置会从0变化到200。  child: Container(  width: 100,  height: 100,  color: Colors.red,  ),  ),  ],  );  }  
}

这些动画都是一些动画元素,可以将多个动画元素组装成复杂动画(Complex Animations),通常指交错动画、序列动画、交错动画、组合动画等;
除了使用系统提供的组件去构建动画外,我们还可以使用CustomPainter构建自定义路径动画。

自定义路径动画

自定义路径动画可以根据需要绘制任何形状的路径,并在动画中改变路径的形状、位置和颜色等属性实现相应的效果。

class MyPainter extends CustomPainter {    bool shouldRepaint(CustomPainter oldDelegate) {  // 返回true表示需要重新绘制,这里我们简单地将返回true,以便每次重新渲染时都会重新绘制路径。  return true;  }    void paint(Canvas canvas, Size size) {  // 获取屏幕的宽高,以便根据需要调整绘制的比例。  var paint = Paint()..strokeCap = StrokeCap.round;  paint.strokeWidth = 10.0;  paint.color = Colors.red; // 设置画笔颜色为红色。  // 创建路径并绘制。这里我们只是简单地将一个圆形的路径绘制在屏幕上。您可以根据需要创建复杂的路径。  var path = Path()..moveTo(0, 0)..circle(size.width / 2, size.height / 2, size.width / 4); // 在屏幕中心绘制一个圆形的路径。您可以根据需要修改路径的形状和位置。  canvas.drawPath(path, paint);  }  
}

除此之外,还可以通过使用Flutter对应的动画库来创建动画。

spring_simulation

spring_simulation是一种物理模拟器,用于创建具有弹簧效果的动画;
它基于弹簧动力学原理,通过模拟弹簧的伸展、压缩和反弹等行为,实现动画的逼真效果。

class MyApp extends StatelessWidget {    Widget build(BuildContext context) {  return MaterialApp(  title: 'SpringSimulation Example',  theme: ThemeData(primarySwatch: Colors.blue),  home: Scaffold(  appBar: AppBar(title: Text('SpringSimulation Example')),  body: Center(  child: SpringSimulation(  // 定义弹簧模拟器的参数  mass: 1.0, // 质量(kg)  stiffness: 400.0, // 刚度(N/m)  damping: 5.0, // 阻尼(N·s/m)  target: 0.0, // 目标位置(m)  // 定义弹簧的初始位置和速度  initialPosition: -1.0, // 初始位置(m)  initialVelocity: 2.0, // 初始速度(m/s)  // 定义弹簧的约束条件(可选)  constraints: SpringConstraints(min: -1.0, max: 1.0), // 限制弹簧的最小和最大位置范围  // 将弹簧附加到屏幕上,这里使用了一个简单的圆点作为示例  onUpdate: (mass, stiffness, damping, position, velocity) {  Positioned(  left: position * 300, // 将弹簧的位置转换为屏幕坐标  child: Container(  width: 30,  height: 30,  decoration: BoxDecoration(shape: BoxShape.circle), // 创建一个圆点作为弹簧的表示  child: Icon(Icons.play_arrow), // 使用播放箭头图标作为示例,您可以根据需要自定义图标或文本等UI元素。  ),  );  },  ),  ),  ),  );  }  
}

还有很多,就不一一列举了。
在Flutter开发中构建动画,需要注意的点是:
选择合适的动画库
根据项目需求选择合适的动画库,如Tween、Dart-Animation-Lib、flutter_animator等。
理解动画生命周期
Flutter中的动画有一个开始、执行和结束的过程。你需要确保在动画开始时设置动画的初始状态,并在动画结束时设置动画的结束状态。
使用状态管理
Flutter中的动画通常与状态变化相关联,因此,建议使用Flutter的状态管理解决方案,如Redux或Bloc,来管理动画状态。
优化性能
在构建动画时,需要注意性能问题,避免在动画执行期间进行不必要的渲染或计算操作,这可能导致动画卡顿。
测试不同设备和场景
在不同设备和不同场景下测试动画效果,以确保动画在不同环境下都能正常工作。
考虑用户反馈
根据用户反馈调整动画效果,以提供更好的用户体验。
遵循Material Design规范
如果项目需要遵循Material Design规范,确保动画效果与规范保持一致。

如何对动画优化性能

对于Flutter开发中的性能优化,有以下几个方向。
避免不必要的渲染和计算
在动画执行期间,应避免进行不必要的渲染或计算操作,这可以减少GPU和CPU的工作负担,提高动画性能;
比如使用Reusable或MemoizedListTile等复用组件,这些组件可以在列表中使用,避免在列表滚动时重复创建和销毁组件,从而提高性能;
使用const关键字,在可能的情况下,尽量使用const关键字来创建不可变对象,这样可以避免在动画执行期间进行不必要的重新构建;
避免在动画执行期间进行复杂的布局和计算,在动画执行期间,应避免进行复杂的布局和计算操作,这些操作可能会导致不必要的渲染和计算;
使用ListView的physics属性,通过设置physics属性为NeverScrollableScrollPhysics(),可以避免在滚动时触发不必要的布局和渲染;
使用WidgetsBinding.instance.addPostFrameCallback,这个方法可以用来在每一帧渲染后执行回调函数,可以在这里进行一些必要的计算和更新操作,避免在动画执行期间进行不必要的渲染和计算;
使用Binding.instance.addPostFrameCallback,这个方法与WidgetsBinding.instance.addPostFrameCallback类似,但作用范围更广,可以在整个应用中避免不必要的渲染和计算。
使用列表复用
在处理大量数据时,使用列表复用技术可以避免频繁创建和销毁列表项,从而提高性能;
比如通过使用ListTile的key属性,可以确保列表项的唯一性,从而实现列表复用;
另外,也可以使用Reusable组件或MemoizedListTile组件来实现列表复用。
使用GPU加速
Flutter的渲染引擎支持GPU加速,通过将部分或全部渲染工作交给GPU处理,可以提高动画性能。
优化Widget树
在构建动画时,应尽量减少Widget树的深度和复杂度,避免不必要的布局和重绘操作,从而提高性能。
使用帧调度器
通过使用Flutter的帧调度器,可以更好地控制动画的执行时间和帧率,从而提高性能。
使用Profile工具
Flutter提供了Profile工具,可以用来分析应用的性能瓶颈,通过使用Profile工具,可以找到性能问题并进行针对性的优化。
使用高性能的动画库
如Tween等高性能的动画库,可以提供更好的动画性能。

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

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

相关文章

Spring之AOP源码(二)

书接上文 文章目录 一、简介1. 前文回顾2. 知识点补充 二、ProxyFactory源码分析1. ProxyFactory2. JdkDynamicAopProxy3. ObjenesisCglibAopProxy 三、 Spring AOP源码分析 一、简介 1. 前文回顾 前面我们已经介绍了AOP的基本使用方法以及基本原理&#xff0c;但是还没有涉…

2023年全国职业院校技能大赛软件测试赛题—单元测试卷⑤

单元测试 一、任务要求 题目1&#xff1a;根据下列流程图编写程序实现相应处理&#xff0c;执行j10*x-y返回文字“j1&#xff1a;”和计算值&#xff0c;执行j(x-y)*(10⁵%7)返回文字“j2&#xff1a;”和计算值&#xff0c;执行jy*log(x10)返回文字“j3&#xff1a;”和计算值…

经典目标检测YOLO系列(二)YOLOv2算法详解

经典目标检测YOLO系列(二)YOLOv2算法详解 YOLO-V1以完全端到端的模式实现达到实时水平的目标检测。但是&#xff0c;YOLO-V1为追求速度而牺牲了部分检测精度&#xff0c;在检测速度广受赞誉的同时&#xff0c;其检测精度也饱受诟病。正是由于这个原因&#xff0c;YOLO团队在20…

clickhouse join查询算法

算法对比&#xff1a; 使用方法&#xff1a; SELECT town,max(price) AS max_price,any(population) AS population FROM uk_xxx_paid JOIN uk_xxx_table ON lower(uk_price_paid.town) lower(uk_populations_table.city) GROUP BY town ORDER BY max_price DESC SETTINGS jo…

代码随想录 Leetcode349. 两个数组的交集

题目&#xff1a; 代码(首刷看解析 2024年1月14日&#xff09;&#xff1a; class Solution { public:vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {unordered_set<int> a;unordered_set<int> res;for(int i 0…

Centos7 安装与卸载mysql

卸载 ps ajx | grep mysql &#xff1a; 查看当前服务器是否有mysql 没有的话就不需要卸载咯。 centos7 通过yum下载安装包通常是以.rpm为后缀&#xff0c;rpm -qa 可以查看当前服务器上所有的安装包&#xff1a; rpm -qa | grep mysql | xargs yum -y remove :将查询到的mysql…

AI辅助编程:同义千问挑战力扣

大家好我是在看&#xff0c;记录普通人学习探索AI之路。 今天我们来聊一聊如何使用AI进行辅助编程。 ChatGPT对于各行各业都带来了工作效率的提升&#xff0c;尤其是程序员这一行。因为ChatGPT可以帮助程序员来生成各种各样的程序代码。 我们先来看一个简单的例子&#xff0c…

多行SQL转成单行SQL

如下图所示 将以上多行SQL转成单行SQL 正则表达式如下 (?s)$[^a-zA-Z()0-9]*结果如下 灵活使用,也未必只能使用Sublime Text

基于卡尔曼滤波的视频跟踪,基于卡尔曼滤波的运动小球跟踪

目录 完整代码和数据下载链接&#xff1a;基于卡尔曼滤波的视频跟踪&#xff0c;基于卡尔曼滤波的运动小球跟踪&#xff08;代码完整&#xff0c;数据齐全&#xff09;资源-CSDN文库 https://download.csdn.net/download/abc991835105/88738577 卡尔曼滤波原理 RBF的定义 RBF理…

ajax+axios——统一设置请求头参数——添加请求头入参——基础积累

最近在写后台管理系统&#xff08;我怎么一直都只写管理系统啊啊啊啊啊啊啊&#xff09;&#xff0c;遇到一个需求&#xff0c;就是要在原有系统的基础上&#xff0c;添加一个仓库的切换&#xff0c;并且需要把选中仓库对应的id以请求头参数的形式传递到每一个接口当中。。。 …

4.【CPP】入门(初始化列表||explicit||static||友元||静态成员变量/函数)

一.初始化列表 1.引入 我们知道在c11中才能在成员对象声明时初始化&#xff0c;像下面这样。 class Date { public: Date(int year, int month, int day): _year(year), _month(month), _day(day) {} private: int _year2000; int _month12; int _day20; };注意&#xff1a;…

Redis:原理速成+项目实战——Redis实战9(秒杀优化)

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位大四、研0学生&#xff0c;正在努力准备大四暑假的实习 &#x1f30c;上期文章&#xff1a;Redis&#xff1a;原理速成项目实战——Redis实战8&#xff08;基于Redis的分布式锁及优化&#xff09; &#x1f4da;订阅专栏&…

C#,史密斯数(Smith Number)的计算方法与源代码

一、关于史密斯数的传说 1、关于理海大学Lehigh University 理海大学&#xff08;Lehigh University&#xff09;&#xff0c;位于宾夕法尼亚州&#xff08;Pennsylvania&#xff09;伯利恒&#xff08;Bethlehem&#xff09;&#xff0c;由富有爱国情怀与民族精神的实业家艾萨…

Java SE入门及基础(12)

do-while 循环 1. 语法 do { //循环操作 } while ( 循环条件 ); 2. 执行流程图 3. 案例 从控制台录入学生的成绩并计算总成绩&#xff0c;输入0 时退出 4. 代码实现 public static void main ( String [] args ) { Scanner sc new Scanner ( System . in )…

SqlAlchemy使用教程(三) CoreAPI访问与操作数据库详解

SqlAlchemy使用教程(一) 原理与环境搭建SqlAlchemy使用教程(二) 入门示例及编程步骤 三、使用Core API访问与操作数据库 Sqlalchemy 的Core部分集成了DB API, 事务管理&#xff0c;schema描述等功能&#xff0c;ORM构筑于其上。本章介绍创建 Engine对象&#xff0c;使用基本的…

使用lodash原地起飞,总结了几个常用的lodash方法

前言 &#x1f4eb; 大家好&#xff0c;我是南木元元&#xff0c;热爱技术和分享&#xff0c;欢迎大家交流&#xff0c;一起学习进步&#xff01; &#x1f345; 个人主页&#xff1a;南木元元 目录 什么是lodash lodash的按需引入 数组操作 求交集 求合集 求差集 求总和…

如何使用C++编程使得在Windows和Linux输入密码的时候保密 linux:tcgetattr tcsetattr

在C编程中&#xff0c;在执行一些操作的时候&#xff0c;终端需要接收用户名和密码&#xff0c;那么在终端输入密码的时候&#xff0c;如何不让别人看见自己的密码&#xff0c;是一个较为关注的问题&#xff1b; 1、问题分析 定义一个登录函数Login //用户登录主循环bool Lo…

Android蓝牙协议栈fluoride(十一) - 音乐播放(4)

上一篇介绍了蓝牙音频的播放通路和编解码器&#xff0c;接下来介绍Source和Sink如何选择编解码器以及编解码流程。 编解码器选择 连接蓝牙后想要播放音乐&#xff0c;需要协商使用哪种编码器&#xff0c;还需要协商编码器使用什么配置&#xff0c;前面介绍了如何协商编码器的…

Redis分布式锁--java实现

文章目录 Redis分布式锁方案&#xff1a;SETNX EXPIRE基本原理比较好的实现会产生四个问题 几种解决原子性的方案方案&#xff1a;SETNX value值是&#xff08;系统时间过期时间&#xff09;方案&#xff1a;使用Lua脚本(包含SETNX EXPIRE两条指令)方案&#xff1a;SET的扩展…