How to use BottomNavigationBar with Navigator? - flutter

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

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.

If I log out, and log back in. the bottom tab navigator does not appear

The routing starts at the main().I dont think the mistake is here.
The first page is CredentialPage() because its set to home
In CredentialPage(), I check if the user is logged in, if he is. I send him to NavigatorView(). The issue could be here maybe.
Once the CredentialPage loads, the user is routed to AllJobsView()
here is a small video of the issue: https://www.youtube.com/watch?v=qkBueUr_gN0
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(
MaterialApp(
title: 'Vendor Management',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const CredentialPage(), // <=======
routes: {
loginRoute: (context) => const LoginView(),
registerRoute: (context) => const RegisterView(),
verifyEmailRoute: (context) => const VerifyEmailView(),
//
allJobsRoute: ((context) => const AllJobsView()),
myJobsRoute: (context) => const MyJobsView(),
newJobRoute: (context) => const CreateUpdateJobView(),
myJobApplicationsRoute: (context) => const JobApplicationView(),
navigatorViewRoute: (context) => const NavigatorView(),
},
),
);
}
class CredentialPage extends StatelessWidget {
const CredentialPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: AuthService.firebase().initialize(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.done:
final user = AuthService.firebase().currentUser;
if (user != null) {
if (user.isEmailVerified) {
return const NavigatorView(); // <============
} else {
return const VerifyEmailView();
}
} else {
return const LoginView();
}
default:
return const CircularProgressIndicator();
}
},
);
}
}
Once the CredentialPage loads, the user is routed to AllJobsView()
class _NavigatorViewState extends State<NavigatorView> {
late final FirebaseCloudStorage _jobsService;
String get userId => AuthService.firebase().currentUser!.id;
#override
void initState() {
_jobsService = FirebaseCloudStorage();
super.initState();
}
int currentIndex = 0;
final screens = [
AllJobsView(), <========
MyJobsView(),
CreateUpdateJobView(),
JobApplicationsView(),
Center(
child: Text('Profile'),
),
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: screens[currentIndex],
bottomNavigationBar: BottomNavigationBar( <=== bottom nav
currentIndex: currentIndex,
selectedItemColor: Colors.white,
backgroundColor: Colors.blue,
type: BottomNavigationBarType.fixed,
selectedFontSize: 10,
onTap: (index) => setState(() => currentIndex = index),
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.dashboard),
label: 'Open jobs',
),
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'My jobs',
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'Create Job',
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Job Applications',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Profile',
),
],
),
);
}
}
the issue was with the nagivation route when i clicked logg in.
instead of sending me to the all jobs, i changed it to navigator view!

Why my pages are not refreshing with bottom navigation in flutter?

