How to call a function when returning to view/controller with `Get.back()`? - flutter

I am switching from the home view to another using Get.toNamed(Routes.DETAIL). When I want to return from the details view to the home view, I am calling Get.back() (or the user is using the back button of the devices).
Back on the home view, I would like to fetch all data from my database again.
Is there any function that is triggered when I am leaving a few and returning to it, so I can put my logic there?
Thank you

I would suggest you to use Get.offNamed() instead of Get.toNamed() as the offNamed() function will clear the data stored in catch and thus will again call the API declared in onInit() or onReady() lifecycle when returning back to that screen.

In Getx they have a funtion like
Get.back(result:"result");
so in order to trigger some funtion when going back to any page route
try doing this as the document written
final gotoHome = await Get.toNamed(Route.name); // or use the simple one Get.to(()=> Home());
then if you trigger to go back in page you should indicate some result e.g.
from back button in phone using willpopscope or a back button in UI.
Get.back(result:"triggerIt"); // this result will pass to the home.
so in will use
// It depend on you on where you gonna put this
// onInit or onReady or anything that would trigger
someTrigger() async{
final gotoHome = await Get.toNamed(Route.name);
if(gotoHome == "triggerIt"){
anyFuntionYouwantoTrigger();
}
}
for more info about it try to read the documentation.
https://github.com/jonataslaw/getx/blob/master/documentation/en_US/route_management.md
Edited: // Maybe some answer will pop up for better
I do have one but it's not that quite a real practice just a sample
e.g // sample you are now in the current page and this page is also connected to homecontroller or using Get.find() it need to bind the controller to the page;
class BindingHome with Bindings{
#override
void dependencies() {
Get.lazyPut(() => HomeController(), fenix: true);
}
}
then from GetPage add Binding
GetPage(
name: "/currentpage",
binding: BindingHome(),
page:() => HomeView(),
),
so while homecontroller is bind to the current page you are now so
// lets assume this one is put to the CurrentController
final homeController = Get.find<HomeController>();
so while calling back button on ui or willpopscope
when back try to trigger the function from home
gotBackfunction(){
Get.back();
homeController.anyFuntionYouwantoTrigger();
}

No you can't really call a function when doing so.
You should be using callback function just before popping the view.
onBackClick() async {
Get.lazyPut<MainController>(
() => MainController(),
);
controller.allItems.refresh();
Get.back();
}
Here is an example how you can do it without any complications:
WillPopScope(
onWillPop: () async {
await onBackClick();
return Future(() => false);
},
child: Scaffold(
appBar: CustomAppBarWithBack(
title: "Appbar",
OnClickBack: onBackClick,
),
body: Widget(),
),
);

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: ...
);

pop and push the same route back with different params in Futter (GET X)

I have 2 screens,
Screen one contains a list view with onPressed action on every item
screen two contains the detail of the pressed item as well as a drawer with the same list view as screen one.
What I want to do here is when the user goes to the detail screen and click on an item from the drawer the detail screen should pop and push back with new params.
Code so far,
Route
GetPage(
name: '/market-detail',
page: () => MarketDetail(),
binding: MarketDetailBinding(),
),
Binding
class MarketDetailBinding extends Bindings {
#override
void dependencies() {
Get.lazyPut(() => MarketDetailController());
}
}
Click action in screen one
onTap: () {
Get.toNamed('market-detail',
arguments: {'market': market});
},
Detail Screen Class
class MarketDetail extends GetView<MarketDetailController> {
final Market market = Get.arguments['market'];
}
Click action in detail screen sidebar
onTap: () {
Get.back();
Get.back();
Get.toNamed('market-detail',
arguments: {'market': market});
},
First Get.back() is to close the drawer, then remove the route and push the same route back again,
Expected behaviour,
MarketDetailController should be deleted from memory and placed again,
What actually happening
The controller only got delete and not getting back in memoery on drawer click action until I hot restart the app(By clicking save).
If anybody understands it, please help I am stuck here.
As I can see, you're trying to pop and push the same route with a different parameter in order to update a certain element on that route. Well, if that's the case then just let me show you a much better way.
In your MarketDetailController class you should add those:
class MarketDetailsController extends GetxController {
// A reactive variable that stores the
// instance of the market you're currently
// showing the details of.....
Rx<Market> currentMarket;
// this method will be called once a new instance
// of this controller gets created
// we will use it to initialize the controller
// with the required values
#override
void onInit() {
// some code here....
// .......
// intializing the variable with the default value
currentMarket = Market().obs;
super.onInit();
}
void updateCurrentMarket(Market market) {
// some code here if you need....
// updating the reative variable value
// this will get detected then by the Obx widgets
// and they will rebuild whatever depends on this variable
currentMarket.value = market;
}
}
Now inside your page UI, you can wrap the widget that will display the market details with the Obx widget like this:
Obx(
() {
final Market currentMarket = controller.currentMarket.value;
// now you have the market details you need
// use it and return your widget
return MyAwesomeMarketDetailsWidget();
},
)
Now for your click action, it can just be like this:
onTap: () => controller.updateCurrentMarket(myNewMarketValue)
This should be it. Also, I advise you to change GetView to GetWidget and Get.lazyPut() to Get.put()

