Reputation: 213
I need a navigator inside each tab, so when I push a new Widget, the tab bar keeps on screen. The Code is working very well, but the android back button is closing the app instead of running Navigator.pop()
import 'package:flutter/material.dart';
void main() {
runApp(const TabBarDemo());
}
class TabBarDemo extends StatelessWidget {
const TabBarDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
length: 1,
child: Scaffold(
bottomNavigationBar: const BottomAppBar(
color: Colors.black,
child: TabBar(
tabs: [
Tab(icon: Icon(Icons.directions_car)),
],
),
),
body: TabBarView(
children: [
Navigator(
onGenerateRoute: (settings) {
return MaterialPageRoute(
builder: (context) => IconButton(
icon: Icon(Icons.directions_car),
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => newPage()))),
);
},
),
],
),
),
),
);
}
}
class newPage extends StatelessWidget {
const newPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("new page"),
),
body: Center(child: Icon(Icons.add)),
);
}
}
the code is also available here but on dartpad you cannot test the android back button.
Upvotes: 3
Views: 1859
Reputation: 1
Home Bloc:
GlobalKey<NavigatorState>? navigatorPage1Key;
GlobalKey<NavigatorState>? navigatorPage2Key;
GlobalKey<NavigatorState>? navigatorPage3Key;
GlobalKey<NavigatorState>? navigatorPage4Key;
GlobalKey<NavigatorState>? navigatorPage5Key;
var pageIndex = 0;
My Home bloc have subpages, exemple page1, page2, page3....
In Home Page:
return WillPopScope(
onWillPop: () async {
switch (homeBloc.pageIndex) {
case 0:
return !(await homeBloc.navigatorPage1Key!.currentState!.maybePop());
case 1:
return !(await homeBloc.navigatorPage2Key!.currentState!.maybePop());
case 2:
return !(await homeBloc.navigatorPage3Key!.currentState!.maybePop());
case 3:
return !(await homeBloc.navigatorPage4Key!.currentState!.maybePop());
case 4:
return !(await homeBloc.navigatorPage5Key!.currentState!.maybePop());
}
return true;
},
child: BlocBuilder<HomeBloc, HomeState>(
builder: (context, state) {
return Scaffold(
body: Material(
child: Column(
children: [
Expanded(
child: Container(
decoration: const BoxDecoration(
color: AppColors.backgroundPrimary,
),
child: IndexedStack(
sizing: StackFit.expand,
index: homeBloc.pageIndex,
children: <TabCustomWidget>[
TabCustomWidget(
tab: const Page1(),
navigatorKey: homeBloc.navigatorPage1Key,
),
TabCustomWidget(
tab: const Page2(),
navigatorKey: homeBloc.navigatorPage2Key,
),
TabCustomWidget(
tab: const Page3(),
navigatorKey: homeBloc.navigatorPage3Key,
),
TabCustomWidget(
tab: const Page4(),
navigatorKey: homeBloc.navigatorPage4Key,
),
TabCustomWidget(
tab: const Page5(),
navigatorKey: homeBloc.navigatorPage5Key,
),
],
),
),
),
Visibility(
visible: state.isViewBottomNavigator,
child: const BottomNavigationBarWidget(),
),
],
),
),
);
},
),
);
Basic my TabCustomWidget
:
class TabCustomWidget extends StatelessWidget {
final Widget tab;
final String tabKeyLogger;
final GlobalKey<NavigatorState>? navigatorKey;
const TabCustomWidget({
super.key,
required this.tab,
required this.tabKeyLogger,
this.navigatorKey,
});
@override
Widget build(BuildContext context) {
return CupertinoTabView(
builder: (context) => tab,
routes: AppRoutes.routes,
navigatorKey: navigatorKey,
navigatorObservers: [RouteObserver(tabKeyLogger, context)],
);
}
}
Basic my RouteObserver
:
class RouteObserver extends NavigatorObserver {
final String tabKey;
final BuildContext context;
RouteObserver(this.tabKey, this.context);
@override
void didPush(Route route, Route? previousRoute) {
super.didPush(route, previousRoute);
log('[TAB-NAVIGATOR] [$tabKey] [PUSH] ${route.settings.name}');
if (route.settings.name!.startsWith('drawer')) {
context.read<HomeBloc>().add(const OnHideBottomNavigatorHomeEvent());
}
}
@override
void didPop(Route route, Route? previousRoute) {
super.didPop(route, previousRoute);
log('[TAB-NAVIGATOR] [$tabKey] [POP] ${route.settings.name}');
if (route.settings.name!.startsWith('drawer')) {
context.read<HomeBloc>().add(const OnShowBottomNavigatorHomeEvent());
}
}
}
Upvotes: 0
Reputation: 1545
Based on omar hatem's answer
In the TabBarController State:
final GlobalKey<NavigatorState> screenANavigatorKey = GlobalKey();
final GlobalKey<NavigatorState> screenBNavigatorKey = GlobalKey();
Helper function:
GlobalKey<NavigatorState> indexToNavigatorKey(int index) {
switch (index) {
case 0: return screenANavigatorKey;
case 1: return screenBNavigatorKey;
}
}
In the WillPopScope.onWillPop
:
// I am saving the current screen index in some variable
GlobalKey<NavigatorState> subScreenNavigatorKey = indexToNavigatorKey(currentScreen);
await subScreenNavigatorKey.currentState?.maybePop();
return ...
Child of the WillPopScope
:
CupertinoTabScaffold(
controller: _controller,
tabBuilder: (context, index) =>
CupertinoTabView(
navigatorKey: screenToNavigatorKey(index),
builder: (context) => // your current screen goes here
Upvotes: 0
Reputation: 1979
First you should create a key for your navigator
final GlobalKey<NavigatorState> homeNavigatorKey = GlobalKey();
then add this key to your navigator
Navigator(
key: homeNavigatorKey,
then wrap your Navigator
in a WillPopScope
widget and add the onWillPop
as follows
child: WillPopScope(
onWillPop: () async {
return !(await homeNavigatorKey.currentState!.maybePop());
},
child: Navigator(
key: homeNavigatorKey,
this will check if the navigatorKey
can pop a route or not, if yes it will pop this route only if no it will pop itself thus closing the app
Upvotes: 3