Проблема, касающаяся вложенного MaterialApp.router() в Flutter Navigator 2.0

#flutter #flutter-web

Вопрос:

Задать вопрос означает, что я пытаюсь создать вложенный навигатор с помощью Navigator 2.0 для своего веб-приложения Flutter. Ниже приведена отправная точка моего приложения.

 void main() {
  runApp(App());
}


class App extends StatefulWidget {
  @override
  _AppState createState() => _AppState();
}

class _AppState extends State<App> {

  @override
  Widget build(BuildContext context) {

    return MaterialApp.router(
        routeInformationParser: AppRouteParser(), routerDelegate: AppRouterDelegate(),
      title: "Demo",
    );
  }

}
 

Как вы можете видеть, я добавил MaterialApp.router() для обработки всех навигаций верхнего уровня.

Теперь я хотел добавить вложенный навигатор внутри этого, который будет работать так же, как и выше, и будет правильно обрабатывать изменения URL-адреса. Вот почему я решил использовать тот же виджет MaterialApp.router() внутри в качестве дочернего элемента, что и мой вложенный навигатор.

После этого все работает нормально, но я получаю два отладочных баннера, как показано на рисунке ниже :

введите описание изображения здесь

Это заставляет меня задуматься, правильно ли я использую метод для достижения результата.

Дочерний навигатор принадлежит виджету страницы 1 корневого навигатора, как показано ниже, это виджет навигатора корневого MaterialApp.маршрутизатор:

 class AppRouterDelegate extends RouterDelegate<AppRoute>
    with ChangeNotifier, PopNavigatorRouterDelegateMixin<AppRoute> {

  final GlobalKey<NavigatorState> _navigatorKey;

  bool isPage1A = false;
  bool isPage1B = false;
  bool isUnknown = false;

  AppRouterDelegate() : _navigatorKey = GlobalKey<NavigatorState>();

  @override
  Widget build(BuildContext context) {

     return Navigator(
         pages: [
           MaterialPage(key: ValueKey("Page 1"),child: Page1(_valueChangeCallback)),
           if(isPage1A)
             MaterialPage(key: ValueKey("Page 1A"),child: Page1A(_valueChangeCallback)),
           if(isPage1B)
             MaterialPage(key: ValueKey("Page 1B"),child: Page1B(_valueChangeCallback)),
          /* if(isUnknown)
             MaterialPage(key: ValueKey("404"),child: TestPage()) */
         ],
         onPopPage: (route,result){print("Pop !!!!");  return route.didPop(result);}
     );
  }


  _valueChangeCallback(bool value,String subPage,[String subPage2]) {

    //print("Value change callback");

      if(subPage2 == null) {
        if(subPage == "A")
          isPage1A = value;
        else if(subPage == "B")
          isPage1B = value;
      }
      else {
        if(subPage2 == "B") {
          isPage1A = !value;
          isPage1B = value;
        }
        else if(subPage2 == "A") {
          isPage1A = value;
          isPage1B = !value;
        }
      }
      notifyListeners();
  }
 

А ниже приведен виджет страницы 1, в котором находится дочерний MaterialApp.router :

 class Page1 extends StatefulWidget {

  Function valueChangeCallback;

  Page1(this.valueChangeCallback);

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

class _Page1State extends State<Page1> {

  @override
  Widget build(BuildContext context) {

    print("Page 1");

    return Scaffold(
      body: Column(
        children: [
          InkWell(
            onTap: () {
              widget.valueChangeCallback(true,"A");
            },
            child: Text("Move to Sub Pages")
          ),
          Expanded(child: MaterialApp.router(routeInformationParser: NestedAppRouteInformationParser(), routerDelegate: NestedAppRouterDelegate())),
        ],
      ),
    );
  }
}
 

Ответ №1:

Если вы посмотрите на приложение.dart MaterialApp-это удобный виджет, который включает в себя несколько «виджетов, которые обычно требуются для приложений material design».

Если бы вы использовали конструктор по умолчанию, для вас был бы настроен объект Навигатора верхнего уровня.

MaterialApp.router () — еще одно удобство. «Создает [Материальное приложение], которое использует [Маршрутизатор] вместо [Навигатора]».

Конструктор маршрутизатора предоставляет вам способ создания приложения MaterialApp, настройки и возврата пользовательского навигатора.

То, что вы делаете, когда используете этот конструктор, — это обертывание виджетов-потомков во все удобные виджеты, которые может предложить MaterialApp(включая баннер отладки).

Для вложенных маршрутизаторов вместо этого вы хотите просто напрямую использовать виджет Router (), и вы избежите вызова всех дополнительных функций, которые MaterialApp предоставляет вам во время инициализации вашего приложения.

Также следует отметить, что в идеале для каждого приложения должен быть только один анализатор информации. в соответствии с примечаниями в маршрутизаторе.дротик, вы должны передать null в Wdiget маршрутизатора nester.

 "To opt out of URL updates entirely, pass null for [routeInformationProvider]
/// and [routeInformationParser]. This is not recommended in general, but may be
/// appropriate in the following cases:
///
/// * The application does not target the web platform.
///
/// * **There are multiple router widgets in the application. Only one [Router]
///   widget should update the URL (typically the top-most one created by the
///   [WidgetsApp.router], [MaterialApp.router], or [CupertinoApp.router]).**
///
/// * The application does not need to implement in-app navigation using the
///   browser's back and forward buttons."


    class _Page1State extends State<Page1> {

  @override
  Widget build(BuildContext context) {

    print("Page 1");

    return Scaffold(
      body: Column(
        children: [
          InkWell(
            onTap: () {
              widget.valueChangeCallback(true,"A");
            },
            child: Text("Move to Sub Pages")
          ),
          Expanded(child: Router(routeInformationParser: null, routerDelegate: NestedAppRouterDelegate(), backButtonDispatcher: ChildBackButtonDispatcher(Router.of(context).backButtonDispatcher),)),
        ],
      ),
    );
  }
 

Также предоставление диспетчера дочерней кнопки «Назад», как показано на рисунке, позволит вам связаться с родительским маршрутизатором при выполнении нажатия кнопки «Назад»…Надеюсь, это поможет!