首页 > 解决方案 > 用于嵌套导航的 Flutter WillPopScope

问题描述

我有带有嵌套导航器的 Flutter App,我想WillPopScope在我的嵌套屏幕中覆盖“onBackPressed”。

假设我有MainScreen,PrimaryScreenSecondaryScreenPrimaryScreen是嵌套导航器,它有几个屏幕,如PrimaryOneScreen,PrimaryTwoScreenPrimaryThreeScreen.

SecondaryScreen也是一个嵌套的导航器,就像它一样PrimaryScreen,它有 3 个屏幕。

这里是我想要实现的插图

MainScreen-> PrimaryScreen( PrimaryOneScreen-> PrimaryTwoScreen-> PrimaryThreeScreen)

当我的位置上时PrimaryTwoScreen,我想回到PrimaryOneScreen使用WillPopScopeWidget 覆盖“onBackPressed”。但是onWillPop当我按下后退按钮时从未调用过,我的屏幕直接返回到MainScreen.

这是我的代码

主屏幕.dart

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: MainApp(),
      onGenerateRoute: (settings){
        WidgetBuilder builder;
        switch(settings.name){
          case 'primary' :
            builder = (BuildContext _) => PrimaryScreen();
            break;
          case 'secondary' :
            builder = (BuildContext _) => SecondaryScreen();
            break;
          case 'main' :
            builder = (BuildContext _) => MainApp();
            break;
          default :
            throw Exception('Invalid route: ${settings.name}');
        }

        return MaterialPageRoute(
            builder: builder,
            settings: settings
        );
      },
    );
  }
}


class MainApp extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: <Widget>[
          FlatButton(
            onPressed: (){
              Navigator.pushNamed(context, 'primary');
            },
            child: Text('To Page Primary'),
          ),
          FlatButton(
            onPressed: (){
              Navigator.pushNamed(context, 'secondary');
            },
            child: Text('To Page Secondary'),
          )
        ],
      ),
    );
  }

}

PrimaryScreen.dart

class PrimaryScreen extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    return Navigator(
        initialRoute: 'primary/pageone',
        onGenerateRoute: (RouteSettings settings){
          WidgetBuilder builder;
          switch(settings.name){
            case 'primary/pageone' :
              builder = (BuildContext _) => PrimaryOneScreen();
              break;
            case 'primary/pagetwo' :
              builder = (BuildContext _) => PrimaryTwoScreen();
              break;
            case 'primary/pagethree' :
              builder = (BuildContext _) => PrimaryThreeScreen();
              break;
            default :
              throw Exception('Invalid route: ${settings.name}');
          }

          return MaterialPageRoute(
              builder: builder,
              settings: settings
          );
        },
      );
  }

}

PrimaryOneScreen.dart

class PrimaryOneScreen extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Primary Page'),
      ),
      body: Center(
        child: Column(
          children: <Widget>[
           FlatButton(
              onPressed: (){
                Navigator.pushNamed(context, 'primary/pagetwo');
              },
              child: Text('To Page Two'),
            ),
            FlatButton(
              onPressed: (){
                Navigator.pushNamed(context, 'primary/pagethree');
              },
              child: Text('To Page Three'),
            ),
          ],
        ),
      ),
    );
  }

}

PrimaryTwoScreen.dart

class PrimaryTwoScreen extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: ()async{
        print('willPopScope');
        Navigator.of(context, rootNavigator: false).pop();
        return true;
      },
      child: Scaffold(
        appBar: AppBar(
          title: Text('Secondary Page'),
        ),
        body: Center(
          child: Column(
            children: <Widget>[
              FlatButton(
                onPressed: (){
                  Navigator.pushNamed(context, 'primary/pageone');
                },
                child: Text('To Page One'),
              ),
              FlatButton(
                onPressed: (){
                  Navigator.pushNamed(context, 'primary/pagethree');
                },
                child: Text('To Page Three'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

PrimaryThreeScreen.dart

class PrimaryThreeScreen extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Primary Page'),
      ),
      body: Center(
        child: Column(
          children: <Widget>[
            FlatButton(
              onPressed: (){
                Navigator.pushNamed(context, 'primary/pageone');
              },
              child: Text('To Page One'),
            ),
            FlatButton(
              onPressed: (){
                Navigator.pushNamed(context, 'primary/pagetwo');
              },
              child: Text('To Page Two'),
            )
          ],
        ),
      ),
    );
  }

}

编辑 我添加了一张图片来说明我想要实现的目标。 在此处输入图像描述

如何仅在嵌套导航器上弹出?

谢谢!

标签: androidflutterflutter-navigation

解决方案


当按下后退按钮时,我认为它会使用你的应用程序的主导航器,因为它不知道你想要与之交互的嵌套导航器。

要解决它,您必须为导航器分配一个键,以便以后能够使用它。并在您使用嵌套导航器处理用户操作的小部件处添加 WillPopScope。

根据您的示例,我添加了一些代码。注意,maybePop() 函数将调用嵌套导航器小部件的 WillPopScope。如果你调用 pop(),它会直接弹出,忽略你的 widget 的 WillPopScope。

class PrimaryScreen extends StatelessWidget{
  final _navigatorKey = GlobalKey<NavigatorState>();

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
        onWillPop: () async {
          if (_navigatorKey.currentState != null) {
            _navigatorKey.currentState!.maybePop();
            return false;
          }

          return true;
        },
        child: Navigator(
          key: _navigatorKey,
          initialRoute: 'primary/pageone',
          onGenerateRoute: (RouteSettings settings) {
            WidgetBuilder builder;
            switch(settings.name){
              case 'primary/pageone' :
                builder = (BuildContext _) => PrimaryOneScreen();
                break;
              case 'primary/pagetwo' :
                builder = (BuildContext _) => PrimaryTwoScreen();
                break;
              case 'primary/pagethree' :
                builder = (BuildContext _) => PrimaryThreeScreen();
                break;
              default :
                throw Exception('Invalid route: ${settings.name}');
            }

            return MaterialPageRoute(
              builder: builder,
              settings: settings
          );
        },
      ),
    );
  }

}

推荐阅读