【Flutter】Hero 动画 ( Hero 实现径向动画 | Hero 组件 createRectTween 设置 )

news/2025/2/12 5:16:35/

文章目录

  • ◯、Hero 构造函数
  • 一、圆形方形组件
  • 二、创建页面 1 的组件 ( Hero 组件 1 )
  • 三、创建页面 2 的组件 ( Hero 组件 2 )
  • 四、完整代码示例
  • 五、相关资源





◯、Hero 构造函数



Hero 构造函数 :

  /// 创建一个 Hero 组件 ;////// tag , child 参数不能为空 ; /// child 参数的值不能是 Hero 组件以及 Hero 组件子类 ;const Hero({Key? key,required this.tag,this.createRectTween,this.flightShuttleBuilder,this.placeholderBuilder,this.transitionOnUserGestures = false,required this.child,}) : assert(tag != null),assert(transitionOnUserGestures != null),assert(child != null),super(key: key);

required this.tag : 不能为空 , 用于 关联两个界面的 Hero 组件 , 两个 Hero 组件有关联关系 , 则设置相同的 tag 字符串 ;

this.createRectTween : 可以为空 , 用于 定义 Hero 组件的边界 , 以及定义 Hero 组件在界面切换时 , 从 源界面的起始位置目的界面的最终位置 , 动画执行的变化过程 ;

required this.child : 不能为空 , 普通的 Widget 组件 , Hero 动画作用的组件 ;


Hero 动画可以实现径向动画 , 径向动画指的是组件形状可变的动画 , 如圆形变方形 , 方形变三角形 ;

Hero 径向动画 与 普通动画的区别就是是否设置了 createRectTween 参数 ;





一、圆形方形组件



圆形方形变化的组件 : 该组件可以根据不同的参数实现圆形到方形的变化 , 或方形到圆形的变化 ;

/// Hero 组件 , 径向动画扩展
/// 该组件主要用于裁剪组件用的
class OvalRectWidget extends StatelessWidget {/// 这里的裁剪大小 clipRectSize 最大半径 / 2 的开方值 再乘以 2const OvalRectWidget({Key key, this.maxRadius, this.child}): clipRectSize = 2.0 * (maxRadius / math.sqrt2),super(key: key);// 最大半径值final double maxRadius;/// 该值需要动态计算final clipRectSize;final Widget child;/// 这里特别注意该圆形裁剪组件/// 如果整个组件的宽高都是 maxRadius ,/// 内部的方形组件宽高是 2.0 * (maxRadius / math.sqrt2)/// 并且该方形组件居中显示/// 那么该方形组件的四个顶点正好处于圆形组件的裁剪半径位置/// 也就是方形组件完整显示 , 没有裁剪到@overrideWidget build(BuildContext context) {/// 布局裁剪组件 , 可以将布局裁剪成圆形return ClipOval(/// 可用于约束布局大小的组件/// 这里的居中显示是关键 , 如果不居中显示 , 最终还是圆形child: Center(child: SizedBox(width: clipRectSize,height: clipRectSize,/// 用于裁剪圆角矩形的组件child: ClipRect(child: child,),),),);}
}

组件形状显示分析 :

① 方形裁剪组件 : ClipOval 组件区域是 红色 矩形所在位置 , 其裁剪区域是蓝色组件位置 , 如果正好有个方形的组件 ClipRect 处于下面橙色区域内 , 那么该方形组件正好躲过了被外围红色区域 ClipOval 裁剪的操作 ; 显示的仍然是方形的组件 ;

在这里插入图片描述

② 圆形裁剪组件 : 如果 ClipOval 圆形裁剪组件 ( 红色 ) 与 ClipRect 方形的裁剪组件 ( 橙色 ) 位置重叠 , 那么该方形的裁剪组件肯定就被裁剪成圆形的了 ;

在这里插入图片描述

上面两个组件就是 Hero 径向动画的主要作用组件 , 该动画执行前 , 组件是圆形的 , 执行后组件是方形的 , 这就是改变了外层的 ClipOval 组件的大小 , 导致形状改变 ;





