How to remove - pop - screens in flutter using navigator 2.0

1.1k Views Asked by At

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,
            );
}
}
0

There are 0 best solutions below