Riverpod - How to refresh() after an async function completes after the user has already left the screen - flutter

I have a Flutter app that has a screen with a ListView of items that is supplied by a provider via an API call. Tapping an item goes to a second "detail" screen with a button (onPressed seen below) that will make an API call to complete an action. On success, the item is removed from the list by running refresh() to make the first API call again to get an updated list. However, if the user navigates away from the detail screen before the action is completed, executing the refresh is no longer possible and will throw the "Looking up a deactivated widget's ancestor is unsafe" exception. This is why I am first checking the mounted property, but is there any way in this scenario to execute the refresh? I am trying to avoid the confusion of the user seeing an outdated list and having to manually refresh. Thanks.
onPressed: () async {
setState(() {
// Disable the submit button
});
someAsyncFunction().then(
(success) {
if (!mounted) {
return;
}
if (success) {
// Success snackBar
ref.refresh(someProvider);
Navigator.pop(context);
} else {
// Failure snackBar
setState(() {
// Re-enable submit button
});
}
},
);
}

Related

How can I change or override android/ios back button's action in flutter

I need to make a change in my page (changing a bool in setState etc...) when the user presses the back button.
I looked it up and I know people use WillPopScope to override the default "back" action, but in all of the examples they were just popping the page, and I need to do a custom function instead of that, and have no Idea how to do so.
my function looks like this, and I need to run in when the user pressed the back button:
Future<void> backToCats() async{
setState(() {
isLoaded=false;
});
if(isFromSearch){
loadCategories();
}
setState(() {
showingBrands=false;
isLoaded=true;
});
}
You can perform custom logic in the onWillPop argument of a WillPopScope widget. Just make sure to return true or false at the end to indicate whether the page is allowed to pop. WillPopScope will activate for the back button, and other actions that automatically navigate back such as swiping from the side of the screen on iOS.
For example, if you need to set a bool to false in your stateful widget, it would look something like this
WillPopScope(
onWillPop: () async {
setState(() => myBool = false);
return true;
}
child: ...
);

Refresh indicator sometime did not perform the action

