In my application, I want to have a custom Navigation (only change part of the screen and keep an history of what I'm doing inside it). For that purpose, I am using a Navigator and it's working fine for simple navigation. However, I want to handle the back button of Android. There is a problem with it in Flutter apparently which forces me to handle the backbutton in the parent widget of the Navigator : https://github.com/flutter/flutter/issues/14083
Due to that, I need to retrieve the instance of my Navigator in the children and call pop() on it. I am trying to use a GlobalKey for this.
I am trying to make it work for a while now and made a sample project just to test this. Here is my code :
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(title: 'Navigation Basics', home: MainWidget()));
}
class MainWidget extends StatelessWidget {
final GlobalKey<NavigatorState> navigatorKey = GlobalKey();
@override
Widget build(BuildContext context) {
return SafeArea(
child: WillPopScope(
onWillPop: () => navigatorKey.currentState.maybePop(),
child: Scaffold(
body: Padding(
child: Column(
children: <Widget>[
Text("Toto"),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: RaisedButton(
child: Text('First'),
onPressed: () {
navigatorKey.currentState.pushNamed('/first');
// Navigator.push(
// context,
// MaterialPageRoute(builder: (context) => SecondRoute()),
// );
},
)),
Expanded(
child: RaisedButton(
child: Text('Second'),
onPressed: () {
navigatorKey.currentState.pushNamed('/second');
},
))
],
),
Expanded(
child: Stack(
children: <Widget>[
Container(
decoration: BoxDecoration(color: Colors.red),
),
ConstrainedBox(
constraints: BoxConstraints.expand(),
child: _getNavigator()),
],
)),
],
),
padding: EdgeInsets.only(bottom: 50),
))));
}
Navigator _getNavigator() {
return Navigator(
key: navigatorKey,
initialRoute: '/',
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
switch (settings.name) {
case '/':
builder = (BuildContext _) => FirstRoute();
break;
case '/second':
builder = (BuildContext _) => SecondRoute();
break;
default:
throw new Exception('Invalid route: ${settings.name}');
}
return new MaterialPageRoute(builder: builder, settings: settings);
});
}
}
class FirstRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Column(
children: <Widget>[
RaisedButton(
child: Text("GO TO FRAGMENT TWO"),
onPressed: () => Navigator.of(context).pushNamed("/second"),
)
],
),
decoration: BoxDecoration(color: Colors.green),
);
}
}
class SecondRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Column(
children: <Widget>[
RaisedButton(
child: Text("GO TO FRAGMENT ONE"),
onPressed: () => Navigator.of(context).pop(),
)
],
),
decoration: BoxDecoration(color: Colors.blue),
);
}
}
This is however not working as I would like. The default Navigator seem to still be used : after opening the SecondRoute and pressing the Android back button, it just leaves the app instead of just going back to the first route.
How can I achieve what I want?
The WillPopScope widget comes with the Flutter framework. It gives us control over the back button action, allowing the current page to go back to the previous one if it meets certain requirements. This is achieved using a callback, which the widget takes in as one of its parameters.
Following the documentation of onWillPop:
/// Called to veto attempts by the user to dismiss the enclosing [ModalRoute].
///
/// If the callback returns a Future that resolves to false, the enclosing
/// route will not be popped.
final WillPopCallback onWillPop;
your handler should indicate that the enclosing route should not be closed, hence returning false will resolve your issue.
changing your handler to this works:
onWillPop: () async {
navigatorKey.currentState.maybePop();
return false;
},
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With