在GoRouter的StatefulShellRoute中实现SlideTransition时出现空白页问题求助
在GoRouter的StatefulShellRoute中实现SlideTransition时出现空白页问题求助
嘿,我看了你的代码,发现几个可能导致空白页的关键点,咱们一步步来解决:
问题分析
- 动画控制器从未被触发:你初始化了
AnimationController但从来没调用_controller.forward()启动动画,而且切换tab时也没重置或重启动画,导致所有页面都停留在初始偏移位置(屏幕左侧外),自然显示空白。 - 动画逻辑未区分页面状态:所有分支导航页都用了相同的从左滑入动画,但实际上切换时应该是当前页滑入、上一页滑出,其他页面保持在屏幕外,你的逻辑没有处理这种区分。
- 未监听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
相关产品推荐
相关产品推荐

