如何实现融合Flutter OpenContainer与Hero动画的自定义过渡效果?
自定义实现OpenContainer + Hero动画融合效果
核心思路
因为官方OpenContainer组件本身不支持Hero动画,我们可以拆分两个核心效果分别实现再整合:
- 用
Hero组件负责共享元素的无缝过渡 - 自定义路由过渡动画,模拟OpenContainer的"从按钮展开至全屏"的缩放+位置偏移效果
- 将Hero的共享元素作为过渡动画的载体,让两个效果同步执行
代码实现步骤
1. 定义共享Hero标签
const _expandHeroTag = 'expand_button_tag';
2. 首页按钮(带Hero+触发跳转)
// 用GlobalKey获取按钮实际位置(可选,用于更精准的位置过渡) final buttonGlobalKey = GlobalKey(); Hero( tag: _expandHeroTag, child: ElevatedButton( key: buttonGlobalKey, style: ElevatedButton.styleFrom( fixedSize: const Size(200, 60), backgroundColor: Colors.blue, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ), onPressed: () { Navigator.push( context, PageRouteBuilder( transitionDuration: const Duration(milliseconds: 500), reverseTransitionDuration: const Duration(milliseconds: 500), pageBuilder: (_, __, ___) => const FullScreenPage(), transitionsBuilder: (context, animation, secondaryAnimation, child) { // 缩放动画:从按钮大小比例放大到全屏 final scaleTween = Tween<double>(begin: 0.3, end: 1.0).animate( CurvedAnimation(parent: animation, curve: Curves.easeInOut), ); // 位置偏移动画:让页面从按钮位置滑到全屏(可根据实际坐标动态计算) final offsetTween = Tween<Offset>( begin: const Offset(0, 0.5), end: Offset.zero, ).animate( CurvedAnimation(parent: animation, curve: Curves.easeInOut), ); // 圆角过渡:模拟OpenContainer的圆角消失效果 final borderRadiusTween = BorderRadiusTween( begin: BorderRadius.circular(8), end: BorderRadius.zero, ).animate( CurvedAnimation(parent: animation, curve: Curves.easeInOut), ); return AnimatedBuilder( animation: borderRadiusTween, builder: (_, child) { return ClipRRect( borderRadius: borderRadiusTween.value, child: SlideTransition( position: offsetTween, child: ScaleTransition( scale: scaleTween, child: child, ), ), ); }, child: child, ); }, ), ); }, child: const Text('展开至全屏', style: TextStyle(color: Colors.white)), ), )
3. 全屏目标页面
class FullScreenPage extends StatelessWidget { const FullScreenPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: Hero( tag: _expandHeroTag, child: Container( width: double.infinity, height: double.infinity, color: Colors.blue, child: Center( child: ElevatedButton( onPressed: () => Navigator.pop(context), child: const Text('返回', style: TextStyle(color: Colors.white)), ), ), ), ), ); } }
进阶优化
- 精准位置计算:用
buttonGlobalKey获取按钮的全局坐标,动态计算offsetTween的起始偏移量,让动画完全从按钮位置展开 - 动画曲线调整:根据需求替换
Curves参数,比如用Curves.elasticOut实现弹性展开效果 - 状态同步:如果需要在过渡中同步更新内容,可以用
AnimatedBuilder监听动画进度,实时修改组件状态
内容的提问来源于stack exchange,提问作者Ahmed
相关产品推荐
相关产品推荐

