How can I persist BottomNavigationBar across all screens in Flutter - flutter

I have BottomNavigationBar in flutter & called separate widgets for each tab. For example I am having button into that widget which redirects to new screen, I want that BottomNavigationBar should be persisted in new screen as well. I have checked couple of articles but unable to understand. Below is code for BottomTabNavigationBar
import 'package:flutter/material.dart';
import 'package:timeout_dubai/fragments/HomeFragment.dart';
import 'package:timeout_dubai/fragments/MtTimeFragment.dart';
import 'package:timeout_dubai/fragments/MyLocFragment.dart';
import 'package:timeout_dubai/fragments/ThingsLoveFragment.dart';
class LoginScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _HomeState();
}
}
class _HomeState extends State<LoginScreen> {
int _currentIndex = 0;
GlobalKey globalKey = new GlobalKey(debugLabel: 'btm_app_bar');
final List<Widget> _children = [
new HomeFragment(),
new ThingsLoveFragment(),
new MyLocFragment(),
new MyTimeFragment()
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: _children[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
onTap: onTabTapped,
key: globalKey,
currentIndex: _currentIndex,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home,),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.favorite),
title: Text('Things I Love'),
),
BottomNavigationBarItem(
icon: Icon(Icons.location_on),
title: Text('My Locations'),
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
title: Text('My Time Out')
),
],
),
);
}
void onTabTapped(int index) {
print("Index $index");
setState(() {
_currentIndex = index;
});
}
}

Related

BottomNavigationBarItem - Unable to link widgets

I need to have the transactions screen and the categories screen widgets as BottomNavigationBarItems
However, i receive this error
The method 'Transactions' isn't defined for the type 'HomeState'. Try correcting the name of an existing method, or defining a method named 'Transactions'.
lib\screens\home.dart
import 'package:demo_app/screens/categories.dart';
import 'package:demo_app/screens/transactions.dart';
import 'package:flutter/material.dart';
class Home extends StatefulWidget{
const Home({super.key});
#override
State<Home> createState() => HomeState();
}
class HomeState extends State<Home> {
List<Widget> widgetOptions = [Transactions(), Categories()];
int selectedIndex = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: const Text('Logged in!'),
),
body: widgetOptions.elementAt(selectedIndex),
bottomNavigationBar: BottomAppBar(
shape: const CircularNotchedRectangle(),
notchMargin: 4,
child: BottomNavigationBar(
backgroundColor: Theme.of(context).primaryColor.withAlpha(0),
elevation: 0,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.account_balance_wallet),
label: 'Transactions'),
BottomNavigationBarItem(
icon: Icon(Icons.category),
label: 'Categories'),
BottomNavigationBarItem(
icon: Icon(Icons.logout),
label: 'Log out'),
],
currentIndex: selectedIndex,
onTap: onItemTapped,
)
)
),
);
}
void onItemTapped(int index){
if(index == 2){
}else{
setState((){
selectedIndex = index;
});
}
}
}
Simply add SizedBox() to widgetOptions list.
List<Widget> widgetOptions = [Transactions(), Categories(), SizedBox()];
Actually the code in the below had issues
screens/categories.dart';
screens/transactions.dart';
lib\screens\home.dart
Solved those and the issue is no longer there.

Hide bottomnavigation in specific pages in flutter?

I created a separate file for bottom navigation bar and included the three screens which is to be included in bottom navigation bar .
class _bottomnavscreen extends State<bottomnavscreen> {
int _selectedIndex = 0;
List<Widget> pageList = [home(), create(), profile()];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.add_circle_outline_sharp),
label: 'Create',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Profile',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
body: pageList.elementAt(_selectedIndex),
);
}
I put this bottomnavscreen as the home in main.dart:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: bottomnavscreen(),
);
}
}
But this bootomnavigation widget is seen in my detailedpost screen and comment screen.
detailedpost screen is pushed from home() through Navigation.push()
Comment screen is pushed from postdetails() through
Navigation.push()
How can I hide this bottom navigation widget in my comment screen and detailedpost screen?
This is how I push to detailpost screen from home()
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => detailpost(
body: document['body'],
title: document['title'],
date: document['date'],
)),
);
You can add condition for specific index like this :
class _bottomnavscreen extends State<bottomnavscreen> {
int _selectedIndex = 0;
List<Widget> pageList = [home(), create(), profile()];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: _selectedIndex == 1 ? Container() : BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.add_circle_outline_sharp),
label: 'Create',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Profile',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
body: pageList.elementAt(_selectedIndex),
);
}
You should start a base page from the MyApp and add BottomNavigations in that page only.
Now when you navigate to detailedpost screen and comment screen, the BottomNavigations will not be visible.
you can use the offstage property to hide the bottom navigation bar on specific pages by wrapping it in an Offstage widget and setting the offstage property to true:
import 'package:flutter/material.dart';
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _currentIndex = 0;
final List<Widget> _pages = [ HomePage(), SettingsPage(), ];
#override
Widget build(BuildContext context) {
return Scaffold(
body: _pages[_currentIndex],
bottomNavigationBar: Offstage(
offstage: _currentIndex == 1,
child: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
title: Text('Settings'),
),
],
),
),
);
}
}

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();

