How to show/hide bottom navigation bar in some screen? - flutter

This blog shows how to preserve page state using PageStorage
This blog shows multiple navigation for each screen. The bottom nav bar here persist on all screens
But how do you make BottomNavigationBar show/hide in some screens? Say, login/register/successful screens? Tried to create a stateful widget for BottomNavigationBar but selectedIndex for the nav bar gets reset every time I use it. I can't seem to make it work.
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Text('app_screen'),
bottomNavigationBar: AppBottomNavigationController(),
);
}
}
app_bottom_navigation_controller.dart
class AppBottomNavigationController extends StatefulWidget {
#override
_AppBottomNavigationControllerState createState() =>
_AppBottomNavigationControllerState();
}
class _AppBottomNavigationControllerState
extends State<AppBottomNavigationController> {
int currentIndex = 0;
final List<Widget> pages = [];
final PageStorageBucket bucket = PageStorageBucket();
#override
void initState() {
pages.addAll([
HomeScreen(
key: PageStorageKey('home_screen'),
),
SearchScreen(
key: PageStorageKey('search_screen'),
),
AccountScreen(
key: PageStorageKey('account_screen'),
)
]);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: PageStorage(
child: pages[currentIndex],
bucket: bucket,
),
bottomNavigationBar: _bottomNavigationBar(currentIndex),
);
}
Widget _bottomNavigationBar(int selectedIndex) {
return BottomNavigationBar(
currentIndex: selectedIndex,
onTap: (index) => setState(() => currentIndex = index),
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
title: Text('Search'),
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
title: Text('Account'),
),
],
);
}
}
Then under account.dart screen, I have account_bag.dart and account_successful.dart screens. I would like to have my account_bag.dart screen to have the bottom nav bar while the account_successful.dart does not.

Related

Flutter BottomNavigationBar in all pages

I want to make this: https://medium.com/coding-with-flutter/flutter-case-study-multiple-navigators-with-bottomnavigationbar-90eb6caa6dbf. I'm working with Getx. First I have a screen called NavigatorPage, the controller is NavigatorController(). The problem is that in my HomePage() I use Get.toNamed('otheRoute') and I lose the Appbar and the BottomNavigationBar. I nedd to achieve the result as the link.
This is my navigator page
class NavigatorPage extends GetWidget<NavigatorController> {
NavigatorPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
appBar: CustomAppBar(),
body: Obx(() => IndexedStack(
index: controller.tabIndex.value,
children: [
HomePage(),
//SecondPage(),
],
)),
bottomNavigationBar: Obx(() => BottomNavigationBar(
onTap: controller.changePage,
currentIndex: controller.tabIndex.value,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'home',
),
BottomNavigationBarItem(
icon: Icon(Icons.download),
label: 'download',
),
])),
);
}
}
This is my navigator controller
class NavigatorController extends GetxController {
var tabIndex = 0.obs;
changePage(int index) {
tabIndex(index);
print('Index: $tabIndex');
}
}

Bottom Navigation Bar disappears when I navigate to other pages by clicking on Flat Button inside Scaffold

