Navigator.push() inside showdialog not working - flutter

Trying to navigate to a new screen using Navigator.push(), but it's not working.
I have created a custom class to show AlertDialog and call the class with the object to show alertDialog
_customerAlertDialog.showConfirmAlertDialog(
context: context,
title: "Login In",
subTitle: "You need to login to purchase.",
onTapResponse: (bool val) async {
if (val) {
/// close AlertDialog
Navigator.of(context).pop();
Navigator.of(context).push(MaterialPageRoute(builder: (context) => LoginScreen()));
print("show the login screen");
} else {
//TODO : when user click no.
}
});
navigator.pop() is working,
print statement is working,
but Navigator.push is not working. Also tried this:
Navigator.push(context,MaterialPageRoute(builder: (context) => LoginScreen()));

The context object that you use in Navigator.of(context).pop() isn't aware of the dialog.
If your custom alert dialog is calling showDialog, consider passing on the BuildContext object that is returned by the builder:
showDialog(
context: context,
builder: (BuildContext ctx) {
// ctx is a context object that will be aware of the dialog
// consider passing this along to onTapResponse as an argument
},
);
Then you can use that BuildContext to get the navigator that will be able to close the dialog:
onTapResponse: (BuildContext ctx, bool val) async {
if (val) {
// close AlertDialog
Navigator.of(ctx).pop();
Navigator.of(ctx).push(MaterialPageRoute(builder: (context) => LoginScreen()));
print("show the login screen");
} else {
//TODO : when user click no.
}
}

Related

Pass value when dialog closed

I'm trying to create an app in Flutter.
When a particular button is pressed a Dialog shows up. In the dialog, the user can write to TextField. I want to use this text in the previous screen when the dialog is closed with pop().
Is there any way to do it?
try this:
showDialog(
context: context,
builder: (context) => Dialog(),
).then((result){
// use the result here
});
and in dialog pop like this:
Navigator.pop(context, result);
You can await to get data from button. also You can pass data .pop(YourValue)
onPressed: () async {
final data = await showDialog(
context: context,
builder: (context) {
final TextEditingController controller =
TextEditingController(); // this can be outside to get direct text from it
return AlertDialog(
content: TextField(
controller: controller,
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop(controller.text);
},
child: Text("Close"))
],
);
},
);
if (data != null) {
//your operation
}
print(data);
},

Call widget in Future<Widget> function Flutter

Basically I have a button(GestureDetector) which to call Future function in the same file. The thing is the widget in that function does not appear as it should but the background process is successfully running.
The trigger:
showDialog(
context: context,
builder: (context) {
AlertDialog(
/// Below the button to call function *resetPassword*
GestureDetector(
child: Text("Yes"),
onTap: () async {
Navigator.of(context).pop();
resetPassword('manan#gmail.com')}))})
The widget function:
Future<Widget> resetPassword(email) async {
try{
await FirebaseAuth.instance.sendPasswordResetEmail(email: email)
return AlertDialog(
///the content of dialog)
}on FirebaseAuthException catch (e) {
return AlertDialog(
///the content of dialog)
}}
Surprisingly the email of reset password was successfully sent.
Disclaimer: I am new to Flutter, hopefully sifus can considerate it.
When you're working with dialogs in general you have to wrap it with showDialog() method, like:
await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Center(child: Text('Reset password')),
content: Builder(
builder: (context) {
return Container(child: ForgotPasswordForm());
},
),
);
},
);
Secondly, I see that you have nested Alert Dialog widgets and I think you should restructure this.

Flutter: How do I pop dialog as well as current page?

Below is the code.
Navigation to Login page, from Home page
ElevatedButton(
onPressed: () => Navigator.of(context, rootNavigator: true)
.push(MaterialPageRoute(
fullscreenDialog: true,
builder: (context) => UserLoginPage(),
)),
child: Text('Login to continue'),
),
Inside Login page:
BlocConsumer<UserAuthCubit, UserAuthState>(
listener: (context, state) {
if (state is UserAuthorized) {
Navigator.of(context, rootNavigator: true).pop();
}
if (state is UserAuthWaiting) {
showModalBottomSheet(
useRootNavigator: true,
isDismissible: false,
context: context,
builder: (context) {
return WillPopScope(
onWillPop: () async => false,
child: Center(
child: Text(state.msg),
),
);
});
dialog = true;
} else {
if (dialog) {
Navigator.of(context, rootNavigator: true).pop();
dialog = false;
}
}
},
builder: (context, state) { // some widget code... }
When the state is UserAuthorized, I want to pop the dialog as well as the LoginPage, so as to return to the last page i.e. Home page. However, with the code above, sometimes, it works, and on another time, the Home page is also popped out.
I tried, with/without rootNavigator set to true, but couldn't achieve my objective.
Please help me understand what I'm missing here.
I have checked the answer here How to dismiss flutter dialog?
.
You can simply use
Navigator.popUntil(context, (route) {
return count++ == 2;
});
UPDATE:
If you don't know how many page you should pop then you should use
Navigator.push(context, MaterialPageRoute(builder: (context)=>YourMaterialClassName(), settings: RouteSettings(name: "nameOfYourClass")));
while you push your material class.
then in the time of poping up use
Navigator.popUntil(context, (route) => route.settings.name == "nameOfYourClass");
I suggest after the dialog return true, instead of using Navigator.of(context).pop().
Or you can try using
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (context) => HomePage()),
)
By doing so you replace the LoginPage with HomePage.