How do I set a default tab in BottomNavigation bar in Flutter?

I have 5 tabs currently, how do I set the a default tab when launching the app? lets say I want the account tab to be the default tab when opening the app.
How to I do that? Btw, Im using WebView plugin to display a site's content.
Below is the code of my main.dart file. Ive tried searching and all but the solutions I found wont work for me (I think im searching the wrong words. Also, I am new to flutter) Thank you very much!
import 'package:syncshop/widgets/cart_page.dart';
import 'package:syncshop/widgets/categories_page.dart';
import 'package:syncshop/widgets/home_page.dart';
import 'package:syncshop/widgets/profile_account.dart';
import 'package:syncshop/widgets/search_page.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
int _selectedPage = 0;
final _pageOptions = [
HomeScreen(),
CategoriesPage(),
SearchPage(),
CartPage(),
ProfileAccount(),
];
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Sync Shop',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: _pageOptions[_selectedPage],
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: _selectedPage,
onTap: (int index) {
setState((){
_selectedPage = index;
});
},
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), title: Text("Home"),
),
BottomNavigationBarItem(icon: Icon(Icons.category), title: Text("Categories"),
),
BottomNavigationBarItem(icon: Icon(Icons.search), title: Text("Search"),
),
BottomNavigationBarItem(icon: Icon(Icons.shopping_cart), title: Text("Cart"),
),
BottomNavigationBarItem(icon: Icon(Icons.account_circle), title: Text("Profile"),
),
],
),
),
);
}
}
You can override initState function of State and can put some initial codes there.
So for your example, can do this.
...
class MyAppState extends State<MyApp> {
int _selectedPage = 0;
#override
void initState() {
super.initState();
_selectedPage = 4;
}
...
When you creating BottomNavigationBar set this.currentindex to index that you want
bottomNavigationBar: BottomNavigationBar(
currentindex=1,//This line, it's default 0
type: BottomNavigationBarType.fixed,
currentIndex: _selectedPage,
onTap: (int index) {
setState(() {
_selectedPage = index;
});
},
You must use tab controller.
First you need to extend TickerProviderStateMixin in class.
class MyAppState extends State<MyApp> with TickerProviderStateMixin {
}
Then define TabController like,
_tabController = new TabController(length: _tabLength, vsync: this, initialIndex: 1);
and finally set controller in your TabBarView
controller: _tabController,
Here you initialize the default tab
int _selectedPage = 0;
You can change it to any tab you wish, If you want Profile
int _selectedPage = 4;
import 'package:flutter/material.dart';
import 'package:syncshop/widgets/cart_page.dart';
import 'package:syncshop/widgets/categories_page.dart';
import 'package:syncshop/widgets/home_page.dart';
import 'package:syncshop/widgets/profile_account.dart';
import 'package:syncshop/widgets/search_page.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
int _selectedPage = 4;
final _pageOptions = [
HomeScreen(),
CategoriesPage(),
SearchPage(),
CartPage(),
ProfileAccount(),
];
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Sync Shop',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: _pageOptions[_selectedPage],
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: _selectedPage,
onTap: (int index) {
setState(() {
_selectedPage = index;
});
},
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text("Home"),
),
BottomNavigationBarItem(
icon: Icon(Icons.category),
title: Text("Categories"),
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
title: Text("Search"),
),
BottomNavigationBarItem(
icon: Icon(Icons.shopping_cart),
title: Text("Cart"),
),
BottomNavigationBarItem(
icon: Icon(Icons.account_circle),
title: Text("Profile"),
),
],
),
),
);
}
}