如何使用Flutter实现如图所示的动画底部导航栏?
Flutter 实现动画底部导航栏
目标效果:点击导航项时,对应图标缩放变色,底部出现高亮背景,页面平滑切换。下面是自定义实现的完整方案:
核心实现代码
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: '动画底部导航栏', theme: ThemeData(primarySwatch: Colors.blue), home: const BottomNavDemo(), ); } } class BottomNavDemo extends StatefulWidget { const BottomNavDemo({super.key}); @override State<BottomNavDemo> createState() => _BottomNavDemoState(); } class _BottomNavDemoState extends State<BottomNavDemo> { int _selectedIndex = 0; final PageController _pageController = PageController(); // 导航项配置:图标、选中颜色、未选中颜色 final List<Map<String, dynamic>> _navItems = [ { 'icon': Icons.home, 'activeColor': Colors.blue, 'inactiveColor': Colors.grey }, { 'icon': Icons.search, 'activeColor': Colors.orange, 'inactiveColor': Colors.grey }, { 'icon': Icons.favorite, 'activeColor': Colors.red, 'inactiveColor': Colors.grey }, { 'icon': Icons.person, 'activeColor': Colors.purple, 'inactiveColor': Colors.grey }, ]; void _onItemTapped(int index) { setState(() => _selectedIndex = index); _pageController.animateToPage( index, duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, ); } @override Widget build(BuildContext context) { return Scaffold( body: PageView( controller: _pageController, onPageChanged: (index) => setState(() => _selectedIndex = index), children: const [ HomePage(), SearchPage(), FavoritePage(), ProfilePage(), ], ), bottomNavigationBar: Container( height: 65, decoration: BoxDecoration( color: Colors.white, boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.3), blurRadius: 10, offset: const Offset(0, -2), ), ], ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: List.generate(_navItems.length, (index) { bool isSelected = _selectedIndex == index; return GestureDetector( onTap: () => _onItemTapped(index), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // 图标缩放动画 AnimatedScale( scale: isSelected ? 1.2 : 1.0, duration: const Duration(milliseconds: 250), child: Icon( _navItems[index]['icon'], color: isSelected ? _navItems[index]['activeColor'] : _navItems[index]['inactiveColor'], size: 28, ), ), const SizedBox(height: 4), // 底部高亮背景动画 AnimatedContainer( width: isSelected ? 24 : 0, height: 4, decoration: BoxDecoration( color: _navItems[index]['activeColor'], borderRadius: BorderRadius.circular(2), ), duration: const Duration(milliseconds: 250), ), ], ), ); }), ), ), ); } } // 页面示例,保持状态用 AutomaticKeepAliveClientMixin 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); return const Center(child: Text('首页')); } } class SearchPage extends StatefulWidget { const SearchPage({super.key}); @override State<SearchPage> createState() => _SearchPageState(); } class _SearchPageState extends State<SearchPage> with AutomaticKeepAliveClientMixin { @override bool get wantKeepAlive => true; @override Widget build(BuildContext context) { super.build(context); return const Center(child: Text('搜索页')); } } class FavoritePage extends StatefulWidget { const FavoritePage({super.key}); @override State<FavoritePage> createState() => _FavoritePageState(); } class _FavoritePageState extends State<FavoritePage> with AutomaticKeepAliveClientMixin { @override bool get wantKeepAlive => true; @override Widget build(BuildContext context) { super.build(context); return const Center(child: Text('收藏页')); } } class ProfilePage extends StatefulWidget { const ProfilePage({super.key}); @override State<ProfilePage> createState() => _ProfilePageState(); } class _ProfilePageState extends State<ProfilePage> with AutomaticKeepAliveClientMixin { @override bool get wantKeepAlive => true; @override Widget build(BuildContext context) { super.build(context); return const Center(child: Text('我的页')); } }
关键实现点说明
- 动画控制:用
AnimatedScale实现图标缩放,AnimatedContainer实现底部高亮条的显示/隐藏过渡,250ms的动画时长保证流畅度。 - 页面切换:
PageController配合PageView实现页面平滑切换,同时监听onPageChanged同步导航栏选中状态。 - 状态保持:每个页面混入
AutomaticKeepAliveClientMixin并设置wantKeepAlive = true,避免切换页面时重新初始化。 - 自定义样式:底部导航栏用
Container包裹添加阴影提升层次感,导航项的颜色和图标可通过_navItems灵活配置。
内容的提问来源于stack exchange,提问作者licerer
相关产品推荐
相关产品推荐