I have one refresh indicator in which I print something. When I drag the refresh indicator sometimes it prints the statement but sometimes I drag the refresh indicator it just moves up quickly and does not perform anything.
I do not know why this strange behavior or what I am missing.
RefreshIndicator(
onRefresh: () async {
print("I am refresshing the data");
await some APi call
};
child:Listview.builder();
Create a function and pass it to onRefresh. And In that function, you should perform your task.
onRefresh: refreshList
Future<Null> refreshList() async{
await Apicall();
}
Apicall(){
//write api call
}

Flutter: How to call function of Home button (home button default of Android)?

I have a button on the View and how to call function of Home button in onClick function of button?
Code example:
InkWell(
onTap: () {
//function of home button
},)
You could use this package:
https://pub.dev/packages/move_to_background
Meaning your code could be something like this:
InkWell(
onTap: () {
MoveToBackground.moveTaskToBack();
},
)
For what you are trying to achieve you need a background notifier because the home button sends app to background. There is a package called flutter_fgbg which can help you - click here pub.dev. Then you can proceed to stream background events, eg:
import 'package:flutter_fgbg/flutter_fgbg.dart';
StreamSubscription<FGBGType> subscription;
...
// in initState
subscription = FGBGEvents.stream.listen((event) {
if (event == FGBGType.foreground){
//if its come back to foreground, then restart your app
} else {
//if its gone to background
}
});
// in dispose
subscription.cancel();
For more assistance, you can check this link, for visual aid in solving problems Mufungo Geeks YouTube

Flutter - re-run previous page code after execution returns to it

I'm trying to figure out the best way to implement the proper navigation flow of a flutter app I'm building that involves a 3rd party authentication page (Azure AD B2C). Currently I have a page that serves simply as a "navigate to 3rd party auth login" page which is set as the initialRoute for my app. The first time through, it runs exactly the way I want it to, but I'm not able to figure out how to get that 'navigate to auth' page to re-run when navigated back to (after logout) so that the user ends up back at the 3rd party auth login page.
Basically what I'd like to do is, on logout - have the app navigate back to that page specified as the initialRoute page, and upon that page being navigated back to, have it re-launch the 3rd party auth login page just like it did the first time it executed.
I tried just awaiting the call to Navigator.push() and then calling setState((){}) afterwards, and that does re-display the page, but it just leaves that initial page sitting there, and doesn't end up triggering the execution the way it did the first time. initState() does not fire again, so neither does any of my code that's in there.
I've tried various methods off the Navigator object trying to reload the page or navigate to itself again, or just calling goToLogin() again after the await Navigator.push() call, nothing works.
Here's what I'm currently doing :
User launches the app, the initialRoute is LoginRedirect
class LoginRedirect extends StatefulWidget {
#override
_LoginRedirectState createState() => _LoginRedirectState();
}
class _LoginRedirectState extends State<LoginRedirect> {
#override
void initState() {
Utility.getConfig().then((value) {
config = value;
oauth = AadOAuth(config);
goToLogin(context);
});
super.initState();
}
void goToLogin(BuildContext context) async {
setState(() {
loading = true;
});
try {
await oauth.login(); // this launches the 3rd party auth screen which returns here after user signs in
String accessToken = await oauth.getAccessToken();
navigateToDashboard();
setState(() {
loading = false;
});
} on Exception catch (error) {
setState(() {
loading = false;
});
}
}
void navigateToDashboard() {
await navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => Dashboard()));
// right here is where I'd like to call goToLogin() again after I Navigator.popUntil() back to this
// page, but if I try that I get an error page about how 'The specified child already
// has a parent. You must call removeView() on the child's parent first., java.lang
// .IllegalStateException and something about the bottom overflowed by 1063 pixels
}
}
After getting some config values and calling oauth.login() then I call a navigateToDashboard() method that pushes the Dashboard page on to the navigation stack.
Elsewhere in the code I have a logout button that ends up calling this code:
oauth.logout();
Navigator.popUntil(context, ModalRoute.withName('/LoginRedirect'));
which returns execution to where I called await Navigator.push() previously. But I can't figure out what I need to do there to have that LoginRedirect page execute again. I can't call goToLogin() again or it errors/crashes. I can't call initState() again, calling setState() doesn't do anything. I'm kinda stumped here, I thought this would be easy.
When logging out try: Navigator.pushReplacementNamed(context, "/LoginRedirect"); instead of Navigator.popUntil(context, ModalRoute.withName('/LoginRedirect'));

Firebase Cloud Messaging onLaunch callback

My app structure is a little bit mess, but I have to add this patch first and then I'll restructure the entire logic. The thing is I first check if there's a firebase user, then if there is one I use StreamBuilder to get the current user profile from Firestore, then I have the _firebaseMessaging.configure method because onLaunch and onResume I use this callback:
void _navigateToGestorResevas(Map<String, dynamic> message, User currentUser) {
Navigator.push(context,
MaterialPageRoute(builder: (context) =>
GestorScreen(user: currentUser)));
}
Because I need to send the User to this screen where he fetch the message from firebase.
onResume this works fine, but onLaunch it goes to the screen and fetch the data but there are like 20 seconds where there are some kind of glitch. It switch like 20-30 times between two states where I have and no have snapshot data in this _initState func:
final snapshot = await _dbRef.child('mensajes').child(widget.user.id).once();
if (snapshot.value != null) {
setState(() {
hayMensajes = true;
});
final data = snapshot.value;
for (var entry in data.entries) {
Message message = Message.fromJson(entry.value);
setState(() {
message.add(message);
});
}
} else {
setState(() {
hayMensajes = false;
});
}
Anyone have an idea what am I doing wrong?
If I am not mistaken, there are some active issues about FCM onLaunch callback with flutter. Some of them are still not fixed. One of the problems most people had to face was that onLaunch callback being called multiple times. I don't know why it happened, but as in your case, you can possibly get rid of the issue by some temporary fixes.
If the same screen is getting pushed over and over again, and glitching, you can pop the stack until it reaches the one you meant to open and set a condition to push navigator only if the new route is different from the old one. Using the named routes,
Navigator.popUntil(context, ModalRoute.withName(routeName));
if (ModalRoute.of(context).settings.name != routeName) {
Navigator.pushNamed(context, routeName);
}
I am not sure if that was the problem you asked, but I hope at least my answer helps somehow.