问题
I need to create a login form. After user successfully login than I need to start some kind of timer (ex: 3 min), so if user has no reaction to app or other word if flutter app state is paused, suspended or inactive more than 3 min. the app will goto main login page. As long as user has interaction with app I need to cancel the timer and only I need to star timer app state is paused, suspended or inactive. How do I do that?
I try to implement the "WidgetsBindingObserver" but its look like is not working as I wanted. If user enters successfully and navigate in app the WidgetsBindingObserver fail (error: state object for widget that no longer appears in the widget tree).
My question is how to implement timed-based flutter app lifecycle, as long as user has interaction with the app? If no user interaction the lifecycle timer will start and if before the timer ends there is a user interaction the timer must be canceled.
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 (
);
}
}
回答1:
You can use the Timer class to trigger a log out function after 3 minutes of inactivity. Something you can try is to wrap your entire app in a GestureDetector
that resets the timer on any event. You'd just have to make sure that any other GestureDetector
s in your app use HitTestBehavior.translucent
so the events are propagated to your root listener. Here's a full example:
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
),
);
}
}
UPDATE: I just discovered the Listener class which might make more sense here than the GestureDetector
. I've personally never used it, but feel free to experiment! Check out the documentation on gestures for more info.
回答2:
For anyone having issues with Navigating, Simple create class with a context as static parameter and then set the context from any of your first widgets in the app, then you can use the context in your timeout function create class:
class ContextClass{ static BuildContext CONTEXT; }
set the context from any of your first widget build method like so
ContextClass.CONTEXT=context;
And use in your time out function like so
Navigator.of(ContextClass.CONTEXT).pushNamedAndRemoveUntil('<Your Route>', (Route<dynamic> route) => false);
回答3:
To access Timer for every screen as well as close all the screens after session timeout and open Login Screen.
Define Session Expiry time at separately Constants.dart file as static.
static const int sessionExpireTimeout = 30; //in seconds
Now after Successful login, at next screen i.e. HomeScreen(), initialize a method called Future.delayed() with expiry time inside Widget build(BuildContext context) method:
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);
});
Remember one thing that you don't have to Pop this HomeScreen while using Navigator. Whenever you want to navigate to another screen. Use pushNamed() or push() method. Then after switching to another screen, you can use any Navigator method.
来源:https://stackoverflow.com/questions/52602606/how-do-i-create-a-time-based-flutter-app