1 year ago
#366510
PHTremor
How to remove - pop - screens in flutter using navigator 2.0
I am using navigator 2.0 and Provider to add and remove screens. when I navigate using the Drawer(), I am able to go from the first screen to the second screen but not back...thus by selecting the menu item on the drawer. The error I am getting is this:
_AssertionError ('package:flutter/src/widgets/navigator.dart': Failed assertion: line 3470 pos 18: '!keyReservation.contains(key)': is not true.) The relevant error-causing widget was Router<dynamic>
. Pressing the back button - of the phone - is working though.
I wanted to know how I can be removing the first screen - for instance - before navigating to the second screen, or any way to work this around.
here is my router code:
class AppRouter extends RouterDelegate
with ChangeNotifier, PopNavigatorRouterDelegateMixin {
@override
// navigatorKey
final GlobalKey<NavigatorState> navigatorKey;
// Screen Managers
final AppStateManager appStateManager;
final ProjectsScreenManager projectScreenManager;
AppRouter({
// initialize all the screen managers
required this.appStateManager,
required this.projectScreenManager,
}) : navigatorKey = GlobalKey<NavigatorState>() {
// add Listerners for all the state managers
appStateManager.addListener(notifyListeners);
projectScreenManager.addListener(notifyListeners);
}
// Dipose all managers after use
@override
void dispose() {
appStateManager.removeListener(notifyListeners);
projectScreenManager.removeListener(notifyListeners);
super.dispose();
}
@override
Widget build(BuildContext context) {
return Navigator(
key: navigatorKey,
onPopPage: _handlePopPage,
pages: [
// A list of Pages & their Conditions
if (!appStateManager.isInitialized) SplashScreen.page(),
if (appStateManager.isInitialized) HomeScreen.page(),
if (appStateManager.goToProjects) ProjectsScreen.page(),
if (projectScreenManager.gotToProjectDetails)
ProjectDetailsScreen.page(),
if (appStateManager.goToDashboard) HomeScreen.page(),
if (appStateManager.goToTasks) TasksScreen.page(),
],
);
}
bool _handlePopPage(Route route, result) {
// Checks if the current route’s pop succeeded
// If it failed, return false; ELSE checks the different routes and
// triggers the appropriate state changes
if (!route.didPop(result)) {
return false;
}
// Handle States when user closes a screen
if (route.settings.name == KaizenPages.projectsScreenPath) {
appStateManager.setgoToProjects(false);
}
if (route.settings.name == KaizenPages.projectDetailsScreenPath) {
projectScreenManager.goToProjectDetails(false);
}
if (route.settings.name == KaizenPages.homePath) {
appStateManager.setgoToDashboard(false);
}
if (route.settings.name == KaizenPages.tasksScreenPath) {
appStateManager.setgoToTasks(false);
}
return true;
}
@override
Future<void> setNewRoutePath(configuration) async => null;
}
Here is the provider:
class AppStateManager extends BaseScreenProvider {
// variables ....Fields
bool _initialized = false;
NavigationItem _navigationItem = NavigationItem.dashboard;
bool _goToProjects = false;
bool _goToDashboard = false;
bool _goToTasks = false;
// Getter Methods
bool get isInitialized => _initialized;
bool get goToProjects => _goToProjects;
bool get goToTasks => _goToTasks;
NavigationItem get getNavItem => _navigationItem;
bool get goToDashboard => _goToDashboard;
// initializeApp Method
void initializeApp() {
Timer(const Duration(milliseconds: 2000), () {
_initialized = true;
notifyListeners();
});
}
void setgoToProjects(bool goToProjects) {
_goToProjects = goToProjects;
notifyListeners();
}
void setgoToTasks(bool goToTasks) {
_goToTasks = goToTasks;
notifyListeners();
}
// Go to dashboard Screen
void setgoToDashboard(bool goToDashboard) {
_goToDashboard = goToDashboard;
notifyListeners();
}
// set nav item
void setNavItem(NavigationItem navigationItem) {
_navigationItem = navigationItem;
notifyListeners();
}
}
Inside the Drawer widget, I have this code that's displaying the menu items on the drawer
//...other widget here....
// Menu Contents|List
SingleChildScrollView(
child: Container(
padding: padding,
child: Column(
children: [
// ...other item removed
// List Tiles
buildMenuItem(
text: "Dashboard",
icon: Icons.home_outlined,
onClicked: () async {
// Navigate to dashboard screen
locator<AppStateManager>().setgoToDashboard(true);
locator<AppStateManager>()
.setNavItem(NavigationItem.dashboard);
},
context: context,
navigationItem: NavigationItem.dashboard,
),
buildMenuItem(
text: "Projects",
icon: Icons.view_stream,
onClicked: () {
locator<AppStateManager>().setgoToProjects(true);
locator<AppStateManager>()
.setNavItem(NavigationItem.projects);
},
context: context,
navigationItem: NavigationItem.projects,
),
buildMenuItem(
text: "Tasks",
icon: Icons.fact_check,
onClicked: () {
locator<AppStateManager>().setgoToTasks(true);
Navigator.pop(context);
locator<AppStateManager>().setNavItem(NavigationItem.tasks);
},
context: context,
navigationItem: NavigationItem.tasks,
),
),
],
),
),
)
the buildMenuItem widget
// List Item| Tile
Widget buildMenuItem({
required String text,
required IconData icon,
VoidCallback? onClicked,
required NavigationItem navigationItem,
required context,
}) {
final currentItem = locator<AppStateManager>().getNavItem;
final isSelected = navigationItem == currentItem;
final color = isSelected ? kaizenOrange : Theme.of(context).iconTheme.color;
return Material(
child: ListTile(
leading: Icon(
icon,
color: color,
),
title: Text(
text,
style: Theme.of(context).textTheme.bodyText2!.copyWith(color: color),
),
onTap: onClicked,
),
);
}
locator() is just a dependency injection package, to use/inject the providers... here is its setup
import 'package:get_it/get_it.dart';
GetIt locator = GetIt.instance;
void setupLocator() {
// Register Services here (API/AuthService/...)
locator.registerLazySingleton(() => KaizenMockAPI());
// Register screen/model Providers here
locator.registerLazySingleton(() => AppStateManager());
locator.registerLazySingleton(() => ProjectsScreenManager());
locator.registerLazySingleton(() => TasksScreenManager());
}
here is how I am using the Router in my main.dart
class _KaizenAppState extends State<KaizenApp> {
// Hold all the state managers here ...
final _appStateManager = locator<AppStateManager>();
final _projectScreenStateManager = locator<ProjectsScreenManager>();
// appRouter here Nav2.0
late AppRouter _appRouter;
@override
void initState() {
_appRouter = AppRouter(appStateManager: _appStateManager, projectScreenManager: _projectScreenStateManager);
super.initState();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: _theme,
),
home: Router(
routerDelegate: _appRouter,
backButtonDispatcher: RootBackButtonDispatcher(),
),
debugShowCheckedModeBanner: false,
);
}
}
flutter
dart
provider
flutter-navigation
flutter-drawer
0 Answers
Your Answer