如何在 Flutter 的导航器中使用 WillPopScope?

Jon*_*ing 12 dart flutter

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

我正在学习使用颤振,我遇到Navigator了创建多个页面的情况(我知道还有其他导航小部件,但我正在寻找只支持程序导航的东西,所以我可以处理所有的用户界面)。

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

我尝试WillPopScope在 a 中Navigator使用,目的是仅渲染路由在onWillPop按下后退按钮时调用它的回调 ( ),但放入WillPopScopeaNavigator什么都不做(不调用回调)。

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

我抬头一看很多问题似乎集中MaterialAppScaffold或处理导航等方式; 我正在寻找如何在没有这些东西带来的 UI 的情况下处理这个问题(用例可能是一个测验应用程序,你需要点击下一步按钮继续前进,或类似的东西)。

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

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}\"");
                }
              });
            })
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

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

从我的理解,Navigator并且WillPopScope是小工具使用了这样的事情,但如果没有的话我将如何实现自我包含(可能嵌套)的路线。

小智 11

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

onWillPop: () async {
    return !await otherNavigatorKey.currentState.maybePop();
}
Run Code Online (Sandbox Code Playgroud)


Tia*_*tos 6

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

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

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

您的密钥可以这样设置

    GlobalKey<NavigatorState> _yourKey = GlobalKey<NavigatorState>();
Run Code Online (Sandbox Code Playgroud)

_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);
Run Code Online (Sandbox Code Playgroud)

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

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

  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();
          },
        ),
      ],
    );
  }
Run Code Online (Sandbox Code Playgroud)

希望清楚!

  • 谢谢。这是唯一让我更清楚的答案 (2认同)
  • @JAHelia 在导航器上方有一个 WillPopScope(它有一个全局键),在导航器下方有一个 WillPopScope,只要您的用例需要它。"next" = WillPopScope 位于导航器小部件下方(就像 OP 已有的那样) (2认同)