How to change current page when app is inactive? - flutter

I need to redirect user to auth page when app is inactive for 5 minutes. I suppose using WidgetsBindingObserver. I detect when app is inactive for 5 minutes, but i don't know how to redirect user to auth page.
Here's part of my code:
#override
void initState() {
super.initState();
homeScreen = widget.homeScreen;
WidgetsBinding.instance.addObserver(this);
}
#override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
setState(() {
if (state == AppLifecycleState.paused) {
Future.delayed(Duration(seconds: 3), () {
setState(() {
// navigate to auth page
});
});
}
});
}

You can use the Navigator:
Navigator.push(context,
MaterialPageRoute(builder: (context) => AuthPage()));

You don't need redirect an app in 5 minutes, you can redirect it when user want to navigate into page (or to do some action) which needed to be authenticated, just log last action timestamp into SharedPreferences and check this timestamp on every needed-auth action.

Related

Flutter firebase auth login is not updating to home page

I am trying to update the home of MaterialApp widget depending on whether the user has sign up or not.
Below is the code inside the state of ``MaterialApp```
String? _userUid;
#override
void initState() {
FirebaseAuth.instance.authStateChanges().listen((user) {
//print('changed');
print(user?.uid);
updateUser(user?.uid);
});
super.initState();
}
void updateUser(String? USER_UID) {
setState(() {
_userUid = USER_UID;
});
}
Below is the code for the home property
home: _userUid == null ? const Onbaording2() : const HomeScreen(),
Somewhere inside the widget tree
final user = await _firebaseAuth.createUserWithEmailAndPassword(
email: email!,
password: password!,
);
After running the code above, there is a user created in firebase but the screen does not change.
Also the signOut works perfectly by signing me out when I use firebaseAuth.instance.signOut();
even if you change your variable it will not be redirected to the home screen because materialapp is only called when you first load or restart the app so rather than adding this condtion in home page or other way can be
#override
void initState() {
FirebaseAuth.instance.authStateChanges().listen((user) {
//print('changed');
print(user?.uid);
if(user == null){ navigate to onboarding }
else{ navigate to home page }
});
super.initState();
}

How to handle redirects from initState in Flutter

I am using the plugin flutter_local_notifications to show a user notifications on the app. I have managed to show the notifications on the app and the user can click on the notifications, the user will be redirected to see all pending items from the notification in a notification page. I have set the function to detect the onclick on initstate. The whole function and method to show notification I have implemented it on the homescreen.
#override
void initState() {
super.initState();
initPlatformState();
getNotifications();
NotificationApi.init(initScheduled: true);
init();
_configureSelectNotificationSubject(); //function to redirect to Notifications page
}
which goes to
void _configureSelectNotificationSubject() {
print("clicked is the notification");
NotificationApi.onNotifications.stream.listen((String? payload) async {
await Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => WANotifications(current: current)),
(Route<dynamic> route) =>
true);
});
}
The challenge am facing with this implementation is that when a user clicks to go to the home screen , the user gets redirected automatically to the notifications page from the home screen without his/her consent. The redirect should only occur when they click on the notifications.
How can I set the redirect to only occur when the user clicks the notification only and not when they click to go to home screen
You can achieve this with the help of didNotificationLaunchApp.
bool get didNotificationLaunchApp =>
notificationAppLaunchDetails?.didNotificationLaunchApp ?? false;
// Use here,
if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) {
selectedNotificationPayload = notificationAppLaunchDetails!.payload;
initialRoute = SecondPage.routeName;
}
Pl check full example here : https://pub.dev/packages/flutter_local_notifications/example
For non main file :
#override
void initState() {
super.initState();
localNotification(); // call below localNotification() here.
}
localNotification() async {
final NotificationAppLaunchDetails? notificationAppLaunchDetails =
await flutterLocalNotificationsPlugin!.getNotificationAppLaunchDetails();
if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) {
// redirect to new screen if true.
}
}

Flutter Trigger bloc event again

On my first screen, I fill out the form and then click the next button it added SubmitDataEvent() to the bloc. Then, the BolcListner listing and when it comes to SuccessSate it navigate to the next screen.
on the second screen, when I click the back button it navigates to the previous screen. After that, when I change the user-input data on the form and again click the next button now SubmitDataEvent() is not added.
I preferred some resources related to this and I understand the problem is that the state is in SuccessSate and it doesn't change to InitialState. So in dispose() I used bloc.close();
#override
void dispose() {
bloc.close();
super.dispose();
}
But still, it's not working. Also, I try with this code
#override
void dispose() {
bloc.emit(InitialState);
bloc.close();
super.dispose();
}
still, it's not working.
I used this to navigate between screens:
Navigator.popAndPushNamed()
What I want to do is:
On the first screen, when clicking on the next button SubmitDataEvent() added to the bloc and it in SuccessState it navigate to the next screen. When I click the back button on the second page it navigates again to the first screen. Now when I click the next button on the first screen I want to run all bloc process again.
There are no dependencies with the first and second screens.
first screen code:
...
#override
void initState() {
super.initState();
bloc = injection<SubmitPersonalDetailsBloc>();
EasyLoading.addStatusCallback((status) {
print('EasyLoading Status $status');
if (status == EasyLoadingStatus.dismiss) {
_timer?.cancel();
}
});
}
#override
void dispose() {
_scrollController.dispose();
bloc.close();
super.dispose();
}
#override
Widget buildView(BuildContext context) {
return Scaffold(
body: BlocProvider<SubmitPersonalDetailsBloc>(
create: (_) => bloc,
child: BlocListener<SubmitPersonalDetailsBloc,
BaseState<PersonalDetailsState>>(
listener: (context, state) {
if (state is LoadingSubmitPersonalDetailsState) {
EasyLoading.show(status: 'Submitting Data');
}
if (state is SubmitPersonalDetailsSuccessState) {
setState(() {
submitPersonalDetailsResponseEntity =
state.submitPersonalDetailsResponseEntity;
});
if (submitPersonalDetailsResponseEntity!.responseCode == "00") {
EasyLoading.showSuccess('Done!');
//Navigate next screen
EasyLoading.dismiss();
}
} else if (state is SubmitPersonalDetailsFailedState) {
EasyLoading.showError(state.error); }
},
....
The problem is on Dependency Injection, Once it creates an instance the parameters don't change. So when navigating to the next screen have to reset that instance.
#override
void dispose() {
_scrollController.dispose();
bloc.close();
injection.resetLazySingleton<SubmitPersonalDetailsBloc>(); // here reset the instance
super.dispose();
}
You can try with this snippet
if (result.responseCode == APIResponse.RESPONSE_SUCCESS) {
yield SubmitPersonalDetailsSuccessState(
submitPersonalDetailsResponseEntity: r);
} else
yield SubmitPersonalDetailsFailedState(error: r.responseMsg);
}

How to use shared preferences to have a one-time login in flutter?

I searched google/stackoverflow alot and tried but nothing worked. This is the flow i want:
User launches the app. Splash screen appears.
Login screen appears after splash screen. User logs in.
User kills(closes) the app.
When user relaunches the app, it should show the splash screen followed by the homepage, as user has already logged in once before.
User will only see login page if he/she logs out.
So far 1 and 2 works. But when user kills/closes the app and relaunches it again, instead of being directed to the home page, they are directed to the login page again.
The code for the splash screen:
class _SplashScreenState extends State<SplashScreen> {
#override
void initState() {
super.initState();
startTimer();}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
height: 150,
width: 150,
child: new SvgPicture.asset(
'assets/logo.png'
),
),
),
);
}
void startTimer() {
Timer(Duration(seconds: 3), () {
navigateUser(); //It will redirect after 3 seconds
});
}
void navigateUser() async{
SharedPreferences prefs = await SharedPreferences.getInstance();
var status = prefs.getBool('isLoggedIn');
print(status);
if (status == true) {
Navigator.pushReplacement(context, MaterialPageRoute(builder: (BuildContext context) => HomePage());
} else {
Navigator.pushReplacement(context, MaterialPageRoute(builder: (BuildContext context) => LoginScreen()));
}
}}
The code for the log out button:
void logoutUser() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs?.clear();
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (BuildContext context) => SplashScreen()),
ModalRoute.withName("/login"),
);
}
Sorry for the lengthy post, and really appreciate the help if someone could point out where i've gone wrong. Or if there's any other way to achieve a one-time login in flutter. Thanks!
I know my answer is late now. But, If you are using FirebaseAuth, this will automatically cache your login, logout log. So you will not need to store it to pref. Just nee to make additional step when you lauch screen to check if user's last status was login or log out by the following. And this information can be used to rediret to the desired screen.
Code:
Future<bool> checkIfAlreadySignedIn () async {
late bool _isAlreadySignedIn;
await FirebaseAuth.instance
.authStateChanges()
.listen((event) async {
if (event == null) {
print('Currentyl signed out');
_isAlreadySignedIn = false;
} else {
_isAlreadySignedIn = true;
}
});
return _isAlreadySignedIn;
}
Where do you set 'isLoggedIn' pref to true?

didChangeAppLifecycleState doesn't work as expected

I hope I understand how didChangeAppLifecycleState worked correctly.
I have page A and page B . When I click the back device button from page B ( Navigator.of(context).pop(); ), I expect didChangeAppLifecycleState in pageA will get called, but it doesn't.
PageA
class _ABCState extends State<ABCrList> with WidgetsBindingObserver {
#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) {
setState(() {
print(...);
});
}else{
print(state.toString());
}
}
....
This is the initState in pageA. The function used to call backend service.
#override
void initState() {
super.initState();
_bloc.getList(context); // return list and populate to ListView
});
}
The way you're thinking it is Android's way where onResume works, but in Flutter, things don't happen this way.
Generally, this gets called when the system puts the app in the background or returns the app to the foreground.
There are mainly 4 states for it:
resumed: The application is visible and responding to user input.
inactive: The application is in an inactive state and is not receiving user input.
paused: The application is not currently visible to the user, not responding user input, and running in the background.
detached: The application is still hosted on a flutter engine but is detached from any host views.
Edit:
When you're navigating to PageB from PageA, use something like:
Navigator.pushNamed(context, "/pageB").then((flag) {
if (flag) {
// you're back from PageB, perform your function here
setState(() {}); // you may need to call this if you want to update UI
}
});
And from PageB, you'll can use
Navigator.pop(context, true);