Flutter中切换home内容时如何添加页面切换动画?
实现固定顶部栏+页面侧滑切换动画的Flutter方案
方案一:嵌套Navigator(贴合原生Navigator侧滑体验)
这种方式能完美保留顶部固定栏,同时让下方内容拥有和原生Navigator一致的侧滑切换动画,核心是在顶部栏下方嵌套一个独立的Navigator来管理页面栈。
修改后的代码示例:
class Segunda extends StatefulWidget { @override _SegundaState createState() => _SegundaState(); // 修正State命名规范 } class _SegundaState extends State<Segunda> { late Size size; late double heightA, widthA; @override void didChangeDependencies() { super.didChangeDependencies(); size = MediaQuery.of(context).size; heightA = size.height; widthA = size.width; // 避免在build中调用setState引发无限重绘,将尺寸计算移至此处 } @override Widget build(BuildContext context) { return Scaffold( body: Column( children: [ // 固定顶部蓝色栏 SizedBox( width: widthA, height: heightA * 0.1, child: Container( color: Colors.blue, child: ElevatedButton( onPressed: () { // 通过嵌套Navigator触发侧滑跳转动画 Navigator.of(_navigatorKey.currentContext!).push( MaterialPageRoute( builder: (context) => Segunda3(), ), ); }, child: const Text("Change"), ), ), ), // 嵌套Navigator管理下方页面 SizedBox( width: widthA, height: heightA * 0.9, child: Navigator( key: _navigatorKey, initialRoute: '/', onGenerateRoute: (settings) { if (settings.name == '/') { return MaterialPageRoute(builder: (_) => Segunda2()); } return MaterialPageRoute(builder: (_) => Segunda3()); }, ), ), ], ), ); } final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>(); }
关键说明:
- 替换原代码中的
MaterialApp为嵌套Navigator,通过GlobalKey获取嵌套Navigator上下文,方便外部按钮触发跳转 - 顶部栏作为父布局的固定部分,不会被嵌套Navigator的页面替换
- 使用
MaterialPageRoute自带原生侧滑入栈动画,也可改用CupertinoPageRoute实现iOS风格侧滑
方案二:PageView(适合简单页面切换)
如果页面数量不多、不需要完整路由栈管理,用PageView可快速实现左右滑动切换,同时支持按钮手动控制切换动画。
代码示例:
class Segunda extends StatefulWidget { @override _SegundaState createState() => _SegundaState(); } class _SegundaState extends State<Segunda> { late Size size; late double heightA, widthA; final PageController _pageController = PageController(initialPage: 0); int _currentPage = 0; @override void didChangeDependencies() { super.didChangeDependencies(); size = MediaQuery.of(context).size; heightA = size.height; widthA = size.width; } @override Widget build(BuildContext context) { return Scaffold( body: Column( children: [ SizedBox( width: widthA, height: heightA * 0.1, child: Container( color: Colors.blue, child: ElevatedButton( onPressed: () { if (_currentPage < 1) { // 假设仅两个页面 _pageController.nextPage( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, ); } }, child: const Text("Change"), ), ), ), SizedBox( width: widthA, height: heightA * 0.9, child: PageView( controller: _pageController, onPageChanged: (index) { setState(() => _currentPage = index); }, children: const [ Segunda2(), Segunda3(), ], ), ), ], ), ); } }
关键说明:
- PageView自带左右滑动手势,也可通过
PageController的nextPage/previousPage方法手动触发切换动画 - 适合页面数量少、无需路由栈(如返回上一页)的场景
方案三:AnimatedSwitcher+SlideTransition(自定义动画)
如果需要完全自定义切换动画(如调整滑动方向、速度),可使用AnimatedSwitcher配合SlideTransition实现:
代码示例:
class Segunda extends StatefulWidget { @override _SegundaState createState() => _SegundaState(); } class _SegundaState extends State<Segunda> { late Size size; late double heightA, widthA; int _currentIndex = 0; final List<Widget> _pages = const [Segunda2(), Segunda3()]; @override void didChangeDependencies() { super.didChangeDependencies(); size = MediaQuery.of(context).size; heightA = size.height; widthA = size.width; } @override Widget build(BuildContext context) { return Scaffold( body: Column( children: [ SizedBox( width: widthA, height: heightA * 0.1, child: Container( color: Colors.blue, child: ElevatedButton( onPressed: () { setState(() { _currentIndex = (_currentIndex + 1) % _pages.length; }); }, child: const Text("Change"), ), ), ), SizedBox( width: widthA, height: heightA * 0.9, child: AnimatedSwitcher( duration: const Duration(milliseconds: 300), transitionBuilder: (child, animation) { // 自定义从左侧滑入的动画 return SlideTransition( position: Tween<Offset>( begin: const Offset(-1.0, 0.0), end: Offset.zero, ).animate(animation), child: child, ); }, child: _pages[_currentIndex], key: ValueKey<int>(_currentIndex), // 必须给子项唯一key才能触发动画 ), ), ], ), ); } }
关键说明:
- 必须为每个子Widget设置唯一
ValueKey,AnimatedSwitcher才能识别页面变化并触发动画 - 修改
Tween<Offset>的begin值可自定义动画方向(如从右侧滑入改为Offset(1.0, 0.0))
原代码小问题修正
- State类命名不符合规范,应改为
_SegundaState(私有类),避免与其他组件混淆 - 不要在
build方法中调用setState,会引发无限重绘循环,屏幕尺寸计算应移至didChangeDependencies中
内容的提问来源于stack exchange,提问作者Anxo Gómez
相关产品推荐
相关产品推荐

