我需要创建一个登录表单。在用户成功登录之后,我需要启动某种计时器(例如3分钟),因此,如果用户对应用程序或其他字词没有任何反应,则当Flutter应用程序状态暂停,暂停或处于非活动状态超过3分钟时。该应用程序将转到主登录页面。只要用户与应用程序进行交互,我就需要取消计时器,而只需要给计时器加注星标,则应用程序的状态已暂停,暂停或无效。我该怎么办?
我尝试实现“ WidgetsBindingObserver”,但是它看起来无法正常工作。如果用户成功输入并在应用程序中导航,则WidgetsBindingObserver会失败(错误:窗口小部件树中不再显示的窗口小部件状态对象)。
我的问题是,只要用户与应用程序进行交互,如何实现基于时间的颤动应用程序生命周期?如果没有用户交互,则生命周期计时器将启动,并且如果在计时器结束之前存在用户交互,则必须取消该计时器。
class _MyUserHomePageState extends State<MyUserHomePage> with WidgetsBindingObserver {
AppLifecycleState _appLifecycleState;
@override
void initState() {
_appStatePasue = false;
WidgetsBinding.instance.addObserver(this);
super.initState();
}
// TODO: DID_CHANGE_APP_LIFE_CYCLE
void didChangeAppLifecycleState(AppLifecycleState state) {
setState(() {
_appLifecycleState = state;
if(_appLifecycleState == AppLifecycleState.paused ||
_appLifecycleState == AppLifecycleState.inactive ||
_appLifecycleState == AppLifecycleState.suspending) {
_appStatePasue = true;
print("timer---fired: $_appLifecycleState");
_timer = Timer.periodic(Duration(minutes: 1), _capitalCallback);
print(_appLifecycleState);
} else {
_appStatePasue = false;
}
});
}
// TODO: APP_LIFE_CYCLE__CALLBACK
void _capitalCallback(_timer) {
if(_appStatePasue == true) {
_timer.cancel();
print("return---main---page: $_appLifecycleState");
setState(() {
Navigator.push(
context,
SlideRightRoute(widget: MyApp())
);
});
} else {
_timer.cancel();
print("timer---canceled: $_appLifecycleState");
}
}
@override
void dispose() {
super.dispose();
}
@override
void onDeactivate() {
super.deactivate();
}
@override
Widget build(BuildContext context) {
return new Scaffold (
);
}
}
答案 0 :(得分:1)
您可以使用Timer类在不活动3分钟后触发注销功能。您可以尝试将整个应用包装到GestureDetector
中,以在发生任何事件时重置计时器。您只需要确保应用中的所有其他GestureDetector
都使用HitTestBehavior.translucent
,就可以将事件传播到您的根侦听器。这是一个完整的示例:
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) => AppRoot();
}
class AppRoot extends StatefulWidget {
@override
AppRootState createState() => AppRootState();
}
class AppRootState extends State<AppRoot> {
Timer _timer;
@override
void initState() {
super.initState();
_initializeTimer();
}
void _initializeTimer() {
_timer = Timer.periodic(const Duration(minutes: 3), (_) => _logOutUser);
}
void _logOutUser() {
// Log out the user if they're logged in, then cancel the timer.
// You'll have to make sure to cancel the timer if the user manually logs out
// and to call _initializeTimer once the user logs in
_timer.cancel();
}
// You'll probably want to wrap this function in a debounce
void _handleUserInteraction([_]) {
if (!_timer.isActive) {
// This means the user has been logged out
return;
}
_timer.cancel();
_initializeTimer();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: _handleUserInteraction,
onPanDown: _handleUserInteraction,
onScaleStart: _handleUserInteraction,
// ... repeat this for all gesture events
child: MaterialApp(
// ... from here it's just your normal app,
// Remember that any GestureDetector within your app must have
// HitTestBehavior.translucent
),
);
}
}
更新:我刚刚发现Listener类,在这里可能比GestureDetector
更有意义。我个人从未使用过它,但是可以随时尝试!查看documentation on gestures了解更多信息。
答案 1 :(得分:0)
对于任何在导航方面遇到问题的人,请使用上下文作为静态参数的简单创建类,然后从应用程序中的任何第一个小部件中设置上下文,然后可以在超时函数中使用上下文 创建课程:
class ContextClass{ static BuildContext CONTEXT; }
从您的第一个小部件构建方法中设置上下文,例如
ContextClass.CONTEXT=context;
并像这样使用超时功能
Navigator.of(ContextClass.CONTEXT).pushNamedAndRemoveUntil('<Your Route>', (Route<dynamic> route) => false);
答案 2 :(得分:0)
更新至Kirollos Morkos's Answer
我们已使用 NavigatorState
键注销。
这是AppRootState
的完整代码。
class AppRootState extends State<AppRoot> {
Timer _timer;
bool forceLogout = false;
final navigatorKey = GlobalKey<NavigatorState>();
@override
void initState() {
super.initState();
_initializeTimer();
}
void _initializeTimer() {
_timer = Timer.periodic(const Duration(minutes: 10), (_) => _logOutUser());
}
void _logOutUser() {
// Log out the user if they're logged in, then cancel the timer.
// You'll have to make sure to cancel the timer if the user manually logs out
// and to call _initializeTimer once the user logs in
_timer.cancel();
setState(() {
forceLogout = true;
});
}
// You'll probably want to wrap this function in a debounce
void _handleUserInteraction([_]) {
print("_handleUserInteraction");
_timer.cancel();
_initializeTimer();
}
void navToHomePage(BuildContext context) {
//Clear all pref's
SharedPreferencesHelper.clearAllValues();
navigatorKey.currentState.pushAndRemoveUntil(
MaterialPageRoute(builder: (context) => LoginPage()),
(Route<dynamic> route) => false);
}
@override
Widget build(BuildContext context) {
if (forceLogout) {
print("ForceLogout is $forceLogout");
navToHomePage(context);
}
return GestureDetector(
onTap: _handleUserInteraction,
onPanDown: _handleUserInteraction,
onScaleStart: _handleUserInteraction,
// ... repeat this for all gesture events
child: MaterialApp(
navigatorKey: navigatorKey,
// ...
// ...
));
}
}
答案 3 :(得分:-1)
访问每个屏幕的计时器以及在会话超时后关闭所有屏幕并打开登录屏幕。
在单独的 Constants.dart 文件中将会话到期时间定义为静态。
static const int sessionExpireTimeout = 30; //in seconds
现在登录成功后,在下一个屏幕即HomeScreen(),初始化一个名为Future.delayed()的方法,在Widget build(BuildContext上下文)方法:
Future.delayed(const Duration(seconds: Constants.sessionTimeout), () async {
await FirebaseAuth.instance.signOut(); // Firebase Sign out before exit
// Pop all the screens and Pushes Login Screen only
Navigator.of(context)
.pushNamedAndRemoveUntil(LoginScreen(), (route) => false);
});
记住,您不必在使用 Navigator 时弹出此 HomeScreen()。 每当您想导航到另一个屏幕时。使用 pushNamed() 或 push() 方法。 然后切换到另一个屏幕后,您可以使用任何导航器方法。