I am using flutter. I would like to navigate between pages of bottom nav-bar from inside the body of page.
Not to create a new screen on top of it. And carry data between pages.
I am using classic bottom navbar in flutter with pages in body of scaffold.
Something like this.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: Home',
style: optionStyle,
),
Text(
'Index 1: Business',
style: optionStyle,
),
Text(
'Index 2: School',
style: optionStyle,
),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Sample'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Business',
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'School',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
I tried pageview but with that I can switch between pages only on swipe and cant send data between them.
you can also get that widget from other places with A GlobalKey :
First, create a GlobalKey in the global scope ( make it outside you StatefulWidegt class )
final glbKey = GlobalKey();
then assign that key to BottomNavigationBar :
bottomNavigationBar: BottomNavigationBar(
key: glbKey,
items: const <BottomNavigationBarItem>[
// ...
Now, you can call it from your onPressed method like this:
onPressed: () {
//...
BottomNavigationBar navigationBar = glbKey.currentWidget as BottomNavigationBar;
navigationBar.onTap!(2); // change 2 with index of your target screen
}
you can simply simulate running the _onItemTapped() with the index of your target navigation item, in your search button icon onPressed, add this:
onPressed: () {
//...
_onItemTapped(2); // replace 2 with you screen index.
}
this will navigate to the search page programmatically.
I'm creating a simple app consisting of three pages, the only thing to be changed when using the navigation bar is the body. I need every body to be in it's own dart file. whenever I try achieving this and replace the text with another widget, I break the app.
Here's my code so far:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Sample App';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 2: Account',
style: optionStyle,
),
Text(
'Index 1: Locate a Store',
style: optionStyle,
),
Text(
'Index 0: Home',
style: optionStyle,
),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
backgroundColor: Colors.amber[400],
bottomNavigationBar: BottomNavigationBar(
showSelectedLabels: false, // <-- HERE
showUnselectedLabels: false, // <-- AND HERE
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.manage_accounts),
label: 'Account',
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
label: 'Store Locator',
),
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home Page',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amberAccent[400],
onTap: _onItemTapped,
iconSize: 35,
),
);
}
}
To sum it up, The Text widget need to be replaced with new body/container that's saved in its own dart file, and work with the navigation bar.
I'm trying to make a Bottom Navigation Bar in Flutter but it keeps showing a shadow in front of the Navigation Bar. The code below is the code that taken from the flutter websites which has guarantee 100% work. But the fact is I keep failing because it doesn't work well on my Flutter. Is the version/SDK the problem?
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: Home',
style: optionStyle,
),
Text(
'Index 1: Business',
style: optionStyle,
),
Text(
'Index 2: School',
style: optionStyle,
),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Sample'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Business',
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'School',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
Set elevation: 0.0, Inside your BottomNavigationBar widget
child: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
//backgroundColor: Theme.of(context).primaryColor,
elevation: 0.0,
In my project, I used indexing for my BottomNavigationBar items. It controlled all the three main screen I have (Home, Ticketing, & Profile). Literally, I just used the bottom navigation buttons to navigate between the screens. As alternative way, I wanted to custom a button which located in Home screen. So that when it tapped, it will navigate to the Ticketing (2nd screen).
I tried to use Navigator.pushNamed(context, screenName);, but it will stack above all the screens and not showing the BottomNavigationBar.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyNavigationBar (),
);
}
}
class MyNavigationBar extends StatefulWidget {
MyNavigationBar ({Key key}) : super(key: key);
#override
_MyNavigationBarState createState() => _MyNavigationBarState();
}
class _MyNavigationBarState extends State<MyNavigationBar > {
int _selectedIndex = 0;
static const List<Widget> _widgetOptions = <Widget>[
Text('Home Page', style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold)),
Text('Search Page', style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold)),
Text('Profile Page', style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold)),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter BottomNavigationBar Example'),
backgroundColor: Colors.green
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
backgroundColor: Colors.green
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
title: Text('Search'),
backgroundColor: Colors.yellow
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
title: Text('Profile'),
backgroundColor: Colors.blue,
),
],
type: BottomNavigationBarType.shifting,
currentIndex: _selectedIndex,
selectedItemColor: Colors.black,
iconSize: 40,
onTap: _onItemTapped,
elevation: 5
),
);
}
}
in _onItemTapped(int index) method just change your view index like, better to you make one controller class you you can achive any othe widget or class.
void _onItemTapped() {
setState(() {
_selectedIndex = 1;
});
}
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
}