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

时间:2019-06-21 16:36:51

标签: flutter dart

WillPopScope的回调在我期望的时候没有被触发。如何在WillPopScope中使用Navigator,以便路由可以处理自己的后按行为。

我正在学习使用颤动,并且偶然发现Navigator用于创建多个页面(我知道还有其他导航小部件,但是我只支持程序化导航,因此我可以处理所有UI )。

我想用Navigator查看的下一件事是回退,我发现WillPopScope包装了一个组件,并具有一个回调,当按下后退按钮时,该回调将被调用(如果该组件呈现)。这对我来说很理想,因为我只希望在呈现Widget的情况下才调用回调。

我试图在WillPopScope中使用Navigator,目的是仅在按下后退按钮时才使渲染的路由调用其回调(onWillPop),但将{{ WillPopScope中的1}}无效(未调用回调)。

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

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

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

main.dart

在路线1或2中按下后退按钮时,应用程序退出。我希望只有在路由1中才是这种情况,路由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}\""); } }); }) ); } } (没有离开页面的导航或离开应用程序)。

据我了解,popping from route 2 disabledNavigator是用于此类事情的小部件,但是如果没有,那么我将如何实现自包含(可能嵌套)的路由。

2 个答案:

答案 0 :(得分:5)

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

mapview.annotations

答案 1 :(得分:3)

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

首先,您需要在导航器外部添加WillPopScope并将NavigatorKey添加到导航器

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

您的钥匙可以像这样

    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);

接下来,您只需要在导航器内的任何位置添加WillPopScope。其onWillPop将用您需要的逻辑进行调用。不要忘记根据是否要弹出返回真或假:)

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

  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();
          },
        ),
      ],
    );
  }

希望很清楚!