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

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

四、完整代码示例


完整代码示例 :


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;
  @override
  Widget build(BuildContext context) {
    return Material(
      /// 获取主题颜色 , 并将透明度设置为 0.25
      color: 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 的开方值 再乘以 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,
          ),
        ),
      ),
    );
  }
}
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 动画标签 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,
              ),
            ],
          ),
        ),
      ),
    );
  }
  /// 创建在界面 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),
                      );
                    });
              }));
            },
          ),
        ),
      ),
    );
  }
  @override
  Widget 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://ucc.alicdn.com/images/user-upload-01/20210330094257242.png", "蜂王"),
            _buildFirstPagWidget(context,
                "https://ucc.alicdn.com/images/user-upload-01/20210330093526559.png", "蜜蜂"),
            _buildFirstPagWidget(context,
                "https://ucc.alicdn.com/images/user-upload-01/2021033009353553.png", "工蜂"),
          ],
        ),
      ),
    );
  }
}



运行效果 :


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


目录
相关文章
|
21天前
|
前端开发 Java 开发工具
【03】完整flutter的APP打包流程-以apk设置图标-包名-签名-APP名-打包流程为例—-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈 章节内容【03】
【03】完整flutter的APP打包流程-以apk设置图标-包名-签名-APP名-打包流程为例—-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈 章节内容【03】
66 18
【03】完整flutter的APP打包流程-以apk设置图标-包名-签名-APP名-打包流程为例—-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈 章节内容【03】
|
15天前
|
Dart 前端开发
【05】flutter完成注册页面完善样式bug-增加自定义可复用组件widgets-严格规划文件和目录结构-规范入口文件-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【05】flutter完成注册页面完善样式bug-增加自定义可复用组件widgets-严格规划文件和目录结构-规范入口文件-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
111 75
【05】flutter完成注册页面完善样式bug-增加自定义可复用组件widgets-严格规划文件和目录结构-规范入口文件-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
|
13天前
|
Dart 前端开发 容器
【07】flutter完成主页-完成底部菜单栏并且做自定义组件-完整短视频仿抖音上下滑动页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【07】flutter完成主页-完成底部菜单栏并且做自定义组件-完整短视频仿抖音上下滑动页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
57 18
【07】flutter完成主页-完成底部菜单栏并且做自定义组件-完整短视频仿抖音上下滑动页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
|
3月前
|
开发工具 UED 容器
Flutter&鸿蒙next 实现长按录音按钮及动画特效
本文介绍了如何在 Flutter 中实现一个带有动画效果的长按录音按钮。通过使用 `GestureDetector` 监听长按手势,结合 `AnimatedContainer` 和 `AnimationController` 实现按钮的动画效果,以及 `flutter_sound` 插件完成录音功能。文章详细讲解了功能需求、实现思路和代码实现,帮助读者逐步掌握这一实用功能的开发方法。
153 5
|
3月前
Flutter 自定义组件继承与调用的高级使用方式
本文深入探讨了 Flutter 中自定义组件的高级使用方式,包括创建基本自定义组件、继承现有组件、使用 Mixins 和组合模式等。通过这些方法,您可以构建灵活、可重用且易于维护的 UI 组件,从而提升开发效率和代码质量。
155 1
|
3月前
|
前端开发 开发者
深入探索 Flutter 鸿蒙版的画笔使用与高级自定义动画
本文深入探讨了 Flutter 中的绘图功能,重点介绍了 CustomPainter 和 Canvas 的使用方法。通过示例代码,详细讲解了如何绘制自定义图形、设置 Paint 对象的属性以及实现高级自定义动画。内容涵盖基本绘图、动画基础、渐变动画和路径动画,帮助读者掌握 Flutter 绘图与动画的核心技巧。
97 1
|
3月前
|
开发工具 UED
Flutter&鸿蒙next中封装一个输入框组件
本文介绍了如何创建一个简单的Flutter播客应用。首先,通过`flutter create`命令创建项目;接着,在`lib`目录下封装一个自定义输入框组件`CustomInput`;然后,在主应用文件`main.dart`中使用该输入框组件,实现简单的UI布局和功能;最后,通过`flutter run`启动应用。本文还提供了后续扩展建议,如状态管理、网络请求和UI优化。
118 1
|
3月前
|
开发工具
Flutter&鸿蒙next中封装一个列表组件
Flutter&鸿蒙next中封装一个列表组件
65 0
|
前端开发 容器
Flutter的AnimatedDefaultTextStyle实现文本样式的动画过渡切换效果
AnimatedDefaultTextStyle通过动画过渡的方式来切换文本的显示样式
Flutter的AnimatedDefaultTextStyle实现文本样式的动画过渡切换效果
|
14天前
flutter开发中Use ‘const’ with the constructor to improve performance. Try adding the ‘const’ keyword to the constructor invocation.报错如何解决-优雅草卓伊凡
flutter开发中Use ‘const’ with the constructor to improve performance. Try adding the ‘const’ keyword to the constructor invocation.报错如何解决-优雅草卓伊凡
16 1

热门文章

最新文章

  • 1
    【05】flutter完成注册页面完善样式bug-增加自定义可复用组件widgets-严格规划文件和目录结构-规范入口文件-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
  • 2
    【01】vs-code如何配置flutter环境-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈-供大大的学习提升
  • 3
    flutter开发-figma交互设计图可以转换为flutter源代码-如何将设计图转换为flutter源代码-优雅草央千澈
  • 4
    【03】完整flutter的APP打包流程-以apk设置图标-包名-签名-APP名-打包流程为例—-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈 章节内容【03】
  • 5
    【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
  • 6
    【07】flutter完成主页-完成底部菜单栏并且做自定义组件-完整短视频仿抖音上下滑动页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
  • 7
    【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
  • 8
    【06】flutter完成注册页面-密码登录-手机短信验证-找回密码相关页面-并且实现静态跳转打包demo做演示-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
  • 9
    flutter开发中Use ‘const’ with the constructor to improve performance. Try adding the ‘const’ keyword to the constructor invocation.报错如何解决-优雅草卓伊凡
  • 10
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex