Bottom Navigation Bar duplicated - flutter

I am programming a Bottom Navigation Bar, but the title it's duplicated. The code:
class _HomePageState extends State<HomePage> {
int _opcaoselecionada = 0;
final List<Widget> _telas = [
Menu(),
HomePage(),
Graficos(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('bem vindo')
),
body: _telas[_opcaoselecionada],
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: _opcaoselecionada,
onTap: onTabTapped,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.add),
label: "Menu",
),
BottomNavigationBarItem(
icon: Icon(Icons.house_outlined),
label: "inicial",
),
BottomNavigationBarItem(
icon: Icon(Icons.auto_graph),
label: "graficos",
)
],
),
);
}
void onTabTapped(int index) {
setState(() {
_opcaoselecionada = index;
});
}
}
This is how it looks like:
Also, everytime I click in the Menu button, it creates a new title. The rest of the code is working properly.

final List<Widget> _telas = [
Menu(),
HomePage(),
Graficos(),
];
You need to return only body part
remove
appBar: AppBar(
title: const Text(label)
),
from Menu(),
HomePage(),
Graficos(), this 3 widgets

Related

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'),
),
],
),
),
);
}
}

Navigating to another screen without bottomNavigationBar Icons in flutter but keeping the bottomNavigationBar in anotherScreen

I have a main.dart with bottomNavigationBar with 5 tabs and when clicked on tabs it goes to the respective pages.
void main() => runApp(MyStatefulWidget());
class MyStatefulWidget extends StatefulWidget {
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
final List<Widget> _children = [
ProfilePage1(),
EventPage3(),
HomePage2(),
AllEventPage(),
ProfilePage1(),
];
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<EventModifier>(
create: (context) => EventModifier()),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(child: _children.elementAt(_selectedIndex)),
bottomNavigationBar: BottomNavigationBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(icon: Icon(Icons.home), title: Text("")),
BottomNavigationBarItem(
icon: Icon(Icons.calendar_today), title: Text("")),
BottomNavigationBarItem(
icon: Icon(
Icons.account_circle,
size: 45,
color: Color(0xFF334192),
),
title: Text("")),
BottomNavigationBarItem(
icon: Icon(Icons.message), title: Text("")),
BottomNavigationBarItem(
icon: Icon(Icons.table_chart), title: Text("")),
],
currentIndex: _selectedIndex,
selectedItemColor: Color(0xFF334192),
unselectedItemColor: Colors.grey,
onTap: _onItemTapped,
),
),
),
);
}
}
Now, in the 3rd tab there is a button called "See All" and when clicked it should go to the 4th page With bottomNavigationBar and automatically it should be clicked the 4th tab of the bottomNavigationBar.
How to do that in flutter?flutt
To change the bottomNavigationBar selected item manually you would set
_selectedIndex = 4;
To navigate to the AllEventTab when the ‘See All’ button is clicked... one way is using Navigator.push() in the button’s onPressed(), like this:
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => AllEventTab()),
);
}
see “Navigation Basics”

flutter Mapping error: type 'String' is not a subtype of type 'Widget' in type cast

I use bottomNavigationBar with two items which I define in a map:
final List<Map<String, Object>> _pages = [
{
'page': CategoriesScreen(),//My categories widget
'title': 'Categories',//title of current page (with navigation bar)
},
{
'page': FavoritesScreen(),//My favorites widget
'title': 'Your Favorite',//title of current page (with navigation bar)
},
];
When I use it like this:
return Scaffold(
appBar: AppBar(
title: Text(_pages[_selectedPageIndex]['title']),
),
body: _pages[_selectedPageIndex]['page'],//<----- ERROR HERE
bottomNavigationBar: BottomNavigationBar(....
When I try to run the app from Android studio I got the error:
type 'String' is not a subtype of type 'Widget' in type cast
Any Idea why?
Update 1 (full code):
import 'package:flutter/material.dart';
import './favorites_screen.dart';
import './categories_screen.dart';
class TabsScreen extends StatefulWidget {
#override
_TabsScreenState createState() => _TabsScreenState();
}
class _TabsScreenState extends State<TabsScreen> {
final List<Map<String, Object>> _pages = [
{
'page': CategoriesScreen(),
'title': 'Categories',
},
{
'page': FavoritesScreen(),
'title': 'Your Favorite',
},
];
int _selectedPageIndex = 0;
void _selectPage(int index) {
setState(() {
_selectedPageIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_pages[_selectedPageIndex]['title']),
),
body: _pages[_selectedPageIndex]['page'],
bottomNavigationBar: BottomNavigationBar(
onTap: _selectPage,
backgroundColor: Theme.of(context).primaryColor,
unselectedItemColor: Colors.white,
selectedItemColor: Theme.of(context).accentColor,
currentIndex: _selectedPageIndex,
// type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
backgroundColor: Theme.of(context).primaryColor,
icon: Icon(Icons.category),
title: Text('Categories'),
),
BottomNavigationBarItem(
backgroundColor: Theme.of(context).primaryColor,
icon: Icon(Icons.star),
title: Text('Favorites'),
),
],
),
);
}
}
Thank you
You don't need to put it into a Map. Just use the List<Widget> for _pages and add the title the State widget of the TabsScreen.
import 'package:flutter/material.dart';
import './favorites_screen.dart';
import './categories_screen.dart';
class TabsScreen extends StatefulWidget {
#override
_TabsScreenState createState() => _TabsScreenState();
}
class _TabsScreenState extends State<TabsScreen> {
final List<Widget> _pages = [
CategoriesScreen(),
FavoritesScreen(),
];
String title = 'Categories';
int _selectedPageIndex = 0;
void _selectPage(int index) {
setState(() {
_selectedPageIndex = index;
if (_selectedPageIndex == 0) {
title = 'Categories';
} else if (_selectedPageIndex == 1) {
title = 'Your Favorite';
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: _pages[_selectedPageIndex],
bottomNavigationBar: BottomNavigationBar(
onTap: _selectPage,
backgroundColor: Theme.of(context).primaryColor,
unselectedItemColor: Colors.white,
selectedItemColor: Theme.of(context).accentColor,
currentIndex: _selectedPageIndex,
// type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
backgroundColor: Theme.of(context).primaryColor,
icon: Icon(Icons.category),
title: Text('Categories'),
),
BottomNavigationBarItem(
backgroundColor: Theme.of(context).primaryColor,
icon: Icon(Icons.star),
title: Text('Favorites'),
),
],
),
);
}
}
I'm still looking for a more elegant way of achieving but this approach works.
following your code this is what you should do instead
import 'package:flutter/material.dart';
import './categories_screen.dart';
import './favourite_screen.dart';
class TabsScreeen extends StatefulWidget {
#override
_TabsScreeenState createState() => _TabsScreeenState();
}
class _TabsScreeenState extends State<TabsScreeen> {
final List<Map<String, Object>> _pages = [
{
"page": CategoriesScreen(),
"title": "Categories",
},
{
"page": FavouriteScreen(),
"title": "Favourites",
}
];
int _selectedPageIndex = 0;
void _selectPage(int index) {
setState(() {
_selectedPageIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_pages[_selectedPageIndex]['title']),
),
body: _pages[_selectedPageIndex]['page'],
bottomNavigationBar: BottomNavigationBar(
unselectedItemColor: Colors.black38,
selectedItemColor: Colors.white,
backgroundColor: Theme.of(context).primaryColor,
onTap: _selectPage,
currentIndex: _selectedPageIndex,
items: [
BottomNavigationBarItem(
backgroundColor: Colors.orangeAccent,
icon: Icon(
Icons.category,
),
label: 'Categories',
),
BottomNavigationBarItem(
backgroundColor: Colors.orangeAccent,
icon: Icon(
Icons.favorite,
),
label: 'Favourites',
)
],
),
);
}
}
Try this
Instead of typing:final List<Map<String, Object>> _pages
do: final List<Map<String, dynamic>> _pages
So replace Object with dynamic type.
I got the same error and it was solved like this:
1- Keep your final List<Map<String, Object>> _pages as it's.
2- Add "As String" to the Text: title: Text(_pages[_selectedPageIndex]['title'] as String),
3- Add "as Widget" to the body: body: _pages[_selectedPageIndex]['page'] as Widget,
The full code:
class _TabsState extends State<Tabs> {
final List<Map<String, Object>> _pages = [
{'page': Categories(), 'title': "Categories"},
{'page': Favorites(), 'title': "Your Favorites"},
];
int _selectedPageIndex = 0;
void _selectPage(int index) {
setState(() {
_selectedPageIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_pages[_selectedPageIndex]['title'] as String),<-- Add this
centerTitle: true,
),
body: _pages[_selectedPageIndex]['page'] as Widget, <-- Add this
bottomNavigationBar: BottomNavigationBar(
unselectedItemColor: Colors.black54,
selectedItemColor: Colors.deepPurple,
currentIndex: _selectedPageIndex,
type: BottomNavigationBarType.shifting,
onTap: _selectPage,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.category),
label: 'Catogries',
),
BottomNavigationBarItem(
icon: Icon(Icons.star),
label: 'Favorites',
),
]),
);
}
}
In your list mapping, change object to dynamic(your value can take any form)
like...
final List<Map<String, dynamic>> _pages

Change AppBar title depending on page with BottomNavigationBar

I'm trying to change the AppBar title depending on which page the user is on - the pages are controlled by a BottomNavigationBar which loads the different classes(pages)
The only way i've managed to change this is by including a appbar for each page, which i believe is not the way to go ahead.
class HomePage extends StatefulWidget {
final String title;
HomePage({Key key, this.auth, this.userId, this.onSignedOut, this.title})
: super(key: key);
final BaseAuth auth;
final VoidCallback onSignedOut;
final String userId;
#override
State<StatefulWidget> createState() => new _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _currentIndex = 0;
final List<Widget> _children = [
Projects(),
TimedProject(),
Overview(),
Clients(),
];
GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey();
#override
Widget build(BuildContext context) {
return new Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text('TITLE I NEED TO CHANGE DEPENDING ON PAGE',
style: TextStyle(color: Colors.black),
),
backgroundColor: Colors.white,
),
endDrawer: AppDrawer(),
body: _children[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
onTap: onTabTapped,
currentIndex: _currentIndex,
selectedItemColor: Theme.of(context).primaryColor,
type: BottomNavigationBarType.fixed,
items: [
new BottomNavigationBarItem(
icon: Icon(Icons.storage),
title: Text('Jobs'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.timer),
title: Text('Timer'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.pie_chart_outlined),
title: Text('Overview'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.supervisor_account), title: Text('Clients'))
],
),
);
}
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
}
Create a variable that holds the appbar title or you can use the same title variable that is passed in your HomePage class but you have to remove the final.
If you are using the title variable in HomePage class, make sure to use
"widget.title"
class HomePage extends StatefulWidget {
final String title;
HomePage({Key key, this.auth, this.userId, this.onSignedOut, this.title})
: super(key: key);
final BaseAuth auth;
final VoidCallback onSignedOut;
final String userId;
#override
State<StatefulWidget> createState() => new _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _currentIndex = 0;
String _title;
final List<Widget> _children = [
Projects(),
TimedProject(),
Overview(),
Clients(),
];
GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey();
#override
initState(){
_title = 'Some default value';
}
#override
Widget build(BuildContext context) {
return new Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text(_title,
style: TextStyle(color: Colors.black),
),
backgroundColor: Colors.white,
),
endDrawer: AppDrawer(),
body: _children[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
onTap: onTabTapped,
currentIndex: _currentIndex,
selectedItemColor: Theme.of(context).primaryColor,
type: BottomNavigationBarType.fixed,
items: [
new BottomNavigationBarItem(
icon: Icon(Icons.storage),
title: Text('Jobs'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.timer),
title: Text('Timer'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.pie_chart_outlined),
title: Text('Overview'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.supervisor_account), title: Text('Clients'))
],
),
);
}
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
switch(index) {
case 0: { _title = 'Jobs'; }
break;
case 1: { _title = 'Timer'; }
break;
case 2: { _title = 'Overview'; }
break;
case 3: { _title = 'Clients'; }
break;
}
});
}
}
Thanks for the solution, is there away to take the bottom nav and out it in its own .dart file like
bottomNavigationBar: BottomNavBar(),
and get the selected tab index
class _HomeScreen2State extends State<HomeScreen2> {
//Hold current Tab Index
int _selectTab = 0;
final _pageOptions = [
HomeScreen(),
NewOrderScreen(),
OrderHistoryScreen(),
ContactScreen(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Gift Shop Dashboard'),
),
body: _pageOptions[_selectTab],
bottomNavigationBar: BottomNavBar(),
);
}
}
To separate BottomNavigationBar in a different .dart file, for example, in config.dart:
import 'package:flutter/material.dart';
class Config {
static List<BottomNavigationBarItem> navigationBarItems = [
BottomNavigationBarItem(
icon: Icon(
Icons.date_range,
),
title: Text(
"Calendar",
),
),
BottomNavigationBarItem(
icon: Icon(
Icons.list, // event_note
),
title: Text(
"List",
),
),
BottomNavigationBarItem(
icon: Icon(
Icons.bookmark_border, // grid_on
),
title: Text(
"Bookmarks",
),
),
BottomNavigationBarItem(
icon: Icon(
Icons.account_circle,
),
title: Text(
"Me",
),
),
];
static BottomNavigationBar navigationBar = BottomNavigationBar(
items: navigationBarItems,
type: BottomNavigationBarType.fixed,
fixedColor: Colors.red,
);
}
and in main.dart:
import 'package:flutter/material.dart';
import 'config.dart';
void main() => runApp(App());
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Hello',
theme: ThemeData(
primarySwatch: Colors.blueGrey,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
HomePage({Key key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _index = 0;
Text _title;
_onTap(int index) {
setState(() {
_index = index;
_title = Config.navigationBarItems[_index].title;
});
}
#override
void initState() {
_title = Config.navigationBarItems[_index].title;
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: _title,
),
body: Container(
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _index,
type: Config.navigationBar.type,
fixedColor: Config.navigationBar.fixedColor,
items: Config.navigationBar.items,
onTap: _onTap,
),
);
}
}
just make condition in ontap
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 1;
String appbarTitleString = "Home";
var appBarTitleText = new Text("Home");
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: Profile',
style: optionStyle,
),
Text(
'Index 1: Home',
style: optionStyle,
),
Text(
'Index 2: More',
style: optionStyle,
),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: appBarTitleText,
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.business),
title: Text('Profile'),
),
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
title: Text('More'),
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
if (index == 0){
appbarTitleString = "Profile" ;
appBarTitleText = new Text(appbarTitleString);
}else if (index == 1){
appbarTitleString = "Home" ;
appBarTitleText = new Text(appbarTitleString);
}else if(index == 2){
appbarTitleString = "More" ;
appBarTitleText = new Text(appbarTitleString);
}
});
}
}
it is too Simple, using only two variable we can handle it.
Take Two Variables like:
int _selectedIndex = 0;
String _page_title = "DashBoard";
and Replace page title on every index changed. like as below,
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
if (index == 0) {
_page_title = "DashBoard";
} else if (index == 1) {
_page_title = "Search";
} else if (index == 2) {
_page_title = "Setting";
} else if (index == 3) {
_page_title = "Profile";
}
});
make a list which decide to perform task from selected tab.
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Home 1',
style: optionStyle,
),
Text(
'Home 2',
style: optionStyle,
),
Text(
'Home 3',
style: optionStyle,
),
Profile(),
];
And adjust code in Build() function as below.
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text(_page_title),
automaticallyImplyLeading: false, // hide back arrow from appbar
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(
Icons.home,
color: Colors.grey,
),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(
Icons.search,
color: Colors.grey,
),
label: 'Search',
),
BottomNavigationBarItem(
icon: Icon(
Icons.settings,
color: Colors.grey,
),
label: 'Setting',
),
BottomNavigationBarItem(
icon: Icon(
Icons.face_retouching_natural_sharp,
color: Colors.grey,
),
label: 'Profile',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.blue,
onTap: _onItemTapped,
),
);
}
}
☻♥ Done.
In case you also have different actions, leadings, etc for different pages, You can have different appbars in separate files and import them like pages.
static final List<Widget> _pages = [
const HomeScreen(),
const BookingScreen(),
const AccountScreen(),
];
static final List<Widget> _appBars = [
const HomeAppBar(),
const BookingAppBar(),
const AccountAppBar(),
];
Then you can have variable:
int _selectedIndex = 0;
Your onTapped function will be like:
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
Your build would be like this:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(56), // 56 is default height
child: _appBars[_selectedIndex],
), // PreferredSize
body: _pages[_selectedIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
onTap: _onItemTapped,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home_rounded),
label: "Home",
), // BottomNavigationBarItem
BottomNavigationBarItem(
icon: Icon(Icons.schedule_rounded),
label: "Bookings",
), // BottomNavigationBarItem
BottomNavigationBarItem(
icon: Icon(Icons.person_rounded),
label: "Account",
), // BottomNavigationBarItem
],
), // BottomNavigationBar
); // Scaffold
}

How to use BottomNavigationBar with Navigator?

The Flutter Gallery example of BottomNavigationBar uses a Stack of FadeTransitions in the body of the Scaffold.
I feel it would be cleaner (and easier to animate) if we could switch pages by using a Navigator.
Are there any examples of this?
int index = 0;
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Stack(
children: <Widget>[
new Offstage(
offstage: index != 0,
child: new TickerMode(
enabled: index == 0,
child: new MaterialApp(home: new YourLeftPage()),
),
),
new Offstage(
offstage: index != 1,
child: new TickerMode(
enabled: index == 1,
child: new MaterialApp(home: new YourRightPage()),
),
),
],
),
bottomNavigationBar: new BottomNavigationBar(
currentIndex: index,
onTap: (int index) { setState((){ this.index = index; }); },
items: <BottomNavigationBarItem>[
new BottomNavigationBarItem(
icon: new Icon(Icons.home),
title: new Text("Left"),
),
new BottomNavigationBarItem(
icon: new Icon(Icons.search),
title: new Text("Right"),
),
],
),
);
}
You should keep each page by Stack to keep their state.
Offstage stops painting, TickerMode stops animation.
MaterialApp includes Navigator.
Output:
Code:
int _index = 0;
#override
Widget build(BuildContext context) {
Widget child;
switch (_index) {
case 0:
child = FlutterLogo();
break;
case 1:
child = FlutterLogo(colors: Colors.orange);
break;
case 2:
child = FlutterLogo(colors: Colors.red);
break;
}
return Scaffold(
body: SizedBox.expand(child: child),
bottomNavigationBar: BottomNavigationBar(
onTap: (newIndex) => setState(() => _index = newIndex),
currentIndex: _index,
items: [
BottomNavigationBarItem(icon: Icon(Icons.looks_one), title: Text("Blue")),
BottomNavigationBarItem(icon: Icon(Icons.looks_two), title: Text("Orange")),
BottomNavigationBarItem(icon: Icon(Icons.looks_3), title: Text("Red")),
],
),
);
}
Here is an example how you can use Navigator with BottomNavigationBar to navigate different screen.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// This navigator state will be used to navigate different pages
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
int _currentTabIndex = 0;
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Navigator(key: _navigatorKey, onGenerateRoute: generateRoute),
bottomNavigationBar: _bottomNavigationBar(),
),
);
}
Widget _bottomNavigationBar() {
return BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text("Home"),
),
BottomNavigationBarItem(
icon: Icon(Icons.account_circle), title: Text("Account")),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
title: Text("Settings"),
)
],
onTap: _onTap,
currentIndex: _currentTabIndex,
);
}
_onTap(int tabIndex) {
switch (tabIndex) {
case 0:
_navigatorKey.currentState.pushReplacementNamed("Home");
break;
case 1:
_navigatorKey.currentState.pushReplacementNamed("Account");
break;
case 2:
_navigatorKey.currentState.pushReplacementNamed("Settings");
break;
}
setState(() {
_currentTabIndex = tabIndex;
});
}
Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case "Account":
return MaterialPageRoute(builder: (context) => Container(color: Colors.blue,child: Center(child: Text("Account"))));
case "Settings":
return MaterialPageRoute(builder: (context) => Container(color: Colors.green,child: Center(child: Text("Settings"))));
default:
return MaterialPageRoute(builder: (context) => Container(color: Colors.white,child: Center(child: Text("Home"))));
}
}
}
Here is example:
int _currentIndex = 0;
Route<Null> _getRoute(RouteSettings settings) {
final initialSettings = new RouteSettings(
name: settings.name,
isInitialRoute: true);
return new MaterialPageRoute<Null>(
settings: initialSettings,
builder: (context) =>
new Scaffold(
body: new Center(
child: new Container(
height: 200.0,
width: 200.0,
child: new Column(children: <Widget>[
new Text(settings.name),
new FlatButton(onPressed: () =>
Navigator.of(context).pushNamed(
"${settings.name}/next"), child: new Text("push")),
],
))
),
bottomNavigationBar: new BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (value) {
final routes = ["/list", "/map"];
_currentIndex = value;
Navigator.of(context).pushNamedAndRemoveUntil(
routes[value], (route) => false);
},
items: [
new BottomNavigationBarItem(
icon: new Icon(Icons.list), title: new Text("List")),
new BottomNavigationBarItem(
icon: new Icon(Icons.map), title: new Text("Map")),
]),
));
}
#override
Widget build(BuildContext context) =>
new MaterialApp(
initialRoute: "/list",
onGenerateRoute: _getRoute,
theme: new ThemeData(
primarySwatch: Colors.blue,
),
);
You can set isInitialRoute to true and pass it to MaterialPageRoute. It will remove pop animation.
And to remove old routes you can use pushNamedAndRemoveUntil
Navigator.of(context).pushNamedAndRemoveUntil(routes[value], (route) => false);
To set current page you can have a variable in your state _currentIndex and assign it to BottomNavigationBar:
Glad You asked, I experimented with this a couple of months back and tried to simplify this through a blog post. I won't be able to post the complete code here since it is pretty long, But I can certainly link all the resources to clarify it.
Everything about the BottomNavigationBar in flutter
complete sample code
Dartpad demo
If you prefer you can also depend on this package https://pub.dev/packages/navbar_router
Here's the resulting output of what the article helps you build
Navigator.of(context).pushNamedAndRemoveUntil(
routes[value], (route) => true);
I had to use true to enable back button.
NB: I was using Navigator.pushNamed() for navigation.
This is the code I am using in my project. If you try to avoid page viewer so you can try this
import 'package:flutter/material.dart';
class Dashboard extends StatefulWidget {
const Dashboard({Key? key}) : super(key: key);
#override
State<Dashboard> createState() => _DashboardState();
}
class _DashboardState extends State<Dashboard> {
int _selectedIndex = 0;
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Sample'),
),
body: SingleChildScrollView(
child: Column(
children: [
if (_selectedIndex == 0)
// you can call custom widget here
Column(
children: const [
Text("0"),
],
)
else if (_selectedIndex == 1)
Column(
children: const [
Text("1"),
],
)
else
Column(
children: const [
Text("2"),
],
),
],
),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.headphones),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Business',
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'School',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
unselectedItemColor: Colors.grey,
onTap: _onItemTapped,
),
);
}
}
Happy Coding