二、创建页面 1 的组件 ( Hero 组件 1 )



页面 1 的 Hero 组件显示的圆形的 , 跳转到页面 2 后 , 相同 tag 的 Hero 组件显示方形 ;

控制 OvalRectWidget 是圆形还是方形 , 主要是控制 OvalRectWidget 组件的宽高 , 这里设置的宽高设置 , 相当于上面的 " ② 圆形裁剪组件 " 情况 , 整个组件被裁剪成圆形的组件 ;

创建页面 1 的组件 :

  /// 创建在界面 1 显示的图标 , 点击后跳转到界面 2/// 页面的核心组件是 Hero 组件 , 而且是 3 个Widget _buildFirstPagWidget(BuildContext context, String imageName, String description) {return Container(/// 界面 1 中的显示的 Hero 组件是小图标/// 图标大小就是半径的两倍width: minRadius * 2.0,height: minRadius * 2.0,/// 主界面的核心 Hero 动画child: Hero(/// 这是 Hero 径向动画与标准 Hero 动画的区别/// 如果没有这个动画 , 中间过程会变成椭圆createRectTween: _createRectTween,/// Hero 动画标签tag: imageName,child: OvalRectWidget(maxRadius: maxRadius,/// 最内层显示的是网络图片组件child: ImageWidget(/// 设置网络图片地址imageUrl: imageName,// 设置点击事件onTap: () {/// 点击后跳转到新界面中Navigator.of(context).push(PageRouteBuilder<void>(pageBuilder:(BuildContext context, Animation<double> animation,Animation<double> secondaryAnimation) {// 创建一个 RoutePageBuilderreturn AnimatedBuilder(animation: animation,builder: (context, child) {/// 设置透明度组件return Opacity(/// 当前的透明度值 , 取值 0.0 ~ 1.0opacity: opacityCurve.transform(animation.value),// 主要显示的使用透明度控制的组件// 页面 2 组件child: _buildSecondPageWidget(context, imageName, description),);});}));},),),),);}




三、创建页面 2 的组件 ( Hero 组件 2 )



页面 1 的 Hero 组件显示的圆形的 , 跳转到页面 2 后 , 相同 tag 的 Hero 组件显示方形 ;

控制 OvalRectWidget 是圆形还是方形 , 主要是控制 OvalRectWidget 组件的宽高 , 这里设置的宽高相当于上面的 " ① 方形裁剪组件 " 设置 , 整个组件没有被裁剪到 , 显示的是方形组件 ;

创建页面 2 的组件 :

  /// 创建页面 2 , 这是点击后跳转到的页面/// 三个参数分别是 : 上下文 , 图片名称 , 页面描述/// 页面的核心组件是 Hero 组件 , 只有 1 个static Widget _buildSecondPageWidget(BuildContext context, String imageName, String description) {return Container(color: Theme.of(context).canvasColor,child: Center(child: Card(/// 设置卡片布局阴影大小elevation: 8,/// 卡片布局中显示图片和图片的描述child: Column(/// 在主轴方向 , 也就是垂直方向 , 应该占用多少空间/// Colum 主轴方向是垂直方向/// Row 主轴方向是水平方向mainAxisSize: MainAxisSize.min,children: [SizedBox(/// 约束布局大小的组件的宽高定义为最大半径的两倍width: maxRadius * 2,height: maxRadius * 2,/// 核心 Hero 组件child: Hero(/// 创建径向动画/// 如果没有这个动画 , 中间过程会变成椭圆createRectTween: _createRectTween,/// Hero 动画标签 IDtag: imageName,/// Hero 动画作用的组件child: OvalRectWidget(/// 这里的半径设置为最大半径值 ,maxRadius: maxRadius,/// 最内层显示的是网络图片组件child: ImageWidget(imageUrl: imageName,onTap: () {/// 点击后关闭当前页面Navigator.of(context).pop();},),),),),/// 图片描述文本Text(// 设置文本内容description,// 设置文本样式, 粗体style: TextStyle(fontWeight: FontWeight.bold),textScaleFactor: 3.0,),/// 空白间隔 , 无实际意义const SizedBox(height: 16,),],),),),);}




