文章目录

  • ◯、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 ( 本篇博客的源码快照 , 可以找到本博客的源码 )

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

  1. flutter 移动通知_Flutter开发之动画

    动画作为产品的重要组成部分,是提升用户体验的重要方式,一个恰当的动画不仅能够缓解用户因为等待而带来的情绪焦躁,还会增加应用的整体用户体验.因此,在应用中增加动画的相关功能,可以增强用户的粘性. 动画的 ...

  2. 112、Flutter实现图片放大缩小的动画小

    Flutter是通过Animation组件来实现动画效果的 main.dart文件中的内容与上一篇显示的代码相同 这一篇博客主要关注放大缩小的动画效果 1.WelcomeScreen  widget继 ...

  3. Flutter框架分析(五)-- 动画

    Flutter框架分析分析系列文章: <Flutter框架分析(一)-- 总览和Window> <Flutter框架分析(二)-- 初始化> <Flutter框架分析(三 ...

  4. Flutter 动画全解析(动画四要素、动画组件、隐式动画组件原理等)

    本文通过拆解 Flutter 中动画的实现方式以及原理来介绍动画实现的整个过程. 1. 动画四要素 动画在各个平台的实现原理都基本相同,是在一段时间内一系列连续变化画面的帧构成的.在 Flutter ...

  5. 【Flutter】Animation 动画 ( AnimatedBuilder 动画使用流程 | 创建动画控制器 | 创建动画 | 创建动画作用的组件 | 关联动画与组件 | 动画执行 )

    文章目录 ◯.AnimatedBuilder 引入 一.创建动画控制器 二.创建动画 三.创建动画作用的组件 四.创建 AnimatedBuilder 关联动画与组件 五.动画运行 六.完整代码示例 ...

  6. 【Flutter】Animation 动画 ( AnimatedWidget 动画使用流程 | 创建动画控制器 | 创建动画 | 创建 AnimatedWidget 动画组件 | 动画运行 )

    文章目录 ◯.AnimatedWidget 组件引入 一.创建 AnimatedWidget 动画组件 二.创建动画控制器 三.创建动画 四.动画运行 五.完整代码示例 六.相关资源 Animated ...

  7. 【Flutter】Animation 动画 ( Flutter 动画基本流程 | 创建动画控制器 | 创建动画 | 设置值监听器 | 设置状态监听器 | 布局中使用动画值 | 动画运行 )

    文章目录 一.创建动画控制器 二.创建动画 三.设置值监听器 四.设置状态监听器 五.布局中使用动画值 六.动画运行 七.完整代码示例 八.相关资源 Flutter 动画基本流程 : ① 创建动画控制 ...

  8. flutter 动画json_Flutter 50: 图解动画小插曲之 Lottie 动画

    和尚在一年前整理过一点 Lottie 在 Android 中的应用,现在 Flutter 也有相关的插件帮助我们快速简单的应用场景复杂的 Lottie 动画: 和尚在官网查询之后发现官网推荐了两个开源 ...

  9. flutter闪屏过渡动画,闪光占位动画

    题记 -- 执剑天涯,从你的点滴积累开始,所及之处,必精益求精,即是折腾每一天. 重要消息 网易云[玩转大前端]配套课程 EDU配套 教程 Flutter开发的点滴积累系列文章 实现的效果 1 添加依 ...

最新文章

  1. python函数图像绘制、函数不固定_无法在函数中绘制tkinter图像
  2. python【力扣LeetCode算法题库】945- 使数组唯一的最小增量
  3. Bochs调试加载符号文件的问题
  4. Node.js 基金会和 JS 基金会准备合并,你怎么看?
  5. 如何在OS X中打开或关闭鼠标定位器
  6. win10右键一直转圈_Win10电脑开机一直转圈无法进入系统的解决方法
  7. [CQOI2015]选数(数论分块+杜教筛)
  8. CentOS 7 安装版本管理 GitLab
  9. 12nm 制程、Zen+ 微架构 AMD Ryzen 7 2700X 处理器详细测试 - 电脑领域 HKEPC Hard
  10. UIPickView 和 UIDatePicker
  11. 干货 | 我可以读哪些论文来跟上现代NLP的最新趋势?
  12. 工作10年才懂的道理,早知道职位比现在高两级
  13. linux 6.8 多网卡绑定,Linux6.1/6.5 双网卡绑定
  14. 用Python合并多个txt文件
  15. 南京大学人工智能学院教授俞扬:我的牛年小结
  16. JS 表单提交信息加密
  17. python 解析pys文件,并将其写入txt和excel文件
  18. 生物信息相关国家自然科学基金汇总(持续更新中)
  19. 71外链论坛_免费发外链平台
  20. ofd怎么转换成图片 java_html如何转换为ofd?

热门文章

  1. 命名式ACL配置实例
  2. Odoo10 启动选项
  3. MongoDB3.6.3 windows安装配置、启动
  4. 201521123011《Java程序设计》第6周学习总结
  5. 简析服务端通过geotools导入SHP至PG的方法
  6. caffe命令及其参数解析
  7. zabbix监控超详细搭建过程
  8. android设置程序开机自启动
  9. 机器学习(五) 关于散点图生成
  10. redis面试问题(二)