Preserve state of widget in flutter even though parent widget rebuilds - flutter

I'm trying to preserve the state of widget pages when switching between widgets using BottomNavigationBar. I've read here that I can do this using IndexedStack, however, that doesn't work in my case for two reasons:
The Scaffold in which the pages are displayed gets rebuilt when switching between pages because for some, but not all, pages the Scaffold should be extended: Scaffold( extendBody: _pageIndex == 1, ...)
The pages should be built for the first time just when the page is opened for the first time and not right from the start
Here's a small example that shows that IndexStack is not working as intended because the Scaffold rebuilds:
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int _pageIndex = 1;
List<Widget> _pages = [Text("hi"), Counter()];
#override
Widget build(BuildContext context) {
return Scaffold(
extendBody: _pageIndex == 1,
appBar: AppBar(),
body: IndexedStack(
children: _pages,
index: _pageIndex,
),
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Goto 0',),
BottomNavigationBarItem(icon: Icon(Icons.business), label: 'Goto 1',),
],
currentIndex: _pageIndex,
onTap: (int index) {
setState(() {
_pageIndex = index;
});
print("idx " + _pageIndex.toString());
},
),
);
}
}
Demo showing that the state is not preserved
This is the Counter which can be replaced by any other stateful widget:
class Counter extends StatefulWidget {
#override
_CounterState createState() => _CounterState();
}
//this part is not important, just to show that state is lost
class _CounterState extends State<Counter> {
int _count = 0;
#override
void initState() {
_count = 0;
super.initState();
}
#override
Widget build(BuildContext context) {
return Center(
child: TextButton(
child: Text("Count: " + _count.toString(), style: TextStyle(fontSize: 20),),
onPressed: () {
setState(() {
_count++;
});
},
),
);
}
}

First off, great question! The trick is to use KeyedSubtree, and conditionally render pages depending on if they have been visited yet or not.
You could adapt your code this way to achieve your desired behavior:
class Page {
const Page(this.subtreeKey, {required this.child});
final GlobalKey subtreeKey;
final Widget child;
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
var _pageIndex = 1;
final _pages = [
Page(GlobalKey(), child: Text('Hi')),
Page(GlobalKey(), child: Counter()),
];
final _builtPages = List<bool>.generate(2, (_) => false);
#override
Widget build(BuildContext context) {
return Scaffold(
extendBody: _pageIndex == 1,
appBar: AppBar(),
body: Stack(
fit: StackFit.expand,
children: _pages.map(
(page) {
return _buildPage(
_pages.indexOf(page),
page,
);
},
).toList(),
),
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Goto 0',
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Goto 1',
),
],
currentIndex: _pageIndex,
onTap: (int index) {
setState(() {
_pageIndex = index;
});
print("idx " + _pageIndex.toString());
},
),
);
}
Widget _buildPage(
int tabIndex,
Page page,
) {
final isCurrentlySelected = tabIndex == _pageIndex;
_builtPages[tabIndex] = isCurrentlySelected || _builtPages[tabIndex];
final Widget view = KeyedSubtree(
key: page.subtreeKey,
child: _builtPages[tabIndex] ? page.child : Container(),
);
if (tabIndex == _pageIndex) {
return view;
} else {
return Offstage(child: view);
}
}
}
You should be able to modify this code to add more tabs, functionality, etc.

Related

Why doesn't BottomNavigatorBarItem currentIndex get updated?

