Flutter Navigator (2.0) 如何在某个 Pop 事件中 return 从 Nested Router 控制到 Base Router?

Flutter Navigator (2.0) How can I return control to the Base Router from a Nested Router on a certain Pop event?

我想使用 Flutter’s (2.0) navigation for routing on my mobile app. I cannot find cookbook examples and followed the recommended guide, implementing the nested router example

注意===

如果呈现的视图在应用程序中具有唯一的路由 (uri),我将其称为页面。

如果它没有唯一的路线,我称之为屏幕。

========

基本路由器 select 在应用程序的页面之间。 “资源仪表板”中的嵌套路由器使用 resourceViewState 对象 select 屏幕在“资源仪表板”页面中呈现。只需使用如下所示的 selectedIndex,我就可以根据用户在 Material Design Drawer.

中 selected 的索引更改屏幕

因此,当用户在上图中的任何非默认屏幕(即细节 A、细节 B)上,并且有弹出事件时,用户会 returned 到默认屏幕。如果用户从默认屏幕弹出,他们将 returned 到嵌套路由器之外的“Select 资源”页面。但是我还有一个棘手的案例要处理(或者我可能没有很好地处理这些案例:))

@override
Widget build(BuildContext context) {
  int selectedIndex = resourceViewState.selectedIndex;
  return Navigator(
    key: navigatorKey,
    pages: [
      MaterialPage(
        key: ValueKey(DEFAULT_PAGE),
        child: Scaffold(
          appBar: AppBar(
            centerTitle: true,
            title: Text(‘DEFAULT’),
          ),
          drawer: ResourceDrawer(
            resourceViewState: resourceViewState,
            navKey: navigatorKey,
          ),
          body: DefaultPage(),
        ),
      ),
      if (selectedIndex != DEFAULT_PAGE) ...[
        MaterialPage(
          key: ValueKey(selectedIndex),
          child: Scaffold(
            appBar: AppBar(
              centerTitle: true,
              title: Text(_getTitle(selectedIndex)),
            ),
            drawer: ResourceDrawer(
              deviceState: deviceState,
              navKey: navigatorKey,
            ),
            body: _getScreen(selectedIndex),
          ),
        ),
      ],
    ],
    onPopPage: (route, result) {
      if (result == "return") {
        print("return ");
        return route.didPop(true);
      }
      resourceViewState.selectedIndex = DEFAULT_PAGE;
      notifyListeners();
      return route.didPop(false);
    },
  );
}

我想在对话框中单击按钮时将用户从 Alert Dialog 导航到“Select 资源”页面。为此我必须这样做(参见图中的 1、2、3)。

如果用户从默认屏幕弹出,3 似乎由基本路由器处理,但我还需要从警报对话框中执行此操作。

  1. 弹出警报对话框
  2. 弹出 Material 设计抽屉
  3. 从嵌套路由器弹出到基本路由器。

这就是图中红色箭头所示的效果。

我可以简单地使用 Navigator.of(context).pop() 作为前两个。我很确定这里使用的 Navigator 与 Nested Router 使用的导航器不同(希望在这里了解一些细节).我相信是这种情况,因为在这些事件中不会为 NestedRouter 调用 onPopPage。

对于 3) 我试过这个策略:

a) 从导航抽屉中调用 navKey.currentState!.pop(“disconnected”)。我传入 Nested Router 的 navKey 如上面的代码所示。

b) 现在向嵌套路由器注册的 onPopPage 侦听器收到此 pop 事件的结果。

onPopPage: (route, result) {
  if (result == "return") {
    print("return ");
    return route.didPop(true);
  }
  resourceViewState.selectedIndex = DEFAULT_PAGE;
  notifyListeners();
  return route.didPop(false);
},

当我看到结果 == "return" 时,我应该从“资源仪表板”页面导航到“Select 资源”页面。但我不确定该怎么做,也不知道在同一条路线(切线)上使用不同的视图是否是一个好策略。

这是一个可行的解决方案。我传递了对父导航器键的引用,然后从中调用 pop。我更希望它完全是声明性的,但我不确定如何使用嵌套导航器模式来实现。

class ResourcePage extends StatefulWidget {
  ResourcePage({
    required this.resourceViewState,
    required this.navigatorKey,
  });

  final ResourceViewState resourceViewState;
  final GlobalKey<NavigatorState> navigatorKey;

  @override
  _ResourcePageState createState() => _ResourcePageState();
}


class _ResourcePageState extends State<DevicePage> {
  late DeviceDelegate _routerDelegate;
  late ChildBackButtonDispatcher _backButtonDispatcher;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // Defer back button dispatching to the child router
    _routerDelegate = InnerRouterDelegate(parentNavigatorKey: widget.navigatorKey, resourceViewState: widget.resourceViewState);
    _backButtonDispatcher = Router.of(context).backButtonDispatcher!.createChildBackButtonDispatcher();
    _backButtonDispatcher.takePriority();
  }

  @override
  void didUpdateWidget(covariant DevicePage oldWidget) {
    super.didUpdateWidget(oldWidget);
    _routerDelegate.state = widget.resourceViewState;
  }

  @override
  Widget build(BuildContext context) {
    return Router(
      routerDelegate: _routerDelegate,
      backButtonDispatcher: _backButtonDispatcher,
    );
  }
}


// InnerRouterDelegate Snippets

  // Constructor
InnerRouterDelegate({
  required this.resourceViewState,
  required this.parentNavigatorKey,
}) {
  resourceViewState.addListener(notifyListeners); // See Nested Navigation Example
}


// On Pop Page

onPopPage: (route, result) {
  if (result == "return") {
      parentNavigatorKey.currentState!.pop();
      return route.didPop(true);
    }
    resourceViewState.selectedIndex = DEFAULT_PAGE;
    notifyListeners();
    return route.didPop(false);
},