Flutter中使用BottomNavigationBar时如何实现页面过渡动画
要给BottomNavigationBar的页面切换加上过渡动画,最直接的方案是用AnimatedSwitcher包裹页面区域,配合自定义动画逻辑实现。以下是具体实现步骤:
核心实现代码
import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'BottomNav Animation', theme: ThemeData(primarySwatch: Colors.blue), home: const MainPage(), ); } } class MainPage extends StatefulWidget { const MainPage({super.key}); @override State<MainPage> createState() => _MainPageState(); } class _MainPageState extends State<MainPage> { int _currentIndex = 0; bool _isForward = true; // 判断页面切换方向,用于自定义动画方向 // 页面列表,替换成你的实际页面 final List<Widget> _pages = const [ HomePage(), ExplorePage(), ProfilePage(), ]; @override Widget build(BuildContext context) { return Scaffold( body: AnimatedSwitcher( duration: const Duration(milliseconds: 300), // 动画时长 transitionBuilder: (Widget child, Animation<double> animation) { // 自定义淡入+滑动动画,可替换成其他动画 return FadeTransition( opacity: animation, child: SlideTransition( position: Tween<Offset>( // 根据切换方向决定滑入位置 begin: _isForward ? const Offset(0.2, 0) : const Offset(-0.2, 0), end: Offset.zero, ).animate(animation), child: child, ), ); }, // 必须给页面设置唯一Key,让AnimatedSwitcher识别页面变化 child: _pages[_currentIndex], key: ValueKey<int>(_currentIndex), ), bottomNavigationBar: BottomNavigationBar( currentIndex: _currentIndex, onTap: (index) { setState(() { _isForward = index > _currentIndex; _currentIndex = index; }); }, items: const [ BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'), BottomNavigationBarItem(icon: Icon(Icons.explore), label: '发现'), BottomNavigationBarItem(icon: Icon(Icons.person), label: '我的'), ], ), ); } } // 示例页面,可替换成你的实际页面 class HomePage extends StatefulWidget { const HomePage({super.key}); @override State<HomePage> createState() => _HomePageState(); } class _HomePageState extends State<HomePage> with AutomaticKeepAliveClientMixin { // 开启状态保持,切换页面后保留页面状态 @override bool get wantKeepAlive => true; @override Widget build(BuildContext context) { super.build(context); // 必须调用super.build return const Center(child: Text('首页')); } } class ExplorePage extends StatelessWidget { const ExplorePage({super.key}); @override Widget build(BuildContext context) { return const Center(child: Text('发现')); } } class ProfilePage extends StatelessWidget { const ProfilePage({super.key}); @override Widget build(BuildContext context) { return const Center(child: Text('我的')); } }
可选动画样式
如果不想用滑动+淡入,也可以换成其他动画:
- 缩放动画
transitionBuilder: (Widget child, Animation<double> animation) { return ScaleTransition( scale: animation, child: child, ); },
- 仅淡入淡出
transitionBuilder: (Widget child, Animation<double> animation) { return FadeTransition(opacity: animation, child: child); },
注意事项
- 必须给每个页面设置唯一
Key,ValueKey<int>(_currentIndex)是最简单的方式,确保AnimatedSwitcher能检测到页面变化 - 如果需要保留页面状态(比如输入框内容、滚动位置),给页面添加
AutomaticKeepAliveClientMixin并实现wantKeepAlive返回true - 动画时长可根据需求调整,300ms是比较符合用户习惯的时长
内容的提问来源于stack exchange,提问作者zubayer bin montasir
相关产品推荐
相关产品推荐