I am working with bottom navigation bar in flutter. I want to refresh every tab when tabs are switched. First I tried to reuse one stateless widget for all the tabs. But it is rerendering pages. My code is as follows:
class _CreateOrderState extends State<CreateOrder> {
int _currentTabIndex = 0;
#override
Widget build(BuildContext context) {
final _kTabPages = <Widget>[
FoodCategory(foodCategory: 'nationalFood'),
FoodCategory(foodCategory: 'fastFood'),
FoodCategory(foodCategory: 'dessert'),
FoodCategory(foodCategory: 'drinks'),
];
final _kBottomNavBarItems = <BottomNavigationBarItem>[
const BottomNavigationBarItem(
icon: Icon(Icons.fastfood_outlined),
label: 'Традиционная',
),
const BottomNavigationBarItem(
icon: Icon(Icons.alarm),
label: 'Фаст Фуд',
),
const BottomNavigationBarItem(
icon: Icon(Icons.food_bank_outlined),
label: 'Дессерты',
),
const BottomNavigationBarItem(
icon: Icon(Icons.emoji_food_beverage),
label: 'Напитки',
),
];
assert(_kTabPages.length == _kBottomNavBarItems.length);
final bottomNavBar = BottomNavigationBar(
items: _kBottomNavBarItems,
currentIndex: _currentTabIndex,
type: BottomNavigationBarType.fixed,
onTap: (int index) {
setState(() => _currentTabIndex = index);
},
);
return WillPopScope(
onWillPop: () => _onWillPop(),
child: Scaffold(
appBar: AppBar(
title: const Text('Создание заказа'),
backgroundColor: Theme.of(context).primaryColor,
actions: [
Container(
child: Stack(
children: [
IconButton(
icon: Icon(Icons.shopping_bag_outlined),
onPressed: () =>
Navigator.pushNamed(context, '/orderReview'),
iconSize: 30,
),
],
),
)
],
),
body: _kTabPages[_currentTabIndex],
// body: IndexedStack(
// index: _currentTabIndex,
// children: _kTabPages,
// ),
bottomNavigationBar: bottomNavBar,
),
);
}
This is my stateless widget:
import 'package:counter/blocs/food/food_bloc.dart';
import 'package:counter/data/repository/food_repository.dart';
import 'package:counter/presentation/widgets/Loading.dart';
import 'package:counter/presentation/widgets/MenuItem.dart';
import 'package:counter/presentation/widgets/network_error.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class FoodCategory extends StatelessWidget {
final String foodCategory;
FoodCategory({#required this.foodCategory});
#override
Widget build(BuildContext context) {
FoodRepository foodRepository = FoodRepository(category: this.foodCategory);
return BlocProvider<FoodBloc>(
create: (BuildContext context) =>
FoodBloc(foodRepository: foodRepository)..add(FoodLoadEvent()),
child: Scaffold(
body: BlocBuilder<FoodBloc, FoodState>(
builder: (context, state) {
if (state is FoodInitial) {
return Text('Initial state');
}
if (state is FoodLoadingState) {
return CustomLoading();
}
if (state is FoodLoadedState) {
return ListView.builder(
itemBuilder: (BuildContext context, index) {
return MenuItem(foodItem: state.loadedFoodItems[index]);
},
itemCount: state.loadedFoodItems.length,
);
} else {
return NetworkErrorWidget();
}
},
),
),
);
}
}
But when I used different widgets for all the tabs, it has started to work properly and refreshed.
final _kTabPages = <Widget>[
NationalFood(foodCategory: 'nationalFood'),
FastFoodScreen(foodCategory: 'fastFood'),
DessertsScreen(foodCategory: 'dessert'),
DrinksScreen(foodCategory: 'drinks'),
];
Inside the initState of your FoodCategory or NationalFood widget, add the function the retrieves your data.
Since you are placing the _kTabPages and _kBottomNavBarItems initialization within the build() method, these variables are assigned new values every time there's a change in state (when you change tabs). This is why the tabs keep re-rendering.
To stop this, place your initialization within the initState(). Something like this:
import 'package:flutter/material.dart';
import 'package:test_flutter_app/test.dart';
class CreateOrder extends StatefulWidget {
#override
_CreateOrderState createState() => _CreateOrderState();
}
class _CreateOrderState extends State<CreateOrder> {
int _currentTabIndex = 0;
List<Widget> _kTabPages;
List<BottomNavigationBarItem> _kBottomNavBarItems;
BottomNavigationBar bottomNavBar;
_updateTabs() {
_kTabPages = <Widget>[
FoodCategory(key: UniqueKey(), foodCategory: 'nationalFood'),
FoodCategory(key: UniqueKey(), foodCategory: 'fastFood'),
FoodCategory(key: UniqueKey(), foodCategory: 'dessert'),
FoodCategory(key: UniqueKey(), foodCategory: 'drinks'),
];
_kBottomNavBarItems = <BottomNavigationBarItem>[
const BottomNavigationBarItem(
icon: Icon(Icons.fastfood_outlined),
label: 'Традиционная',
),
const BottomNavigationBarItem(
icon: Icon(Icons.alarm),
label: 'Фаст Фуд',
),
const BottomNavigationBarItem(
icon: Icon(Icons.food_bank_outlined),
label: 'Дессерты',
),
const BottomNavigationBarItem(
icon: Icon(Icons.emoji_food_beverage),
label: 'Напитки',
),
];
bottomNavBar = BottomNavigationBar(
items: _kBottomNavBarItems,
currentIndex: _currentTabIndex,
type: BottomNavigationBarType.fixed,
onTap: (int index) {
setState(() => _currentTabIndex = index);
},
);
assert(_kTabPages.length == _kBottomNavBarItems.length);
}
#override
void initState() {
_updateTabs();
super.initState();
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => true,
child: Scaffold(
appBar: AppBar(
title: const Text('Создание заказа'),
backgroundColor: Theme.of(context).primaryColor,
),
body: _kTabPages[_currentTabIndex],
// body: IndexedStack(
// index: _currentTabIndex,
// children: _kTabPages,
// ),
bottomNavigationBar: bottomNavBar,
),
);
}
}

Is there a way to display the BottomNavigationBar on every View?

I am trying to display the BottomNavigationBar on every View I have, it's working but this is a "dumb" way to do that...
I have a custom BottomNavigationBar which I am inserting in every View.
var selectedIndex = 0;
class CustomBottombar extends StatefulWidget {
CustomBottombar({Key key}) : super(key: key);
#override
_CustomBottombarState createState() => _CustomBottombarState();
}
class _CustomBottombarState extends State<CustomBottombar> {
List _viewList = [FirstView(), SecondView()];
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
currentIndex: selectedIndex,
onTap: _onItemTapped,
items: _items,
);
}
void _onItemTapped(int index) {
setState(() {
selectedIndex = index;
Navigator.of(context).popUntil((route) => route.isFirst
);
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => _viewList[index]),
);
});
}
final _items = [
BottomNavigationBarItem(
icon: Icon(
Icons.refresh,
color: Color(0xFFACACAC),
size: 35,
),
title: Text("first")),
BottomNavigationBarItem(
icon: Icon(
Icons.phone,
color: Color(0xFFACACAC),
size: 35,
),
title: Text("second"),
),
BottomNavigationBarItem(
icon: Icon(
Icons.add_shopping_cart,
color: Color(0xFFACACAC),
size: 35,
),
title: Text("thrid"),
),
];
}
in the _onItemTapped function I pop everything from the "Navigationstack" and then I am displaying the Screen that is in my Items.
in my FirstView() I have then this code
class FirstView extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(""),
bottomNavigationBar: CustomBottombar(),
endDrawer: CustomDrawer(),
body: Center(
child: RaisedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ContactView()),
);
},
child: Text('First'),
),
),
);
}
}
Now I want to move to "ContactView" which is not an Item in the BottomNavigationBar
class ContactState extends State<ContactView> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar("title"),
endDrawer: CustomDrawer(),
bottomNavigationBar: CustomBottombar(),
body: SafeArea(
bottom: true,
child: SingleChildScrollView(
child: Container(child: Text("Contact"),),
)),
);
}
}
I'll also have a lot of other views which are not in the items array but I want to display the BottomNavigationBar on.
My Issue is really this function.
void _onItemTapped(int index) {
setState(() {
selectedIndex = index;
Navigator.of(context).popUntil((route) => route.isFirst
);
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => _viewList[index]),
);
});
}
because here I'm deleting the navigation history to display the View which is in the Items Array.
Is there a standard way to do this, Hopefully, someone can help.
EDIT:
For clarification: I have like 10 Screens. Only 3 of those are navigatiable via BottomNavigationBar, Let's say the first 3 of those 10. now I want to Navigate to Screen4 from Screen1. The navigationbar disappears on screen4. I want Want to keep the Navigationbar on all Screens.
Edit 2
#Dhaval Kansara answer worked for me but I got a new Problem.
I have an enddrawer, before the fix it was above the BottomNavigationBar now the BottomNavigationBar is above.
but I want it like this
Use CupertinoTabBar as shown below for the static BottomNavigationBar.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:mqttdemo/Screen2.dart';
import 'package:mqttdemo/Screen3.dart';
import 'Screen1.dart';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int _currentIndex;
List<Widget> _children;
#override
void initState() {
_currentIndex = 0;
_children = [
Screen1(),
Screen2(),
Screen3(),
];
super.initState();
}
#override
Widget build(BuildContext context) {
return CupertinoTabScaffold(
tabBar: CupertinoTabBar(
currentIndex: _currentIndex,
onTap: onTabTapped,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text("Screen 1"),
),
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text("Screen 2"),
),
BottomNavigationBarItem(
icon: Icon(Icons.home), title: Text("Screen 3")),
],
),
tabBuilder: (BuildContext context, int index) {
return CupertinoTabView(
builder: (BuildContext context) {
return SafeArea(
top: false,
bottom: false,
child: CupertinoApp(
home: CupertinoPageScaffold(
resizeToAvoidBottomInset: false,
child: _children[_currentIndex],
),
),
);
},
);
}
);
}
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
}
Navigate to screen4 from Screen3 as shown below:
class Screen3 extends StatefulWidget {
#override
_Screen3State createState() => _Screen3State();
}
class _Screen3State extends State<Screen3> {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.black,
child: Center(
child: RaisedButton(
child: Text("Click me"),
onPressed: () {
Navigator.of(context, rootNavigator: false).push(MaterialPageRoute(
builder: (context) => Screen4(), maintainState: false));
},
),
),
);
}
}

