在Flutter中,使用BottomNavigationBar
和IndexedStack
可以实现一个功能完整的底部导航栏。BottomNavigationBar
用于显示底部的导航按钮,而IndexedStack
则用于管理页面的切换,确保每个页面的状态得以保留(即页面不会因为切换而重新构建)。
以下是实现的完整代码示例及详细解释:
代码实现
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
title: 'BottomNavigationBar + IndexedStack',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const BottomNavigationExample(),
);
}
}
class BottomNavigationExample extends StatefulWidget {
const BottomNavigationExample({Key? key}) : super(key: key);
_BottomNavigationExampleState createState() =>
_BottomNavigationExampleState();
}
class _BottomNavigationExampleState extends State<BottomNavigationExample> {
// 当前选中的索引
int _currentIndex = 0;
// 页面列表
final List<Widget> _pages = [
const HomePage(),
const SearchPage(),
const ProfilePage(),
];
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: _currentIndex, // 根据当前索引显示对应的页面
children: _pages, // 所有页面
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex, // 当前选中的索引
onTap: (int index) {
setState(() {
_currentIndex = index; // 更新索引
});
},
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
label: 'Search',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Profile',
),
],
),
);
}
}
// 首页
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Icon(Icons.home, size: 50, color: Colors.blue),
Text('Home Page', style: TextStyle(fontSize: 24)),
],
),
);
}
}
// 搜索页
class SearchPage extends StatelessWidget {
const SearchPage({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Icon(Icons.search, size: 50, color: Colors.green),
Text('Search Page', style: TextStyle(fontSize: 24)),
],
),
);
}
}
// 个人中心页
class ProfilePage extends StatelessWidget {
const ProfilePage({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Icon(Icons.person, size: 50, color: Colors.purple),
Text('Profile Page', style: TextStyle(fontSize: 24)),
],
),
);
}
}
代码解析
IndexedStack
的作用IndexedStack
是一个特殊的堆栈组件,它会根据index
属性显示其子组件列表中的某一个子组件。- 与普通的
Stack
不同,IndexedStack
只渲染当前索引对应的子组件,但所有子组件的状态都会被保留。因此,当用户切换回某个页面时,该页面的状态不会丢失。
BottomNavigationBar
的配置currentIndex
: 表示当前选中的导航项索引。onTap
: 当用户点击导航项时触发的回调函数,用于更新_currentIndex
。items
: 定义了导航栏的每一项,包括图标和标签。
页面状态保留
- 使用
IndexedStack
管理页面,而不是直接使用PageView
或Navigator
,可以确保每个页面的状态在切换时不会被销毁。这对于需要保存滚动位置、输入框内容等场景非常有用。
- 使用
页面切换逻辑
- 用户点击底部导航栏时,
onTap
回调会触发setState
,更新_currentIndex
,从而让IndexedStack
显示对应索引的页面。
- 用户点击底部导航栏时,
运行效果
- 应用启动后,默认显示首页(
HomePage
)。 - 点击底部导航栏的不同选项,页面会切换到对应的页面(
SearchPage
或ProfilePage
)。 - 切换回之前的页面时,页面状态保持不变。
注意事项
性能优化
如果页面较多或页面内容较复杂,可以考虑懒加载页面(例如通过AutomaticKeepAliveClientMixin
实现),以减少内存占用。动态数据更新
如果页面需要动态更新数据,可以在每个页面中使用StatefulWidget
,并通过setState
或其他状态管理工具(如Provider
、Riverpod
)来更新页面内容。更多自定义
BottomNavigationBar
支持更多自定义样式,例如背景颜色、字体大小、图标大小等,可以根据需求调整。