Flutter "showDialog" with Navigator.pop() - dart

I have problem with showDialog, when i press nothing happens but if i use Navigator.pushNamed(context, "/screen1") it works. I can not run Navigator.pop(context), it does not return any errors.
_showDialog(BuildContext context) {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text("Alert Dialog title"),
actions: <Widget>[
new FlatButton(
child: new Text("Back"),
onPressed: () {
//Navigator.pushNamed(context, "/screen1");
Navigator.pop(context);
},
),
],
);
});}
In my build() :
IconButton(
iconSize: 30.0,
onPressed: () => _showDialog(context),
icon: Icon(
Icons.clear,
color: Colors.white,
),
)

had the same issue .having useRootNavigator: false, in showDialog params solved my issue .

Use pop() two times:-
Navigator.of(context).pop();
Navigator.of(context).pop();
Reason: first pop function dismiss the dialog and Second pop function close the screen

The above answer should be accepted for this question, just elaborating the above answer
return showDialog(
context: context,
useRootNavigator: false, //this property needs to be added
builder: (BuildContext context) {....});

Try calling Navigator.of(context).pop(); instead of Navigator.pop(context);

For closing dialogs, you can also use:
Navigator.pop(context, true);
Source: https://docs.flutter.io/flutter/widgets/Navigator/pop.html

For those who have nested/multiple Navigators, you can also use the pop method as shown below (notice the named parameter rootNavigator set to true):
Navigator.of(context, rootNavigator: true).pop();
As suggested by others, I tried setting the useRootNavigator in the showDialog as false but this way the barrierColor wasn't covering the whole screen (it was only covering the screen displayed by Navigator object the dialog was in).
In summary, you can use the way showed above if you have nested navigators and want to achieve having the barrierColor covering the whole screen.

Related

Why does WillPopScope always disable swipe backwards?

When pushing on a new screen and wanting to disable the ability to swipe backwards I found the WillPopScope widget.
WillPopScope(
onWillPop: () async => false,
child: <some child>
however, it prevents the swipe gesture regardless of whether or not I return true of false.
Is it possible to tell this widget that it can pop back on certain screen states?
So effectively I would have:
WillPopScope(
onWillPop: () async => widget._canSwipeBack,
child: <some child>
Right now I have add/not-add the widget based on the screen state which seems quite odd.
Please refer to the below code
Without wrapping the widget with WillPopScope usually its performs Navigator.pop(context);
// disables swiping back
WillPopScope(
// disables swiping back or navigating back
onWillPop: () {},
child: Scaffold(
body: Container(),
),
);
WillPopScope(
onWillPop: () {
// whenever you want to navigate back to specific route
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
},
child: Scaffold(
body: Container(),
),
);
WillPopScope(
onWillPop: () {
// pop back
Navigator.pop(context);
},
child: Scaffold(
body: Container(),
),
);

Dismissible confirmDismiss in combination with new route navigation causes Flutter crash

The context:
I stumbled upon a minor crash while testing a ListView of Dismissibles in Flutter. When swiping a dismissible, a Dialog is shown using the confirmDismiss option, for confirmation. This all works well, however the UI crashes when testing an unlikely use case. On the page are several options to navigate to other (named) routes. When a dismissible is swiped, and during the animation an option to navigate to a new route is tapped, the crash happens.
How to replicate the crash:
Dismiss the Dismissible
During the animation that follows (the translation of the position of the dismissible), tap on an action that brings you to a
new route. The timeframe to do this is minimal, I've extended it in the example.
The new route loads and the UI freezes
For reference, this is the error message:
AnimationController.reverse() called after AnimationController.dispose()
The culprit is the animation that tries to reverse when it was already disposed:
package:flutter/…/widgets/dismissible.dart:449
Things I've tried:
Initially, I tried checking this.mounted inside the showDialog builder but quickly realised the problem is not situated there.
Another idea was to circumvent the problem by using CancelableOperation.fromFuture and then cancelling it in the dispose() method of the encompassing widget, but that was to no avail.
What can I do solve or at least circumvent this issue?
The code (can also be found and cloned here):
// (...)
class _DimissibleListState extends State<DimissibleList> {
int childSize = 3;
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: childSize,
itemBuilder: (context, index) {
if (index == 0) {
return _buildNextPageAction(context);
}
return _buildDismissible();
},
),
);
}
Widget _buildNextPageAction(context) {
return FlatButton(
child: Text("Go to a new page"),
onPressed: () => Navigator.of(context).pushNamed('/other'),
);
}
Dismissible _buildDismissible() {
GlobalKey key = GlobalKey();
return Dismissible(
key: key,
child: ListTile(
title: Container(
padding: const EdgeInsets.all(8.0),
color: Colors.red,
child: Text("A dismissible. Nice."),
),
),
confirmDismiss: (direction) async {
await Future.delayed(const Duration(milliseconds: 100), () {});
return showDialog(
context: context,
builder: (context) {
return Dialog(
child: FlatButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text("Confirm dismiss?"),
),
);
},
);
},
resizeDuration: null,
onDismissed: (direction) => setState(() => childSize--),
);
}
}
I had almost same problem with confirmDismiss ,in my case I was using (await Navigator.push() ) inside of confirmDismiss to navigate to another screen but in return I faced this error :
AnimationController.reverse() called after
AnimationController.dispose()
so to solve my problem inside of confirmDismiss I call a future function out side of confirmDismiss (without await ) and then add return true or false after that function call to finish animation of confirmDismiss.