I am new to flutter and I did find similar questions on SO but am too novice to understand the nuance so apologies if this question is too similar to an already asked one.
I have a BottomNavigationBar which has 3 icons (Home, Play and Create) which should navigate between these 3 routes/pages.
main.dart
routes: {
"/home": (context) => MyHomePage(title: "STFU"),
"/play": (context) => Play(),
"/create": (context) => Create(),
"/settings": (context) => Settings(),
},
I extracted my navbar into a custom class so my 3 separate pages could use it:
bottom-nav.dart
class MyBottomNavBar extends StatefulWidget {
MyBottomNavBar({
Key? key,
}) : super(key: key);
#override
State<MyBottomNavBar> createState() => _MyBottomNavBarState();
}
class _MyBottomNavBarState extends State<MyBottomNavBar> {
int _selectedIndex = 0;
void _onTapped(int index) => {
print("_onTapped called with index = $index"),
setState(
() => _selectedIndex = index,
)
};
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
backgroundColor: Colors.orangeAccent[100],
currentIndex: _selectedIndex,
onTap: (value) => {
print("value is $value"),
// find index and push that
_onTapped(value),
if (value == 0)
{Navigator.pushNamed(context, "/home")}
else if (value == 1)
{Navigator.pushNamed(context, "/play")}
else if (value == 2)
{Navigator.pushNamed(context, "/create")}
},
items: [
BottomNavigationBarItem(label: "Home", icon: Icon(Icons.home_filled)),
BottomNavigationBarItem(
label: "Play", icon: Icon(Icons.play_arrow_rounded)),
BottomNavigationBarItem(label: "Create", icon: Icon(Icons.create)),
],
);
}
}
so now i just set this MyBottomNavBar class to the bottomNavigationBar property of the Scaffold widget my inside Home page, Play page and Create page for eg.
home.dart
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home"),
),
body: Column(
children: [
Container(
padding: EdgeInsets.all(20.00),
child: Text("inside home"),
),
],
),
bottomNavigationBar: MyBottomNavBar(),
);
}
}
play.dart
class Play extends StatefulWidget {
const Play({super.key});
#override
State<Play> createState() => _PlayState();
}
class _PlayState extends State<Play> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text("inside play page"),
SizedBox(
height: 30.00,
),
Text("some text"),
],
),
),
bottomNavigationBar: MyBottomNavBar(),
);
}
}
The nav bar buttons work to switch between pages but for some reason the currentIndex value isn't getting updated and stays at 0 (i.e on the "Home" icon). When I debug it I can see _selectedIndex getting updated inside inside the _onTapped function which should update the currentIndex value but it doesn't appear to do so. Any help would be appreciated
What you have here is three different pages with their separate BottomNavBar class instance. Instead you should have a shared Scaffold and one bottomNavBar so that when you navigate bottomNavbar state does not reset.
You can use PageView to do this.
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
super.initState();
}
List<Widget> pages = const [Home(), Play(), Create()];
final _pageController = PageController();
int _selectedIndex = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
onTap: (index) {
_pageController.jumpToPage(index);
_selectedIndex = index;
setState(() {});
},
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.play_arrow), label: 'Play'),
BottomNavigationBarItem(icon: Icon(Icons.create), label: 'Create'),
],
backgroundColor: Colors.greenAccent,
),
body: PageView(
controller: _pageController,
children: pages,
),
);
}
}
class Home extends StatelessWidget {
const Home({super.key});
#override
Widget build(BuildContext context) {
return const Placeholder();
}
}
class Play extends StatelessWidget {
const Play({super.key});
#override
Widget build(BuildContext context) {
return const Placeholder();
}
}
class Create extends StatelessWidget {
const Create({super.key});
#override
Widget build(BuildContext context) {
return const Placeholder();
}
}

How to reload page on indexedstack in Flutter

Currently i'm using indexedstack and bottomnavigationbar, there are two pages "HomePage" & "SearchPage". These two pages i put in a children inside indexedstack widget. Now the problem is
If i switch to Search Page or switch back to Home Page it does not reload the page. How to solve this issue using the current widget which is indexedstack widget.
Whenever i run the app it loads all the pages including Search page which is not a current page.
Below is the sample code.
main
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: App(),
);
}
}
class App extends StatefulWidget {
#override
State<StatefulWidget> createState() => AppState();
}
class AppState extends State<App> {
static int currentTab = 0;
final List<TabItem> tabs = [
TabItem(
tabName: "Home",
icon: Icons.home_outlined,
page: HomePage(),
),
TabItem(
tabName: "Search",
icon: Icons.search,
page: SearchPage(),
),
];
AppState() {
tabs.asMap().forEach((index, details) {
details.setIndex(index);
});
}
void _selectTab(int index) {
if (index == currentTab) {
tabs[index].key.currentState!.popUntil((route) => route.isFirst);
} else {
setState(() => currentTab = index);
}
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
final isFirstRouteInCurrentTab =
!await tabs[currentTab].key.currentState!.maybePop();
if (isFirstRouteInCurrentTab) {
if (currentTab != 0) {
_selectTab(0);
return false;
}
}
return isFirstRouteInCurrentTab;
},
child: Scaffold(
body: IndexedStack(
index: currentTab,
children: tabs.map((e) => e.page).toList(),
),
bottomNavigationBar: BottomNavigation(
onSelectTab: _selectTab,
tabs: tabs,
),
),
);
}
}
TabItem
class TabItem {
final String tabName;
final IconData icon;
final GlobalKey<NavigatorState> key = GlobalKey<NavigatorState>();
int _index = 0;
late Widget _page;
TabItem({
required this.tabName,
required this.icon,
required Widget page,
}) {
_page = page;
}
void setIndex(int i) {
_index = i;
}
int getIndex() => _index;
Widget get page {
return Visibility(
visible: _index == AppState.currentTab,
maintainState: true,
child: Navigator(
key: key,
onGenerateRoute: (routeSettings) {
return MaterialPageRoute(
builder: (_) => _page,
);
},
),
);
}
}
BottomNavigation
class BottomNavigation extends StatelessWidget {
BottomNavigation({
required this.onSelectTab,
required this.tabs,
});
final ValueChanged<int> onSelectTab;
final List<TabItem> tabs;
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: tabs
.map(
(e) => _buildItem(
index: e.getIndex(),
icon: e.icon,
tabName: e.tabName,
),
)
.toList(),
onTap: (index) => onSelectTab(
index,
),
);
}
BottomNavigationBarItem _buildItem(
{required int index, required IconData icon, required String tabName}) {
return BottomNavigationBarItem(
icon: Icon(
icon,
color: _tabColor(index: index),
),
title: Text(
tabName,
style: TextStyle(
color: _tabColor(index: index),
fontSize: 12,
),
),
);
}
Color _tabColor({required int index}) {
return AppState.currentTab == index
? Colors.red
: Colors.black;
}
}

