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

在GoRouter的StatefulShellRoute中实现SlideTransition时出现空白页问题求助

在GoRouter的StatefulShellRoute中实现SlideTransition时出现空白页问题求助

嘿,我看了你的代码,发现几个可能导致空白页的关键点,咱们一步步来解决:

问题分析

  1. 动画控制器从未被触发:你初始化了AnimationController但从来没调用_controller.forward()启动动画,而且切换tab时也没重置或重启动画,导致所有页面都停留在初始偏移位置(屏幕左侧外),自然显示空白。
  2. 动画逻辑未区分页面状态:所有分支导航页都用了相同的从左滑入动画,但实际上切换时应该是当前页滑入、上一页滑出,其他页面保持在屏幕外,你的逻辑没有处理这种区分。
  3. 未监听tab切换事件:当currentIndex变化时,你的组件没有响应这个变化来触发动画更新。

修复后的完整代码

class AnimatedBranchContainer extends StatefulWidget {
  const AnimatedBranchContainer({
    super.key,
    required this.currentIndex,
    required this.children,
  });

  final int currentIndex;
  final List<Widget> children;

  @override
  State<AnimatedBranchContainer> createState() => _AnimatedBranchContainerState();
}

class _AnimatedBranchContainerState extends State<AnimatedBranchContainer>
    with SingleTickerProviderStateMixin {
  late final AnimationController _controller = AnimationController(
    duration: const Duration(milliseconds: 300), // 把时长改短一点更符合常规体验
    vsync: this,
  );
  int? _previousIndex; // 记录上一次的索引,用于处理滑出动画

  @override
  void initState() {
    super.initState();
    _previousIndex = widget.currentIndex;
    // 首次加载时当前页面不需要动画,直接显示
  }

  @override
  void didUpdateWidget(covariant AnimatedBranchContainer oldWidget) {
    super.didUpdateWidget(oldWidget);
    // 当索引变化时,重置并启动动画
    if (widget.currentIndex != oldWidget.currentIndex) {
      _previousIndex = oldWidget.currentIndex;
      _controller.reset();
      _controller.forward();
    }
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: widget.children.asMap().entries.map((entry) {
        final index = entry.key;
        final navigator = entry.value;
        final isCurrent = index == widget.currentIndex;
        final isPrevious = index == _previousIndex;

        // 为不同状态的页面设置动画偏移
        Offset beginOffset;
        Offset endOffset;

        if (isCurrent) {
          // 当前页面从左侧滑入
          beginOffset = const Offset(-1, 0);
          endOffset = Offset.zero;
        } else if (isPrevious) {
          // 上一个页面滑出到右侧
          beginOffset = Offset.zero;
          endOffset = const Offset(1, 0);
        } else {
          // 其他页面保持在屏幕左侧外,不显示
          beginOffset = endOffset = const Offset(-1, 0);
        }

        final animation = Tween<Offset>(
          begin: beginOffset,
          end: endOffset,
        ).animate(CurvedAnimation(
          parent: _controller,
          curve: Curves.easeInOut, // 换成更自然的曲线,elasticIn可能会有奇怪的回弹
        ));

        return SlideTransition(
          position: animation,
          child: _branchNavigatorWrapper(index, navigator),
        );
      }).toList(),
    );
  }

  Widget _branchNavigatorWrapper(int index, Widget navigator) => IgnorePointer(
        ignoring: index != widget.currentIndex,
        child: TickerMode(
          enabled: index == widget.currentIndex,
          child: navigator,
        ),
      );
}

关键修改说明

  • 添加了_previousIndex来跟踪上一次的选中索引,实现切换时的双向动画(新页面滑入,旧页面滑出)
  • 在didUpdateWidget中监听索引变化,每次切换时重置并启动动画控制器
  • 为不同状态的页面(当前、上一个、其他)设置了不同的偏移逻辑,确保只有当前页面在屏幕内,其他页面处于屏幕外
  • 调整了动画时长和曲线,让过渡更符合常规APP的交互体验
  • 首次加载时不触发动画,避免初始页面从左侧滑入的突兀感

另外还要确保你在StatefulShellRoute中正确传入了这个容器:

StatefulShellRoute.indexedStack(
  builder: (context, state, navigationShell) {
    return AnimatedBranchContainer(
      currentIndex: navigationShell.currentIndex,
      children: navigationShell.branches.map((branch) => branch.navigator).toList(),
    );
  },
  branches: [
    // 你的分支路由配置
  ],
);

试试这个修改,应该就能解决空白页的问题了!

备注:内容来源于stack exchange,提问作者Daniel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 12:54:33