How to send data back and navigate to a specific screen? [Flutter]

I have a calendar widget which selects a date in the form of DateTime. I want to send this value using Navigator.pop but i also want to use a MaterialPageRoute which sends the user back to a page. I cannot use Navigator.pop this way :
onPressed: () async{
DateTime send = date;
Navigator.pop(context, MaterialPageRoute(
builder: (context) => DayPageViewExample(date),
)
);
},
because the user will be navigating to the calendar screen in this flow : initial screen => second screen => calendar screen. and the selected date in the calendar must be sent back to the initial screen.
I essentially want something like this:
onPressed: () async{
DateTime send = date;
Navigator.pop(context, send);
},
but i want to send the DateTime send as a parameter to MaterialPageRoute.
Could i get some suggestion on how to refactor the Navigator.pop so that i can send a parameter back using MaterialPageRoute?
I had a similar issue: TabScreen -> TakePhotoScreen -> DisplayPhotoScreen and after the photo was displayed, I wanted to return to the TabScreen with a specific tab selected.
In DisplayScreen I wrapped the main widget with WillPopScope:
body: Center(
child: WillPopScope(
onWillPop: _handleNavigatorPop,
child: ...
)
)
_handleNavigatorPop method shows an alert dialog. On "Yes" action:
actions: <Widget>[
FlatButton(
child: Text("Yes"),
onPressed: () async {
final PageRouteBuilder _route = PageRouteBuilder(
pageBuilder: (BuildContext context, _, __) {
return TabsScreen(tabIndex: tabIndex);
});
//to remove all the routes below the pushed route, use a RoutePredicate that always returns false (e.g. (Route<dynamic> route) => false).
await Navigator.pushAndRemoveUntil(context, _route, (Route<dynamic> r) => false);
return Future.value(false);
},
),
FlatButton(
child: Text("No"),
onPressed: () {
Navigator.of(context).pop();
},
),
]
Read more: pushAndRemoveUntil
For return to first route you can use popUntil - like this:
Navigator.of(context).popUntil((route) => route.isFirst);
But if you want to send result - I think you can use pushReplacement
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => DayPageViewExample(date),
)
);
It will just open new route with closing existing ones
Hope this is what you need
Sounds like you want to do something like this:
Navigator.pushAndRemoveUntil(
MaterialPageRoute(builder: (BuildContext context) => DayPageViewExample(date)),
(Route<dynamic> route) => false),
);
This will remove all the routes below the pushed route.
Read more about pushAndRemoveUntil

Reload current screen when an exception occurs

I have a screen on which a user can input some number which he then confirms. Confirming makes an API call. This call can fail. When it fails I show an AlertDialog with showDialog.
What I want is, that when the user dismisses the dialog the screen refreshes/reloads. The input data should be erased (and there are some other effects which should be reset too).
I'm not sure what the best way to achieve this is.
void _handleError(e) {
showDialog(
context: context,
builder: (BuildContext builder) {
return AlertDialog(
title: Text(e.toString()),
content: Text('Some content'),
);
},
);
// I'm guessing I should do something here?
}
// This is the handler for the confirm buttons `onPressed` field.
void _pay(context) {
double amount = double.parse(textFieldController.text);
apiClient
.createInvoice(amount)
.then((Map<String, dynamic> invoice) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return Pay(invoice);
}),
);
}).catchError(handleError);
}
The showDialog() function returns a Future when it gets dismissed. So you can chain a then() call to the showDialog() and perform your clean up there.
void _handleError(e) {
showDialog(
context: context,
builder: (BuildContext builder) {
return AlertDialog(
title: Text(e.toString()),
content: Text('Some content'),
);
},
).then((_){
//do your clean up
_inputTextController.text = '';
});
}