Используйте вложенный навигатор с WillPopScope во Flutter

В моем приложении я хочу иметь настраиваемую навигацию (изменять только часть экрана и сохранять в нем историю того, что я делаю). Для этой цели я использую навигатор, и он отлично работает для простой навигации. Однако я хочу обработать кнопку возврата Android. Во Flutter, по-видимому, есть проблема, которая заставляет меня обрабатывать обратную кнопку в родительском виджете навигатора: https://github.com/flutter/flutter/issues/14083

Из-за этого мне нужно получить экземпляр моего навигатора в дочерних элементах и ​​вызвать для него pop (). Я пытаюсь использовать для этого GlobalKey.

Я пытаюсь заставить его работать какое-то время и сделал образец проекта, просто чтобы проверить это. Вот мой код:

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(title: 'Navigation Basics', home: MainWidget()));
}

class MainWidget extends StatelessWidget {
  final GlobalKey<NavigatorState> navigatorKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return SafeArea(
        child: WillPopScope(
            onWillPop: () => navigatorKey.currentState.maybePop(),
            child: Scaffold(
                body: Padding(
              child: Column(
                children: <Widget>[
                  Text("Toto"),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: <Widget>[
                      Expanded(
                          child: RaisedButton(
                        child: Text('First'),
                        onPressed: () {
                          navigatorKey.currentState.pushNamed('/first');
                          // Navigator.push(
                          //   context,
                          //   MaterialPageRoute(builder: (context) => SecondRoute()),
                          // );
                        },
                      )),
                      Expanded(
                          child: RaisedButton(
                        child: Text('Second'),
                        onPressed: () {
                          navigatorKey.currentState.pushNamed('/second');
                        },
                      ))
                    ],
                  ),
                  Expanded(
                      child: Stack(
                    children: <Widget>[
                      Container(
                        decoration: BoxDecoration(color: Colors.red),
                      ),
                      ConstrainedBox(
                          constraints: BoxConstraints.expand(),
                          child: _getNavigator()),
                    ],
                  )),
                ],
              ),
              padding: EdgeInsets.only(bottom: 50),
            ))));
  }

  Navigator _getNavigator() {
    return Navigator(
        key: navigatorKey,
        initialRoute: '/',
        onGenerateRoute: (RouteSettings settings) {
          WidgetBuilder builder;
          switch (settings.name) {
            case '/':
              builder = (BuildContext _) => FirstRoute();
              break;
            case '/second':
              builder = (BuildContext _) => SecondRoute();
              break;
            default:
              throw new Exception('Invalid route: ${settings.name}');
          }
          return new MaterialPageRoute(builder: builder, settings: settings);
        });
  }
}

class FirstRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Column(
        children: <Widget>[
          RaisedButton(
            child: Text("GO TO FRAGMENT TWO"),
            onPressed: () => Navigator.of(context).pushNamed("/second"),
          )
        ],
      ),
      decoration: BoxDecoration(color: Colors.green),
    );
  }
}

class SecondRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Column(
        children: <Widget>[
          RaisedButton(
            child: Text("GO TO FRAGMENT ONE"),
            onPressed: () => Navigator.of(context).pop(),
          )
        ],
      ),
      decoration: BoxDecoration(color: Colors.blue),
    );
  }
}

Однако это работает не так, как хотелось бы. Навигатор по умолчанию, похоже, все еще используется: после открытия SecondRoute и нажатия кнопки возврата Android он просто покидает приложение, а не просто возвращается к первому маршруту.

Как я могу добиться того, чего хочу?


person leb1755    schedule 04.07.2019    source источник


Ответы (1)


Следуя документации onWillPop:

  /// Called to veto attempts by the user to dismiss the enclosing [ModalRoute].
  ///
  /// If the callback returns a Future that resolves to false, the enclosing
  /// route will not be popped.
  final WillPopCallback onWillPop;

ваш обработчик должен указать, что включающий маршрут не должен быть закрыт, поэтому возврат false решит вашу проблему.

изменение вашего обработчика на это работает:

    onWillPop: () async {
      navigatorKey.currentState.maybePop();
      return false;
    },
person MozesM    schedule 04.07.2019
comment
Как заставить работать смахивание назад в iOS при использовании виджета WillPopScope? - person nicowernli; 22.11.2019
comment
Вместо navigatorKey.currentState.maybePop(); return false; вы можете использовать return !await navigatorKey.currentState.maybePop();, чтобы разрешить извлечение внешнего навигатора, когда внутренний уже находится наверху. - person Benjamin Bisinger; 01.06.2021