Flutter 底部导航与顶部选项卡实战:状态保持与鸿蒙适配
一个复杂的 App 通常包含多个功能模块。打开微信、淘宝或抖音,你会发现它们都有一个共同的架构:底部有 4-5 个图标切换主页面,顶部还有'关注/推荐'这样的分类切换。这就是移动端最经典的 '底 Tab + 顶 Tab' 双导航架构。
一、底部导航:App 的根基
Flutter 提供了两种主流的底部导航组件,选择哪种取决于你的设计风格和版本需求。
1.1 经典款:BottomNavigationBar
这是最传统、兼容性最好的组件,适合大多数场景。
class MainPage extends StatefulWidget {
const MainPage({super.key});
@override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
int _currentIndex = 0;
// 页面列表
final List<Widget> _pages = [
const HomePage(),
const CategoryPage(),
const ProfilePage(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: _pages[_currentIndex], // 根据下标显示对应页面
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) => setState(() => _currentIndex = index),
type: BottomNavigationBarType.fixed, // 超过 3 个 item 必须设置 fixed
selectedItemColor: Colors.blue,
unselectedItemColor: Colors.grey,
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
BottomNavigationBarItem(icon: Icon(Icons.category), label: '发现'),
BottomNavigationBarItem(icon: Icon(Icons.person), label: '我的'),
],
),
);
}
}
1.2 新潮款:NavigationBar (M3)
Material 3 引入了更高、更圆润的 NavigationBar。它自带点击涟漪和胶囊状的指示器,视觉效果更好,但需要注意兼容旧版本系统。
NavigationBar(
selectedIndex: _currentIndex,
onDestinationSelected: (index) => setState(() => _currentIndex = index),
destinations: const [
NavigationDestination(
icon: Icon(Icons.home_outlined),
selectedIcon: Icon(Icons.home),
label: '首页',
),
NavigationDestination(
icon: Icon(Icons.explore_outlined),
selectedIcon: Icon(Icons.explore),
label: '发现',
),
// ...更多项
],
)
二、顶部选项卡:TabBar
TabBar 通常用于在同一个主栏目下,切换不同的子分类(如新闻 App 的频道)。它必须配合 TabController 使用。
2.1 DefaultTabController (推荐)
最简单的方法是在父级包裹一个 DefaultTabController,这样我们就不用手动管理 Controller 了,代码更简洁。
class NewsPage extends StatelessWidget {
const NewsPage({super.key});
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3, // Tab 数量
child: Scaffold(
appBar: AppBar(
title: const Text('资讯'),
bottom: const TabBar(
tabs: [
Tab(text: '推荐'),
Tab(text: '科技'),
Tab(text: '体育'),
],
),
),
body: const TabBarView(
children: [
Center(child: Text('推荐内容列表')),
Center(child: Text('科技内容列表')),
Center(child: Text('体育内容列表')),
],
),
),
);
}
}


