Flutter Navigation 不会在后按时弹出 Navigator 堆栈
Flutter Navigation doesn't pop Navigator stack on back press
我将 Navigator
用于命名路由,以使用 BottomNavigationBar
在屏幕上切换页面。在第 3 页,我可以通过 NavigationKey.currentState.pushNamed(Page4Route)
.
导航到第 4 页
在第 4 页,我可以通过调用 NavigationKey.currentState.pop
导航回第 3 页,但按下设备的后退按钮会关闭应用程序。知道为什么后退按钮不会在导航堆栈中弹出当前屏幕吗?我怎样才能更好地处理这个问题?
Widget build(BuildContext context) {
return Scaffold(
body: Navigator(
key: navigationKey,
initialRoute: Pages.home,
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
// Manage your route names here
switch (settings.name) {
case Pages.home:
builder = (BuildContext context) => _page1();
break;
case Pages.page1:
builder = (BuildContext context) => _page1();
break;
case Pages.page2:
builder = (BuildContext context) => _page2();
break;
case Pages.page3:
builder = (BuildContext context) => _page3();
break;
case Pages.page4:
builder = (BuildContext context) => Page4Screen(navigatorKey: navigationKey);
break;
default:
throw Exception('Invalid route: ${settings.name}');
}
return MaterialPageRoute(
builder: builder,
settings: settings,
);
},
),
bottomNavigationBar: BottomNavigationBar(),
...
);
}
演示
最小复制
import 'package:flutter/material.dart';
void main() => runApp(App());
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Nested Routing Demo',
home: HomePage(),
);
}
}
class Pages{
static const home = '/';
static const page1 = '/page1';
static const page2 = '/page2';
static const page3 = '/page3';
static const page4 = '/page4';
}
class HomePage extends StatefulWidget {
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<HomePage> {
final GlobalKey<NavigatorState> navigationKey = GlobalKey<NavigatorState>();
var _currentPage = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Navigator(
key: navigationKey,
initialRoute: '/',
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
// Manage your route names here
switch (settings.name) {
case Pages.home:
builder = (BuildContext context) => _page1();
break;
case Pages.page1:
builder = (BuildContext context) => _page1();
break;
case Pages.page2:
builder = (BuildContext context) => _page2();
break;
case Pages.page3:
builder = (BuildContext context) => _page3();
break;
case Pages.page4:
builder = (BuildContext context) => Page4Screen(navigatorKey: navigationKey);
break;
default:
throw Exception('Invalid route: ${settings.name}');
}
// You can also return a PageRouteBuilder and
// define custom transitions between pages
return MaterialPageRoute(
builder: builder,
settings: settings,
);
},
),
bottomNavigationBar: BottomNavigationBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.mood),
label: 'Page 1',
),
BottomNavigationBarItem(
icon: Icon(Icons.connect_without_contact),
label: 'Page 2',
),
BottomNavigationBarItem(
icon: Icon(Icons.message),
label: 'Page 3',
),
],
currentIndex: _currentPage,
// selectedItemColor: Colors.amber[800],
onTap: (value) {
/// Update page if a different tab from the current was clicked
if (value != _currentPage)
setState(() {
_currentPage = value;
switch (value) {
case 0:
navigationKey.currentState!
.pushReplacementNamed(Pages.page1);
break;
case 1:
navigationKey.currentState!
.pushReplacementNamed(Pages.page2);
break;
case 2:
navigationKey.currentState!
.pushReplacementNamed(Pages.page3);
break;
default:
/// TODO Error 404 page
throw Exception('Invalid route: $value');
}
});
},
),
);
}
Widget _page1() {
return Scaffold(
body: Container(
color: Colors.lightBlueAccent,
child: Center(
child: Text('Page 1'),
),
),
);
}
Widget _page2() {
return Scaffold(
body: Container(
color: Colors.orangeAccent,
child: Center(
child: Text('Page2'),
),
),
);
}
Widget _page3() {
return Scaffold(
body: Container(
color: Colors.lightGreenAccent,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Page 3'),
ElevatedButton(onPressed: (){
navigationKey.currentState!.pushNamed(Pages.page4);
}, child: Text('Page 4')),
],
),
),
),
);
}
}
class Page4Screen extends StatefulWidget {
Page4Screen({Key? key, required GlobalKey<NavigatorState> navigatorKey}) : _navigatorKey = navigatorKey, super(key: key);
final GlobalKey<NavigatorState> _navigatorKey;
@override
createState() => Page4State();
}
class Page4State extends State<Page4Screen>{
@override
Widget build(BuildContext context) {
return Scaffold(
// appBar: AppBar(
// title: Text(AppLocalizations.of(context)!.txtTitleMood),
// automaticallyImplyLeading: false,
// ),
body: Container(
color: Colors.redAccent,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Page 4'),ElevatedButton(
// Within the SecondScreen widget
onPressed: () {
// Navigate back to the first screen by popping the current route
// off the stack.
widget._navigatorKey.currentState!.pop();
},
child: Text('Go Back!'),
),
],
),
),
),
);
}
}
Navigator
小部件默认情况下不处理后退按钮,如果您定义了 Navigator
小部件,这就是您的工作。您可以通过 WillPopScope
小部件捕捉回按。它需要一个 Future<bool> Function()
,每当用户想要返回时都会调用它。如果它 returns false 那么你的默认 Navigator
位于 MaterialApp
将不会弹出当前路由,在这种情况下它只是显示你的 HomePage
。因此,如果您的嵌套导航器有要弹出的内容(例如 Page4
),那么它将弹出该内容并阻止您的主 Navigator
弹出您的 HomePage
.
class _HomeState extends State<HomePage> {
...
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => !(await navigationKey.currentState!.maybePop()),
child: Scaffold(
...
),
);
}
}
我将 Navigator
用于命名路由,以使用 BottomNavigationBar
在屏幕上切换页面。在第 3 页,我可以通过 NavigationKey.currentState.pushNamed(Page4Route)
.
在第 4 页,我可以通过调用 NavigationKey.currentState.pop
导航回第 3 页,但按下设备的后退按钮会关闭应用程序。知道为什么后退按钮不会在导航堆栈中弹出当前屏幕吗?我怎样才能更好地处理这个问题?
Widget build(BuildContext context) {
return Scaffold(
body: Navigator(
key: navigationKey,
initialRoute: Pages.home,
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
// Manage your route names here
switch (settings.name) {
case Pages.home:
builder = (BuildContext context) => _page1();
break;
case Pages.page1:
builder = (BuildContext context) => _page1();
break;
case Pages.page2:
builder = (BuildContext context) => _page2();
break;
case Pages.page3:
builder = (BuildContext context) => _page3();
break;
case Pages.page4:
builder = (BuildContext context) => Page4Screen(navigatorKey: navigationKey);
break;
default:
throw Exception('Invalid route: ${settings.name}');
}
return MaterialPageRoute(
builder: builder,
settings: settings,
);
},
),
bottomNavigationBar: BottomNavigationBar(),
...
);
}
演示
最小复制
import 'package:flutter/material.dart';
void main() => runApp(App());
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Nested Routing Demo',
home: HomePage(),
);
}
}
class Pages{
static const home = '/';
static const page1 = '/page1';
static const page2 = '/page2';
static const page3 = '/page3';
static const page4 = '/page4';
}
class HomePage extends StatefulWidget {
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<HomePage> {
final GlobalKey<NavigatorState> navigationKey = GlobalKey<NavigatorState>();
var _currentPage = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Navigator(
key: navigationKey,
initialRoute: '/',
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
// Manage your route names here
switch (settings.name) {
case Pages.home:
builder = (BuildContext context) => _page1();
break;
case Pages.page1:
builder = (BuildContext context) => _page1();
break;
case Pages.page2:
builder = (BuildContext context) => _page2();
break;
case Pages.page3:
builder = (BuildContext context) => _page3();
break;
case Pages.page4:
builder = (BuildContext context) => Page4Screen(navigatorKey: navigationKey);
break;
default:
throw Exception('Invalid route: ${settings.name}');
}
// You can also return a PageRouteBuilder and
// define custom transitions between pages
return MaterialPageRoute(
builder: builder,
settings: settings,
);
},
),
bottomNavigationBar: BottomNavigationBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.mood),
label: 'Page 1',
),
BottomNavigationBarItem(
icon: Icon(Icons.connect_without_contact),
label: 'Page 2',
),
BottomNavigationBarItem(
icon: Icon(Icons.message),
label: 'Page 3',
),
],
currentIndex: _currentPage,
// selectedItemColor: Colors.amber[800],
onTap: (value) {
/// Update page if a different tab from the current was clicked
if (value != _currentPage)
setState(() {
_currentPage = value;
switch (value) {
case 0:
navigationKey.currentState!
.pushReplacementNamed(Pages.page1);
break;
case 1:
navigationKey.currentState!
.pushReplacementNamed(Pages.page2);
break;
case 2:
navigationKey.currentState!
.pushReplacementNamed(Pages.page3);
break;
default:
/// TODO Error 404 page
throw Exception('Invalid route: $value');
}
});
},
),
);
}
Widget _page1() {
return Scaffold(
body: Container(
color: Colors.lightBlueAccent,
child: Center(
child: Text('Page 1'),
),
),
);
}
Widget _page2() {
return Scaffold(
body: Container(
color: Colors.orangeAccent,
child: Center(
child: Text('Page2'),
),
),
);
}
Widget _page3() {
return Scaffold(
body: Container(
color: Colors.lightGreenAccent,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Page 3'),
ElevatedButton(onPressed: (){
navigationKey.currentState!.pushNamed(Pages.page4);
}, child: Text('Page 4')),
],
),
),
),
);
}
}
class Page4Screen extends StatefulWidget {
Page4Screen({Key? key, required GlobalKey<NavigatorState> navigatorKey}) : _navigatorKey = navigatorKey, super(key: key);
final GlobalKey<NavigatorState> _navigatorKey;
@override
createState() => Page4State();
}
class Page4State extends State<Page4Screen>{
@override
Widget build(BuildContext context) {
return Scaffold(
// appBar: AppBar(
// title: Text(AppLocalizations.of(context)!.txtTitleMood),
// automaticallyImplyLeading: false,
// ),
body: Container(
color: Colors.redAccent,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Page 4'),ElevatedButton(
// Within the SecondScreen widget
onPressed: () {
// Navigate back to the first screen by popping the current route
// off the stack.
widget._navigatorKey.currentState!.pop();
},
child: Text('Go Back!'),
),
],
),
),
),
);
}
}
Navigator
小部件默认情况下不处理后退按钮,如果您定义了 Navigator
小部件,这就是您的工作。您可以通过 WillPopScope
小部件捕捉回按。它需要一个 Future<bool> Function()
,每当用户想要返回时都会调用它。如果它 returns false 那么你的默认 Navigator
位于 MaterialApp
将不会弹出当前路由,在这种情况下它只是显示你的 HomePage
。因此,如果您的嵌套导航器有要弹出的内容(例如 Page4
),那么它将弹出该内容并阻止您的主 Navigator
弹出您的 HomePage
.
class _HomeState extends State<HomePage> {
...
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => !(await navigationKey.currentState!.maybePop()),
child: Scaffold(
...
),
);
}
}