I am using firebase_auth to signup and login.
I am facing a problem with Streambuilder.
I would like to show page depends on User Logged in or not. It seems working fine.
But, the problem is that I can't use Get.off('/app'); in StreamBuilder and FutureBuilder.
if I can't use Getx.off('/app'); user can get back just pressing the back button, and
I would like to avoid this, so I am trying to use Get.off page route.
But, as vs code shows that FutureBuilder and StreamBuilder's builder return Widget,
and I have no idea how to code.
Any suggestion for this matter?
// main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return GetMaterialApp(
debugShowCheckedModeBanner: false,
title: 'Karrot Market Clone',
theme: ThemeData(
primaryColor: Colors.black,
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
appBarTheme: AppBarTheme(
color: Colors.white,
),
),
initialBinding: InitBinding(),
initialRoute: '/',
getPages: [
GetPage(
name: '/',
page: () => BridgeFirebase(),
),
GetPage(
name: '/bridge_page',
page: () => BridgePage(),
),
GetPage(
name: '/app',
page: () => App(),
transition: Transition.rightToLeft,
),
GetPage(
name: '/start',
page: () => Start(),
),
GetPage(
name: '/login',
page: () => Login(),
transition: Transition.rightToLeft,
),
GetPage(
name: '/signup',
page: () => SignUp(),
transition: Transition.rightToLeft,
),
],
);
}
}
class BridgeFirebase extends StatelessWidget {
const BridgeFirebase({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: Firebase.initializeApp(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(child: Text('Firebase load fail'));
}
if (snapshot.connectionState == ConnectionState.done) {
return BridgePage();
}
return Center(
child: CircularProgressIndicator(
color: ColorsKM.primary,
),
);
},
);
}
}
class BridgePage extends StatelessWidget {
const BridgePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (BuildContext context, AsyncSnapshot<User?> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Splash();
}
if (snapshot.hasData) {
return App();
} else {
return Start();
}
},
);
}
}
// app.dart
class App extends GetView<AppController> {
const App({Key? key}) : super(key: key);
Widget _bodyWidget() {
switch (RouteName.values[controller.currentIndex.value]) {
case RouteName.HOME:
return Home();
break;
case RouteName.MYLOCAL:
return MyLocal();
break;
case RouteName.NEARBY:
return Nearby();
break;
case RouteName.CHATS:
return Chats();
break;
case RouteName.MYKARROT:
return MyKarrot();
break;
}
return Container();
}
BottomNavigationBarItem _bottomNavigationBarItem(
String iconName, String label) {
return BottomNavigationBarItem(
icon: Padding(
padding: EdgeInsets.only(bottom: 5.0),
child: SvgPicture.asset('assets/svg/${iconName}_off.svg', width: 22),
),
activeIcon: Padding(
padding: EdgeInsets.only(bottom: 5.0),
child: SvgPicture.asset('assets/svg/${iconName}_on.svg', width: 22),
),
label: label,
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Obx(
() {
return _bodyWidget();
},
),
bottomNavigationBar: Obx(
() => BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: controller.currentIndex.value,
selectedFontSize: 12.0,
showSelectedLabels: true,
selectedItemColor: Colors.black,
selectedLabelStyle: TextStyle(color: Colors.black),
onTap: controller.changePageIndex,
items: [
_bottomNavigationBarItem('home', 'home'),
_bottomNavigationBarItem('notes', 'neighbor'),
_bottomNavigationBarItem('location', 'nearby'),
_bottomNavigationBarItem('chat', 'chat'),
_bottomNavigationBarItem('user', 'my karrot'),
],
),
),
);
}
}
// app_controller.dart
enum RouteName {
HOME,
MYLOCAL,
NEARBY,
CHATS,
MYKARROT,
}
class AppController extends GetxService {
static AppController get to => Get.find();
late RxInt currentIndex = 0.obs;
void changePageIndex(int index) {
currentIndex(index);
}
}
Because the build method is not completed when you navigate in Streambuilder so you can delay some time to the build method complete then navigate. do this when you want to navigate in Streambuilder :
Future.delayed(Duration.zero).then((value) => Get.off('/app'));
or just navigate in your controller or bloc class.
Related
I'm using flutter_form_bloc (https://pub.dev/packages/flutter_form_bloc) and I created two classes, the first is the login page:
class LoginScreen extends StatelessWidget {
const LoginScreen({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter - Parse Server',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: FutureBuilder<bool>(
future: hasUserLogged(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return SafeArea(
child: Scaffold(
body: Center(
child: Container(
width: 100,
height: 100,
child: CircularProgressIndicator()),
),
),
);
default:
if (snapshot.hasData && snapshot.data!) {
return UserPage();
} else {
return LoginPage();
}
}
}),
);
}
}
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final controllerUsername = TextEditingController();
final controllerPassword = TextEditingController();
final controllerConfirmPassword = TextEditingController();
bool isLoggedIn = false;
#override
Widget build(BuildContext context) {
return SafeArea(
child: SafeArea(
child: Scaffold(
body: SingleChildScrollView(
...
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'Don\'t have an Account? ',
overflow: TextOverflow.ellipsis,
),
new InkWell(
child: new Text(
'Sign Up',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
onTap: () => Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (_) =>
const listRegistrationScreen2()))),
],
),
),
);
}
class UserPage extends StatelessWidget {
...
}
And the second is list registration:
class listRegistrationScreen2 extends StatelessWidget {
const listRegistrationScreen2({super.key});
#override
Widget build(BuildContext context) {
return _listRegistrationScreen();
}
}
class _listRegistrationScreen extends StatefulWidget {
#override
__listRegistrationScreen createState() => __listRegistrationScreen();
}
class __listRegistrationScreen extends State<_listRegistrationScreen> {
static var flag = false;
...
#override
void initState() {
formBloc = null;
super.initState();
}
#override
void dispose() {
formBloc = null;
super.dispose();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
iconTheme: IconThemeData(
color: Colors.black,
),
centerTitle: true,
actions: [
IconButton(
onPressed: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (_) => const LoginScreen()));
},
icon: Icon(Icons.arrow_back))
],
),
body: BlocProvider(
create: (context) => ConditionalFieldsForm(),
child: Builder(
builder: (context) {
formBloc = BlocProvider.of<ConditionalFieldsForm>(context);
return Theme(
data: Theme.of(context).copyWith(
inputDecorationTheme: InputDecorationTheme(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(20),
),
),
),
child: SafeArea(
child: Scaffold(
body: FormBlocListener<ConditionalFieldsForm, String,
String>(
onSubmitting: {
...
},
onSubmissionFailed: (context, state) {
LoadingDialog.hide(context);
},
onSuccess: (context, state) {
LoadingDialog.hide(context);
},
onFailure: (context, state) {
LoadingDialog.hide(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(state.failureResponse!)));
},
child: SingleChildScrollView(
physics: const ClampingScrollPhysics(),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
...
),
ElevatedButton(
onPressed: formBloc.submit,
child: Text('SUBMIT'),
),
],
),
),
),
),
),
),
);
},
),
)),
);
}
}
class LoadingDialog extends StatelessWidget {
...
}
class ConditionalFieldsForm extends FormBloc<String, String> {
static final roles = SelectFieldBloc(
validators: [FieldBlocValidators.required],
items: ['Trainee', 'Employer', 'Department'],
);
static final TraineeEmail = TextFieldBloc(
validators: [FieldBlocValidators.required, FieldBlocValidators.email],
);
...
ConditionalFieldsForm() {
addFieldBlocs(
fieldBlocs: [
roles,
],
);
...
#override
Future<void> close() {
TraineeEmail.close();
TraineePassword.close();
TraineeConfirmPassword.close();
TraineeDOB.close();
TraineePhoneNumber.close();
TraineeSkills.close();
TraineeNationality.close();
TraineeAddress.close();
TraineeCurrentInstituation.close();
TraineeMajor.close();
TraineeName.close();
TraineeGender.close();
EmployerPhoneNumber.close();
EmployerWebsite.close();
EmployerPassword.close();
EmployerEmail.close();
EmployerAddressID.close();
EmployerDescription.close();
EmployerName.close();
DepartmentPhoneNumber.close();
DepartmentPassword.close();
DepartmentEmail.close();
DepartmentAddressID.close();
DepartmentCategory.close();
DepartmentName.close();
roles.close();
terms.close();
traineeRegistred.close();
employerRegistred.close();
return super.close();
}
#override
onSubmitting() async {
}
}
The problem is when I navigate from login to list registration page then back to login page and try to back again to list registration page I got this error:
error message screen
I tried to use dispose and change login screen from state less to state full but that didn't work with me.
I'm using go_router and I am about to do this in a callback of one of my buttons:
EvelatedButton(
onPressed: () {
GoRouter.of(context)
..push('/page-1')
..push('/page-2');
},
)
This is to push 2 pages in the history at once. After the user click on this button, he ends up on the page page-2 and when he pops the page, there is page-1.
Is it acceptable to do that or is there any reason not to do it?
What would be those reasons and what should I do instead?
I don't think I've seen anything like that in go_router's examples.
For more context, here is a code snippet (or checkout https://github.com/ValentinVignal/flutter_app_stable/tree/go-router/push-twice-at-once):
When the button is pressed, I want to display the dialog page with the page-1 in the background.
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
void main() {
runApp(const MyApp());
}
final router = GoRouter(
initialLocation: '/page-0',
routes: [
GoRoute(
path: '/page-0',
builder: (_, __) => const Page0Screen(),
),
GoRoute(
path: '/page-1',
builder: (_, __) => const Page1Screen(),
),
GoRoute(
path: '/dialog',
pageBuilder: (context, state) => DialogPage(
key: state.pageKey,
child: const DialogScreen(),
),
),
],
);
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: router,
);
}
}
class Page0Screen extends StatelessWidget {
const Page0Screen({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Page 0')),
body: Center(
child: ElevatedButton(
onPressed: () {
GoRouter.of(context)
..push('/page-1')
..push('/dialog');
},
child: const Text('Push'),
),
),
);
}
}
class Page1Screen extends StatelessWidget {
const Page1Screen({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Page 1')),
body: const Center(
child: Text('Page 1'),
),
);
}
}
class DialogScreen extends StatelessWidget {
const DialogScreen({super.key});
#override
Widget build(BuildContext context) {
return const AlertDialog(
title: Text('Dialog'),
);
}
}
class DialogPage extends Page {
const DialogPage({
required this.child,
super.key,
});
final Widget child;
#override
Route createRoute(BuildContext context) {
return DialogRoute(
settings: this,
context: context,
builder: (context) {
return child;
},
);
}
}
Assuming your goal is to display a dialog you can use the showDialog function in flutter.
Below is a sample
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Basic dialog title'),
content: const Text('A dialog is a type of modal window that\n'
'appears in front of app content to\n'
'provide critical information, or prompt\n'
'for a decision to be made.'),
actions: <Widget>[
TextButton(
style: TextButton.styleFrom(
textStyle: Theme.of(context).textTheme.labelLarge,
),
child: const Text('Disable'),
onPressed: () {
GoRouter.of(context).pop();
},
),
TextButton(
style: TextButton.styleFrom(
textStyle: Theme.of(context).textTheme.labelLarge,
),
child: const Text('Enable'),
onPressed: () {
GoRouter.of(context).pop();
},
),
],
);
},
);
go_router doesn't support pushing two routes at the same time. And it is not a good practice to push 2 pages at the same time.
What can you do instead?
You can transition from page1 to page2
Go to dialog page in the init method of the page2 using context.go('/dialog');
On exiting dialog page you can use context.pop() which will land you in page1
I realise a Flutter app and I'm a really beginner. I'm using Riverpod for the state management and go_router for the routing. I try to implement a navbar visible only if you are logged. But I think I have a state management issue: when I press a navbar button, nothing happened (no console error neither) but If I logout and login or if I modify my code and save, my Emulator go to the right page.
I try to wrap my pages in a bigger Scaffold, to persist the AppBar and NavBar.
Here is my main.dart:
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends ConsumerWidget {
const MyApp({Key? key}) : super(key: key);
// This widgets is the root of your application.
#override
Widget build(BuildContext context, WidgetRef ref) {
final router = ref.watch(routerProvider);
return MaterialApp.router(
title: 'Ludocal 2',
theme: ThemeData(
primarySwatch: Colors.deepOrange,
),
debugShowCheckedModeBanner: false,
routeInformationProvider: router.routeInformationProvider,
routeInformationParser: router.routeInformationParser,
routerDelegate: router.routerDelegate,
);
}
}
My router:
List<GoRoute> get _routes => [
GoRoute(
name: 'login',
builder: (context, state) => const LoginScreen(),
path: '/login'),
GoRoute(
path: '/:screenName(home|game|event|profile)',
builder: (BuildContext context, GoRouterState state) {
final String screenName = state.params['screenName']!;
return LoggedScreen(screenName: screenName);
})
];
My logged_screen.dart wrapping my other screens:
class LoggedScreen extends HookConsumerWidget {
const LoggedScreen({super.key, required this.screenName});
final String screenName;
#override
Widget build(BuildContext context, WidgetRef ref) {
Future.delayed(Duration.zero, () {
switch (ref.read(indexProvider.state).state) {
case 0:
context.go('/home');
break;
case 1:
context.go('/game');
break;
case 2:
context.go('/event');
break;
case 3:
context.go('/profile');
break;
}
});
return Scaffold(
appBar: AppBar(
title: Text("Ludocal 2"),
backgroundColor: Colors.deepOrangeAccent,
actions: [
TextButton.icon(
icon: Icon(
Icons.logout_rounded,
color: Colors.white,
),
label: Text('', style: TextStyle(color: Colors.white)),
onPressed: () async {
ref.read(loginControllerProvider.notifier).signOut();
},
),
]),
body: BodyTab(screenName: screenName),
bottomNavigationBar: const BottomTab(),
);
}
}
class BodyTab extends ConsumerWidget {
const BodyTab({super.key, required this.screenName});
final String screenName;
#override
Widget build(BuildContext context, WidgetRef ref) {
return Column(
children: [
Expanded(
child: screenName == 'home'
? const HomeScreen()
: screenName == 'game'
? const GameScreen()
: screenName == 'event'
? const EventScreen()
: const ProfileScreen()),
],
);
}
}
class BottomTab extends ConsumerWidget {
const BottomTab({Key? key}) : super(key: key);
#override
Widget build(BuildContext context, WidgetRef ref) {
return BottomNavigationBar(
currentIndex: ref.read(indexProvider.state).state,
onTap: (int index) => ref.read(indexProvider.state).state = index,
backgroundColor: Colors.deepOrangeAccent,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.emoji_emotions),
label: 'Game',
),
BottomNavigationBarItem(
icon: Icon(Icons.calendar_today_rounded),
label: 'Event',
),
],
);
}
}
final indexProvider = StateProvider<int>((ref) {
return 0;
});
The login_controller.dart:
class LoginController extends StateNotifier<LoginState> {
LoginController(this.ref) : super(const LoginStateInitial());
final Ref ref;
void login(String email, String password) async {
state = const LoginStateLoading();
try {
await ref.read(authRepositoryProvider).signInWithEmailAndPassword(
email,
password,
);
state = const LoginStateSuccess();
} catch (e) {
state = LoginStateError(e.toString());
}
}
void signOut() async {
await ref.read(authRepositoryProvider).signOut();
state = const LoginStateInitial();
}
}
final loginControllerProvider =
StateNotifierProvider<LoginController, LoginState>((ref) {
return LoginController(ref);
});
Appreciate if someone can advise. Thank you in advance!
For navigation you need to use listen like below.
ref.listen(indexProvider, (previous, next) {
switch (next) {
case 0:
context.go('/home');
break;
case 1:
context.go('/game');
break;
case 2:
context.go('/event');
break;
case 3:
context.go('/profile');
break;
}
});
I'm making an "inventory" app, to keep track of objects food etc in my house. I wanted to use a navigator, to make multiple pages, without "modifying" the widgets in the scaffold.
This is my code: (i have as you can see multiple file, ask me if you need something else)
Just to be clear, the utils.dart is an export file that I use to import every thing else
import 'package:flutter/material.dart';
import 'package:storage_system/utils.dart';
import 'package:flutter_barcode_scanner/flutter_barcode_scanner.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Storage System',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.yellow,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _index = 0;
void _onTap(int i) {
setState(() {
_index = i;
});
}
Future<void> getBarcodeData() async {
String resultData = await FlutterBarcodeScanner.scanBarcode(
"0xFFFFFF", "Close", false, ScanMode.BARCODE);
print(resultData);
}
Widget selectPage(int i) {
switch (i) {
case 0:
return MainPage();
case 1:
return SettingsPage();
default:
return Container();
}
}
#override
Widget build(BuildContext context) => Navigator(
onGenerateRoute: (settings) => MaterialPageRoute(
builder: (context) => Container(
child: Scaffold(
floatingActionButtonLocation:
FloatingActionButtonLocation.centerDocked,
floatingActionButton: FloatingActionButton(
onPressed: getBarcodeData,
child: Icon(Icons.add),
),
appBar: AppBar(
actions: <Widget>[],
title: Text(
"Storage System",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 28),
),
),
bottomNavigationBar: BottomNavigationBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home), label: "Home"),
BottomNavigationBarItem(
icon: Icon(Icons.settings), label: "Setings")
],
currentIndex: _index,
onTap: _onTap,
elevation: 15,
),
body: Container(
child: selectPage(_index),
),
),
)),
);
}
I'm using CupertinoTabScaffold and CupertinoTabView to build navigation bottom bar in my App. For one CupertinoTabView I go to others pushed routes name, I would like to get the current name of a CupertinoTabView, but I get Null
I define the routes in main like that
CupertinoApp(
home: MyApp(),
title: 'machin',
routes: appRoutes,)
final appRoutes = {
'/pushedName': (context) => PushedName(),
};
MyApp class //
final GlobalKey<NavigatorState> profileTabNavKey =
GlobalKey<NavigatorState>();
CupertinoTabScaffold(
tabBar: CupertinoTabBar(
activeColor: Color(0xff077018),
border: Border.all(color: Color(0xffffffff)),
currentIndex: widget.currentIndex,
onTap: (index) {},
items: <BottomNavigationBarItem>[....],
),
tabBuilder: (BuildContext context, int index) {
switch (index) {
case 0:
return CupertinoTabView(
navigatorKey: profileTabNavKey,
routes: appRoutes,
builder: (BuildContext context) =>
SettingsView());
break;
default:
return HomePage();
}
},
),
In the SettingsView I pushed a named route by using
Navigator.pushNamed(context, '/pushedName')
I tried to get the route name in the my app class by using
print(ModalRoute.of(profileTabNavKey.currentContext).settings.name);
nb: in the pushedName View i get it perfectly any help , thanks in advance
Just use the BuildContext from the build widget to get the ModalRoute data :
ModalRoute.of(context).settings.name
Working example :
import 'package:flutter/cupertino.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CupertinoApp(
debugShowCheckedModeBanner: false,
theme: CupertinoTheme.of(context).copyWith(
brightness: Brightness.light,
),
home: MainPage(),
);
}
}
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
int currentIndex = 0;
#override
Widget build(BuildContext context) {
return CupertinoTabScaffold(
tabBar: CupertinoTabBar(
currentIndex: currentIndex,
onTap: (index) {
setState(() {
currentIndex = index;
});
},
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
label: 'Home',
icon: Icon(CupertinoIcons.home),
),
BottomNavigationBarItem(
label: 'Setting',
icon: Icon(CupertinoIcons.settings),
),
],
),
tabBuilder: (BuildContext context, int index) {
switch (index) {
case 1:
return CupertinoTabView(
routes: <String, WidgetBuilder>{
'/setting': (context) => SettingsPage(),
'/setting/2': (context) => SettingsPage(2),
'/setting/2/3': (context) => SettingsPage(3),
},
builder: (context) => SettingsPage(),
);
break;
default:
return Center(
child: Text('Home page'),
);
}
},
);
}
}
class SettingsPage extends StatelessWidget {
final int index;
SettingsPage([this.index = 1]);
#override
Widget build(BuildContext context) {
// here we go to get the current route name
print(ModalRoute.of(context).settings.name);
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
backgroundColor: CupertinoColors.systemGrey.withOpacity(0.5),
middle: Text(index > 1 ? 'Settings page - $index' : 'Settings page'),
),
child: Center(
child: CupertinoButton.filled(
child: Text('Go'),
onPressed: () {
if (index == 1) {
Navigator.pushNamed(context, '/setting/2');
} else if (index == 2) {
Navigator.pushNamed(context, '/setting/2/3');
}
},
),
),
);
}
}
Go to Dartpad
I found a solution that doesn't sound like best practice, but it works!
Instead of using ModalRouter and other libraries like navigation_history_observer, I used Navigator.popUntil and blocked the popup from getting the current route from the argument and assigned it to a variable.
WillPopScope(
onWillPop: () async {
String currentRoute;
navigatorKeys[_tabController.index].currentState.popUntil((route) {
currentRoute = route.settings.name;
return true;
});
if (currentRoute == '/') {
return Future.value(false);
} else {
return !await navigatorKeys[_tabController.index]
.currentState
.maybePop();
}
},
// ...
);