I am working on the bottom navigation bar feature. I have 4 screens associated with a bottom navigation bar.
Page1 Index0 (It has a button)
Page2 Index1
Page3 Index2
Page4 Index3
Each click on the bottom icon will change the index of a bottom navigation bar and triggers the specific screen to load.
Now I want to click on the button of screen 1 which is index 0 of a bottom navigation bar, this should make a call to load page2 as if I select the second icon of a bottom navigation bar.
I tried using the navigator.push(context,page()) which loaded the page however the bottom navigation bar was not visible. This is not the way, Is there any way to do it correctly?
Thanks
Use IndexedStack widget for this. Example is as follows:
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int _currentIndex = 0; //Current selected index of the bottom navigation bar tab
void changeTab(int index) {
setState(() => _currentIndex = index);
}
#override
Widget build(BuildContext context) {
//List of Screens that you want to put
final screens = [
const Screen1(),
const Screen2(),
const Screen3(),
];
return Scaffold(
body: IndexedStack(
index: _currentIndex,
children: screens,
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) => changeTab(index),
type: BottomNavigationBarType.fixed,
unselectedItemColor: Colors.grey,
selectedItemColor: Colors.red,
showUnselectedLabels: false,
showSelectedLabels: false,
iconSize: 20,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.foo),
label: 'Screen1',
),
BottomNavigationBarItem(
icon: Icon(Icons.bar),
label: 'Screen2',
),
BottomNavigationBarItem(
icon: Icon(Icons.baz),
label: 'Screen3',
),
],
),
);
}
}
I fixed this issue using a Provider. The issue has been resolved now.
I've read your question and that's what I found out:
You want to change the bottom navigation bar pages by clicking on a button.
To do so, simply change the index variable that you are passing to the selected index of the bottom navigation to the index of the page you want, and you will need to make it inside a setState function so the variable get updated.
Like the code below:
onTap: () {
setState((() => this.index = 0));
},
You can change bottom navigation bar index from every screen
static int bottomNavigationTabIndex = 0;
static void selectPage(BuildContext context,int index) {
BottomNavigationScreenState? stateObject = context.findAncestorStateOfType<BottomNavigationScreenState>();
stateObject?.setState((){
bottomNavigationTabIndex = index;
});
}
Related
I have a bottom navigation bar with 4 tabs; Home, Loans, Insurance and Settings. The Settings page has buttons leading to many other pages but those pages are not supposed to be on the navigation bar. However the navigation bar needs to persist across all the pages to make it easy to go back to the main pages like the Home Page. Below is the code for my bottom navigation bar:
import 'package:bima/Insurance/insurance.dart';
import 'package:bima/Loans/loans.dart';
import 'package:bima/Settings/settings.dart';
import 'package:flutter/material.dart';
import '../Home/home.dart';
class Dashboard extends StatefulWidget {
const Dashboard({Key? key}) : super(key: key);
#override
_DashboardState createState() => _DashboardState();
}
class _DashboardState extends State<Dashboard> {
int _currentIndex = 0;
final tabs = [
const Home(),
const Loans(),
const Insurance(),
const Settings()
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: tabs[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
unselectedItemColor: Colors.black,
selectedItemColor: const Color.fromRGBO(212, 164, 24, 1),
currentIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.wallet_outlined),
label: 'Loans',
),
BottomNavigationBarItem(
icon: Icon(Icons.shield_moon_outlined),
label: 'Insurance',
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: 'Settings',
),
],
),
);
}
}
This is a picture of the pages with a navigation bar and the pages without:
How can I make sure the navigation bar appears and works across all the pages without having to add extra tabs to the index? Edit: The code I have posted works so far for navigating the pages in the index namely: Home, Loans, Insurance and Settings. I do not want to add any other pages to the index, I would just like to display the navigation bar on the other pages which are not included in the index.
1 - Take a look at this package: https://pub.dev/packages/google_nav_bar.
2 - check that your bottom nav is implemented in the right place.
Instead of using BottomNavigationBar use TabBar it has controller that can be used.
First initialize _tabController in init
late TabController _tabController;
void initState() {
super.initState();
_tabController = TabController(length: 2, initialIndex: 0, vsync: this);
//_tabController.addListener(_handleTabChange);
}
void _handleTabChange() async {
//controller handel tab change by default so no need to handle it here
//this is anything custom you want to perform when tab is changed
}
final tabIcons = [
// Homeicon,
// Loansicon,
// Insuranceicon,
// Settingsicon
];
bottom: TabBar(
controller: _tabController,
indicatorColor: Colors.red,
tabs: tabIcons
),
And in body use with list of tabs as you declared above
TabBar(
controller: _tabController,
indicatorColor: Colors.red,
tabs: tabs, //list of tabs as you declared already use here a
),
And if you want to continue on with BottomNavigationBar follow below instructions
It might not be optimal solution but it was working for me and hope so it will also work for you, on navigating use Navigator.pushReplacement if you don't want to navigate back to last page visited, and if you want to navigate to previous page on back press use simple Navigator.push
onTap use this instead of only updating current index
onTap: (index) {
setState(() {
_currentIndex = index;
});
goToIndexPage(index);
},
goToIndexPage(int index) {
if (index == 0) {
//navigate to 1st
// Navigator.pushReplacement(
// context,
// MaterialPageRoute(builder: (context) => const Home()),
// );
} else if (index == 1) {
//navigate to 2nd page
// Navigator.pushReplacement(
// context,
// MaterialPageRoute(builder: (context) => const Loan()),
// );
} else if (index == 2) {
//navigate to 3rd page
} else if (index == 3) {
//navigate to 4th page
}
}
On one of the pages, nested inside one of bottom navigation bar pages I want to hide the bottom navigation bar, which is set as global. To be clear, I'm talking about this bar:
I can't just use Navigator.pushNamed because I'm creating viewModel and passing arguments in this way:
openConversation(BuildContext context) async {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => ChangeNotifierProvider(
create: (context) => ConversationVM(...),
child: ConversationScreen(
(...)
),
),
));
}
I've tried to set the Scaffold parameter bottomNavigationBar to null, but without effect, I need to resolve that problem somewhere higher.
Nav bar snippet:
class NavigationBar extends StatefulWidget {
static String id = 'navigation_screen';
#override
State<StatefulWidget> createState() {
return _NavigationBarState();
}
}
class _NavigationBarState extends State<NavigationBar> {
//track the index of our currently selected tab
int _currentIndex = 0;
//st of widgets that we want to render
final List<Object> _children = [
PostedQueries(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
//body of our scaffold which is the widget that gets displayed between our app bar and bottom navigation bar.
body: _children[_currentIndex], // new
bottomNavigationBar: BottomNavigationBar(
onTap: onTabTapped,
// new
currentIndex: _currentIndex,
// new
unselectedItemColor: Colors.grey,
selectedItemColor: Colors.deepOrange,
items: [
BottomNavigationBarItem(
icon: new Icon(Icons.home),
label: ('Home'),
),
],
),
);
}
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
}
I used the bottom navigation bar and added 3 buttons to the bottom.I was swapping pages with setsate. These buttons work fine. But if I call another page inside the pages called with those buttons, the BottomNavigationBar disappears. I've researched it and found this click me. Is it logical to use Provider to call pages? I think it keeps every page in memory, doesn't it consume too much ram?
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int selectedPage = 0;
final _pageOptions = [
HomeScreen(),
InboxScreen(),
SignInScreen()
];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: _pageOptions[selectedPage],
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(icon: Icon(Icons.home, size: 30), title: Text('Home')),
BottomNavigationBarItem(icon: Icon(Icons.mail, size: 30), title: Text('Inbox')),
BottomNavigationBarItem(icon: Icon(Icons.account_circle, size: 30), title: Text('Account')),
],
selectedItemColor: Colors.green,
elevation: 5.0,
unselectedItemColor: Colors.green[900],
currentIndex: selectedPage,
backgroundColor: Colors.white,
onTap: (index){
setState(() {
selectedPage = index;
});
},
)
);
}
}
This is my home page.BottomNavigationBar disappears If I clicked on the text
class _HomePage extends State<HomePage> {
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(appBar: AppBar(title: Text('Page2')),body:GestureDetector(
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewScreen()));
},
child:Text('clik')));
}
}
I want to like this
But my code run this
The code you posted looks perfectly fine !
Your problem must be elsewhere, probably in one of the Widgets
final _pageOptions = [
HomeScreen(),
InboxScreen(),
SignInScreen()
];
There can be many root causes to your problem, you could start by identifying which Widget is making the bottomNavigationBar disappear.
Once you found it out, be sure that you didn't use the method Navigator.of(context).push which could be one of the reasons why your bottomNavigationbar is disappearing.
If you're still stuck, feel free to update your question with the source code of the Widget making the bar disappear and I'll update my answer
EDIT
As I mentioned earlier, whenever you call the Navigator.push(context, UserProfilePage); method, you're replacing your previous widget with a new one.
Since your previous Widget was holding your bottomNavigationBar, that's why it's disappearing.
What you are looking for here is, how to have a persistent navigation bar.
Here are two useful links that'll help you and the other people having this need, a Video tutorial to understand how to implement it and a well written article to fully understand how it works
I'm working on a project with flutter, I have a bottom navigation bar , this bar allow me to move on different scenes . I would like to remove my bottom navigation bar when i navigate to a detail page, there is any method? I'm trying all the possible solution , but i can't solve the problem, maybe i've implemented wrong my navigation bar !
Some Code:
My HomePage where i implemented the bottom navigation bar and where i call all the others pages:
class homeScreen extends StatefulWidget {
#override
_homeScreenState createState() => _homeScreenState();
}
class _homeScreenState extends State<homeScreen> {
TextEditingController _linkController;
int _currentIndex = 0;
final _pageOptions = [
meetingScreen(),
dashboardScreen(),
notificationScreen(),
profileScreen(),
];
#override
void initState() {
// TODO: implement initState
super.initState();
_linkController = new TextEditingController();
}
#override
Widget build(BuildContext context) {
ScreenUtil.instance = ScreenUtil(width: 1125, height: 2436)..init(context);
return MaterialApp(
title: myUi.myTitle,
theme: myUi.myTheme ,
debugShowCheckedModeBanner: false,
home:Scaffold(
bottomNavigationBar: _buildBottomNavigationBar(),
body: _pageOptions[_currentIndex] ,
) ,
);
}
Widget _buildBottomNavigationBar() {
return BottomNavigationBar(
backgroundColor: Colors.white,
selectedItemColor: Theme.of(context).primaryColor,
//fixedColor: gvar.secondaryColor[gvar.colorIndex],
currentIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.explore,size: ScreenUtil().setWidth(80),),
title: new Text("Esplora"),
),
BottomNavigationBarItem(
icon:Icon(Icons.dashboard,size: ScreenUtil().setWidth(80),),
title: new Text("Attività "),
),
BottomNavigationBarItem(
icon:Icon(Icons.notifications,size: ScreenUtil().setWidth(80),),
title: new Text("Notifiche"),
),
BottomNavigationBarItem(
icon:Icon(Icons.person,size: ScreenUtil().setWidth(80),),
title: new Text("Profilo"),
),
],
);
}
}
This is the code i use into the dashboardScreen to push to my detail page:
onTap: () {
//method: navigate to Meeting
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) => new meetingScreen()),
);
},
In the new meetingScreen i have some ui , but i cant remove my old bottom navigation bar, i tried implementing a new navigation bar but it isn't displayed.
A partial working solution was to do like so:
Navigator.of(context, rootNavigator: true).pushReplacement(MaterialPageRoute(builder: (context) => new meetingScreen()));
But now I can't do Navigator.pop with the cool animation , because the new page is the root!
Thanks guys for the support !!
To navigate to a different screen without the bottom Navigation bar, you'd need to call Navigator.push() from _homeScreenState. For you to be able to do this, you need to create a NavigatorKey in _homeScreenState and set it in MaterialApp().
final _mainNavigatorKey = GlobalKey<NavigatorState>();
...
MaterialApp(
navigatorKey: _mainNavigatorKey,
...
)
You can then pass the NavigatorKey to your pages. To use _mainNavigatorKey, fetch its current state to be able to invoke push().
navigatorKey.currentState!.push(...)
Here's a sample that I have. Though instead of a bottom Navigation bar, I'm using a Navigation Drawer. The same method applies in this sample.
In fluter, I have 2 tab pages that show different view. I wanted to have the floatingactionbutton to show only in one of the view and hidden in other tabs.
But the floatingactionbutton stays floating even when the view is switched.
Can anyone help me on this? If there is any code or tutorial, it would be appreciated.
demo pic:
tab1 show fab
tab2 hide fab
You can set floatingActionButton to null when _selectedIndex == 0, then FAB is gone with animation, so cool. And remember to change _selectedIndex in BottomNavigationBar onTap method.
Here is the example code with some comments:
final _tabTitle = [
'Title 1',
'Title 2'
]; // BottomNavigationBarItem title
final _tabIcon = [
Icon(Icons.timer_off),
Icon(Icons.timeline),
]; // BottomNavigationBarItem icon
class _MyHomePageState extends State<MyHomePage> {
int _selectedIndex = 0;
String title = _tabTitle[0];
// tap BottomNavigationBar will invoke this method
_onItemTapped(int index) {
setState(() {
// change _selectedIndex, fab will show or hide
_selectedIndex = index;
// change app bar title
title = _tabTitle[index];
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(title),
),
body: Center(
child: Text(_tabTitle[_selectedIndex]),
),
// two tabs
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(title: Text(_tabTitle[0]), icon: _tabIcon[0]),
BottomNavigationBarItem(title: Text(_tabTitle[1]), icon: _tabIcon[1])
],
currentIndex: _selectedIndex,
onTap: _onItemTapped,
),
// key point, fab will show in Tab 0, and will hide in others.
floatingActionButton: _selectedIndex == 0 ? FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
) : null,
);
}
}
You can create a list of FloatingActionButtons for each page.
Call index method on the TabController variable to know the index of the tab that is active and use that to select a fab from the list.
Don't forget to call addListener on the TabController variable.
here is snippet code of how I did it:
// in the main statefulwidget class
TabController tabController;
var fabIndex;
#override
void initState() {
super.initState();
tabController = new TabController(length: 3, vsync: this,initialIndex: 0);
tabController.addListener(_getFab);
fabIndex=0;
}
void dispose() {
tabController.dispose();
super.dispose();
}
final List<FloatingActionButton> fabs=[
new FloatingActionButton(child: new Icon(Icons.access_time),onPressed: (){},),
new FloatingActionButton(child: new Icon(Icons.account_balance),onPressed: (){},),
new FloatingActionButton(child: new Icon(Icons.add_alert),onPressed: (){},)
];
void _getFab(){
setState((){`enter code here`
fabIndex=tabController.index;
});
}
Use the fabIndex in the scaffold's floatingActionButton property as follows:
floatingActionButton: fabs[fabIndex],
I would wrap the tab you want with a scaffold and give it a fab.
TabBarView(
children:[
Text("tab1"),
Scaffold(body:Text("tab2"),
floatingActionButton: FloatingActionButton(...)
)
)
The easiest way is to just give your each tab it's own Scaffold as the top widget, and provide it's own FAB there (Instead of the Scaffold where each tabs are kept, the main Scaffold, place it individually inside every tab's Scaffold, and remove from the top Scaffold). If you already have one, you can wrap it inside another. But if you want to keep the FAB the same in every other screen except some, you then can follow other solutions written here.