四、完整代码示例



完整代码示例 :

import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart' show timeDilation;
import 'dart:math' as math;void main() {runApp(MaterialApp(// 该组件本质是 StatelessWidget 组件子类home: RadialHeroAnimation(),));
}/// Hero 组件 , 跳转前后两个页面都有该组件
class ImageWidget extends StatelessWidget {/// 构造方法const ImageWidget({Key key, this.imageUrl, this.onTap}) : super(key: key);/// Hero 动画之间关联的 ID , 通过该标识/// 标识两个 Hero 组件之间进行动画过渡/// 同时该字符串也是图片的 url 网络地址final String imageUrl;/// 点击后的回调事件final VoidCallback onTap;@overrideWidget build(BuildContext context) {return Material(/// 获取主题颜色 , 并将透明度设置为 0.25color: Colors.green,/// 按钮child: InkWell(/// 按钮点击事件onTap: onTap,child: LayoutBuilder(builder: (BuildContext context, BoxConstraints size) {return Image.network(imageUrl,fit: BoxFit.contain,);},),),);}
}/// Hero 组件 , 径向动画扩展
/// 该组件主要用于裁剪组件用的
class OvalRectWidget extends StatelessWidget {/// 这里的裁剪大小 clipRectSize 最大半径 / 2 的开方值 再乘以 2const OvalRectWidget({Key key, this.maxRadius, this.child}): clipRectSize = 2.0 * (maxRadius / math.sqrt2),super(key: key);// 最大半径值final double maxRadius;/// 该值需要动态计算final clipRectSize;final Widget child;/// 这里特别注意该圆形裁剪组件/// 如果整个组件的宽高都是 maxRadius ,/// 内部的方形组件宽高是 2.0 * (maxRadius / math.sqrt2)/// 并且该方形组件居中显示/// 那么该方形组件的四个顶点正好处于圆形组件的裁剪半径位置/// 也就是方形组件完整显示 , 没有裁剪到@overrideWidget build(BuildContext context) {/// 布局裁剪组件 , 可以将布局裁剪成圆形return ClipOval(/// 可用于约束布局大小的组件/// 这里的居中显示是关键 , 如果不居中显示 , 最终还是圆形child: Center(child: SizedBox(width: clipRectSize,height: clipRectSize,/// 用于裁剪圆角矩形的组件child: ClipRect(child: child,),),),);}
}class RadialHeroAnimation extends StatelessWidget {/// 最小半径/// 使用该半径作为组件大小时 , 组件被裁剪成圆形static const double minRadius = 32.0;/// 最大半径/// 使用该半径作为组件大小时 , 组件被裁剪成方形static const double maxRadius = 128.0;/// 动画差速器static const opacityCurve = Interval(0.0, 0.75, curve: Curves.fastOutSlowIn);/// 创建径向动画static RectTween _createRectTween(Rect begin, Rect end) {/// MaterialRectCenterArcTween 就是从方形到圆形变化的辅助类return MaterialRectCenterArcTween(begin: begin, end: end);}/// 创建页面 2 , 这是点击后跳转到的页面/// 三个参数分别是 : 上下文 , 图片名称 , 页面描述/// 页面的核心组件是 Hero 组件 , 只有 1 个static Widget _buildSecondPageWidget(BuildContext context, String imageName, String description) {return Container(color: Theme.of(context).canvasColor,child: Center(child: Card(/// 设置卡片布局阴影大小elevation: 8,/// 卡片布局中显示图片和图片的描述child: Column(/// 在主轴方向 , 也就是垂直方向 , 应该占用多少空间/// Colum 主轴方向是垂直方向/// Row 主轴方向是水平方向mainAxisSize: MainAxisSize.min,children: [SizedBox(/// 约束布局大小的组件的宽高定义为最大半径的两倍width: maxRadius * 2,height: maxRadius * 2,/// 核心 Hero 组件child: Hero(/// 创建径向动画/// 如果没有这个动画 , 中间过程会变成椭圆createRectTween: _createRectTween,/// Hero 动画标签 IDtag: imageName,/// Hero 动画作用的组件child: OvalRectWidget(/// 这里的半径设置为最大半径值 ,maxRadius: maxRadius,/// 最内层显示的是网络图片组件child: ImageWidget(imageUrl: imageName,onTap: () {/// 点击后关闭当前页面Navigator.of(context).pop();},),),),),/// 图片描述文本Text(// 设置文本内容description,// 设置文本样式, 粗体style: TextStyle(fontWeight: FontWeight.bold),textScaleFactor: 3.0,),/// 空白间隔 , 无实际意义const SizedBox(height: 16,),],),),),);}/// 创建在界面 1 显示的图标 , 点击后跳转到界面 2/// 页面的核心组件是 Hero 组件 , 而且是 3 个Widget _buildFirstPagWidget(BuildContext context, String imageName, String description) {return Container(/// 界面 1 中的显示的 Hero 组件是小图标/// 图标大小就是半径的两倍width: minRadius * 2.0,height: minRadius * 2.0,/// 主界面的核心 Hero 动画child: Hero(/// 这是 Hero 径向动画与标准 Hero 动画的区别/// 如果没有这个动画 , 中间过程会变成椭圆createRectTween: _createRectTween,/// Hero 动画标签tag: imageName,child: OvalRectWidget(maxRadius: maxRadius,/// 最内层显示的是网络图片组件child: ImageWidget(/// 设置网络图片地址imageUrl: imageName,// 设置点击事件onTap: () {/// 点击后跳转到新界面中Navigator.of(context).push(PageRouteBuilder<void>(pageBuilder:(BuildContext context, Animation<double> animation,Animation<double> secondaryAnimation) {// 创建一个 RoutePageBuilderreturn AnimatedBuilder(animation: animation,builder: (context, child) {/// 设置透明度组件return Opacity(/// 当前的透明度值 , 取值 0.0 ~ 1.0opacity: opacityCurve.transform(animation.value),// 主要显示的使用透明度控制的组件// 页面 2 组件child: _buildSecondPageWidget(context, imageName, description),);});}));},),),),);}@overrideWidget build(BuildContext context) {/// 时间膨胀系数 , 用于降低动画运行速度/// 1.0 是标准速度timeDilation = 5.0;/// 主界面显示内容return Scaffold(appBar: AppBar(title: Text("Hero 径向动画演示"),),body: Container(padding: EdgeInsets.all(32),alignment: FractionalOffset.bottomLeft,/// 横向列表显示 3 个图标child: Row(/// 排列方式 : 平分空间mainAxisAlignment: MainAxisAlignment.spaceBetween,children: [_buildFirstPagWidget(context,"https://img-blog.csdnimg.cn/20210330094257242.png", "蜂王"),_buildFirstPagWidget(context,"https://img-blog.csdnimg.cn/20210330093526559.png", "蜜蜂"),_buildFirstPagWidget(context,"https://img-blog.csdnimg.cn/2021033009353553.png", "工蜂"),],),),);}
}

运行效果 :

在这里插入图片描述





五、相关资源



参考资料 :

  • Flutter 官网 : https://flutter.dev/
  • Flutter 插件下载地址 : https://pub.dev/packages
  • Flutter 开发文档 : https://flutter.cn/docs ( 强烈推荐 )
  • 官方 GitHub 地址 : https://github.com/flutter
  • Flutter 中文社区 : https://flutter.cn/
  • Flutter 实用教程 : https://flutter.cn/docs/cookbook
  • Flutter CodeLab : https://codelabs.flutter-io.cn/
  • Dart 中文文档 : https://dart.cn/
  • Dart 开发者官网 : https://api.dart.dev/
  • Flutter 中文网 : https://flutterchina.club/ , http://flutter.axuer.com/docs/
  • Flutter 相关问题 : https://flutterchina.club/faq/ ( 入门阶段推荐看一遍 )
  • GitHub 上的 Flutter 开源示例 : https://download.csdn.net/download/han1202012/15989510
  • Flutter 实战电子书 : https://book.flutterchina.club/chapter1/

重要的专题 :

  • Flutter 动画参考文档 : https://flutterchina.club/animations/

博客源码下载 :

  • GitHub 地址 : https://github.com/han1202012/flutter_animation ( 随博客进度一直更新 , 有可能没有本博客的源码 )

  • 博客源码快照 : https://download.csdn.net/download/han1202012/16245277 ( 本篇博客的源码快照 , 可以找到本博客的源码 )


http://www.ppmy.cn/news/142591.html

相关文章

Coursehero解锁Coursehero UnlockCoursehero查答案chegg查题下载

文章目录 Coursehero解锁Coursehero UnlockCoursehero查答案下载一、打开coursehero解锁网站二、购买Coursehero解锁账户三、登录Coursehero账户解锁 Coursehero解锁Coursehero UnlockCoursehero查答案下载 一、打开coursehero解锁网站 打开Coursehero解锁下载答案网站&#…

Flutter Hero 实现共享元素转场动画

系列文章 Flutter 旋转动画 — RotationTransitionFlutter 平移动画 — 4种实现方式Flutter 淡入淡出与逐渐出现动画Flutter 尺寸缩放、形状、颜色、阴影变换动画Flutter 列表Item动画 — AnimatedList实现Item左进左出、淡入淡出Flutter Hero 实现共享元素转场动画Flutter He…

【Flutter】Hero 动画 ( Hero 动画使用流程 | 创建 Hero 动画核心组件 | 创建源页面 | 创建目的页面 | 页面跳转 )

文章目录 ◯、Hero 动画简介一、创建 Hero 动画核心组件二、创建源页面三、创建目的页面四、页面跳转五、完整代码示例六、相关资源 ◯、Hero 动画简介 Hero Widget 动画效果 : Hero 通过动画从 源界面 运动到 目标界面 时 , 目标界面 透明度逐渐增加 , 淡入显示 ; Hero 是界面…

31、Flutter之Hero动画

Flutter Hero动画 Hero指的是可以在路由(页面)之间“飞行”的widget。使用Flutter的Hero widget创建hero动画。将 hero从一个路由飞到另一个路由。将 hero 的形状从圆形转换为矩形&#xff0c;同时将其从一个路由飞到另一个路由的过程中进行动画处理。Flutter中的Hero widget实…

hero登录器服务器列表为空,Hero引擎登陆器配置图文教程

1.首先启动配置器,如图: 2.游戏列表格式,也就是登录器左边显示的游戏区,保存为文本文件,例如GameList.txt 以下是设置4个区时登录器显示的样子,此文件需要保存在你网站的空间中,如上图 按照如下格式设置好你的游戏区后,并且配置好后启动登录器效果如下图: [电信区] ServerName=…

习题Hero

&#xff08;1&#xff09;定义一个游戏中Hero 英雄的类&#xff0c;在该类中定义英雄的名字&#xff0c; 生命值和等级3 个属性&#xff0c;定义一个构造函数完成对生命值和等级的初始化&#xff0c;分别赋初值为100&#xff0c;1。同时实现名字的输入和英雄信息的输出。 &am…

《Flutter 控件大全》第五十二个:Hero

如果你对Flutter还有疑问或者技术方面的疑惑,欢迎加入Flutter交流群(微信:laomengit)。同时也欢迎关注我的Flutter公众号【老孟程序员】,公众号首发Flutter的相关内容。Flutter地址:http://laomengit.com 里面包含160多个组件的详细用法。Hero是我们常用的过渡动画,当用…

Hero

Theres a hero 有一位英雄 If you look inside your heart 如果你探寻你的内心 You dont have to be afraid 你不必担心害怕 Of what you are 自己是谁 Theres an answer 有一种答案 If you reach into your soul 如果你深入自己的灵魂 And the sorrow that you know 你所经历的…