Flutter Hero组件无法在Widget与Dialog间实现过渡动画
Flutter Hero 过渡动画未生效排查与修复
我实现了一个包含图标、文本和容器背景的SquareTile组件,同时封装了基于showDialog的自定义showCustomDialog函数。点击SquareTile会弹出对话框,我给背景、图标、文本三个元素设置了Hero标签,预期触发过渡动画,但动画完全没效果,也没有报错信息。
tile.dart(SquareTile组件代码)
import 'package:flutter/material.dart'; // ignore: must_be_immutable class SquareTile extends StatelessWidget { Icon icon; String text = ''; String bgTag; String iconTag; String textTag; SquareTile({ required this.icon, required this.text, required this.bgTag, required this.iconTag, required this.textTag, super.key, }); @override Widget build(BuildContext context) { return Stack( children: [ Hero( tag: bgTag, child: Material( type: MaterialType.transparency, child: Container( width: 300, height: 50, decoration: BoxDecoration( color: Colors.grey[100], border: Border.all(color: Colors.white), borderRadius: const BorderRadius.all( Radius.circular(10), ), ), ), ), ), Positioned( left: 10, top: 0, bottom: 0, child: SizedBox( height: 50, width: 50, child: Hero( tag: iconTag, child: Material(type: MaterialType.transparency, child: icon), ), ), ), Positioned( left: 80, top: 0, bottom: 0, child: SizedBox( width: 150, child: Center( child: Hero( tag: textTag, child: Material( type: MaterialType.transparency, child: Text( text, textAlign: TextAlign.center, style: const TextStyle( color: Colors.black, fontWeight: FontWeight.w800, ), ), ), ), ), ), ), ], ); } }
showCustomDialog.dart(自定义对话框代码)
import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:smooth_like_flutter/components/service_field.dart'; void showCustomDialog( BuildContext context, Icon icon, String title, String rates, String bgTag, String iconTag, String textTag, ) { final quantityController = TextEditingController(); final linkController = TextEditingController(); showDialog( context: context, builder: (BuildContext context) { return Scaffold( resizeToAvoidBottomInset: true, backgroundColor: Colors.transparent, body: Stack( children: [ GestureDetector( onTap: () => Navigator.pop(context), child: SizedBox( width: double.infinity, height: double.infinity, child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), child: Container( color: Colors.black.withOpacity(0), ), ), ), ), Align( alignment: Alignment.center, child: Hero( tag: bgTag, child: Container( height: 400, width: 280, decoration: BoxDecoration( border: Border.all(color: Colors.white, width: 1.5), color: Colors.grey[300], borderRadius: const BorderRadius.all(Radius.circular(10))), ), ), ), Align( alignment: Alignment.center, child: ConstrainedBox( constraints: const BoxConstraints(maxHeight: 350), child: Column( children: [ const SizedBox( height: 10, ), Hero( tag: iconTag, child: const Icon( Icons.group_rounded, color: Colors.black, size: 40, ), ), Hero( tag: textTag, child: Material( type: MaterialType.transparency, child: Text( title, textAlign: TextAlign.center, style: const TextStyle( color: Colors.black, fontWeight: FontWeight.w800, ), ), ), ), const SizedBox( height: 5, ), Text( rates, style: TextStyle(color: Colors.grey[700]), ), const SizedBox( height: 30, ), MyServiceField( type: TextInputType.number, controller: quantityController, hint: 'Quantity', ), const SizedBox( height: 10, ), MyServiceField( type: TextInputType.emailAddress, controller: linkController, hint: 'Link', ), const SizedBox( height: 35, ), Container( width: 200, height: 50, decoration: const BoxDecoration( color: Colors.black, borderRadius: BorderRadius.all(Radius.circular(30))), child: const Center( child: Text( 'Place Order', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold), ), ), ) ], ), ), ), ], ), ); }); }
instagram.dart(SquareTile初始化代码)
SizedBox( height: 400, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ GestureDetector( onTap: () { showCustomDialog( context, const Icon(Icons.group_rounded), 'Followers', '100 -> 100', 'followers_bg', 'followers-icon', 'followers-text', ); }, child: SquareTile( icon: const Icon(Icons.group_rounded), text: 'Followers', bgTag: 'followers_bg', iconTag: 'followers-icon', textTag: 'follwers-text'), ), ], ), )
问题排查与修复方案
1. 标签拼写错误(核心问题之一)
instagram.dart中SquareTile的textTag拼写为'follwers-text',但调用showCustomDialog时传入的是'followers-text',标签不匹配直接导致Hero动画失效。修正后确保两端标签完全一致:
SquareTile( // ...其他参数 textTag: 'followers-text', // 修正拼写 )
2. 对话框Hero组件的Material包裹缺失
Hero动画要求两端组件的渲染上下文一致,对话框中的背景和图标Hero缺少Material(type: MaterialType.transparency)包裹,导致渲染异常:
- 修复背景Hero:
Hero( tag: bgTag, child: Material( type: MaterialType.transparency, child: Container(/* ...原有容器属性 */), ), )
- 修复图标Hero:同时替换硬编码图标为传入的
icon参数,确保前后组件一致:
Hero( tag: iconTag, child: Material( type: MaterialType.transparency, child: icon, // 使用传入的icon参数 ), )
3. 对话框路由的兼容性调整
showDialog的默认路由处理可能干扰Hero动画,若上述修复后仍无效,可改用Navigator.push弹出自定义页面路由,或确保对话框的Scaffold处于正确的MaterialApp上下文范围内。
4. 尺寸过渡的一致性优化
SquareTile背景尺寸为300x50,对话框背景为280x400,尺寸差异较大,可适当调整两端的Decoration属性(如border宽度、圆角)保持一致,避免动画异常。
内容的提问来源于stack exchange,提问作者Sami Khan
相关产品推荐
相关产品推荐