How retain Bottom Nav when navigating between page views

I have 4 Pages a page view with their respective bottom navigator, At the first Index, I'm executing a function that navigates to the 4th page. However, after navigation, The bottom nav disappears, which isn't consistent. How do I make it consistent..,
class HomePage extends StatefulWidget {
HomePage({
Key key,
this.category,
this.shopname,
}) : super(key: key); //update this to include the uid in the constructor
final String shopname;
final DocumentSnapshot category;
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
Page controller,
PageController _tabsPageController;
int _selectedTab = 0;
#override
void initState() {
_tabsPageController = PageController();
super.initState();
}
#override
void dispose() {
_tabsPageController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: PageView(
controller: _tabsPageController,
onPageChanged: (num) {
setState(() {
_selectedTab = num;
});
},
children: [
Navigator at landing page
LandingPage(), //<<================
SavedTab(
shopname: widget.shopname,
),
MyDrawer(),
Navigates to HomePage()
HomeTab() //<<================
],
),
),
BottomTabs(
selectedTab: _selectedTab,
tabPressed: (num) {
_tabsPageController.animateToPage(num,
duration: Duration(milliseconds: 300),
curve: Curves.easeOutCubic);
},
),
],
),
);
}
}
You actually just need the Bottom Navigator Bar that routes to the desired pages. It basically renders all the screens inside the BottomNavBar widget so the Bottom Navigation Bar is always showing. Here is an example:
class BottomNavBar extends StatefulWidget {
#override
_BottomNavBarState createState() => _BottomNavBarState();
}
class _BottomNavBarState extends State<BottomNavBar> {
int _selectedIndex = 0;
static List<Widget> _widgetOptions = <Widget>[
Screen1(),
Screen2(),
Screen3(),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.dashboard),
label: 'Screen 1',
),
BottomNavigationBarItem(
icon: Icon(Icons.work),
label: 'Screen 2',
),
BottomNavigationBarItem(
icon: Icon(Icons.directions_car),
label: 'Screen 3',
),
],
currentIndex: _selectedIndex,
onTap: _onItemTapped,
),
);
}
}
Let me know in the comments if it works for you.
Check my code
https://github.com/hoc081098/Movie-Ticket-Booking/blob/master/MobileApp/datn/lib/ui/app_scaffold.dart
or
https://github.com/hoc081098/nested_navigation
Custom Scaffold
class AppScaffold extends StatefulWidget {
final List<BottomNavigationBarItem> items;
final List<AppScaffoldWidgetBuilder> builders;
const AppScaffold({
Key? key,
required this.items,
required this.builders,
}) : super(key: key);
#override
_AppScaffoldState createState() => _AppScaffoldState();
static NavigatorState navigatorOfCurrentIndex(
BuildContext context, {
AppScaffoldIndex? switchToNewIndex,
}) {
final appScaffoldState =
context is StatefulElement && context.state is _AppScaffoldState
? context.state as _AppScaffoldState
: context.findAncestorStateOfType<_AppScaffoldState>()!;
final currentIndex = appScaffoldState.currentIndex;
final navigatorKeys = appScaffoldState.navigatorKeys;
final newIndex = switchToNewIndex?.rawValue;
if (newIndex != null &&
newIndex != currentIndex &&
appScaffoldState.mounted) {
appScaffoldState.onTap(newIndex);
return navigatorKeys[newIndex].currentState!;
}
return navigatorKeys[currentIndex].currentState!;
}
static NotReplayValueStream<AppScaffoldIndex> currentIndexStream(
BuildContext context) =>
context.findAncestorStateOfType<_AppScaffoldState>()!.indexS;
static NavigatorState navigatorByIndex(
BuildContext context,
AppScaffoldIndex index,
) {
final appScaffoldState =
context.findAncestorStateOfType<_AppScaffoldState>()!;
return appScaffoldState.navigatorKeys[index.rawValue].currentState!;
}
}
class _AppScaffoldState extends State<AppScaffold> with DisposeBagMixin {
var navigatorKeys = <GlobalKey<NavigatorState>>[];
final indexS = ValueSubject(AppScaffoldIndex.home, sync: true);
#pragma('vm:prefer-inline')
#pragma('dart2js:tryInline')
int get currentIndex => indexS.requireValue.rawValue;
#override
void initState() {
super.initState();
navigatorKeys = List.generate(
widget.builders.length,
(_) => GlobalKey<NavigatorState>(),
);
indexS.disposedBy(bag);
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () {
final navigatorState = navigatorKeys[currentIndex].currentState!;
final canPop = navigatorState.canPop();
if (canPop) {
navigatorState.maybePop();
}
if (!canPop && currentIndex > 0) {
onTap(0);
return Future.value(false);
}
return Future.value(!canPop);
},
child: RxStreamBuilder<AppScaffoldIndex>(
stream: indexS,
builder: (context, snapshot) {
final index = snapshot!.rawValue;
return Scaffold(
body: buildBody(index),
bottomNavigationBar: BottomNavigationBar(
items: widget.items,
type: BottomNavigationBarType.fixed,
currentIndex: index,
onTap: onTap,
),
);
},
),
);
}
void onTap(final int newIndex) {
if (currentIndex == newIndex) {
navigatorKeys[currentIndex]
.currentState
?.popUntil((route) => route.isFirst);
} else {
indexS.add(_fromRawValue(newIndex));
}
}
Widget buildBody(int index) {
return IndexedStack(
index: index,
children: [
for (int i = 0; i < widget.builders.length; i++)
Navigator(
key: navigatorKeys[i],
onGenerateRoute: (settings) => MaterialPageRoute(
settings: settings,
builder: (context) => widget.builders[i](context, settings),
),
observers: [
HeroController(),
],
)
],
);
}
}
Home page
return AppScaffold(
key: appScaffoldKey,
builders: [
(context, settings) => homeRoutes[settings.name]!(context, settings),
(context, settings) =>
favoritesRoutes[settings.name]!(context, settings),
(context, settings) =>
notificationsRoutes[settings.name]!(context, settings),
(context, settings) => profileRoutes[settings.name]!(context, settings),
],
items: [
BottomNavigationBarItem(
icon: const Icon(Icons.home_rounded),
label: S.of(context).home,
),
BottomNavigationBarItem(
icon: const Icon(Icons.favorite_rounded),
label: S.of(context).favorites,
),
BottomNavigationBarItem(
icon: const Icon(Icons.notifications),
label: S.of(context).notifications,
),
BottomNavigationBarItem(
icon: const Icon(Icons.person_rounded),
label: S.of(context).profile,
),
],
);