How to exit the app on back button press? [duplicate]

This question already has an answer here:
How To Override the “Back” button in Flutter? [duplicate]
(1 answer)
Closed 4 years ago.
I have an app that needs move from screen 1 to screen two ....now when the user presses the back button..it should show a dialog box...if the user presses yes it has to exit...any help?the upper solutions don't work
It seems like you can use WillPopScope. You also need to pass a callback function which will indicate what will happen on pressing the back button.
In your case, you can add the code to show an AlertDialog which will ask for exit confirmation from the user.
You can simply wrap your Scaffold inside a WillPopScope.
Example:
Widget build(BuildContext context) {
return WillPopScope(
child: /*Your scaffold widget*/
onWillPop: () {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Confirm Exit"),
content: Text("Are you sure you want to exit?"),
actions: <Widget>[
FlatButton(
child: Text("YES"),
onPressed: () {
SystemNavigator.pop();
},
),
FlatButton(
child: Text("NO"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
}
);
return Future.value(true);
},

TextField's autofocus property causing the widget into 'dirty' State

AppBar has a search icon, when clicked it displays a TextField in the 'title' property. When search icon is clicked I need TextField to autofocus. Problem is with 'autofocus' property, if set to true, instead of only changing the state of the title property, something is causing the widget to turn into a 'dirty' state. This causes the main build function to get called which rebuilds the entire thing.
Tried to replicate this and provide a sample app but strangely enough it seems to work just fine in the demo.
Any debug suggestions?
AppBar(
centerTitle: true,
title: StreamBuilder(
stream: false,
initialData: symbolBloc.isSearching,
builder: (BuildContext context, AsyncSnapshot<bool> snapshot){
if(snapshot.data) {
return TextField(
// autofocus: true, <--- here
controller: searchQuery,
style: new TextStyle(
color: Colors.white,
),
);
}
return new Text("", style: new TextStyle(color: Colors.white)
);
}),
backgroundColor: Colors.blueGrey,
elevation: 0.0,
actions: <Widget>[
StreamBuilder(
stream: bloc.isSearchActive,
initialData: false,
builder: (BuildContext context, AsyncSnapshot<bool> snapshot){
if(snapshot.data)
return activeSearchIconButton(symbolBloc);
return searchIconButton(symbolBloc);
},
),
],
),
searchIconButton(SymbolBloc bloc){
return new IconButton(
icon: new Icon(
Icons.search,
color: Colors.white,
),
tooltip: 'Search',
onPressed: (){
bloc.displaySearchField(true);
},
);
}
activeSearchIconButton(SymbolBloc bloc){
return new IconButton(
icon: new Icon(
Icons.close,
color: Colors.white,
),
tooltip: 'Search',
onPressed: (){
bloc.displaySearchField(false);
},
);
}
You can set FocusNode property of TextField and on Button click call FocusScope.of(context).requestFocus(_focusNode);
The problem had do with InheritedWidgets. Having more than one InheritedWidget was the cause. I had a parent widget wrapping the runApp() function (holds a reference to api object) and a child widget wrapping each route (holds reference to bloc class for each specific screen).
Problem went away once I moved the contents of child widget over to the parent and removed the child InheritedWidget altogether.

Dismissing a Cupertino dialogue action Flutter

While dismissing a presented Cupertino alert dialogue action using the explained method my entire screen gets popped of and the alert dialogue stays on the screen. This is my code.
if (deviceList.isEmpty){
var alert = new CupertinoAlertDialog(
title: new Text("Alert"),
content: new Text("There was an error signing in. Please try again."),
actions: <Widget>[
new CupertinoDialogAction(
child: const Text('Discard'),
isDestructiveAction: true,
onPressed: () { Navigator.pop(context, 'Discard'); }
),
new CupertinoDialogAction(
child: const Text('Cancel'),
isDefaultAction: true,
onPressed: () { Navigator.pop(context, 'Cancel'); }
),
],
);
showDialog(context: context, child: alert);
}
Is there anything wrong in what I am doing? I cant find any other solution to dismiss the alert dialogue. Please help.
In this case, you need to specify the rootNavigator to of() :
Navigator.of(context, rootNavigator: true).pop("Discard");
Check the implementation proposed in the documentation

Resources