在Flutter的嵌套导航器结构中,如何获得特定的导航器?

Arc*_*nes 9 flutter flutter-navigation

当我有Nested时,我遇到了这个问题Navigator。所以像

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      initialRoute: "/",
      routes: {
        '/': (context) => SomeOneView(),
        '/two': (context) => SomeTwoView(),
        '/three': (context) => SomeThreeView(),
      },
    );
  }
}

class SomeOneView extends StatefulWidget {
  @override
  _SomeOneViewState createState() => _SomeOneViewState();
}

class _SomeOneViewState extends State<SomeOneView> {
  @override
  Widget build(BuildContext context) {
   return Container(
      width: double.infinity,
      height: double.infinity,
      color: Colors.indigo,
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          MaterialButton(
            color: Colors.white,
            child: Text('Next'),
            onPressed: () => Navigator.of(context).pushNamed('/two'),
          ),
        ],
      ),
    );
  }
}



class SomeTwoView extends StatefulWidget {

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

class _SomeTwoViewState extends State<SomeTwoView> {
  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async {
        // Some implementation
      },
      child: Navigator(
        initialRoute: "two/home",
        onGenerateRoute: (RouteSettings settings) {
          WidgetBuilder builder;
          switch (settings.name) {
            case "two/home":
              builder = (BuildContext context) => HomeOfTwo();
              break;
            case "two/nextpage":
              builder = (BuildContext context) => PageTwoOfTwo();
              break;
          }
          return MaterialPageRoute(builder: builder, settings: settings);
        },
      ),
    );
  }
}

class HomeOfTwo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      height: double.infinity,
      color: Colors.white,
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          MaterialButton(
            color: Colors.white,
            child: Text('Next'),
            onPressed: () => Navigator.of(context).pushNamed('two/nextpage'),
          ),
        ],
      ),
    );
  }
}

class PageTwoOfTwo extends StatelessWidget {

   @override
   Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      height: double.infinity,
      color: Colors.teal,
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          MaterialButton(
            child: Text('Next'),
            onPressed: () => Navigator.of(context).pushNamed('/three'),
          ),
        ],
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,我从Navigator提供的“最热门”开始导航,然后转到MaterialApp孩子Navigator'two/nextpage',然后转到MaterialApp '/three'。问题在于,这样做onPressed: () => Navigator.of(context).pushNamed('/three'),返回Navigator的电流context是孩子Navigator。我需要访问MaterialAppNavigator导航正确。什么是正确的方法?

另外,如何处理Navigator我要访问的情况位于Navigators 堆栈中间的某个位置?

Rém*_*let 13

大多数情况下,您只有2个导航器。

这意味着要获取嵌套的,请执行以下操作:

Navigator.of(context)
Run Code Online (Sandbox Code Playgroud)

并获取根目录,请执行以下操作:

Navigator.of(context, rootNavigator: true)
Run Code Online (Sandbox Code Playgroud)

对于更复杂的体系结构,到目前为止,最简单的方法是使用GlobalKey(因为在构建过程中您永远不会阅读Navigator )

final GlobalKey<NavigatorState> key =GlobalKey();
final GlobalKey<NavigatorState> key2 =GlobalKey();

class Foo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorKey: key,
      home: Navigator(
        key: key2,
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

然后可以使用这种方式:

key.currentState.pushNamed('foo')
Run Code Online (Sandbox Code Playgroud)

  • 我一生中这个答案在哪里...我必须传递 6 级深度的回调才能将页面推送到父上下文...谢谢! (2认同)

Hem*_*Raj 8

实际上,Navigator当您有子导航流或内部旅程时,您必须使用嵌套。请阅读嵌套导航器的文档。

但是,要访问根导航器,您可以Navigator从 current递归查找父级,NavigatorNavigator在没有 parent 时返回 current Navigator

例子:

NavigatorState getRootNavigator(BuildContext context) {
  final NavigatorState state = Navigator.of(context);
  try {
    return getRootNavigator(state.context);
  } catch (e) {
    return state;
  }
}

//use it from any widget like
getRootNavigator(context);
Run Code Online (Sandbox Code Playgroud)

编辑:

解决方案1:

要获得特定的父级Navigator,我可以考虑扩展当前Navigator类以接受 anid并找到Navigatorby id。就像是:

class NavigatorWithId extends Navigator {
  const NavigatorWithId(
      {Key key,
      @required this.id,
      String initialRoute,
      @required RouteFactory onGenerateRoute,
      RouteFactory onUnknownRoute,
      List<NavigatorObserver> observers = const <NavigatorObserver>[]})
      : assert(onGenerateRoute != null),
        assert(id != null),
        super(
          key: key,
          initialRoute: initialRoute,
          onGenerateRoute: onGenerateRoute,
          onUnknownRoute: onUnknownRoute,
          observers: observers,
        );

  // when id is null, the `of` function returns top most navigator
  final int id;

  static NavigatorState of(BuildContext context, {int id, ValueKey<String> key}) {
    final NavigatorState state = Navigator.of(
      context,
      rootNavigator: id == null,
    );
    if (state.widget is NavigatorWithId) {
      // ignore: avoid_as
      if ((state.widget as NavigatorWithId).id == id) {
        return state;
      } else {
        return of(state.context, id: id);
      }
    }

    return state;
  }
}
Run Code Online (Sandbox Code Playgroud)

使用NavigatorWithId而不是Navigator在需要时使用,例如

return NavigatorWithId(
  id: 1,
  initialRoute: '/',
  onGenerateRoute: (_) =>
      MaterialPageRoute<dynamic>(builder: (_) => const YourPage()),
)
Run Code Online (Sandbox Code Playgroud)

然后像这样访问它:

NavigatorWithId.of(context, id: 1)
Run Code Online (Sandbox Code Playgroud)

解决方案2:

传递ValueKey给导航器并创建一个 util 函数来匹配 key 并返回所需的Navigator.

一个类似的功能

NavigatorState getNavigator(BuildContext context, {bool rootNavigator = false, ValueKey<String> key}) {
  assert(rootNavigator != null);
  final NavigatorState state = Navigator.of(
    context,
    rootNavigator: rootNavigator,
  );
  if (rootNavigator) {
    return state;
  } else if (state.widget.key == key) {
    return state;
  }
  try {
    return getNavigator(state.context, key: key);
  } catch (e) {
    return state;
  }
}
Run Code Online (Sandbox Code Playgroud)

return Navigator(
  key: const ValueKey<String>('Navigator1'),
  initialRoute: '/',
  onGenerateRoute: (_) =>
      MaterialPageRoute<void>(builder: (_) => const RootPage()),
);
Run Code Online (Sandbox Code Playgroud)

并像访问它一样访问它

getNavigator(context, key: const ValueKey<String>('Navigator1'))
Run Code Online (Sandbox Code Playgroud)

我可以看到这种方法的缺点是并非所有类型的密钥都受支持。

注意:我不认为上述任何解决方案是最好的或最优的。这是我想出的几种方法。如果有人能想出更好的方法,我很想学习:)

希望这可以帮助!