My app has a bottom navigation bar with 4 items one of them being home.
Basically you need to login to use the other 3 items such as profile,messages,orders
So i created a login page for profile if the user is not logged it sends the user to that page and if the user is logged in it sends the user to the actual profile page. It works but the problem is if the user is logged in and wants to switch between tabs using the navigation bar like going to messages and back to profile etc. It shows the login page for a split second then the actual page it is hard to notice but very annoying.
I dont understand why it doesnt see that it has data the first time. I tried delaying reading of data by 100ms so that it wouldnt rush it but couldnt get that to work. I used a pageview to animate between tabs on navigation bar that way it didnt flicker between loginPage and the actual page but animating from Home(page 0) to profile(page 3) it animates between the pages and it looks like fast forwarded rubbish. Is there a way to make this work?
class AuthProfile extends StatelessWidget {
AuthProfile({Key? key}) : super(key: key);
final Stream<User?> authInstance = FirebaseAuth.instance.authStateChanges();
#override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: authInstance,
builder: (context, snapshot) {
if (snapshot.hasData) {
return const Profile();
}
else{
return const ProfileLogin();
}
});
}
}
main:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarBrightness: Brightness.light,
statusBarIconBrightness: Brightness.light,
systemNavigationBarColor: Colors.white,
systemNavigationBarIconBrightness: Brightness.light));
return const MaterialApp(
home: Main(),
);
}
}
class Main extends StatefulWidget {
const Main({super.key});
#override
State<Main> createState() => _Main();
}
class _Main extends State<Main> {
int _selectedIndex = 0;
final PageController _pageController = PageController(initialPage: 0);
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: PageView(
controller: _pageController,
physics: const NeverScrollableScrollPhysics(),
children: [
const Home(),
AuthListings(),
AuthMessages(),
AuthProfile(),
]),
bottomNavigationBar: BottomNavigationBar(
iconSize: 30,
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.white,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home_rounded),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.insert_chart_rounded),
label: 'Listings',
),
BottomNavigationBarItem(
icon: Icon(Icons.mark_as_unread_rounded),
label: 'Messages',
),
BottomNavigationBarItem(
icon: Icon(Icons.account_circle_rounded), label: 'Profile')
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.purple,
unselectedItemColor: Colors.black,
showUnselectedLabels: false,
showSelectedLabels: false,
onTap: _onTappedBar,
),
);
}
void _onTappedBar(int value) {
setState(() {
_selectedIndex = value;
});
_pageController.jumpToPage(value);
}
}
EDIT: Fixed it with the help of solution
created two different lists for loggedIn and loggedOut scenario
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarBrightness: Brightness.light,
statusBarIconBrightness: Brightness.light,
systemNavigationBarColor: Colors.white,
systemNavigationBarIconBrightness: Brightness.light));
return const MaterialApp(
home: Main(),
);
}
}
class Main extends StatefulWidget {
const Main({super.key});
#override
State<Main> createState() => _Main();
}
class _Main extends State<Main> {
int _selectedIndex = 0;
static final List<Widget> _loggedIn = <Widget>[
const Home(),
const Listings(),
const Messages(),
const Profile(),
];
static final List<Widget> _loggedOut = <Widget>[
const Home(),
const ListingsLogin(),
const MessagesLogin(),
const ProfileLogin(),
];
#override
Widget build(BuildContext context) {
final Stream<User?> authInstance = FirebaseAuth.instance.authStateChanges();
return Scaffold(
resizeToAvoidBottomInset: false,
body: Center(
child: StreamBuilder<User?>(
stream: authInstance,
builder: (context, snapshot) {
if (snapshot.hasData) {
return _loggedIn.elementAt(_selectedIndex);
} else {
return _loggedOut.elementAt(_selectedIndex);
}
}),
),
bottomNavigationBar: BottomNavigationBar(
iconSize: 30,
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.white,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home_rounded),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.insert_chart_rounded),
label: 'Listings',
),
BottomNavigationBarItem(
icon: Icon(Icons.mark_as_unread_rounded),
label: 'Messages',
),
BottomNavigationBarItem(
icon: Icon(Icons.account_circle_rounded), label: 'Profile')
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.purple,
unselectedItemColor: Colors.black,
showUnselectedLabels: false,
showSelectedLabels: false,
onTap: _onTappedBar,
),
);
}
void _onTappedBar(int value) {
setState(() {
_selectedIndex = value;
});
}
}
The problem is everytime you go to AuthProfile() Stream checks the authStateChanges() so everytime it shows ProfileLogin() by default and update it with Profile() screen, what you can do is move the logic in your onTapBar function, there check if the user is logged in, then send him only to Profile() or ProfileLogin().
Related
i having a bottom navigation bar that look like this below
class BottomNavigationsBar extends StatefulWidget {
const BottomNavigationsBar({Key? key}) : super(key: key);
#override
State<BottomNavigationsBar> createState() => _BottomNavigationsBarState();
}
class _BottomNavigationsBarState extends State<BottomNavigationsBar> {
int currentIndex = 0;
final List _pages = [
EcoHomePageView(),
ChatCheckLogin(),
EcoNotificationPageView(),
UserCheckLogin(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: _pages[currentIndex],
bottomNavigationBar: BottomNavigationBar(
unselectedItemColor: Colors.grey.shade700,
selectedItemColor: Colors.red,
showSelectedLabels: true,
showUnselectedLabels: true,
type: BottomNavigationBarType.fixed,
onTap: ontap,
currentIndex: currentIndex,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home_outlined),
label: "Home",
activeIcon: Icon(Icons.home)),
BottomNavigationBarItem(
icon: Icon(Icons.message_outlined),
label: "Chat",
activeIcon: Icon(Icons.message)),
BottomNavigationBarItem(
icon: Icon(Icons.notifications_outlined),
label: "Home",
activeIcon: Icon(Icons.notifications)),
BottomNavigationBarItem(
icon: Icon(Icons.person_outline),
label: "User",
activeIcon: Icon(Icons.person)),
],
),
);
}
void ontap(int index) {
setState(() {
currentIndex = index;
});
}
}
it works properly till when i need to go to new screen, using this code below
onTap: () =>
Get.toNamed(Routes.UPLOAD_PRODUCT),
but when i arrive at the other page. the bottom navigation bar still there. how can i directly go to other page leaving the bottom navigation bar behind??
this is the main.dart code
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
await GetStorage.init();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final authC = Get.put(AuthController(), permanent: true);
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: Future.delayed(Duration(seconds: 3)),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return MaterialApp(
home: Scaffold(
body: BottomNavigationsBar(),
),
);
}
return FutureBuilder(
future: authC.firstInitialized(),
builder: (context, snapshot) => SplashScreen(),
);
},
);
}
}
Use persistent_bottom_nav_bar for keep bottom navigation bar to all the screens.
https://pub.dev/packages/persistent_bottom_nav_bar
I have the following code that is working properly:
import 'package:flutter/material.dart';
import 'screen_curiosities.dart';
import 'screen_movies.dart';
import 'screen_releases.dart';
import '../utils/side_menu.dart';
import '../utils/bottom_menu.dart';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int selectedIndex = 0;
List screens = [
ScreenMovies(),
ScreenReleases(),
ScreenCuriosities()
];
void onClicked(int index) {
setState(() {
selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Movies'),
backgroundColor: Colors.black,
),
body: Center(
child: screens.elementAt(selectedIndex),
),
drawer: SideMenu(),
bottomNavigationBar: BottomNavigationBar(
items:[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Movies',
),
BottomNavigationBarItem(
icon: Icon(Icons.new_releases),
label: 'Releases',
),
BottomNavigationBarItem(
icon: Icon(Icons.question_answer),
label: 'Curiosities',
)
],
currentIndex: selectedIndex,
onTap: onClicked,
selectedItemColor: Colors.red[800],
backgroundColor: Colors.black,
unselectedItemColor: Colors.white,
)
);
}
}
Now I am trying to separate the Widget BottomNavigationBar to another file and call him on the property "bottomNavigationBar" from Scaffold. It would be like this:
bottomNavigationBar: BottomMenu()
I did it with the Widget Drawer and it worked, but when I tried the same thing with bottomNavigationBar it wasn't successful.
When I try to use the variable selectedIndex in the new Widget it is always undefined.
I tried many things, but I couldn't solve this. Is there any way to use the Widget bottomNavigationBar in a separated file?
EDIT
Below, follow the 2 files that I need to make this link from the menu to the page in order to make them work together:
file home.dart
import 'package:flutter/material.dart';
import 'screen_curiosities.dart';
import 'screen_movies.dart';
import 'screen_releases.dart';
import '../utils/side_menu.dart';
import '../utils/bottom_menu.dart';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int selectedIndex = 0;
List screens = [
ScreenMovies(),
ScreenReleases(),
ScreenCuriosities()
];
void onClicked(int index) {
setState(() {
selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Movies'),
backgroundColor: Colors.black,
),
body: Center(
child: screens.elementAt(selectedIndex),
),
drawer: SideMenu(),
bottomNavigationBar: BottomMenu() // Using the Widget here
}
}
file bottom_menu.dart
import 'package:flutter/material.dart';
class BottomMenu extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
items:[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Movies',
),
BottomNavigationBarItem(
icon: Icon(Icons.new_releases),
label: 'Releases',
),
BottomNavigationBarItem(
icon: Icon(Icons.question_answer),
label: 'Curiosities',
)
],
currentIndex: selectedIndex, // the variable is undefined
onTap: onClicked, // the function is undefined
selectedItemColor: Colors.red[800],
backgroundColor: Colors.black,
unselectedItemColor: Colors.white,
);
}
}
You can copy paste run two full code below
You can pass selectedIndex and onClicked to BottomMenu
code snippet
bottomNavigationBar: BottomMenu(
selectedIndex: selectedIndex,
onClicked: onClicked,
));
...
class BottomMenu extends StatelessWidget {
final selectedIndex;
ValueChanged<int> onClicked;
BottomMenu({this.selectedIndex, this.onClicked});
working demo
full code main.dart
import 'package:flutter/material.dart';
import 'bottom.dart';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int selectedIndex = 0;
List screens = [ScreenMovies(), ScreenReleases(), ScreenCuriosities()];
void onClicked(int index) {
setState(() {
selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Movies'),
backgroundColor: Colors.black,
),
body: Center(
child: screens.elementAt(selectedIndex),
),
//drawer: SideMenu(),
bottomNavigationBar: BottomMenu(
selectedIndex: selectedIndex,
onClicked: onClicked,
));
}
}
class ScreenMovies extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Text("ScreenMovies"),
);
}
}
class ScreenReleases extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Text("ScreenReleases"),
);
}
}
class ScreenCuriosities extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Text("ScreenCuriosities"),
);
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Home(),
);
}
}
full code bottom.dart
import 'package:flutter/material.dart';
class BottomMenu extends StatelessWidget {
final selectedIndex;
ValueChanged<int> onClicked;
BottomMenu({this.selectedIndex, this.onClicked});
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Movies',
),
BottomNavigationBarItem(
icon: Icon(Icons.new_releases),
label: 'Releases',
),
BottomNavigationBarItem(
icon: Icon(Icons.question_answer),
label: 'Curiosities',
)
],
currentIndex: selectedIndex,
onTap: onClicked,
selectedItemColor: Colors.red[800],
backgroundColor: Colors.black,
unselectedItemColor: Colors.white,
);
}
}
I'm trying to keep only one page alive, but it doesn't work. I have all the required stuff.
added the with AutomaticKeepAliveClientMixin<PageName>
added the #override bool get wantKeepAlive => true; at the end
added the super.build(context); inside the build
Here's the code of the Page with the AutomaticKeepAliveClientMixin:
class MatchingPage extends StatefulWidget {
//
static final id = 'match';
//
#override
_MatchingPageState createState() => _MatchingPageState();
}
class _MatchingPageState extends State<MatchingPage> with AutomaticKeepAliveClientMixin<MatchingPage> {
#override
void initState() {
super.initState();
print('MATCHING PAGE');
print(User.uid);
}
#override
Widget build(BuildContext context) {
super.build(context);
setState(() {
screenHeight = MediaQuery.of(context).size.height;
screenWidth = MediaQuery.of(context).size.width;
});
return ModalProgressHUD(
inAsyncCall: showSpinner,
child: AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarColor: Colors.white.withOpacity(0.10),
statusBarIconBrightness: Brightness.dark),
child: Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: Text('MatchPage'),
),
),
),
);
}
#override
bool get wantKeepAlive => true;
}
And here's the code of the Page that has the BottomNavBar:
List<Widget> _pages = [ProfilePage(), MatchingPage(), ChatsPage()];
class HomePage extends StatefulWidget {
//
static String id = 'home';
//
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
//
int pageIndex = 1;
//
#override
Widget build(BuildContext context) {
return CustomScaffoldWithNavBar(
statusBarColor: Colors.white,
statusBarIconBrightness: Brightness.dark,
backgroundColor: Colors.white,
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Colors.white,
currentIndex: pageIndex,
selectedItemColor: Colors.balck,
unselectedItemColor: _kAppIconGray,
type: BottomNavigationBarType.fixed,
showSelectedLabels: false,
showUnselectedLabels: false,
items: [
BottomNavigationBarItem(icon: Icon(...), title: Text('')),
BottomNavigationBarItem(icon: Icon(...), title: Text('')),
BottomNavigationBarItem(icon: Icon(...), title: Text('')),
],
onTap: (int index) {
setState(() => pageIndex = index);
},
),
child: _pages[pageIndex],
);
}
}
Try to use PageView. The PageView widget wraps the current pages with PageStorage, and should keep the page state
List<Widget> _pages = [ProfilePage(), MatchingPage(), ChatsPage()];
class HomePage extends StatefulWidget {
//
static String id = 'home';
//
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
//
int pageIndex = 1;
PageController _pageController;
//
#override
void initState() {
_pageController = PageController(initialPage: pageIndex, keepPage: true);
super.initState();
}
#override
Widget build(BuildContext context) {
return CustomScaffoldWithNavBar(
statusBarColor: Colors.white,
statusBarIconBrightness: Brightness.dark,
backgroundColor: Colors.white,
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Colors.white,
currentIndex: pageIndex,
selectedItemColor: Colors.balck,
unselectedItemColor: _kAppIconGray,
type: BottomNavigationBarType.fixed,
showSelectedLabels: false,
showUnselectedLabels: false,
items: [
BottomNavigationBarItem(icon: Icon(...), title: Text('')),
BottomNavigationBarItem(icon: Icon(...), title: Text('')),
BottomNavigationBarItem(icon: Icon(...), title: Text('')),
],
onTap: (int index) {
setState(() {
pageIndex = index;
_pageController.jumpToPage(index);
} );
},
),
child: PageView(
physics: NeverScrollableScrollPhysics(),
controller: _pageController,
children: _pages,
),
);
}
}
I have a BottomNavigationBar that navigates to other pages when pressing an icon. This works fine, except the value of the currentIndex property in BottomNavigationBar doesn't update when the state updates, which means there is no change on the acual BottomNavigationBar. enter image description here
I'm using a vaiable (_selectedPage) to keep track of the selected index in the BottomNavigationBar, and the value changes when tapping an item, but it's not updating the currentIndex property when the state updates.. why is that?
import 'package:flutter/material.dart';
import 'package:independentproject/pages/home_page.dart';
import 'package:independentproject/pages/cook_recipe.dart';
class PageNavigationBar extends StatefulWidget {
#override
_PageNavigationBarState createState() => _PageNavigationBarState();
}
class _PageNavigationBarState extends State<PageNavigationBar> {
//default page showing in bottom navigation bar will be CookRecipe()
int _selectedPage = 1;
//all pages optional in bottom navigation bar
final List<Widget> _pageOptions = [
HomePage(),
CookRecipe(),
];
void onTapped(int pageTapped) {
setState(() {
//print(pageTapped);
_selectedPage = pageTapped;
Navigator.push(context, MaterialPageRoute(builder: (context) => _pageOptions[pageTapped]));
//print(_selectedPage);
});
}
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
//TODO: currentIndex: doesn't update when the state updates, why?
currentIndex: _selectedPage,
//items showing in bottom navigation bar
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Homepage'),
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
title: Text('Search recipe'),
),
],
onTap: (int pageTapped) {
onTapped(pageTapped);
},
);
}
}
import 'package:flutter/material.dart';
import 'package:independentproject/page_navigation_bar.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Home page'),
),
body: Center(
child: Text('Home page'),
),
bottomNavigationBar: PageNavigationBar(),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:independentproject/page_navigation_bar.dart';
class CookRecipe extends StatefulWidget {
#override
_CookRecipeState createState() => _CookRecipeState();
}
class _CookRecipeState extends State<CookRecipe> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Search recipe'),
),
body: Center(
child: Text('Search recipes'),
),
bottomNavigationBar: PageNavigationBar(),
),
);
}
}
I would advise you to create a widget that will contain the BottomNavigationBar and also PageView that would allow you to switch pages with PageController.
For example:
class _MainScreenState extends State<MainScreen> {
PageController _pageController;
int _page = 1;
Duration pageChanging = Duration(milliseconds: 300);//this is for page animation-not necessary
Curve animationCurve = Curves.linear;//this is for page animation-not necessary
_MainScreenState();
#override
void initState() {
super.initState();
_pageController = PageController(initialPage: 1);
}
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
physics: BouncingScrollPhysics(),
scrollDirection: Axis.horizontal,
controller: _pageController,
onPageChanged: onPageChanged,
children: <Widget>[
//here are all the pages you need:
//CookRecipe(),
],
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(
Icons.message,
),
title: Container(height: 0.0),
),
BottomNavigationBarItem(
icon: Icon(
Icons.home,
),
title: Container(height: 0.0),
),
BottomNavigationBarItem(
icon: Icon(
Icons.person,
),
title: Container(height: 0.0),
),
],
onTap: navigationTapped,
selectedItemColor: Theme.of(context).backgroundColor,
currentIndex: _page,
),
),
);
}
void navigationTapped(int page) {
_pageController.animateToPage(page,duration: pageChanging,
curve: animationCurve,);
}
#override
void dispose() {
super.dispose();
_pageController.dispose();
}
void onPageChanged(int page) {
if (this.mounted){
setState(() {
this._page = page;
});
}}
You can also do this without the PageView,and use only the controller to switch pages.
BTW-you create new instance of the navigation bar when you load a page which is bad practice
it is because PageNavigationBar is a own class, when you call there a setstate only this class updates
take a look at the Provider
a very usefull state management Package
or you can also handle your Page, when the NavBar is your Main Page and you have only one Page
return MaterialApp(
home: Scaffold(
appBar: ownAppBar(_selectedPage),
body: _bodyOptions[_selectedPage],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedPage,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Homepage'),
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
title: Text('Search recipe'),
),
],
onTap: (int pageTapped) {
onTapped(pageTapped);
},
)
),
);
final List<Widget> _bodyOptions = [
HomePage(),
CookRecipe(),
];
You don't need to use Navigator to change pages, I modified your code just try.
import 'package:flutter/material.dart';
main() {
runApp(MaterialApp(home: PageNavigationBar()));
}
class PageNavigationBar extends StatefulWidget {
#override
_PageNavigationBarState createState() => _PageNavigationBarState();
}
class _PageNavigationBarState extends State<PageNavigationBar> {
//default page showing in bottom navigation bar will be CookRecipe()
int _selectedPage = 1;
//all pages optional in bottom navigation bar
final List<Widget> _pageOptions = [
HomePage(),
CookRecipe(),
];
void onTapped(int pageTapped) {
setState(() {
//print(pageTapped);
_selectedPage = pageTapped;
// Navigator.push(context, MaterialPageRoute(builder: (context) => _pageOptions[pageTapped]));
//print(_selectedPage);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: _pageOptions[_selectedPage],
bottomNavigationBar: BottomNavigationBar(
//TODO: currentIndex: doesn't update when the state updates, why?
currentIndex: _selectedPage,
//items showing in bottom navigation bar
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Homepage'),
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
title: Text('Search recipe'),
),
],
onTap: (int pageTapped) {
onTapped(pageTapped);
},
),
);
}
}
class CookRecipe extends StatefulWidget {
#override
_CookRecipeState createState() => _CookRecipeState();
}
class _CookRecipeState extends State<CookRecipe> {
#override
Widget build(BuildContext context) {
return Center(
child: Text('Search recipes'),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Center(
child: Text('Home page'),
);
}
}
I have an app with three routes and uses the bottomNavigationBar to navigate between them. In one of the routes I have a button in the page that will also navigate to one of the pages.
Heres my main page
import 'package:flutter/material.dart';
import 'page_two.dart';
import 'page_three.dart';
void main() {
return runApp(MyApp());
}
/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
List<Widget> _widgetOptions = <Widget>[
Text('Main'),
PageTwo(),
PageThree(),
];
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, color: Colors.black),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.business, color: Colors.black),
title: Text('Business'),
),
BottomNavigationBarItem(
icon: Icon(Icons.business, color: Colors.black),
title: Text('Business'),
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
Page Two
import 'package:flutter/material.dart';
import 'main.dart';
class PageTwo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: RaisedButton(
child: Text('Go page 1'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MyApp()),
);
},
),
);
}
}
and Page Three with a button that navigates to page two
import 'package:flutter/material.dart';
import 'page_two.dart';
class PageThree extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: RaisedButton(
child: Text('Go page 1'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => PageTwo()),
);
},
),
);
}
}
When I press the button on Page Three, it will go to Page Two without the AppBar and the BottomNavigationBar
Use GlobalKey and In PageTwo Widget call MyStatefulWidgetState's _onItemTapped function
You can see working demo and full code below
code snippet
final scakey = new GlobalKey<_MyStatefulWidgetState>();
...
child: Text('Go page 2'),
onPressed: () {
scakey.currentState._onItemTapped(1);
full code
import 'package:flutter/material.dart';
void main() {
return runApp(MyApp());
}
final scakey = new GlobalKey<_MyStatefulWidgetState>();
/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: MyStatefulWidget(key: scakey),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
final myKey = new GlobalKey<_MyStatefulWidgetState>();
List<Widget> _widgetOptions = <Widget>[
Text('Main'),
PageTwo(),
PageThree(),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: myKey,
appBar: AppBar(
title: const Text('BottomNavigationBar Sample'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home, color: Colors.black),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.business, color: Colors.black),
title: Text('Business'),
),
BottomNavigationBarItem(
icon: Icon(Icons.business, color: Colors.black),
title: Text('Business'),
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
class PageTwo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: RaisedButton(
child: Text('Go page 1'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MyApp()),
);
},
),
);
}
}
class PageThree extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: RaisedButton(
child: Text('Go page 2'),
onPressed: () {
scakey.currentState._onItemTapped(1);
/*Navigator.push(
context,
MaterialPageRoute(builder: (context) => PageTwo()),
);*/
},
),
);
}
}
When using navigation bar to navigate between pages, you are tapping on BottomNavigationBarItem to change the index by calling setState() and as the result, build method is triggered with a new _selectedIndex and that index is used to render your appropriate widget.
_widgetOptions.elementAt(_selectedIndex)
Navigator.push on the other hand is just pushing a new route on top of the navigation stack. You are not getting an AppBar or BottomNavigationBar since you don't have them on PageTwo. What I would recommend you is to create a callback function in PageTwo and call that function on button tap. You can now use that callback in MyStatefulWidget to change the index with setState. Here is an example
Declare a final like below in your pages.
final void Function(int index) pageChanged;
In the onTap event of your button, call this function.
widget.pageChanged(1); // PageTwo
In MyStatefulWidget, when you are creating pages, pass the function.
PageTwo(pageChanged:(index){
setState(){_selectedIndex = index;}
});