14

WillPopScope的回调没有在我预期的时候被触发。我如何WillPopScope在 a 内部使用,Navigator以便路线可以处理它自己的后压行为。

我正在学习使用颤振,并且遇到过Navigator创建多个页面(我知道还有其他导航小部件,但我正在追求仅支持编程导航的东西,因此我可以处理所有 UI)。

我想看的下一件事Navigator是返回,我发现WillPopScope它包装了一个组件并有一个回调,当按下返回按钮时(如果组件被渲染)。这对我来说似乎很理想,因为我只希望在呈现 Widget 时调用回调。

我尝试WillPopScope在 a 内使用,目的是仅在按下后退按钮时调用Navigator它的回调( ),但在 a内什么都不做(不调用回调)。onWillPopWillPopScopeNavigator

目的是Navigator导航到顶级路线,并且这些路线本身可能具有Navigators,因此放入WillPopScope内部意味着每条路线(或子路线)都负责它自己的后退导航。

我查过的许多问题似乎都集中在MaterialAppScaffold或其他处理导航的方式上;我正在寻找如何在没有这些东西带来的 UI 的情况下处理这个问题(一个用例可能是一个测验应用程序,您需要单击下一步按钮才能继续前进,或类似的东西)。

这是我希望路由 2 处理它自己的后退导航的最小main.dart文件(为了简单起见,我没有在此示例中放置嵌套路由)。

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Navigation',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key}) : super(key: key);

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

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext parentContext) {
    return Container(
        color: Theme.of(context).colorScheme.primary,
        child: Navigator(
            initialRoute: "1",
            onGenerateRoute: (settings) {
              return PageRouteBuilder(pageBuilder: (BuildContext context,
                  Animation animation, Animation secondaryAnimation) {
                switch (settings.name) {
                  case "1":
                    return Container(
                        color: Colors.red,
                        child: GestureDetector(onTap: () {
                          debugPrint("going from 1 to 2");
                          Navigator.of(context).pushNamed("2");
                        }));
                  case "2":
                    return WillPopScope(
                      child: Container(color: Colors.green),
                      onWillPop: ()async {
                        debugPrint("popping from route 2 disabled");
                        return false;
                      },
                    );
                  default:
                    throw Exception("unrecognised route \"${settings.name}\"");
                }
              });
            })
    );
  }
}

在路线 1 或 2 中按下后退按钮时,应用程序将退出。我希望仅在路线 1 中出现这种情况,而路线 2 应该只记录popping from route 2 disabled(没有导航离开页面或离开应用程序)。

据我了解,Navigator并且WillPopScope是用于此类事情的小部件,但如果不是,那么我将如何实现自包含(可能嵌套)的路由。

4

2 回答 2

13

您可以使用以下代码将 onWillPop 转发到另一个导航器:

onWillPop: () async {
    return !await otherNavigatorKey.currentState.maybePop();
}
于 2020-03-20T13:34:07.833 回答
9

创建导航器时,您会自动创建一个新堆栈。您.push在 Navigator 下方使用Navigator.of(context)的所有内容都将添加到您刚刚创建的新 Navigator 堆栈中。但是,当您按下后退按钮时,它不知道您要弹出什么(如果是根导航器或新导航器)。

首先,您需要在 Navigator 之外添加一个 WillPopScope 并将 NavigatorKey 添加到您的 Navigator

    return WillPopScope(
      onWillPop: () => _backPressed(_yourKey),
      child: Scaffold(
        body: Navigator(
          key: _yourKey
          onGenerateRoute: _yourMaterialPageRouteLogic,
        ),
        bottomNavigationBar: CustomNavigationBar(navBarOnTapCallback),
      ),
    );

你的钥匙可以这样instatiated

    GlobalKey<NavigatorState> _yourKey = GlobalKey<NavigatorState>();

_backPressed方法将接收您在设备上执行的任何 backPressed。根据定义,它返回true,我们并不总是想要弹出。

我们已经为导航器添加了一个键,现在它将用于了解新导航器是否在堆栈中有任何内容以便弹出(即是否为“canPop”)。

Future<bool> _backPressed(GlobalKey<NavigatorState> _yourKey) async {
  //Checks if current Navigator still has screens on the stack.
  if (_yourKey.currentState.canPop()) {
    // 'maybePop' method handles the decision of 'pop' to another WillPopScope if they exist. 
    //If no other WillPopScope exists, it returns true
    _yourKey.currentState.maybePop();
    return Future<bool>.value(false);
  }
//if nothing remains in the stack, it simply pops
return Future<bool>.value(true);

接下来,您只需在 Navigator 内的任何位置添加 WillPopScope。onWillPop它将以您想要的逻辑调用。不要忘记根据您是否要弹出它来返回 true 或 false :)

这是我在小部件中的onWillPop方法 ( _popCamera )的示例WillPopScope,它位于我的 Navigator 小部件树中。在此示例中,当用户按下相机小部件中的后退按钮时,我添加了一个对话框:

  static Future<bool> _popCamera(BuildContext context) {
    debugPrint("_popCamera");
    showDialog(
        context: context,
        builder: (_) => ExitCameraDialog(),
        barrierDismissible: true);

    return Future.value(false);
  }

class ExitCameraDialog extends StatelessWidget {
  const ExitCameraDialog({
    Key key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: Text('Leaving camera forever'),
      content:
      Text('Are you S-U-R-E, S-I-R?'),
      actions: <Widget>[
        FlatButton(
          child: Text('no'),
          onPressed: Navigator.of(context).pop,
        ),
        FlatButton(
          //yes button
          child: Text('yes'),
          onPressed: () {
            Navigator.of(context).pop();
            _yourKey.currentState.pop();
          },
        ),
      ],
    );
  }

希望清楚!

于 2020-07-07T13:55:10.773 回答