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

Flutter底部导航栏页面动态可变水平退出过渡动画实现方法

解决方案

最佳实现方案(推荐)

你的需求刚好匹配Flutter原生PageView组件的能力,完全不需要自定义复杂的路由动画,代码量最少、性能最优:

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

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final PageController _pageController = PageController(initialPage: 1);
  int _currentIndex = 1;

  void _onTabTapped(int index) {
    // 点击底部导航时触发PageView的横向切换动画
    _pageController.animateToPage(
      index,
      duration: const Duration(milliseconds: 300),
      curve: Curves.easeInOut,
    );
    setState(() => _currentIndex = index);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // PageView天生是横向排列的子页面,自带横向滑动切换效果
      body: PageView(
        controller: _pageController,
        // 禁止手动滑动,仅通过底部导航控制切换,不需要可以删掉这行
        physics: const NeverScrollableScrollPhysics(),
        children: const [FirstPage(), SecondPage(), ThirdPage()],
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        onTap: _onTabTapped,
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: "第一页"),
          BottomNavigationBarItem(icon: Icon(Icons.business), label: "第二页"),
          BottomNavigationBarItem(icon: Icon(Icons.school), label: "第三页"),
        ],
      ),
    );
  }
}

如果需要保留每个页面的状态不被切换时重建,只需要给对应页面的State混入AutomaticKeepAliveClientMixin即可。

次选方案:AnimatedSwitcher自定义过渡

如果你的场景需要更灵活的自定义效果、或者tab数量不固定,可以用AnimatedSwitcher手动控制切换动画:

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

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int _currentIndex = 1;
  int _lastIndex = 1;
  final List<Widget> _pages = const [FirstPage(), SecondPage(), ThirdPage()];

  void _onTabTapped(int index) {
    setState(() {
      _lastIndex = _currentIndex;
      _currentIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: AnimatedSwitcher(
        duration: const Duration(milliseconds: 300),
        // 把新旧页面放在Stack里实现同时滑动效果
        layoutBuilder: (currentChild, previousChildren) {
          return Stack(
            fit: StackFit.expand,
            children: [
              ...previousChildren,
              if (currentChild != null) currentChild,
            ],
          );
        },
        transitionBuilder: (child, animation) {
          // 计算滑动方向:index变大新页面从右侧进,反之从左侧进
          final direction = _currentIndex - _lastIndex;
          return SlideTransition(
            position: Tween<Offset>(
              begin: Offset(direction > 0 ? 1 : -1, 0),
              end: Offset.zero,
            ).animate(CurvedAnimation(parent: animation, curve: Curves.easeInOut)),
            child: child,
          );
        },
        child: KeyedSubtree(
          key: ValueKey(_currentIndex),
          child: _pages[_currentIndex],
        ),
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        onTap: _onTabTapped,
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: "第一页"),
          BottomNavigationBarItem(icon: Icon(Icons.business), label: "第二页"),
          BottomNavigationBarItem(icon: Icon(Icons.school), label: "第三页"),
        ],
      ),
    );
  }
}

原路由方案的问题说明

你之前的代码错误点在于:两个SlideTransition嵌套后所有动画都作用在新页面上,完全没有控制旧页面的退场效果。而且Flutter原生路由体系中,旧页面的退场动画是在旧路由创建时定义的,无法动态根据后续跳转目标修改,实现你要的可变退场效果成本很高,完全不如前面两种方案高效。
如果你必须保留嵌套Navigator的独立路由栈能力,可以参考成熟的路由库(如GoRouter)的自定义过渡能力,在每次切换tab传入方向参数时,同步控制新旧页面的滑动偏移即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 13:18:03