从AppLifecycleState.resumed事件导航到新的屏幕?
从AppLifecycleState.resumed事件导航到新的屏幕?
我试图在AppLifecycleState事件中启动一个新的屏幕,但是什么都没有发生。这是因为在这个事件中没有可用的上下文来包含导航器。
每次应用程序从恢复状态返回(AppLifecycleState.resumed),应用程序都必须打开锁屏。最简单的情况是,每次打开银行应用程序时都需要进行锁屏保护。
如何在代码的任何位置显示新屏幕?
我提供的不起作用的代码:
import 'package:alarm_prevozi/screens/home_screen/home_screen.dart';
import 'package:alarm_prevozi/screens/lock_screen/lock_screen.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:alarm_prevozi/helpers/translations.dart';
import 'package:flutter/material.dart';
void main() async {
// 然后启动应用程序
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State
BuildContext myContext;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
// 监听应用程序进入后台或前台状态
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
// 用户返回到我们的应用程序
_showLockScreenDialog();
} else if (state == AppLifecycleState.inactive) {
// 应用程序不活动
} else if (state == AppLifecycleState.paused) {
// 用户暂时退出我们的应用程序
} else if (state == AppLifecycleState.suspending) {
// 应用程序暂停(iOS中不使用)
}
}
// 主要初始化
@override
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
// 告诉系统支持的语言
supportedLocales: translationService.supportedLocales(),
home: HomeScreen()
);
}
void _showLockScreenDialog() {
Navigator.of(context)
.pushReplacement(new MaterialPageRoute(builder: (BuildContext context) {
return LockScreen();
}));
}
}
问题的出现原因是在AppLifecycleState.resumed事件中从一个层级高于MaterialApp的观察者中访问Navigator,导致在推送路由时出现错误。
解决方法是使用一个全局键(GlobalKey)来访问MaterialApp提供的Navigator。首先,创建一个GlobalKey,并将其分配给MaterialApp的navigatorKey属性。然后,在触发AppLifecycleState.resumed事件时,通过访问navigatorKey.currentState来推送新的路由。
具体解决方法如下:
1. 在_MyAppState类中创建一个StreamController来处理resume事件,创建一个StreamSubscription来监听_stream的事件。
2. 在didChangeAppLifecycleState方法中,当state为AppLifecycleState.resumed时,通过_showLockScreenStream.add(true)向stream中添加一个事件。
3. 在build方法中,将navigatorKey分配给MaterialApp的navigatorKey属性。
4. 创建一个_showLockScreenDialog方法,在该方法中通过_navigatorKey.currentState来推送新的路由。
这样,当应用程序进入到前台时,会触发resume事件,并通过navigatorKey.currentState推送新的路由。这样就解决了从AppLifecycleState.resumed事件导航到新屏幕的问题。
需要注意的是,在使用GlobalKey时要谨慎使用,不要在整个应用程序中过多地使用它们。
以下是修改后的代码示例:
class _MyAppState extends State
StreamController
StreamSubscription _showLockScreenSubs;
GlobalKey
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_showLockScreenSubs = _showLockScreenStream.stream.listen((bool show) {
if (mounted && show) {
_showLockScreenDialog();
}
});
}
void dispose() {
WidgetsBinding.instance.removeObserver(this);
_showLockScreenSubs?.cancel();
super.dispose();
}
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
_showLockScreenStream.add(true);
} else if (state == AppLifecycleState.inactive) {
// app is inactive
} else if (state == AppLifecycleState.paused) {
// user is about quit our app temporarily
} else if (state == AppLifecycleState.detached) {
// detached from any host views
}
}
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: _navigatorKey,
...
);
}
void _showLockScreenDialog() {
_navigatorKey.currentState.pushReplacement(
MaterialPageRoute(builder: (BuildContext context) {
return PassCodeScreen();
})
);
}
}
感谢您的帮助!但是,这仍然会产生Unhandled Exception: Navigator operation requested with a context that does not include a Navigator的错误。您是对的,我已更新答案。我认为这是使用GlobalKeys的一个很好的案例,但不建议在应用程序中过多地使用它们。使用GlobalKey解决方案后,您几乎可以跳过StreamController的部分。我将这个选择留给您。