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,
),
);
}
}
Related
This is the main page where I have written the bottom bar navigation code. I have run the code, but the bar does not appear on my home page. It does not give me any errors. which means the code is fine and i just need to call it properly. How do I call the function so it displays across all other pages?
import 'package:thehunt/views/hunt/profile_view.dart';
import 'package:thehunt/views/hunt/settings_view.dart';
import 'package:thehunt/views/login_view.dart';
class HuntView extends StatelessWidget {
const HuntView({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return HomeView();
} else {
return AuthView();
}
},
),
);
}
}
//bottom navigation bar
class BottomNavView extends StatefulWidget {
const BottomNavView({super.key});
#override
State<BottomNavView> createState() => _BottomNavViewState();
}
class _BottomNavViewState extends State<BottomNavView> {
List views = [
const HomeView(),
const CurrentLocationView(),
const ProfileView(),
const SettingsView()
];
int currentIndex = 0;
void onTap(int index) {
currentIndex = index;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: views[currentIndex],
bottomNavigationBar: BottomNavigationBar(
unselectedFontSize: 0,
selectedFontSize: 0,
type: BottomNavigationBarType.fixed,
backgroundColor: Color.fromARGB(255, 198, 176, 235),
onTap: onTap,
currentIndex: currentIndex,
selectedItemColor: Colors.black54,
unselectedItemColor: Colors.grey.withOpacity(0.5),
showUnselectedLabels: false,
showSelectedLabels: false,
elevation: 0,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
label: 'Home',
icon: Icon(Icons.home_outlined),
),
BottomNavigationBarItem(
label: 'Map',
icon: Icon(Icons.map),
),
BottomNavigationBarItem(
label: 'Settings',
icon: Icon(Icons.settings),
),
BottomNavigationBarItem(
label: 'Profile',
icon: Icon(Icons.person),
),
],
),
);
}
}
Below is my home page view where I want the bottom navigation bar to be displayed
class HomeView extends StatefulWidget {
const HomeView({super.key});
#override
State<HomeView> createState() => _HomeViewState();
}
class _HomeViewState extends State<HomeView> {
final user = FirebaseAuth.instance.currentUser!;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(' Home'),
backgroundColor: Colors.deepPurple[200],
elevation: 0,
),
drawer: const NavigationDrawer(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Logged In as: ' + user.email!),
MaterialButton(
onPressed: () {
FirebaseAuth.instance.signOut();
},
color: Colors.deepPurple[200],
child: Text('sign out'),
),
MaterialButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
return const CurrentLocationView();
},
),
);
},
color: Colors.deepPurple[200],
child: const Text('User location')),
],
),
),
);
}
}
Call BottomNavView in HuntView instead of HomeView as below code
class HuntView extends StatelessWidget {
const HuntView({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return BottomNavView(); <------- call here BottomNavView
} else {
return AuthView();
}
},
),
);
}
}
I would like to call function between another clas. So when the menu tapped from grabDrawer it will change the currentIndex at Main() class. Do you know how to do that? Here is so far I have tried.
main.dart
class _MainState extends State<Main> {
int currentIndex = 0;
Map<String,dynamic> searchParameter = {};
List screens = [
Home(),
Search({}),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
actions: [
Builder(builder: (context){
return IconButton(
onPressed: (){
Scaffold.of(context).openEndDrawer();
},
icon: const Icon(Icons.menu),
);
}),
],
),
endDrawer: const Drawer(
child:DrawerObject(),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.arrow_upward),
onPressed: () async{
await Future.delayed(Duration(milliseconds: 100),(){
globals.scrollController.animateTo(0, duration: Duration(milliseconds: 500), curve: Curves.fastOutSlowIn);
});
},
),
body: screens[currentIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: currentIndex,
onTap: (index) => setState(() {
if (index == 1) {
getSearchForm(context);
} else {
currentIndex = index;
searchParameter = {};
}
}),
selectedItemColor: Colors.white,
unselectedItemColor: Colors.grey[100],
type: BottomNavigationBarType.shifting,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
backgroundColor: Colors.blue[500],
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
label: 'Pencarian',
backgroundColor: Colors.orange[500],
),
],
),
);
}
//main function ===> NEED TO CALL THIS FUNCTION INSIDE grabDrawer.dart
Future UpdateIndex({int Index = 0}) async{
setState(() {
currentIndex = Index;
});
}
Future getSearchForm(BuildContext context) async {
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => SearchForm(parameter:searchParameter)),
);
setState(() {
if (result != null) {
currentIndex = 1;
if(result!=searchParameter){
searchParameter = result;
screens[1] = CallLoading(show: ''); //set default to load
//set to new parameter (rebuilding widget)
Future.delayed(Duration(milliseconds: 500),(){
setState(() {
screens[1] = Search(searchParameter);
});
});
}
}
else{
}
});
}
}
Under this file, I need to call function from Main.UpdateIndex.
grabDrawer.dart
class DrawerObject extends StatelessWidget {
const DrawerObject({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: ListView(
children: [
ListTile(
leading: Icon(Icons.home),
title: Text('Cari Properti?'),
onTap: (){
===> CALL IT HERE
}
),
],
),
);
}
}
I really appreciate any answers. Thank you.
Change your grabDrawer.dart like this
class DrawerObject extends StatelessWidget {
void Function()? UpdateIndex;
DrawerObject({
this.UpdateIndex,
});
#override
Widget build(BuildContext context) {
return Container(
child: ListView(
children: [
ListTile(
leading: Icon(Icons.home),
title: Text('Cari Properti?'),
onTap: (){
UpdateIndex!();
}
),
],
),
);
}
}
And in your main.dart, call Drawer class like this
endDrawer: const Drawer(
child:DrawerObject(
UpdateIndex: UpdateIndex,
);
),
Hope this works for you.
Here is the clear way to pass data between one class to another class
void main() {
runApp(MaterialApp(
home: Modalbtn(),
));
}
class Modalbtn extends StatefulWidget {
#override
_ModalbtnState createState() => _ModalbtnState();
}
class _ModalbtnState extends State<Modalbtn> {
String value = "0";
// Pass this method to the child page.
void _update(String newValue) {
setState(() => value = newValue);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
IconButton(
onPressed: () {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
height: 200,
child: Column(
children: [StatefulModalbtn(update: _update)],
),
);
});
},
icon: Icon(Icons.add),
iconSize: 20,
),
Text(
value,
style: TextStyle(fontSize: 40),
),
],
),
),
);
}
}
import 'package:flutter/material.dart';
class StatefulModalbtn extends StatelessWidget {
final ValueChanged<String> update;
StatefulModalbtn({required this.update});
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () => update("100"), // Passing value to the parent widget.
child: Text('Update (in child)'),
);
}
}
I'm trying to make navigation inside the body of the widget where the bottomNavigationBar remains outside, here is my code:
class WrapperController extends GetxController {
int currentIndex = 0;
List<BottomNavigationElement> items = [];
Widget navigationTab({GlobalKey<NavigatorState> naviKey, Widget widget}) {
return Navigator(
key: naviKey,
onGenerateRoute: (routeSettings) {
return GetPageRoute(page: () => widget);
},
);
}
Widget bottomNavigationBar() {
return BottomNavigationBar(
selectedItemColor: Get.theme.accentColor,
type: BottomNavigationBarType.fixed,
currentIndex: currentIndex,
backgroundColor: Get.theme.primaryColor,
onTap: (int index) => _selectTab(index),
items: items.map((e) => e.bottomBarItem).toList(),
);
}
void _selectTab(int index) {
if (index == currentIndex) {
items[index]
.navigationKey
.currentState
.popUntil((route) => route.isFirst);
} else {
currentIndex = index;
}
update();
}
Future<bool> onWillPop() async {
final isFirstRouteInCurrentTab =
!await items[currentIndex].navigationKey.currentState.maybePop();
if (isFirstRouteInCurrentTab) {
if (currentIndex != 0) {
// _selectTab(1);
return false;
}
}
return isFirstRouteInCurrentTab;
}
#override
void onInit() {
super.onInit();
items = [
//Каталог
BottomNavigationElement(
bottomBarItem: BottomNavigationBarItem(
icon: Icon(Icons.search),
label: "Home",
),
bottomBarView: HomePage(),
navigationKey: Get.nestedKey('0'),
),
//Любимое
BottomNavigationElement(
bottomBarItem: BottomNavigationBarItem(
icon: Icon(Icons.favorite_border),
label: "Favorites",
),
bottomBarView: FavoritesPage(),
navigationKey: Get.nestedKey('1'),
),
//Корзина
BottomNavigationElement(
bottomBarItem: BottomNavigationBarItem(
icon: Icon(Icons.shopping_cart_outlined),
label: "Cart",
),
bottomBarView: CartPage(),
navigationKey: Get.nestedKey('2'),
),
//Заказы
BottomNavigationElement(
bottomBarItem: BottomNavigationBarItem(
icon: Icon(Icons.history),
label: "History",
),
bottomBarView: HistoryPage(),
navigationKey: Get.nestedKey('3'),
),
//Меню
BottomNavigationElement(
bottomBarItem: BottomNavigationBarItem(
icon: Icon(Icons.menu),
label: "Menu",
),
bottomBarView: MenuPage(),
navigationKey: Get.nestedKey('4'),
),
];
}
}
class BottomNavigationElement {
Widget bottomBarView;
BottomNavigationBarItem bottomBarItem;
GlobalKey<NavigatorState> navigationKey;
BottomNavigationElement({
#required this.bottomBarView,
#required this.bottomBarItem,
#required GlobalKey<NavigatorState> navigationKey,
});
}
and here is the Wrapper widget :
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GetBuilder<WrapperController>(builder: (controller) {
return Scaffold(
body: IndexedStack(
index: controller.currentIndex,
children: controller.items
.map((e) => controller.navigationTab(
naviKey: e.navigationKey,
widget: e.bottomBarView,
))
.toList(),
),
bottomNavigationBar: controller.bottomNavigationBar(),
);
});
}
}
here is the HomePage("Search") which has a navigation button:
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home"),
),
body: Center(
child: ElevatedButton(
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => Detail(),
),
),
child: Text("Detail"),
),
),
);
}
}
the question is, when I use Navigator.of (context) Get.to () instead, then the navigation is done entirely and not inside the body. I need internal navigation using Get.to (), how do I do this?
There is one quick solution
Create a separate navigator for the bottom navbar (if you want to separate auth from the main logic).
Code for that is:
Expanded(
child: Navigator(
key: Get.nestedKey(1),
initialRoute: controller.pageName.value,
onGenerateRoute: HomeRouter.generateRoute,
)
)
For Navigation
void gotoPage(String page, BuildContext context) {
pageName.value = page;
print("Request to go to ${pageName.value}");
Get.keys[1]!.currentState!.pushNamed(page);
//if you want to change the URL in browser as well
html.window.history.pushState(null, "/", page);
}
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));
},
),
),
);
}
}
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"),
),
);
}
}