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,
),
);
}
}
Related
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().
So, I have a "home" page, that has a scaffold, appbar, body, and bottomNavigationBar.
Body is loaded as widget, from a list, depending on what's clicked on bottomNavigationBar.
I'd like to be able to press something in the child widget currently loaded in parent body, and change the parent's body widget to another child widget, while passing a value to the child widget as well.
Think of it like pressing a link in an iframe in a browser, just the iframe changes and you can pass a value like ?id=12.
This is the "home" page:
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _selectedIndex = 0;
final List<Widget> _children = [
Page1(),
Page2(),
Page3(),
Page4()
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
TextButton(
onPressed: () {},
child: Text("Log Out", style: TextStyle(color: Colors.white)),
),
],
backgroundColor: Colors.blue.shade900,
),
body: _children[_selectedIndex],
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.work),
label: 'Page 1',
),
BottomNavigationBarItem(
icon: Icon(Icons.inventory),
label: 'Page 2',
),
BottomNavigationBarItem(
icon: Icon(Icons.alt_route),
label: 'Page 3',
),
BottomNavigationBarItem(
icon: Icon(Icons.article),
label: 'Page 4',
),
],
currentIndex: _selectedIndex,
onTap: _onItemTapped,
type: BottomNavigationBarType.fixed,
),
);
}
}
And this is the child widget that would be defined by _children[_selectedIndex] by pressing one of the buttons in the bottomNavigationBar
class Page1 extends StatefulWidget {
#override
_Page1State createState() => _Page1State();
}
class _Page1State extends State<Page1> {
#override
Widget build(BuildContext context) {
return Text("Click Here",onPressed:(){
//objective here is to open Page5 in the body of HomePage(not in _children list of homepage, and pass it some value (ie. id 12))
},
),
);
}
}
To exemplify expectation, this is what I'd like in Page5 (which should be loaded in the body of HomePage
class Page5 extends StatefulWidget {
#override
_Page5State createState() => _Page5State();
}
class _Page5State extends State<Page5> {
int _variableFromPage1;
#override
Widget build(BuildContext context) {
return Text(_variableFromPage1);
}
}
Can anyone advise the best way of doing that?
You can define custom callback function like the following code sample.
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _selectedIndex = 0;
int _selectedID = 0;
List<Widget> _children;
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
void initState() {
super.initState();
_children = [
Page1(
callback: (value) {
setState(() {
_selectedID = value;
});
},
),
Page2(),
];
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: (_selectedIndex == 0 && _selectedID > 0)
? Page5(id: _selectedID)
: _children[_selectedIndex],
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.work),
label: 'Page 1',
),
BottomNavigationBarItem(
icon: Icon(Icons.inventory),
label: 'Page 2',
),
],
currentIndex: _selectedIndex,
onTap: _onItemTapped,
type: BottomNavigationBarType.fixed,
),
);
}
}
typedef IntCallback(int value);
class Page1 extends StatelessWidget {
Page1({Key key, #required this.callback}) : super(key: key);
final IntCallback callback;
#override
Widget build(BuildContext context) {
return Center(
child: InkWell(
onTap: () {
callback(12);
},
child: Text("Page1 ::: id >>> 12"),
),
);
}
}
class Page2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Text("Page2"),
);
}
}
class Page5 extends StatelessWidget {
Page5({Key key, #required this.id}) : super(key: key);
final int id;
#override
Widget build(BuildContext context) {
return Center(child: Text("Page5 ::: id $id"));
}
}
You try this way
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
// This widget is the root of your application.
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
int _selectedIndex = 0;
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case "Home":
return MaterialPageRoute(builder: (context) => Home());
case "Settings":
return MaterialPageRoute(builder: (context) => Settings());
default:
return MaterialPageRoute(builder: (context) => Default());
}
}
void _onItemTapped(int index) {
switch (index) {
case 1:
_navigatorKey.currentState!
.pushNamedAndRemoveUntil("Home", (route) => false);
break;
case 2:
_navigatorKey.currentState!
.pushNamedAndRemoveUntil("Settings", (route) => false);
break;
default:
_navigatorKey.currentState!
.pushNamedAndRemoveUntil("Default", (route) => false);
}
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Navigator(key: _navigatorKey, onGenerateRoute: generateRoute),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: 'Setting',
),
],
showSelectedLabels: true,
showUnselectedLabels: false,
currentIndex: _selectedIndex,
selectedItemColor: Colors.white,
unselectedItemColor: Color(0xFF9e9e9e),
iconSize: 16,
backgroundColor: Color.fromRGBO(58, 66, 86, 1.0),
onTap: _onItemTapped,
),
),
);
}
}
How is it possible to add new page in pageview widget while I am swiping ?
I tried to setstate after animation and adding new page in list but it doesn't works.
Try this snippet:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
List<String> _list = ['test', 'test', 'test'];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('TabBar Demo'),
),
body: PageView.builder(
onPageChanged: (index) {
_list.add('test');
setState(() {});
},
itemCount: _list.length,
itemBuilder: (context, index) {
return Center(
child: Container(
child: Text(
_list[index],
),
),
);
},
),
);
}
}
import 'package:flutter/material.dart';
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
PageController _pageController = PageController(
initialPage: 0,
);
int currentIndex = 0;
Widget childWidget = ChildWidget(
number: AvailableNumber.First,
);
#override
void dispose() {
_pageController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
selectedItemColor: Theme.of(context).primaryColor,
unselectedItemColor: Colors.grey[500],
currentIndex: currentIndex,
onTap: (value) {
currentIndex = value;
_pageController.animateToPage(
value,
duration: Duration(milliseconds: 200),
curve: Curves.linear,
);
setState(() {});
},
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text("First"),
),
BottomNavigationBarItem(
icon: Icon(Icons.trending_up),
title: Text("Second"),
),
BottomNavigationBarItem(
icon: Icon(Icons.dashboard),
title: Text("Third"),
),
BottomNavigationBarItem(
icon: Icon(Icons.dashboard),
title: Text("Third"),
),
],
),
body: PageView(
controller: _pageController,
onPageChanged: (page) {
setState(() {
currentIndex = page;
});
},
children: <Widget>[
Text('1'),
Text(2'),
Text('3'),
],
),
);
}
}
video example
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 am new to flutter. I am trying to separate the bottomappbar widget from my home screen. Thing is, it is I need to send back the index to the home screen file so I can switch the body of the screen. I've been learning BloC lately, but I think it is an overkill for this case, even though I am going to use it in other parts of the app (hopefully this is the right assumption). So, how can I send the index to the parent?
Parent
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
int _selectedIndex = 0;
final _bottomNavigationPages = [
Screen1(),
Screen2(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
iconTheme: IconThemeData(
color: Colors.blueGrey,
),
title:
Text('xxx', style: new TextStyle(fontWeight: FontWeight.w400)),
),
body: _bottomNavigationPages[_selectedIndex],
bottomNavigationBar: HomeBottomAppBar(),
);
}
}
Child
class HomeBottomAppBar extends StatefulWidget {
#override
_HomeBottomAppBarState createState() => _HomeBottomAppBarState();
}
class _HomeBottomAppBarState extends State<HomeBottomAppBar> {
int _selectedIndex = 0;
void _itemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return BottomAppBar(
shape: CircularNotchedRectangle(),
notchMargin: 5.0,
clipBehavior: Clip.antiAlias,
child: BottomNavigationBar(
items: [
BottomNavigationBarItem(
icon: Icon(Icons.x), title: Text("1")),
BottomNavigationBarItem(
icon: Icon(Icons.x), title: Text("2")),
],
currentIndex: _selectedIndex,
onTap: _itemTapped,
),
);
}
}
Also, I am going under the assumption that this is good practice. Maybe it is just better to have everything in the same file.
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
int _selectedIndex = 0;
final _bottomNavigationPages = [
Screen1(),
Screen2(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
iconTheme: IconThemeData(color: Colors.blueGrey),
title: Text('xxx', style: new TextStyle(fontWeight: FontWeight.w400)),
),
body: _bottomNavigationPages[_selectedIndex],
bottomNavigationBar: HomeBottomAppBar(refresh: _refresh),
);
}
void _refresh(int index) {
setState(() {
_selectedIndex = index;
});
}
}
class HomeBottomAppBar extends StatefulWidget {
final Function refresh;
const HomeBottomAppBar({Key key, this.refresh}) : super(key: key);
#override
_HomeBottomAppBarState createState() => _HomeBottomAppBarState();
}
class _HomeBottomAppBarState extends State<HomeBottomAppBar> {
int _selectedIndex = 0;
void _itemTapped(int index) {
_selectedIndex = index;
widget.refresh(index);
}
#override
Widget build(BuildContext context) {
return BottomAppBar(
shape: CircularNotchedRectangle(),
notchMargin: 5.0,
clipBehavior: Clip.antiAlias,
child: BottomNavigationBar(
items: [
BottomNavigationBarItem(icon: Icon(Icons.x), title: Text("1")),
BottomNavigationBarItem(icon: Icon(Icons.x), title: Text("2")),
],
currentIndex: _selectedIndex,
onTap: _itemTapped,
),
);
}
}