Wait for Navigator.pop ignoring Navigator.pushReplacement

I have the following setup:
class FirstScreen {
// ...
Future<void> doSomething() async {
final bool isCool = await Navigator.of(context).pushNamed('/second-screen');
print(isCool ? 'Cool.' : 'Not cool.');
}
// ...
}
class SecondScreen {
// ...
Future<void> replace() async {
await Navigator.of(context).pushReplacementNamed('/third-screen');
}
// ...
}
class ThirdScreen {
// ...
Future<void> goBack() async {
await Navigator.of(context).pop(true);
}
// ...
}
However, this would crash, since the pushReplacement procs the await and my application won't wait until the pop is used.
How can I wait for pop 's value to be returned?
UPDATE:
The problem here is a little bit more complex than I told.
#Alok suggested to not pop the route but push it after the sequence, however, this is a very trivial version of my code.
I currently have a HomeScreen with a nested Navigator that pushes to a list of questions. Then, using Navigator.of(context, rootNavigator: true), I navigate to the examLoadingScreen, etc. (You can read about this in the comments)
If I push the HomeScreen when the exam is completed, I would lose all the navigation done in the mentioned nested Navigator.
I seriously need to pop in this scenario. I have multiple workarounds such as pop chaining but it doesn't seem very performant or convenient.
See, Zeswen, as far this documentation on pushReplacementNamed is concerned. It states that:
Replace the current route of the navigator that most tightly encloses the given context by pushing the route named routeName and then disposing the previous route once the new route has finished animating in.
Can you see that, it clearly mentions that it removes the previous route after you are done animating it.
Now, what are you trying to achieve is, or how Navigator.pop() value retrieval works, is it is mandatory to have that PrevoiusPage there when you move from one page to another
//What you're doing with pushReplacementNamed
1 -> SeconPage => ThidPage
2 -> SecondPage [Removed]
3 -> ThirdPage is trying to come to the previous page, that is SecondPage to return it's value, but SecondPage has been removed HENCE CRASHES!!
//What is needs to be done to use something like push() or pushNamed(), which used named route
1 -> SecondPage => ThirdPage
2 -> SecondPage is there in the stack
3 -> ThirdPage => SecondPage [Returns Value]
REMEMBER pop() always need the immediate precedence to accept it's value, not any page. So, if you remove the SecondPage, it will always crash.
Now, if you want to go to the page MainPage or in this case, FirstPage. Use pushAndRemoveUntil. It basically removes all the routes in the stack, and go to the page
SOLUTION: Pass the result score to the MainPage, via ResultPage. Make the MainPage accepts the Result Score too
class ThirdScreen(){
// ...
Future<void> goBack() async {
await Navigator.pushAndRemoveUntil(context,
MaterialPageRoute( builder: (context) => FirstPage(result: result),
(_) => false
);
}
}
And do your operation in your FirstPage accordingly, if you have result != 0 || result != null, and show it to the user. Let me know if that works out for you.
UPDATED ANSWER WITH A BEST POSSIBLE WORKAROUND
I have just added this answer, because, I feel like the above would be helpful in future as well.
Now, my idea is basic, and is workable according to the trivial information available for me.
THEORY: According to the theory, pop() value can be accessed by the predecessor only, immediate one.
SOLUTION
1. First Page -> Second Page
2. Second Page -> Third Page
3. Third Page -> Second Page with value
// Now following 3. step
1. Value check, if the value is true, pop immediately
2. Return the value to the first page
3. Print the value in the first page
Just follow your trivial data, and I hope you would understand the logic. After that implementation is just a cakewalk.
class FirstScreen {
Future<void> doSomething() async {
// We get the value from second page, which is technically passing
// the third page's value, and doesn't appear to us in UI
// So serving the purpose
final bool isCool = await Navigator.pushNamed(context, '/second-screen');
print(isCool ? 'Cool.' : 'Not cool.');
}
}
class SecondScreen {
Future<void> replace() async {
// No need of pushReplacementNamed, since we're are popping
// based upon our values, so it won't appear eventually
// and pass the value as well for the First Page
final bool value = await Navigator.pushNamed(context, '/third-screen');
// Now we check, whether what value we got from third page,
// If that is true, then immediately pop and return the value for first page
if(value == true){
Navigator.pop(context, value);
}
}
}
class ThirdScreen {
// async not required for performing pop()
// void is fine
void goBack() {
Navigator.pop(context, true);
}
}
Just check it. This logic will help you achieve the purpose, and it is safe and error free.

Flutter - set result to return when the user navigates back

When calling Navigator.pop() a result can be passed to the previous screen. Is there a way to set the result so that if the user navigates back on their own the result is still returned to the previous page?
I could pass an object to the second page and then modify it so that the first page can check it when the second page returns, but I'd rather use the result returned from the Navigator as it's more readable.
Overriding the back button's tap detector as I've seen suggested elsewhere is not an acceptable solution because the user may navigate back in some other way such as swiping or pressing the Android back button.
Yes, you can pass data between screens both ways.
While popping back, send the data you wish to send like this from the second screen
RaisedButton(
onPressed: () {
// The Nope button returns "data" as the result.
Navigator.pop(context, 'data');
},
child: Text('Nope!'),
);
And catch the result in the your first screen like this
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()),
);
source
For the other concern where the user is able to go back to the previous screen by pressing the back button, you can wrap your second screen with WillPopScope widget and provide the onWillPop callback. This will override the popping of the second screen and execute the callback where you can define the value you wish to return from the second screen.
#override
Widget build(BuildContext context) {
return WillPopScope(
child: Scaffold(), // or your widget
onWillPop: () {
return Future.delayed(Duration(microseconds: 0), () {
return Navigator.pop(context, "return-data");
});
},
);
}
Yes, this is possible, when navigating to Screen B from Screen A, make the onTap() functionasynchronous and await the result from Screen B.
Check the code below: It works perfectly:
On Screen A, put this code:
onTap: () async {
// receive the data you are sending from screen B here
final result = await Navigator.push( context);
},
On Screen B, put this code:
onTap: (){
// pass the data you want to use in screen A as the second paramter
Navigator.pop(context,number);
},
I hope this helps

How can I control what is passed to Navigator.pop() when clicking outside of a showModalBottomSheet?

I launch a modal bottom sheet, then use the data that is returned as its future.
var modalFuture = showModalBottomSheet(
// ...
);
modalFuture.then((data) {
// Use data
});
I return data to it from the modal widget with:
Navigator.pop(context, data);
This is working well when completing the modal interaction with a widget I've set up.
I encounter my issue when clicking outside of the modal. Clicking outside of the modal causes the modal to dismiss automatically (with a Navigator.pop(context) call?). I am okay with this closing interaction, but I would like to send data back with it (with a Navigator.pop(context, data) call). Is there anyway to override the implicit pop when clicking outside of a showModalBottomSheet modal?
You can wrap your ModalWidget with WillPopScope. You can see the example below
WillPopScope(
onWillPop: () async {
Navigator.pop(context, data);
return true; // return true if needs to be popped
},
child: ModelWidget(
…
),
);
This will ensure that Navigator.pop is called when auto popped using the back button.