Screen navigation in flutter

Update:
The app has two stateful widget screens: Home, and Search. Both screens have search boxes and a bottom navigation.
The problem that needs to be solved is when a user taps the search box at the top of the home screen, the app should take them to the search screen without hiding the bottom navigation (just like what the eBay app does).
I have tried calling the Search class when the user taps the search box on the Home screen. And this approach works. However, the new screen hides the navigation bar at the bottom.
The following code handles the navigation between screens.
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int _currentIndex = 0;
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Navigator(
key: _navigatorKey,
onGenerateRoute: (RouteSettings settings) {
switch (settings.name) {
case 'Search':
return MaterialPageRoute(builder: (context) => Search());
default:
return MaterialPageRoute(builder: (context) => UserHome());
}
}),
bottomNavigationBar: BottomNavigationBar(
onTap: _onTap,
items: [
BottomNavigationBarItem(icon: Icon(Icons.home),title: Text('Home'))
BottomNavigationBarItem(icon: Icon(Icons.search), title: Text('Search'))
],
),
);
}
void _onTap(int tappedIndex) {
setState(() => _currentIndex = tappedIndex);
switch (tappedIndex) {
case 0:
_navigatorKey.currentState.pushReplacementNamed('Home');
break;
case 1:
_navigatorKey.currentState.pushReplacementNamed('Search');
break;
}
}
}
If you are trying to do this for automated testing. You can do so using widget testing. Widget tests in flutter can simulate button taps and check for the expected output
class TestPage extends StatefulWidget {
#override
_TestPageState createState() => _TestPageState();
}
class _TestPageState extends State<TestPage> {
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
bool home;
#override
void initState() {
super.initState();
home = true;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: home ? UserHome() : Search(),
bottomNavigationBar: BottomNavigationBar(
onTap: _onTap,
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), title: Text('Home')),
BottomNavigationBarItem(
icon: Icon(Icons.search), title: Text('Search'))
],
),
);
}
void _onTap(int tappedIndex) {
setState(() {
if (tappedIndex == 0) {
home = true;
} else {
home = false;
}
});
}
}
class UserHome extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Center(
child: Container(
color: Colors.yellow,
child: Text('USER HOME'),
),
)
],
);
}
}
class Search extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Center(
child: Container(
color: Colors.green,
child: Text('SEARCH'),
),
)
],
);
}
}
I know this is not exactly very similar to your initial solution but it achieves the behavior you intend.

