持久化底部导航栏
Flutter 一个高度可定制的持久化底部导航栏
注意:从2.0.0之前的版本迁移的用户应查看最新的Readme和说明,因为2.0.0更新中引入了许多重大更改。

样式
| 样式15 | 样式16 |
|---|---|
![]() |
![]() |
| 样式1 | 样式9 |
|---|---|
![]() |
![]() |
| 样式7 | 样式10 |
|---|---|
![]() |
![]() |
| 样式12 | 样式13 |
|---|---|
![]() |
![]() |
| 样式3 | 样式6 |
|---|---|
![]() |
![]() |
| Neumorphic | 无副标题的拟态风格 |
|---|---|
![]() |
![]() |
注意:这些不包含所有样式变体
功能
- 高度可定制的
持久化底部导航栏。 - 能够推送新屏幕,无论是否带有底部导航栏。
- 底部导航栏的20种样式。
- 包括用于推送带有或不带有底部导航栏的新屏幕的函数,即 `pushNewScreen()` 和 `pushNewScreenWithRouteSettings()`。
- 基于 Flutter 的 Cupertino (iOS) 底部导航栏。
- 可以为特定标签页设置为
半透明。 - 自定义导航栏样式。点击此处获取更多信息。
- 处理 Android 的硬件/软件返回按钮。
入门
在您的 Flutter 项目中添加依赖项
dependencies:
persistent_bottom_nav_bar: any
导入包
import 'package:persistent_bottom_nav_bar/persistent-tab-view.dart';
持久化底部导航栏使用PersistentTabController作为其控制器。以下是声明它的方法:
PersistentTabController _controller;
_controller = PersistentTabController(initialIndex: 0);
然后声明的主要小部件是PersistentTabView。注意:此小部件包含 SCAFFOLD(基于CupertinoTabScaffold),因此无需声明它。以下是用于演示的示例:
class MyApp extends StatelessWidget {
const MyApp({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return PersistentTabView(
context,
controller: _controller,
screens: _buildScreens(),
items: _navBarsItems(),
confineInSafeArea: true,
backgroundColor: Colors.white, // Default is Colors.white.
handleAndroidBackButtonPress: true, // Default is true.
resizeToAvoidBottomInset: true, // This needs to be true if you want to move up the screen when keyboard appears. Default is true.
stateManagement: true, // Default is true.
hideNavigationBarWhenKeyboardShows: true, // Recommended to set 'resizeToAvoidBottomInset' as true while using this argument. Default is true.
decoration: NavBarDecoration(
borderRadius: BorderRadius.circular(10.0),
colorBehindNavBar: Colors.white,
),
popAllScreensOnTapOfSelectedTab: true,
popActionScreens: PopActionScreensType.all,
itemAnimationProperties: ItemAnimationProperties( // Navigation Bar's items animation properties.
duration: Duration(milliseconds: 200),
curve: Curves.ease,
),
screenTransitionAnimation: ScreenTransitionAnimation( // Screen transition animation on change of selected tab.
animateTabTransition: true,
curve: Curves.ease,
duration: Duration(milliseconds: 200),
),
navBarStyle: NavBarStyle.style1, // Choose the nav bar style with this property.
);
}
}
List<Widget> _buildScreens() {
return [
MainScreen(),
SettingsScreen()
];
}
List<PersistentBottomNavBarItem> _navBarsItems() {
return [
PersistentBottomNavBarItem(
icon: Icon(CupertinoIcons.home),
title: ("Home"),
activeColorPrimary: CupertinoColors.activeBlue,
inactiveColorPrimary: CupertinoColors.systemGrey,
),
PersistentBottomNavBarItem(
icon: Icon(CupertinoIcons.settings),
title: ("Settings"),
activeColorPrimary: CupertinoColors.activeBlue,
inactiveColorPrimary: CupertinoColors.systemGrey,
),
];
}
导航器函数
注意:您仍然可以使用常规的 Navigator 函数,如 'pushNamed',但请确保检查 `PersistentBottomNavBarItem` 中的 `routeAndNavigatorSettings` 参数以获取路由设置和其他一些与导航器相关的属性。
要推送新屏幕,请使用以下函数来控制特定屏幕上底部导航栏的可见性。您可以使用自己的逻辑来实现平台特定的行为。解决方案之一是使用 `withNavBar` 属性并根据平台切换它。
在平台特定行为中,在推送新屏幕时,在Android上,它会推送屏幕而不带底部导航栏,但在iOS上,它会保留底部导航栏。这是每个平台指定的默认行为。
pushNewScreen(
context,
screen: MainScreen(),
withNavBar: true, // OPTIONAL VALUE. True by default.
pageTransitionAnimation: PageTransitionAnimation.cupertino,
);
pushNewScreenWithRouteSettings(
context,
settings: RouteSettings(name: MainScreen.routeName),
screen: MainScreen(),
withNavBar: true,
pageTransitionAnimation: PageTransitionAnimation.cupertino,
);
如果您正在推送新的模态屏幕,请使用以下函数:
pushDynamicScreen(
context,
screen: HomeModalScreen(),
withNavBar: true,
);
一些有用的提示
-
弹出到给定标签页的导航图中的任何屏幕
Navigator.of(context).popUntil((route) { return route.settings.name == "ScreenToPopBackTo"; }); -
弹出回给定标签页的导航图中的第一个屏幕
Navigator.of(context).popUntil(ModalRoute.withName("/"));Navigator.of(context).pushAndRemoveUntil( CupertinoPageRoute( builder: (BuildContext context) { return FirstScreen(); }, ), (_) => false, ); -
要在导航栏之上推送底部工作表,请使用 `showModalBottomScreen` 并将其 `useRootNavigator` 属性设置为 `true`。有关说明,请参阅示例项目。
自定义导航栏样式
如果您想拥有自己的导航栏样式,请遵循以下步骤:
-
声明您的自定义小部件。请记住,您将不得不自己处理 `onSelectedItem` 函数和 `selectedIndex` 整数以保持完整的功能。此外,请注意,您可以定义自己的导航栏项模型,而不是使用提供的 `PersistentBottomNavBarItem`。有关更多信息,请参阅此示例:
class CustomNavBarWidget extends StatelessWidget { final int selectedIndex; final List<PersistentBottomNavBarItem> items; // NOTE: You CAN declare your own model here instead of `PersistentBottomNavBarItem`. final ValueChanged<int> onItemSelected; CustomNavBarWidget( {Key key, this.selectedIndex, @required this.items, this.onItemSelected,}); Widget _buildItem( PersistentBottomNavBarItem item, bool isSelected) { return Container( alignment: Alignment.center, height: 60.0, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: <Widget>[ Flexible( child: IconTheme( data: IconThemeData( size: 26.0, color: isSelected ? (item.activeColorSecondary == null ? item.activeColorPrimary : item.activeColorSecondary) : item.inactiveColorPrimary == null ? item.activeColorPrimary : item.inactiveColorPrimary), child: item.icon, ), ), Padding( padding: const EdgeInsets.only(top: 5.0), child: Material( type: MaterialType.transparency, child: FittedBox( child: Text( item.title, style: TextStyle( color: isSelected ? (item.activeColorSecondary == null ? item.activeColorPrimary : item.activeColorSecondary) : item.inactiveColorPrimary, fontWeight: FontWeight.w400, fontSize: 12.0), )), ), ) ], ), ); } @override Widget build(BuildContext context) { return Container( color: Colors.white, child: Container( width: double.infinity, height: 60.0, child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: items.map((item) { int index = items.indexOf(item); return Flexible( child: GestureDetector( onTap: () { this.onItemSelected(index); }, child: _buildItem( item, selectedIndex == index), ), ); }).toList(), ), ), ); } } -
在主要的 `PersistentTabView` 小部件中,将 `navBarStyle` 属性设置为 `NavBarStyle.custom`,并将您刚刚创建的自定义小部件传递到 `customWidget` 属性中,如下所示:
class MyApp extends StatelessWidget { const MyApp({Key key}) : super(key: key); @override Widget build(BuildContext context) { return PersistentTabView.custom( context, controller: _controller, itemCount: items.length, // This is required in case of custom style! Pass the number of items for the nav bar. screens: _buildScreens(), confineInSafeArea: true, handleAndroidBackButtonPress: true, onItemSelected: (int) { setState(() {}); // This is required to update the nav bar if Android back button is pressed }, customWidget: CustomNavBarWidget( // Your custom widget goes here items: _navBarsItems(), selectedIndex: _controller.index, onItemSelected: (index) { setState(() { _controller.index = index; // NOTE: THIS IS CRITICAL!! Don't miss it! }); }, ), ); } }注意:在 `customWidget` 的 `onSelected` 函数中,不要忘记更改控制器的索引。 -
完成!正如我们所见,`iconSize`、`items` 等其他属性在这里不是必需的,因此您可以跳过这些属性。要控制屏幕的底部填充,请使用 `bottomScreenPadding`。如果您设置了过多的 `bottomScreenPadding` 但自定义小部件中的高度较少,或者反之亦然,可能会出现布局问题。
为了更好地理解,请参阅官方 git 仓库中的示例项目。