Flutter/Dart: Navigator routes always take me to home screen with the selectedIndex = 0

I am having an issue within my app where when I navigate to a screen within my HomeScreen, e.g Navigator.of(context).push_________(Screen.routeName) and click back on the app bar, it always takes me to the HomeScreen with the selectedIndex of the HomeScreen equal to 0. This might be an easy solution I'm fairly new to programming. I believe it has something to do with the fact that selectedValue is initialized to 0 in my HomeScreen<State> class.
Here's my code. I think I just need to make that value depends on where I navigate BACK from..(I want to go back to whatever index I Navigated from.
For example, if I am on _selectedIndex = 2, and I click to go into a screen within _selectedIndex = 2 when I click the back button, I want to go back to the HomeScreen but with _selectedIndex = 2
class _HomeScreenState extends State<HomeScreen> {
int _selectedIndex = 0; <--------------------------
static const TextStyle optionStyle = TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static List<Widget> _widgetOptions = <Widget>[
Screen1(), //index 0
Screen2(), //index 1
Screen3(), //index 2
Screen4(), //index 3
Screen5(), //index 4
];
#override
Widget build(BuildContext context) {
final authData = Provider.of<Auth>(context, listen: true);
final filters = Provider.of<Filters>(context, listen: true);
return Scaffold(
appBar: AppBar(
body: Center(
child:
_widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Colors.black,
elevation: 0,
iconSize: 22,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home,
color: _selectedIndex == 0 ? Colors.greenAccent : Colors.grey,),
title: Text(''),
),
BottomNavigationBarItem(
icon: Icon(FontAwesomeIcons.home,
color: _selectedIndex == 1 ? Colors.greenAccent : Colors.grey,),
title: Text(''),
),
BottomNavigationBarItem(
icon: Icon(Icons.home,
color: _selectedIndex == 2 ? Colors.greenAccent : Colors.grey,),
title: Text(''),
),
BottomNavigationBarItem(
icon: Icon(Icons.home,
color: _selectedIndex == 3 ? Colors.greenAccent : Colors.grey,),
title: Text(''),
),
BottomNavigationBarItem(
icon: Icon(Icons.home,
color: _selectedIndex == 4 ? Colors.greenAccent : Colors.grey,),
title: Text(''),
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.greenAccent,
onTap: _onItemTapped,
),
I navigate from _selectedIndex = 2 (the third tab) to a screen within _selectedIndex = 2 like this
Navigator.of(context).pushNamed(ChatScreen.routeName);
And then from that screen when I click the back button on the appBar which onPressed is defined as
leading: IconButton(icon: Icon(Icons.arrow_back), onPressed: () {
Navigator.pop(context);
} ),
It takes me back to the HomeScreen but with selectedIndex = 0. I want to go back to selectedIndex = 2.
Can you please share us your _onItemTapped method?
If you're just using Navigator.push, Navigator.pushNamed or navigating without disposing the previous route, then using Navigator.pop() alone should do the job. Exiting the current route whilst persisting the previous route's state.
However, since you are already using provider, I suggest using it all the way for handling screen wide or global states.
Here's an example how you can achieve that:
main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class AppNotifier extends ChangeNotifier {
var selectedIndex = 0;
void changeSelectedIndexPage(int index) {
selectedIndex = index;
notifyListeners();
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) => AppNotifier(),
),
],
child: MaterialApp(
home: HomeScreen(),
),
);
}
}
class HomeScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _HomeScreenState();
}
}
class _HomeScreenState extends State<HomeScreen> {
static List<Widget> _childWidgets = <Widget>[
HomePage(),
SettingsPage(),
];
#override
Widget build(BuildContext context) {
AppNotifier _appNotifier = Provider.of<AppNotifier>(context);
return Scaffold(
body: _childWidgets.elementAt(_appNotifier.selectedIndex),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _appNotifier.selectedIndex,
onTap: (int index) {
_appNotifier.changeSelectedIndexPage(index);
},
items: [
BottomNavigationBarItem(
icon: Icon(
Icons.home,
),
title: Text("Home"),
),
BottomNavigationBarItem(
icon: Icon(
Icons.settings,
),
title: Text("Settings"),
),
],
),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home"),
),
body: Center(
child: RaisedButton(
onPressed: () {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) => JustAnotherNewScreen(),
),
);
},
child: Text("Open new page"),
),
),
);
}
}
class SettingsPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Settings"),
),
body: Center(
child: RaisedButton(
onPressed: () {
// Let's create a new instance of the other screen
// And destroy home's screen instance
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) => JustAnotherNewScreen(),
),
);
},
child: Text("Open new page"),
),
),
);
}
}
class JustAnotherNewScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
// Navigator.pop(context);
// Let's create a new instance of the home page
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) => HomeScreen(),
),
);
},
),
),
body: Center(
child: Text("Just another new page"),
),
);
}
}