文章目录
◯、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 的开方值 再乘以 2 const 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) /// 并且该方形组件居中显示 /// 那么该方形组件的四个顶点正好处于圆形组件的裁剪半径位置 /// 也就是方形组件完整显示 , 没有裁剪到 @override Widget 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) { // 创建一个 RoutePageBuilder return AnimatedBuilder( animation: animation, builder: (context, child) { /// 设置透明度组件 return Opacity( /// 当前的透明度值 , 取值 0.0 ~ 1.0 opacity: 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 动画标签 ID tag: 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, ), ], ), ), ), ); }