class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyNavigationBar(),
);
}
}
class MyNavigationBar extends StatefulWidget {
#override
_MyNavigationBarState createState() => _MyNavigationBarState();
}
class _MyNavigationBarState extends State<MyNavigationBar> {
int _currentIndex = 0;
// all pages
final List<Widget> _children = [
ListScreen(),
HomaPage(),
FourthScreen(),
ThirdScreen(),
];
void OnTappedBar(int index){
setState(() {
_currentIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: _children[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
selectedItemColor: Colors.black,
unselectedItemColor: Colors.grey,
onTap: OnTappedBar,
currentIndex: _currentIndex,
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), title: Text("accueil")),
BottomNavigationBarItem(icon: Icon(Icons.search), title: Text("recherche")),
BottomNavigationBarItem(icon: Icon(Icons.calendar_today), title: Text("Mess Pass")),
BottomNavigationBarItem(icon: Icon(Icons.person), title: Text("Mon Profil")),
],
),
);
}
}
Please! can anyone tell me how the bottomNavigationBar appears on all pages.
When I click on the Flat button in Scaffold on any page then the bottom app bar is hidden, but when I use the bottom app bar items to navigate on other pages then the bottom app bar does not disappear. So how can i fix this. I want that the bottom app bar is present on all the pages even I used buttons in the scaffold to navigate or bottom app bar items???
You can use this package persistent_bottom_nav_bar to achieve this kind of functionality
By default, Bottom navigation bar not persistent in MaterialApp.
You can achieve this using CupertinoTabScaffold, but not with Material concepts.
There is workaround for the same in Material and Scaffold by implementing custom Navigator for your tabs and bottom navigation. But here we need to handle many cases for Push/Pop navigators.
Currently, I'm using this custom navigator approach for my persistant bottom navigation use case, Will let you know once completed my stuff.
meanwhile some resources that helped me proceed
https://codewithandrea.com/articles/multiple-navigators-bottom-navigation-bar/
https://medium.com/#theboringdeveloper/common-bottom-navigation-bar-flutter-e3693305d2d
I believe you are calling 'Navigator.push()' inside FlatButton's onPressed method.
What's happening here is that when you call Navigator.push() the screen with BottomNavigationBar hides under the new screen, or goes down in the ScreenStack(a made-up term).
What you should do instead is use PageController to navigate to different pages inside the Flatbutton's onPressed.
pageController.animateToPage(index);
See this answer
https://stackoverflow.com/a/56540176/10285344
In order to customise your current implementation to fit your requirement, I would suggest you the following changes.
You should either use PageView to achieve this, because it provides you a controller to switch between pages.
For your case
If you do not want to use PageView for some reason, I would recommend the following workaround I used in one of my recent projects I use Provider to switch between Screens without using Navigator or PageView.
You'll need Provider package for this customisation.
https://pub.dev/packages/provider/install
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: ChangeNotifierProvider<ValueNotifier<int>>.value(
value: ValueNotifier<int>(0), //PageIndex is set to 0 to open first when when the app launches
child: MyNavigationBar(),
),
);
}
}
class MyNavigationBar extends StatefulWidget {
#override
_MyNavigationBarState createState() => _MyNavigationBarState();
}
class _MyNavigationBarState extends State<MyNavigationBar> {
// all pages
final List<Widget> _children = [
ListScreen(),
HomaPage(),
FourthScreen(),
ThirdScreen(),
];
void OnTappedBar(int index){
Provider.of<ValueNotifier<int>>(context, listen: false).value = index;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: _children[Provider.of<ValueNotifier<int>>(context).value],
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
selectedItemColor: Colors.black,
unselectedItemColor: Colors.grey,
onTap: OnTappedBar,
currentIndex: Provider.of<ValueNotifier<int>>(context).value,,
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), title: Text("accueil")),
BottomNavigationBarItem(icon: Icon(Icons.search), title: Text("recherche")),
BottomNavigationBarItem(icon: Icon(Icons.calendar_today), title: Text("Mess Pass")),
BottomNavigationBarItem(icon: Icon(Icons.person), title: Text("Mon Profil")),
],
),
);
}
}
How to switch to different page from inside of a page?
class ListScreen extends StatefulWidget {
#override
_ListScreenState createState() => _ListScreenState();
}
class _ListScreenState extends State<ListScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: FlatButton(
child: Text('Switch To HomePage'),
onPressed: (){
Provider.of<ValueNotifier<int>>(context, listen: false).value = 1;
},
),),
);
}
}

How to use or set "on Tap" for Bottomnavigationbar

I am really new at flutter. I need a tap on bottomnavigationBar which will open a new page. But where should I use onTap? On Top (body) I have an other Navigation(Tabbarview). Why I dont have an "onTap" inside the BottomnavigationBar
bottomNavigationBar: SnakeNavigationBar(
style: SnakeBarStyle.floating,
backgroundColor: Color(0xffFFFFFF),
snakeColor: Color(0xff3f51b5),
currentIndex: _selectedItemPosition,
onPositionChanged: (index) =>
setState(() => _selectedItemPosition = index),
padding: EdgeInsets.all(4),
items: [
new BottomNavigationBarItem(icon: Icon(Icons.home)),
new BottomNavigationBarItem(icon: Icon(Icons.description)),
new BottomNavigationBarItem(icon: Icon(Icons.people)),
new BottomNavigationBarItem(icon: Icon(Icons.settings)),
new BottomNavigationBarItem(icon: Icon(Icons.battery_unknown)),
],
)));
There's many ways to use a BottomNavigationBar, in your case, that's a library an uses "onPositionChanged" as "onTap" there, not only you can update your variable, but open change the current widget display on screen.
Here a full example to switch between widgets using a BottomNavigationBar:
import 'package:flutter/material.dart';
void main() => runApp(Demo());
class Demo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: DemoApp());
}
}
class DemoApp extends StatefulWidget {
#override
_DemoAppState createState() => _DemoAppState();
}
class _DemoAppState extends State<DemoApp> {
Widget _currentWidget = Container();
var _currentIndex = 0;
Widget _homeScreen() {
return Container(
color: Colors.green,
);
}
Widget _settingsScreen() {
return Container(
color: Colors.red,
);
}
#override
void initState() {
super.initState();
_loadScreen();
}
void _loadScreen() {
switch(_currentIndex) {
case 0: return setState(() => _currentWidget = _homeScreen());
case 1: return setState(() => _currentWidget = _settingsScreen());
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: _currentWidget,
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) {
setState(() => _currentIndex = index);
_loadScreen();
},
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), title: Text('Home')),
BottomNavigationBarItem(icon: Icon(Icons.settings), title: Text('Settings')),
],
),
);
}
}
The result of this is the following app:

