首页 > 解决方案 > Flutter Navigator popUntilOrPush

问题描述

想象一下,您有一个BottomNavigationBar带有 4 个图标的图标,可以导航到 4 个屏幕。当我这样做时:
A > B > D > B
我希望正常行为会弹出到第一个 B,这使我的堆栈只剩下 A > B。popUntil()我可以做到这一点。但是,当使用popUntil()and B 在堆栈中不存在时,它会弹出整个堆栈。pushNamed()相反,如果 B 不存在,我想这样做。由于我无权访问堆栈历史记录,我该怎么做?

@GrahamD 你是对的,我编辑了它。

标签: flutternavigation

解决方案


使用@GrahamD 的建议,这是我目前的导航配置,对我来说似乎有点 hacky 但有效:

main.dart

...
return MaterialApp(
  navigatorObservers: [NavObserver()],
  navigatorKey: serviceLocator<NavigationService>().navigatorKey,
  onGenerateRoute: Router().generateRoute,
...

navigation_service.dart

class NavigationService {
  final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

  List<AppRoute> routeStack = [];

  ///navigate to destination and add destination to stack
  void navigateTo(AppRoute destination, {Object arguments}) {
    if (routeStack.isEmpty || routeStack.last != destination) {
      navigatorKey.currentState
          .pushNamed(destination.index.toString(), arguments: arguments);
    }
  }

  ///pushNamedAndRemoveUntil and add destination to stack after clearing it
  void pushNamedAndRemoveUntil(AppRoute route, {Object arguments}) {
    navigatorKey.currentState.pushNamedAndRemoveUntil(
        route.index.toString(), (route) => false,
        arguments: arguments);
  }

  ///pop
  void pop({Object arguments}) {
    return navigatorKey.currentState.pop(arguments);
  }

  ///if the route exists in the stack, popUntil, otherwise pushNamed
  void popUntilOrPush(AppRoute route) {
    if (routeStack.contains(route)) {
      return navigatorKey.currentState
          .popUntil(ModalRoute.withName(route.index.toString()));
    } else {
      navigateTo(route);
    }
  }
}

class NavObserver extends NavigatorObserver {
  final navService = serviceLocator<NavigationService>();

  @override
  void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
    if (route.settings.name == "/") {
      //initial route is provided by the app
      navService.routeStack.add(AppRoute.Home);
    } else {
      navService.routeStack
          .add(AppRoute.values[int.parse(route.settings.name)]);
    }
  }

  @override
  void didStopUserGesture() {}

  @override
  void didStartUserGesture(
      Route<dynamic> route, Route<dynamic> previousRoute) {}

  @override
  void didReplace({Route<dynamic> newRoute, Route<dynamic> oldRoute}) {}

  @override
  void didRemove(Route<dynamic> route, Route<dynamic> previousRoute) {
    navService.routeStack.lastWhere((AppRoute appRoute) => route.settings.name == appRoute.index.toString());
  }

  @override
  void didPop(Route<dynamic> route, Route<dynamic> previousRoute) {
    navService.routeStack.removeLast();
  }
}

class Router {
  final nav = serviceLocator<NavigationService>();

  Route<dynamic> generateRoute(RouteSettings settings) {
    final AppRoute appRoute = AppRoute.values[int.parse(settings.name)];
    switch (appRoute) {
      case AppRoute.AuthLogin:
        return PageRouteBuilder(
            settings: settings,
            pageBuilder: (_, __, ___) =>
                AuthScreen(dynamicLink: settings.arguments));
      case AppRoute.AuthEmail:
        return PageRouteBuilder(
            settings: settings, pageBuilder: (_, __, ___) => EmailScreen());
      case AppRoute.Home:
        return PageRouteBuilder(
            settings: settings, pageBuilder: (_, __, ___) => HomeScreen());
      case AppRoute.Search:
        return PageRouteBuilder(
            settings: settings, pageBuilder: (_, __, ___) => SearchScreen());
      case AppRoute.PublishGallery:
        return PageRouteBuilder(
            settings: settings,
            pageBuilder: (_, __, ___) => PublishGalleryScreen());
      case AppRoute.Notifications:
        return PageRouteBuilder(
            settings: settings,
            pageBuilder: (_, __, ___) => NotificationsScreen());
      case AppRoute.Profile:
        return PageRouteBuilder(
            settings: settings, pageBuilder: (_, __, ___) => ProfileScreen());
      default:
        //should never be called
        return PageRouteBuilder(
            pageBuilder: (_, __, ___) => Scaffold(
                  body: Center(
                      child: Text('No route defined for ${settings.name}')),
                ));
    }
  }
}

enum AppRoute {
  AuthLogin,
  AuthEmail,
  Wizard,
  Home,
  Search,
  PublishGallery,
  Notifications,
  Profile
}

推荐阅读