Flutter - How to change index of BottomNavigationBar if NavigationBar is not visible?

This is my setup:
Home.dart
final List<Widget> _pages = [
Screen1(),
Screen2(),
Screen3(),
Screen4(),
];
int _selectedPageIndex = 0;
void _selectPage(int index) {
setState(() {
_selectedPageIndex = index;
});}
#override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
body: _pages[_selectedPageIndex],
bottomNavigationBar: _selectedPageIndex != 2
? Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
...
Now when we navigate to Screen3() I'm hiding the complete BottomNavigationBar and show the Screen in Fullscreen. With a button I want to navigate back to any other position. How to do this? I don't want to use any Routes to close. How can we access _selectedPageIndex or do you have another good idea?
Would appreciate any ideas.
You can copy paste run full code below
Step 1: keep previous index, needed when go back from full screen page
void _onItemTapped(int index) {
setState(() {
_previousIndex = _selectedPageIndex;
_selectedPageIndex = index;
});
}
Step 2: pass refresh() callback to full screen page here is Setting()
void refresh() {
setState(() {
_selectedPageIndex = _previousIndex;
});
}
...
case 2:
{
print("settings");
return Settings(
callback: refresh,
);
}
Step 3: In full screen page's Raised Button call this callback
RaisedButton(
onPressed: () {
widget.callback();
},
child: Text("Go back"),
)
working demo
full code
import 'package:flutter/material.dart';
void main() => 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 _selectedPageIndex = 0;
int _previousIndex = 0;
void refresh() {
setState(() {
_selectedPageIndex = _previousIndex;
});
}
void _onItemTapped(int index) {
setState(() {
_previousIndex = _selectedPageIndex;
_selectedPageIndex = index;
});
}
Widget pageCaller(int index) {
print(index);
switch (index) {
case 0:
{
return Category();
}
case 1:
{
return Feed();
}
case 2:
{
print("settings");
return Settings(
callback: refresh,
);
}
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Home'),
),
body: Center(
child: pageCaller(_selectedPageIndex),
),
bottomNavigationBar: _selectedPageIndex == 2
? null
: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Category'),
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
title: Text('Feed'),
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
title: Text('Settings'),
),
],
currentIndex: _selectedPageIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
class Category extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text("Category"),
),
);
}
}
class Feed extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text("Feed"),
),
);
}
}
class Settings extends StatefulWidget {
VoidCallback callback;
Settings({this.callback});
#override
_SettingsState createState() => _SettingsState();
}
class _SettingsState extends State<Settings> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Text("This is setting page"),
RaisedButton(
onPressed: () {
widget.callback();
},
child: Text("Go back"),
),
],
));
}
}