You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter跨页面切换时如何同时对多个Widget执行过渡动画

Flutter跨页面共享元素+同步入场动画实现方案

这种场景不需要做全页面硬替换,用Hero共享元素动画+路由级同步动画控制器的组合方案即可实现,既保证两个动画完全同步,也能避开第一页原有元素的遮挡问题。


核心实现思路

  • 带文本的Container作为共享元素,用Hero组件包裹后设置全局唯一的相同tag,Flutter会自动处理跨页面的位移动画,不需要手动写位移逻辑。
  • 新增的TextField入场动画直接绑定页面路由的原生动画控制器,和Hero动画走同一个时间线,从根源上保证进度完全同步。
  • 调整TextField动画的插值区间,让它在路由动画执行到一定阶段后再开始入场,刚好避开第一页原有元素的显示周期,解决遮挡问题。

具体实现代码

1. 封装共享Container组件

两个页面的Container复用同一个组件,外层统一套Hero:

class SharedTextContainer extends StatelessWidget {
  final String content;
  const SharedTextContainer({super.key, required this.content});

  @override
  Widget build(BuildContext context) {
    return Hero(
      tag: 'shared_text_container', // 两个页面的tag必须完全一致
      child: Container(
        width: 120,
        height: 40,
        alignment: Alignment.center,
        color: Colors.blue,
        child: Text(content, style: const TextStyle(color: Colors.white, fontSize: 14)),
      ),
    );
  }
}

2. 自定义页面路由,统一动画时长

用PageRouteBuilder定义第二页的路由,禁用默认页面过渡效果,统一动画时长:

PageRoute buildSecondRoute() {
  return PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) => const SecondPage(),
    transitionsBuilder: (context, animation, secondaryAnimation, child) => child, // 关闭默认过渡,完全走自定义动画
    transitionDuration: const Duration(milliseconds: 600), // 全局统一动画时长
  );
}

3. 第二页实现TextField同步入场动画

监听路由自带的动画控制器,绑定TextField的透明度+位移动画:

class SecondPage extends StatefulWidget {
  const SecondPage({super.key});

  @override
  State<SecondPage> createState() => _SecondPageState();
}

class _SecondPageState extends State<SecondPage> {
  Animation<double>? _textFieldAnim;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // 获取当前路由的原生动画控制器
    final routeAnimation = ModalRoute.of(context)?.animation;
    if (routeAnimation != null && _textFieldAnim == null) {
      // 动画从路由进度30%开始执行,避开第一页元素遮挡
      _textFieldAnim = Tween<double>(begin: 0, end: 1).animate(
        CurvedAnimation(
          parent: routeAnimation,
          curve: const Interval(0.3, 1.0, curve: Curves.easeInOut),
        ),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView(
        children: [
          // 侧边偏移的共享Container
          Padding(
            padding: const EdgeInsets.only(left: 30, top: 50),
            child: SharedTextContainer(content: '共享文本'),
          ),
          // 同步入场的TextField
          AnimatedBuilder(
            animation: _textFieldAnim!,
            builder: (context, child) {
              return Opacity(
                opacity: _textFieldAnim!.value,
                child: Transform.translate(
                  // 配合淡入加轻微上滑效果,比单纯淡入更自然
                  offset: Offset(0, 20 * (1 - _textFieldAnim!.value)),
                  child: const Padding(
                    padding: EdgeInsets.all(20),
                    child: TextField(decoration: InputDecoration(hintText: '请输入内容')),
                  ),
                ),
              );
            },
          ),
          // 其他ListView元素
        ],
      ),
    );
  }
}

问题排查

  • 如果动画不同步:不要给TextField单独创建AnimationController,必须绑定路由自带的动画控制器作为父动画。
  • 如果仍然有遮挡:调整Interval的起始值,把TextField入场时机延后到路由动画后半段即可。
  • 如果需要自定义共享元素过渡效果:给Hero组件传入flightShuttleBuilder参数,自定义位移过程中的中间态样式。

内容的提问来源于stack exchange,提问作者Sergey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 15:06:02