Flutter: value of currentIndex property in BottomNavigationBar doesn't update when the state updates

I have a BottomNavigationBar that navigates to other pages when pressing an icon. This works fine, except the value of the currentIndex property in BottomNavigationBar doesn't update when the state updates, which means there is no change on the acual BottomNavigationBar. enter image description here
I'm using a vaiable (_selectedPage) to keep track of the selected index in the BottomNavigationBar, and the value changes when tapping an item, but it's not updating the currentIndex property when the state updates.. why is that?
import 'package:flutter/material.dart';
import 'package:independentproject/pages/home_page.dart';
import 'package:independentproject/pages/cook_recipe.dart';
class PageNavigationBar extends StatefulWidget {
#override
_PageNavigationBarState createState() => _PageNavigationBarState();
}
class _PageNavigationBarState extends State<PageNavigationBar> {
//default page showing in bottom navigation bar will be CookRecipe()
int _selectedPage = 1;
//all pages optional in bottom navigation bar
final List<Widget> _pageOptions = [
HomePage(),
CookRecipe(),
];
void onTapped(int pageTapped) {
setState(() {
//print(pageTapped);
_selectedPage = pageTapped;
Navigator.push(context, MaterialPageRoute(builder: (context) => _pageOptions[pageTapped]));
//print(_selectedPage);
});
}
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
//TODO: currentIndex: doesn't update when the state updates, why?
currentIndex: _selectedPage,
//items showing in bottom navigation bar
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Homepage'),
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
title: Text('Search recipe'),
),
],
onTap: (int pageTapped) {
onTapped(pageTapped);
},
);
}
}
import 'package:flutter/material.dart';
import 'package:independentproject/page_navigation_bar.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Home page'),
),
body: Center(
child: Text('Home page'),
),
bottomNavigationBar: PageNavigationBar(),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:independentproject/page_navigation_bar.dart';
class CookRecipe extends StatefulWidget {
#override
_CookRecipeState createState() => _CookRecipeState();
}
class _CookRecipeState extends State<CookRecipe> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Search recipe'),
),
body: Center(
child: Text('Search recipes'),
),
bottomNavigationBar: PageNavigationBar(),
),
);
}
}
I would advise you to create a widget that will contain the BottomNavigationBar and also PageView that would allow you to switch pages with PageController.
For example:
class _MainScreenState extends State<MainScreen> {
PageController _pageController;
int _page = 1;
Duration pageChanging = Duration(milliseconds: 300);//this is for page animation-not necessary
Curve animationCurve = Curves.linear;//this is for page animation-not necessary
_MainScreenState();
#override
void initState() {
super.initState();
_pageController = PageController(initialPage: 1);
}
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
physics: BouncingScrollPhysics(),
scrollDirection: Axis.horizontal,
controller: _pageController,
onPageChanged: onPageChanged,
children: <Widget>[
//here are all the pages you need:
//CookRecipe(),
],
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(
Icons.message,
),
title: Container(height: 0.0),
),
BottomNavigationBarItem(
icon: Icon(
Icons.home,
),
title: Container(height: 0.0),
),
BottomNavigationBarItem(
icon: Icon(
Icons.person,
),
title: Container(height: 0.0),
),
],
onTap: navigationTapped,
selectedItemColor: Theme.of(context).backgroundColor,
currentIndex: _page,
),
),
);
}
void navigationTapped(int page) {
_pageController.animateToPage(page,duration: pageChanging,
curve: animationCurve,);
}
#override
void dispose() {
super.dispose();
_pageController.dispose();
}
void onPageChanged(int page) {
if (this.mounted){
setState(() {
this._page = page;
});
}}
You can also do this without the PageView,and use only the controller to switch pages.
BTW-you create new instance of the navigation bar when you load a page which is bad practice
it is because PageNavigationBar is a own class, when you call there a setstate only this class updates
take a look at the Provider
a very usefull state management Package
or you can also handle your Page, when the NavBar is your Main Page and you have only one Page
return MaterialApp(
home: Scaffold(
appBar: ownAppBar(_selectedPage),
body: _bodyOptions[_selectedPage],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedPage,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Homepage'),
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
title: Text('Search recipe'),
),
],
onTap: (int pageTapped) {
onTapped(pageTapped);
},
)
),
);
final List<Widget> _bodyOptions = [
HomePage(),
CookRecipe(),
];
You don't need to use Navigator to change pages, I modified your code just try.
import 'package:flutter/material.dart';
main() {
runApp(MaterialApp(home: PageNavigationBar()));
}
class PageNavigationBar extends StatefulWidget {
#override
_PageNavigationBarState createState() => _PageNavigationBarState();
}
class _PageNavigationBarState extends State<PageNavigationBar> {
//default page showing in bottom navigation bar will be CookRecipe()
int _selectedPage = 1;
//all pages optional in bottom navigation bar
final List<Widget> _pageOptions = [
HomePage(),
CookRecipe(),
];
void onTapped(int pageTapped) {
setState(() {
//print(pageTapped);
_selectedPage = pageTapped;
// Navigator.push(context, MaterialPageRoute(builder: (context) => _pageOptions[pageTapped]));
//print(_selectedPage);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: _pageOptions[_selectedPage],
bottomNavigationBar: BottomNavigationBar(
//TODO: currentIndex: doesn't update when the state updates, why?
currentIndex: _selectedPage,
//items showing in bottom navigation bar
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Homepage'),
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
title: Text('Search recipe'),
),
],
onTap: (int pageTapped) {
onTapped(pageTapped);
},
),
);
}
}
class CookRecipe extends StatefulWidget {
#override
_CookRecipeState createState() => _CookRecipeState();
}
class _CookRecipeState extends State<CookRecipe> {
#override
Widget build(BuildContext context) {
return Center(
child: Text('Search recipes'),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Center(
child: Text('Home page'),
);
}
}

How to add drawer in bottom navigation bar in flutter?

I want to show a drawer when user clicks on the 4th(more_vert) icon, but I am not able to implement it. In my current implementation when 4th icon is clicked flutter takes me to a new page and there shows the drawer not over the current screen as it should. What am I doing wrong ? Also what is the differnce between BottomNavigationBar and BottomAppBar I could not find the difference anywhere. I checked out a few articles and it I think BottomAppBar is used to show the Fab floating in the bottom appbar. Is there any other difference between the two and when should one use one over the other.
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
List<Widget> _widgetOptions = <Widget>[
Page1(),
Page2(),
Page3(),
Page4(), // this page implements the drawer
];
int _currentSelected = 0;
void _onItemTapped(int index) {
setState(() {
_currentSelected = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: _widgetOptions.elementAt(_currentSelected),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
onTap: _onItemTapped,
currentIndex: _currentSelected,
showUnselectedLabels: true,
unselectedItemColor: Colors.grey[800],
selectedItemColor: Color.fromRGBO(10, 135, 255, 1),
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(AntDesign.carryout),
),
BottomNavigationBarItem(
icon: Icon(MaterialCommunityIcons.sack),
),
BottomNavigationBarItem(
icon: Icon(Octicons.archive),
),
BottomNavigationBarItem(
icon: Icon(Icons.more_vert),
)
],
),
// backgroundColor: Colors.black,
);
}
}
You don't need an extra page for that.
You could do it like that:
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
List<Widget> _widgetOptions = <Widget>[
Page(),
Page(),
Page(),
];
int _currentSelected = 0;
GlobalKey<ScaffoldState> _drawerKey = GlobalKey();
void _onItemTapped(int index) {
index == 3
? _drawerKey.currentState.openDrawer()
: setState(() {
_currentSelected = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _drawerKey,
body: _widgetOptions.elementAt(_currentSelected),
drawer: Drawer(),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
onTap: _onItemTapped,
currentIndex: _currentSelected,
showUnselectedLabels: true,
unselectedItemColor: Colors.grey[800],
selectedItemColor: Color.fromRGBO(10, 135, 255, 1),
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
title: Text('Page 1'),
icon: Icon(Icons.access_alarm),
),
BottomNavigationBarItem(
title: Text('Page 2'),
icon: Icon(Icons.accessible),
),
BottomNavigationBarItem(
title: Text('Page 3'),
icon: Icon(Icons.adb),
),
BottomNavigationBarItem(
title: Text('Drawer'),
icon: Icon(Icons.more_vert),
)
],
),
);
}
}
class Page extends StatelessWidget {
const Page({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container();
}
}
Adding a GlobalKey for the Scaffold which implements the drawer and implementing the Drawer in your root Scaffold.
The BottomNavigationBar doesn't show the drawer icon like the AppBar does.
To open the drawer programmatically :
Create this variable as state :
GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey();
Set it as the Scaffold's key :
Scaffold(
key: _scaffoldKey,
Then, you can use the key state to open the drawer :
_scaffoldKey.currentState.openDrawer();
The solutions provided above work if you are not using the newer versions of Flutter with Null Safety. In case you are using Flutter 2.0 or greater. Due to Null Safety, you will get an error saying:
The method 'openDrawer' can't be unconditionally invoked because the receiver can be 'null'.
Try making the call conditional (using '?.') or adding a null check to the target
Use: _drawerKey.currentState!.openDrawer();