jb3rndt/PersistentBottomNavBarV2
A highly customizable persistent bottom navigation bar for Flutter
前往 github 下載Persistent Bottom Navigation Bar Version 2
A highly customizable bottom navigation bar for Flutter. It is shipped with 17 prebuilt styles you can choose from (see below), but can also be used with your very own style without sacrificing any features. View on pub.dev
NOTE: This package is a continuation of persistent_bottom_nav_bar.
[!IMPORTANT]
If you are migrating from Version 4.x.x to Version 5 read this MIGRATION GUIDE.
Table of Contents
Styles
Style1 | Style2 | Style3 |
---|---|---|
![]() |
![]() |
![]() |
Style4 | Style5 | Style6 |
---|---|---|
![]() |
![]() |
![]() |
Style7 | Style8 | Style9 |
---|---|---|
![]() |
![]() |
![]() |
Style10 | Style11 | Style12 |
---|---|---|
![]() |
![]() |
![]() |
Style13 | Style14 | Style15 |
---|---|---|
![]() |
![]() |
![]() |
Style16 | Neumorphic |
---|---|
![]() |
![]() |
Note: These do not include all style variations
Features
- New pages can be pushed with or without showing the navigation bar.
- 17 prebuilt navigation bar styles ready to use.
- Each style is fully customizable (see below)
- Supports custom navigation bars
- Persistent Tabs -> Navigation Stack is not discarded when switching to another tab
- Supports transparency and blur effects
- Handles hardware/software Android back button.
- Supports go_router to make use of flutters Router API
Getting Started
1. Install the package
Follow the install instructions.
2. Import the package
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
3. Use the PersistentTabView
The PersistentTabView
is your top level container that will hold both your navigation bar and all the pages (just like a Scaffold
). Thats why it is not recommended, to wrap the PersistentTabView
inside a Scaffold.body
, because it does all of that for you. So just create the config for each tab and insert the PersistentTabView
like this and you are good to go:
import 'package:flutter/material.dart';
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
void main() => runApp(PersistenBottomNavBarDemo());
class PersistenBottomNavBarDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Persistent Bottom Navigation Bar Demo',
home: PersistentTabView(
tabs: [
PersistentTabConfig(
screen: YourFirstScreen(),
item: ItemConfig(
icon: Icon(Icons.home),
title: "Home",
),
),
PersistentTabConfig(
screen: YourSecondScreen(),
item: ItemConfig(
icon: Icon(Icons.message),
title: "Messages",
),
),
PersistentTabConfig(
screen: YourThirdScreen(),
item: ItemConfig(
icon: Icon(Icons.settings),
title: "Settings",
),
),
],
navBarBuilder: (navBarConfig) => Style1BottomNavBar(
navBarConfig: navBarConfig,
),
),
);
}
}
Styling
You can customize the Navigation Bar with all the parameters, each style allows. Every style allows you to pass an instance of NavBarDecoration
. This inherits from BoxDecoration
and thus offers everything the BoxDecoration
is capable of. As an example, you could set a different border radius by passing BorderRadius.circular(8)
to the NavBarDecoration.border
. Styles that include animations also allow you to adjust the timings and interpolation curves of the animation.
Using a custom Navigation Bar
You can replace the Style1BottomNavBar
widget with your own custom widget. As you can see, the navBarBuilder
gives you a navBarConfig
, which should be everything you need to build your custom navigation bar. Here is an example of a custom navigation bar widget:
class CustomNavBar extends StatelessWidget {
final NavBarConfig navBarConfig;
final NavBarDecoration navBarDecoration;
CustomNavBar({
Key key,
@required this.navBarConfig,
this.navBarDecoration = const NavBarDecoration(),
}) : super(key: key);
Widget _buildItem(ItemConfig item, bool isSelected) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: IconTheme(
data: IconThemeData(
size: item.iconSize,
color: isSelected
? item.activeColorPrimary
: item.inactiveColorPrimary),
child: isSelected ? item.icon : item.inactiveIcon,
),
),
Padding(
padding: const EdgeInsets.only(top: 15.0),
child: Material(
type: MaterialType.transparency,
child: FittedBox(
child: Text(
item.title,
style: item.textStyle.apply(
color: isSelected
? item.activeColorPrimary
: item.inactiveColorPrimary,
),
),
),
),
),
],
);
}
@override
Widget build(BuildContext context) {
return DecoratedNavBar(
decoration: this.navBarDecoration,
filter: this.navBarConfig.selectedItem.filter,
opacity: this.navBarConfig.selectedItem.opacity,
height: this.navBarConfig.navBarHeight,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: this.navBarConfig.items.map((item) {
int index = this.navBarConfig.items.indexOf(item);
return Expanded(
child: InkWell(
onTap: () {
this.navBarConfig.onItemSelected(index); // This is the most important part. Without this, nothing would happen if you tap on an item.
},
child: _buildItem(
item,
this.navBarConfig.selectedIndex == index,
),
),
);
}).toList(),
),
);
}
}
In your PersistentTabView
, you can use it just like the predefined style:
PersistentTabView(
tabs: ...,
navBarBuilder: (navBarConfig) => CustomNavBar(
navBarConfig: navBarConfig,
),
),
The most important thing is that you call the navBarConfig.onItemSelected
function with the index of the tapped item, otherwise the PersistentTabView
will not react to anything.
You dont need to use either the DecoratedNavBar
widget, nor the NavBarDecoration
, it is just a helper for you. You can do whatever you want in that custom navigation bar widget, as long as you remember to invoke the onItemSelected
callback.
Controlling the Navigation Bar programmatically
Internally, the PersistentTabView
uses a PersistentTabController
. So you can pass a controller to the PersistentTabView
to use it later for changing the tab programmatically:
PersistentTabController _controller = PersistentTabController(initialIndex: 0);
PersistentTabView(
controller: _controller,
...
);
_controller.jumpToTab(2);
// Navigate to the previously selected Table
_controller.jumpToPreviousTab();
Navigation
Each of your Tabs will get its own Navigator, so they dont interfere with eachother. That means there will now be a difference between calling Navigator.of(context).push()
(which will push a new screen inside the current tab) and Navigator.of(context, rootNavigator: true).push()
(which will push a new screen above the whole PersistentTabView
, ultimately hiding your navigation bar).
The package includes the following utility functions for expressive navigation.
pushScreen(
context,
screen: MainScreen(),
withNavBar: true/false,
);
pushWithNavBar(
context,
MaterialPageRoute(builder: (context) => ...)
);
pushWithoutNavBar(
context,
MaterialPageRoute(builder: (context) => ...)
);
By default, each of the tabs navigators will inherit all the settings of the root navigator. So every configuration you do to the named routes (etc.) of the root navigator, will work just the same in each tab. If you want specific settings for each navigator (like additional routes, NavigatorObservers
etc.), you can do so by passing a NavigatorConfig
to the respective PersistentTabConfig
.
The PersistentTabView
has the ability to remember the navigation stack for each tab, so when you switch back to it you will see the exact same content when you left. This behavior can be toggled with the PersistentTabView.stateManagement
parameter.
Router API
To utilize flutters Router API for navigation in combination with this package, go_router must be used. Follow the setup in the go_router documentation to get started with declarative routing. To integrate your Persistent Navigation Bar, you have to setup a StatefulShellRoute.indexedStack
as one of your routes, which will contain the PersistentTabView
. See the example for a full code example or the code snippet below:
- use
PersistentTabView.router
instead ofPersistentTabView
- pass the
navigationShell
to thePersistentTabView.router
(this will contain each tab view) - use
PersistentRouterTabConfig
instead ofPersistentTabConfig
(notice the missingscreen
argument because the screens are specified by the routes in eachStatefulShellBranch
)
StatefulShellRoute.indexedStack(
builder: (context, state, navigationShell) =>
PersistentTabView.router(
tabs: [
PersistentRouterTabConfig(
item: ItemConfig(
icon: const Icon(Icons.home),
title: "Home",
),
),
PersistentRouterTabConfig(
item: ItemConfig(
icon: const Icon(Icons.message),
title: "Messages",
),
),
PersistentRouterTabConfig(
item: ItemConfig(
icon: const Icon(Icons.settings),
title: "Settings",
),
),
],
navBarBuilder: (navBarConfig) => Style1BottomNavBar(
navBarConfig: navBarConfig,
),
navigationShell: navigationShell,
),
branches: [
// The route branch for the 1st Tab
StatefulShellBranch(
routes: <RouteBase>[
GoRoute(
path: "home",
builder: (context, state) => const MainScreen(
useRouter: true,
),
routes: [
GoRoute(
path: "detail",
builder: (context, state) => const MainScreen2(
useRouter: true,
),
),
],
),
],
),
// The route branch for 2nd Tab
StatefulShellBranch(
routes: <RouteBase>[
GoRoute(
path: "messages",
builder: (context, state) => const MainScreen(
useRouter: true,
),
),
],
),
// The route branch for 3rd Tab
StatefulShellBranch(
routes: <RouteBase>[
GoRoute(
path: "settings",
builder: (context, state) => const MainScreen(
useRouter: true,
),
),
],
),
],
),
Useful Tips
-
Try the interactive example project in the official git repo to get a better feeling for the package
-
Pop to any screen in the navigation graph for a given tab:
Navigator.of(context).popUntil((route) { return route.settings.name == "ScreenToPopBackTo"; });
-
Pop back to first screen in the navigation graph for a given tab:
Navigator.of(context).popUntil(ModalRoute.withName("/"));
In order for this to work, you will need your
PersistentNavBarItem
to be named ‘/’ like:PersistentBottomNavBarItem( title: ("Home"), routeAndNavigatorSettings: RouteAndNavigatorSettings(initialRoute: '/')),
Or instead of using a named Route you can also do this:
Navigator.of(context).pushAndRemoveUntil( CupertinoPageRoute( builder: (BuildContext context) { return FirstScreen(); }, ), (_) => false, );
-
To push bottom sheet on top of the Navigation Bar, use showModalBottomScreen and set it’s property
useRootNavigator
to true. See example project for an illustration. -
If you need e.g. notification counters on the icons in the navBar, you can use the badges package like so: (see Issue 11)
PersistentTabConfig( screen: ..., item: ItemConfig( icon: Badge( animationType: BadgeAnimationType.scale, badgeContent: UnreadIndicator(), child: const Icon( Icons.chat_rounded, ), ), title: "Chat", ), ),
![](/images/banner-woo.jpg)
與 jb3rndt/PersistentBottomNavBarV2 相關優秀專案推薦下載
AppFlowy
47859
AppFlowy is an open-source alternative to Notion. You are in charge of your data and customizations. Built with Flutter and Rust.
localsend
33797
An open-source cross-platform alternative to AirDrop
spotube
23772
🎧 Open source Spotify client that doesn't require Premium nor uses Electron! Available for both desktop & mobile!
revanced-manager
15035
💊 Application to use ReVanced on Android
gsy_github_app_flutter
14559
Flutter 超完整的开源项目,功能丰富,适合学习和日常使用。GSYGithubApp系列的优势:我们目前已经拥有Flutter、Weex、ReactNative、kotlin 四个版本。 功能齐全,项目框架内技术涉及面广,完成度高,持续维护,配套文章,适合全面学习,对比参考。跨平台的开源Github客户端App,更好的体验,更丰富的功能,旨在更好的日常管理和维护个人Github,提供更好更方便的驾车体验Σ( ̄。 ̄ノ)ノ。同款Weex版本 : https://github.com/CarGuo/GSYGithubAppWeex 、同款React Native版本 : https://github.com/CarGuo/GSYGithubApp 、原生 kotlin 版本 https://github.com/CarGuo/GSYGithubAppKotlin
dio
12249
A powerful HTTP client for Dart and Flutter, which supports global settings, Interceptors, FormData, aborting and canceling a request, files uploading and downloading, requests timeout, custom adapters, etc.
gopeed
11832
A modern download manager that supports all platforms. Built with Golang and Flutter.
bloc
11441
A predictable state management library that helps implement the BLoC design pattern
getx
9899
Open screens/snackbars/dialogs/bottomSheets without context, manage states and inject dependencies easily with Get.
flame
8846
A Flutter based game engine.
flutter_deer
7595
🦌 Flutter 练习项目(包括集成测试、可访问性测试)。内含完整UI设计图,更贴近真实项目的练习。Flutter practice project (including integration testing and accessibility testing). Contains complete UI design drawings for a more realistic practice project.
fish-redux
7343
An assembled flutter application framework.
hiddify-next
7232
Multi-platform auto-proxy client, supporting Sing-box, X-ray, TUIC, Hysteria, Reality, Trojan, SSH etc. It’s an open-source, secure and ad-free.
ente
6686
Fully open source, End to End Encrypted alternative to Google Photos and Apple Photos
fl_chart
6451
FL Chart is a highly customizable Flutter chart library that supports Line Chart, Bar Chart, Pie Chart, Scatter Chart, and Radar Chart.
pixez-flutter
6360
一个支持免代理直连及查看动图的第三方Pixiv flutter客户端
Flutter-Responsive-Admin-Panel-or-Dashboard
6355
Responsive Admin Panel or Dashboard using Flutter
aidea
6021
AIdea 是一款支持 GPT 以及国产大语言模型通义千问、文心一言等,支持 Stable Diffusion 文生图、图生图、 SDXL1.0、超分辨率、图片上色的全能型 APP。
riverpod
5828
A reactive caching and data-binding framework. Riverpod makes working with asynchronous code a breeze.
pikapika
5463
美观易用且无广告的漫画和游戏客户端,同时支持MacOS,Windows,Android,iOS。