Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter WillPopScope for nested Navigation

I have Flutter App with nested Navigator and I want to override "onBackPressed" using WillPopScope in my nested screen.

Let's say I have MainScreen, PrimaryScreen, and SecondaryScreen. PrimaryScreen is nested Navigator, it have several screen like PrimaryOneScreen, PrimaryTwoScreen, and PrimaryThreeScreen.

SecondaryScreen is also a nested Navigator, just like PrimaryScreen, it have 3 screens.

Here the illustration of what i want to achieve

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

When my position on PrimaryTwoScreen, I want to get back to PrimaryOneScreen with overriding "onBackPressed" using WillPopScope Widget. But onWillPop never called when I press back button, and my screen go back directly to MainScreen.

Here are my codes

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

}

EDIT I added an image to illustrate what I want to achieve. enter image description here

How to pop only on nested navigator?

Thanks!

like image 638
fajar ainul Avatar asked Jul 09 '20 04:07

fajar ainul


Video Answer


1 Answers

When pressing the back button, I think it will use the main navigator of your app, because it doesn't know the nested navigator what you want to interact with.

To solve it, you have to assign a key for your navigator to be able to use it later. And adding a WillPopScope at widget which you use your nested navigator to handle user action.

Based on your example, I added some code. Noting, the maybePop() function will call the WillPopScope of your widget of the nested navigator. If you call pop(), it will pop directly, ignoring the WillPopScope of your widget.

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

}
like image 194
Thanh Vũ Trần Avatar answered Oct 12 '22 22:10

Thanh Vũ Trần