I implemented bottomnavigationbar in the app with two tabs Page A & Page B. In Page B contains one button at the center.
What i want to achieve is that if i click on the button in Page B then it should be back to rootViewController just like in native iOS, so how to achieve this in flutter. Below is the dart code
main.dart
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: "Page A",
icon: Icons.home_outlined,
page: PageA(),
),
TabItem(
tabName: "Page B",
icon: Icons.search,
page: PageB(),
),
];
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.dart
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.dart
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;
}
}
Related
I have a bottom navigation bar and realized that the different pages/widgets that the navigator was going to were pretty much the exact same page (except for 2 parameters that changed). So instead of creating 2 pages/widgets which were pretty much identical (with only 2 differing parameters), I wanted to consolidate it into only one widget and pass the parameters from the page with the bottom navigator. The problem is that now that I did that it won't change the page it displays, or at least it won't change consistently (it usually will only show the page that corresponds to the first tab in the navigator (i.e., index = 0)). Here is my page with the bottom navigator:
class FreestylePage extends StatefulWidget {
const FreestylePage({Key? key}) : super(key: key);
#override
State<StatefulWidget> createState() {
return _FreestylePageState();
}
}
class _FreestylePageState extends State<FreestylePage> {
int _currentIndex = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: showCategory(_currentIndex),
)),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.looks_one_outlined),
label: 'Single rope',
backgroundColor: Color.fromRGBO(204, 16, 138, 1)),
BottomNavigationBarItem(
icon: Icon(Icons.looks_two_outlined),
label: 'Double dutch',
backgroundColor: Color.fromRGBO(204, 16, 138, 1)),
],
onTap: (index) {
if (mounted) {
setState(() {
_currentIndex = index;
});
}
},
),
);
}
showCategory(index) {
if (index == 0) {
return [
WorkoutListPage(categoryIndex: 2, subCategories: Utils.srfDropdown)
];
} else {
return [
WorkoutListPage(categoryIndex: 3, subCategories: Utils.ddfDropdown)
];
}
}
}
And the WorkoutListPage looks as follows:
class WorkoutListPage extends StatefulWidget {
final int categoryIndex;
final List<String> subCategories;
const WorkoutListPage(
{Key? key, required this.categoryIndex, required this.subCategories})
: super(key: key);
#override
State<StatefulWidget> createState() {
return _WorkoutListPageState();
}
}
class _WorkoutListPageState extends State<WorkoutListPage> {
bool isLoading = true;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) =>
FutureBuilder<List<Map<String, dynamic>>>(
future: MyCard.getData(widget.categoryIndex, widget.subCategories)!
.whenComplete(() => setState(() {
isLoading = false;
})),
builder: ((context, snapshot) {
if (snapshot.hasData && snapshot.data!.isNotEmpty) {
return FutureBuilder<List<MyCard>>(
future: MyCard.readData(snapshot.data),
builder: (context, cards) {
if (cards.hasData) {
final card = cards.data!;
return Expanded(
child: ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: card.length,
itemBuilder: (context, index) {
return MyCard.buildCard(card[index], context);
},
),
);
} else {
return const Text("No data");
}
});
} else {
return isLoading
? Column(
children: const [CircularProgressIndicator()],
)
: const Text("You do not have any workouts yet");
}
}),
);
}
This doesn't work, but ironically if I change my showCategory function in the widget with the bottom navigation bar to the following:
showCategory(index) {
if (index == 0) {
return [
WorkoutListPage(categoryIndex: 2, subCategories: Utils.srfDropdown)
];
} else {
return [const FreestyleDDPage()];
}
}
where the FreestyleDDPage is the following:
class FreestyleDDPage extends StatefulWidget {
const FreestyleDDPage({Key? key}) : super(key: key);
#override
State<StatefulWidget> createState() {
return _FreestyleDDPageState();
}
}
class _FreestyleDDPageState extends State<FreestyleDDPage> {
var isLoading = true;
#override
Widget build(BuildContext context) =>
FutureBuilder<List<Map<String, dynamic>>>(
future: MyCard.getData(3, Utils.ddfDropdown)!
.whenComplete(() => setState(() {
isLoading = false;
})),
builder: ((context, snapshot) {
if (snapshot.hasData && snapshot.data!.isNotEmpty) {
return FutureBuilder<List<MyCard>>(
future: MyCard.readData(snapshot.data),
builder: (context, cards) {
if (cards.hasData) {
final card = cards.data!;
return Expanded(
child: ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: card.length,
itemBuilder: (context, index) {
return MyCard.buildCard(card[index], context);
},
),
);
} else {
return const Text("No data");
}
});
} else {
return isLoading
? Column(
children: const [CircularProgressIndicator()],
)
: const Text("You do not have any workouts yet");
}
}),
);
}
then it works.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
CustomWidgetWithParametr(index: 0 , categoryName: "HOME"),
CustomWidgetWithParametr(index: 1 , categoryName: "BUSINES"),
CustomWidgetWithParametr(index: 2 , categoryName: "SCHOOL"),
CustomWidgetWithParametr(index: 3 , categoryName: "Settings"),
];
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),
label: 'Home',
backgroundColor: Colors.red,
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Business',
backgroundColor: Colors.green,
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'School',
backgroundColor: Colors.purple,
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: 'Settings',
backgroundColor: Colors.pink,
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
class CustomWidgetWithParametr extends StatefulWidget {
const CustomWidgetWithParametr({Key? key, required this.index, required this.categoryName}) : super(key: key);
final int index;
final String categoryName;
#override
State<CustomWidgetWithParametr> createState() => _CustomWidgetWithParametrState();
}
class _CustomWidgetWithParametrState extends State<CustomWidgetWithParametr> {
#override
Widget build(BuildContext context) {
return
Column(mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(widget.index.toString()),
Text(widget.categoryName),
],
);
}
}
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;
}
}
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.
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,
),
],
);
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"),
),
],
));
}
}