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

Flutter中如何为AnimatedSwitcher实现SharedAxisTransition滑动过渡

AnimatedSwitcher实现水平轴向滑动过渡方案

完全可以基于AnimatedSwitcher实现和animations包中SharedAxisTransition一致的水平滑动效果,且能保证背景完全静态不参与动画,核心是自定义过渡构建器,不需要依赖Navigator。

实现要点:

  • 所有被AnimatedSwitcher切换的子组件必须设置唯一Key(推荐用ValueKey绑定当前页面的唯一标识,比如页面索引、枚举值),否则框架无法识别组件变化,动画不会触发。
  • 自定义transitionBuilder时,同步实现位移动画+透明度动画,匹配SharedAxis的动效逻辑:新页面从水平方向滑入同时渐显,旧页面向反方向滑出同时渐隐。
  • 用layoutBuilder配置切换时的堆叠逻辑,避免新旧组件切换时出现布局跳动。

对应原有代码结构的修改版本:

Scaffold(
  body: Stack(
    children: [
      // 静态背景层,永远不会参与动画、不会重建
      SizedBox(
        height: screenHeight,
        width: screenWidth,
        child: Container(color: backgroundColor),
      ),
      // 用AnimatedSwitcher包裹需要切换的内容层
      AnimatedSwitcher(
        duration: const Duration(milliseconds: 300),
        reverseDuration: const Duration(milliseconds: 300),
        // 自定义水平共享轴过渡逻辑
        transitionBuilder: (child, animation) {
          // 判断当前是进入组件还是退出组件
          final isEntering = animation.status == AnimationStatus.forward 
                          || animation.status == AnimationStatus.completed;
          
          // 位移动画:进入组件从右侧20%屏宽位置滑入,退出组件向左侧20%屏宽位置滑出
          final slideAnimation = Tween<Offset>(
            begin: isEntering ? const Offset(0.2, 0) : const Offset(-0.2, 0),
            end: Offset.zero,
          ).chain(CurveTween(curve: Curves.fastOutSlowIn)).animate(animation);
          
          // 透明度动画:同步实现渐隐渐显
          final fadeAnimation = Tween<double>(
            begin: 0,
            end: 1,
          ).chain(CurveTween(curve: Curves.easeOutCubic)).animate(animation);

          return SlideTransition(
            position: slideAnimation,
            child: FadeTransition(
              opacity: fadeAnimation,
              child: child,
            ),
          );
        },
        // 切换时堆叠新旧组件,避免布局跳动
        layoutBuilder: (currentChild, previousChildren) {
          return Stack(
            alignment: Alignment.center,
            children: [
              ...previousChildren,
              if (currentChild != null) currentChild,
            ],
          );
        },
        // 你需要切换的内容组件,必须加唯一Key
        child: OtherWidgetsHere(
          key: ValueKey(currentPageId), // 换页时更新currentPageId即可触发动画
        ),
      ),
    ],
  )
)

如果要完全对齐官方animations包的SharedAxisTransition效果,可以直接参考源码调整偏移系数、动画曲线、时长参数,效果完全一致。

不需要让Scaffold跟着页面切换重建,两种常用实现方式:

  • 方案1:全局背景挂载
    把静态背景放在App最外层,包裹MaterialApp,所有页面的Scaffold背景设为透明,路由配置为非不透明,切换时只有上层页面元素参与动画,背景完全静态。
    核心代码示例:
    @override
    Widget build(BuildContext context) {
      return Stack(
        children: [
          // 全局静态背景,永远不参与路由动画
          Positioned.fill(
            child: Container(
              // 可配置纯色、背景图、渐变等任意背景内容
              color: const Color(0xFFF2F3F5),
            ),
          ),
          MaterialApp(
            theme: ThemeData(
              // 全局设置Scaffold背景透明,透出下层静态背景
              scaffoldBackgroundColor: Colors.transparent,
            ),
            onGenerateRoute: (settings) {
              return PageRouteBuilder(
                // 关键:设置路由为非不透明,否则无法透出下层背景
                opaque: false,
                transitionDuration: const Duration(milliseconds: 300),
                pageBuilder: (context, animation, secondaryAnimation) {
                  // 返回对应页面的Scaffold,不要给Scaffold设置不透明背景
                  return getPageFromSettings(settings);
                },
                transitionsBuilder: (context, animation, secondaryAnimation, child) {
                  // 这里可以直接复用前面写的水平共享轴过渡逻辑,
                  // 也可以使用任意自定义过渡动画,背景不会受影响
                  final slideAnimation = Tween<Offset>(
                    begin: const Offset(0.2, 0),
                    end: Offset.zero,
                  ).chain(CurveTween(curve: Curves.fastOutSlowIn)).animate(animation);
                  final fadeAnimation = Tween<double>(begin: 0, end: 1)
                      .chain(CurveTween(curve: Curves.easeOutCubic))
                      .animate(animation);
                  return SlideTransition(
                    position: slideAnimation,
                    child: FadeTransition(opacity: fadeAnimation, child: child),
                  );
                },
              );
            },
          )
        ],
      );
    }
    
  • 方案2:嵌套Navigator
    如果不需要全局固定背景,只需要在某个模块内切换页面时固定背景,可以在该模块的Scaffold内嵌套一个Navigator,把背景放在Scaffold的底层,嵌套Navigator管理的页面全部设为透明背景,切换逻辑和方案1一致,背景不会被路由切换影响。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 22:51:26