I'm pushing a new route like so in my app
Navigator.of(context)
.push<void>(
FilterTypesPage.routeFullScreen(context),
).then(
(value) {
log('PAGGGE POPPED');
},
),
static Route routeFullScreen(BuildContext context) {
return MaterialPageRoute<void>(
settings: const RouteSettings(name: routeName),
builder: (_) => BlocProvider.value(
value: BlocProvider.of<FeatureBloc>(context),
child: const FilterTypesPage(),
),
fullscreenDialog: true);
}
for some reason log('PAGGGE POPPED'); doesn't get called on page close
I'd like to trigger a bloc event or a function when I close this page
You should just call
Navigator.pop(context, someData);
from your RouteSettings where someData is the data you want to pass from the RouteSettings to the former page.
Then from your former page, you can perform your event handling inside the then block. The value inside the then block is the data that was passed from the RouteSettings page.
Alternatively, you can also use async-await instead of then in your former page.
onPressed: () async {
final someData = await Navigator.of(cotext).push(.....);
// Now perform your event handling which will be invoked after you pop `RouteSettings` page.
}
Related
Is there any way to refresh the previous page/ stack when Navigator.pop(context) is called? I need to do the API calling of the previous page to refresh the state of the page. The navigator.pop will sometimes be an alert dialog or maybe a back button. Is there a way to do the API calling? ThankYou.
use the then function after you push another route.
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => MyHomePage(),
),
).then(
(value) {
if (value) {
//refresh here
}
},
);
and when you return to previous screen, provide the pop function a value that determines whether to perform some action.
Navigator.of(context).pop(true);
Previous page
void goToNextPage()async {
var refresh = await Navigator.push(_context, new MaterialPageRoute(
builder: (BuildContext context) => new nextPage(context))
);
if(refresh ) setState((){});
}
NextPage
Navigator.pop(context, true);
//calling a different class for dialog on submit click it's not updating list data
It's refreshing data but not going to the last opened tab on the second tab I perform this action but on tap, it's going on the first tab.
onTap: () {
setState(() {
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) =>
EmployeePersonalDetailsUpdateActivity(_token),));
widget.listener.onEmployeeFamilyClick(_day, _month, _year,
_working);
});
Navigator.pop(context,true);
}
Your are calling your widget.listener after you navigate, call it before you navigate the route & if you want to pass the api response to the EmployeePersonalDetailsUpdateActivity widget, you must handle it, and it's your choice when you want to refresh your screen.
setState(() {
widget.listener.onEmployeeFamilyClick(_day,_month,_year,_working);
Navigator.pushReplacement(context, MaterialPageRoute(builder:
(context) => EmployeePersonalDetailsUpdateActivity(_token),));
});
There's another way:
widget.listener.onEmployeeFamilyClick(_day,_month,_year,_working).then((value) {
Navigator.pushReplacement(context, MaterialPageRoute(builder:
(context) => EmployeePersonalDetailsUpdateActivity(_token), value));
});
you can navigate your widget and pass the value of the response and show the updated value on the widget.
I have a form on my page.
When I fill out the form, if it is valid a loading pop up appears and I start to perform calculations. Depending on the state of the calculations, the message on the loading popup will change.
I'm trying to do a function in my provider which will change the message depending on the state of the popup.
When I print the values they are displayed correctly but my message does not change in the widget
How I call my screen :
return MultiProvider(
providers: [
ChangeNotifierProvider<FormCalculatorNotifier>(
create: (BuildContext context) => FormCalculatorNotifier()
),
ChangeNotifierProvider<LoaderNotifier>(
create: (BuildContext context) => LoaderNotifier()
),
],
child: CalculatorScreen(),
);
In my screen, how I call the popup and calculations:
Expanded(
child: ButtonComponent.primary(
context: context,
text: AppTextButton.CALCUL,
onPressed: () async {
await _formProvider.submitForm(context);
if(_formProvider.state == FormProviderState.isSuccess){
// --------------------------------------
// Here I call the popup with the message variable
DialogComponent.loadingPopUp(context: context, description: _loaderProvider.message);
// --------------------------------------
// Here I try to change the popup message because I have my calculations
_loaderProvider.state = LoaderState.isLoadingJsons;
await _loaderProvider.updateMessage();
}
},
),
),
The popUp notifier:
class LoaderNotifier with ChangeNotifier {
LoaderState state = LoaderState.isReady;
String message = "Start...";
Future<void> updateMessage() async{
print('update message');
if(state == LoaderState.isLoadingJsons){
message = "Loading files...";
}
notifyListeners();
}
}
EDIT: I just saw that my problem is that I call the updateMessage function after calling it from the DialogComponent
If I want to follow my logic which is to study if the form is good to display the popup then perform the calculations and according to the calculations change the message of the popup, what to do since I will call the updateMessage function afterwards?
How to call a function in flutter ,after moving back to a screen from another screen?
For Example:
Screen 1
function1(){
}
Screen2
function2(){
//Go back to screen 1 and then call function1()
}
It's simple.
Navigator.push(context, MaterialPageRoute(builder: (context)=> SecondScreen())).then((_){
// This method gets callback after your SecondScreen is popped from the stack or finished.
function1();
});
You should also refer the Flutter Navigation & Routing.
Here is the solution!
Second Screen
Navigator.pop(context, [1]);
or, if you don't want to send back any data, you can only call
Navigator.pop(context);
First Screen
Navigator.push( context, MaterialPageRoute( builder: (context) => SecondScreen(), ), ).then((value) { //do something after resuming screen
});
Imho the solutions provided here aren't valid solutions.
If you use a routes Future it may be called multiple times and will even be called in case of a forward navigation.
Instead use a NavigatorObserver:
class AppNavigationObserver extends NavigatorObserver {
#override
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
print("AppNavigationObserver: ${route.settings.name}");
print("AppNavigationObserver: ${previousRoute?.settings.name}");
}
}
You can then use it for example like this:
MaterialApp(
navigatorObservers: [
AppNavigationObserver()
],
onGenerateRoute: (RouteSettings settings) {
return PageRouteBuilder(
maintainState: true,
settings: settings,
transitionDuration: const Duration(milliseconds: 300),
pageBuilder: (context, animation, secondaryAnimation) {
// Your route builder according to settings
},
);
},
)
The important part is passing onGenerateRoute's settings paramter to the PageRouteBuilder settings. Otherwise settings.arguments and settings.name will be null in the didPop handler.
On app homepage I set up Model2 which make API call for data. User can then navigate to other page (Navigator.push). But I want make API call from Model2 when user press back (_onBackPress()) so can refresh data on homepage.
Issue is Model2 is not initialise for all user. But if I call final model2 = Provider.of<Model2>(context, listen: false); for user where Model2 is not initialise, this will give error.
How I can call Provider only on condition? For example: if(user == paid)
StatefulWidget in homepage:
#override
Widget build(BuildContext context) {
return ChangeNotifierProxyProvider<Model1, Model2>(
initialBuilder: (_) => Model2(),
builder: (_, model1, model2) => model2
..string = model1.string,
),
child: Consumer<Model2>(
builder: (context, model2, _) =>
...
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute(context: context)),
In Page2:
Future<void> _onBackPress(context) async {
// if(user == paid)
final model2 = Provider.of<Model2>(context, listen: false);
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return
// if(user == paid)
Provider.value(value: model2, child:
AlertDialog(
title: Text('Back'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('Go back'),
],
),
),
actions: <Widget>[
FlatButton(
child: Text('OK'),
onPressed: () async {
// if(user == paid)
await model2.getData();
Navigator.of(context).pop();
},
),
],
),
);
},
);
}
Alternative method (maybe more easy): How to call provider on previous page (homepage) on Navigator.of(context).pop();?
TLDR: What is best solution for call API so can refresh data when user go back to previous page (but only for some user)?
You can wrap your second page interface builder in a WillPopScope widget, and then, pass whatever method you want to call to the onWillPop callback of the WillPopScope widget. This way, you can make your API call when user presses the back button. Find more about the WillPopScope widget on this WillPopScope Flutter dev documentation article.
tldr; Establish and check your single point of truth before the call to the Provider
that may result in a null value or evaluate as a nullable reference.
Perhaps you can change the architecture a bit to establish a single (nullable or bool) reference indicating whether the user has paid. Then use Darts nullability checks (or just a bool) to implement the behavior you want. This differs from your current proposal in that there would be no need to call on the Provider to instantiate the model. Just add a single point of truth to your User object that is initialized to null or false, and then change that logic only when the User has actually paid.
Toggling widgets/behavior in this way could be a solution.
Alternatives considered:
Packaging critical data points into a separate library so that the values can be imported where needed.
Other state management methods for key/value use.
If you want to simply hide/show parts of a page consider using the OffStage class or the Visibility class
Ref
Dart null-checking samples