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);
},
Related
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(),
),
);
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 do I add Progressbar?
After click button, I need to display a progress bar while executing the task and after complete task, the dialog should be dismissed and move to next page if successful login.
in Raised Button:
onPressed: () async {
(isOffline)
? showSnackBar(context)
: checkingValidation(usernameController, context, _url);
}
checkingValidation(TextEditingController usernameController, BuildContext context, String _url) async {
if(....){
...
}else{
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HomePage()))
}
You should use Stack and inside that use alert dialog as you want.
Add a flag like bool showProgress, whenever you need a progress alert dialog to be shown or hide just set showProgress=true or showProgress=false respectively in setState to show or hide the alert dialog widget.
or simply use this template I created.
static showProgressDialog(BuildContext context, String title) {
try {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
return AlertDialog(
content: Flex(
direction: Axis.horizontal,
children: <Widget>[
CircularProgressIndicator(),
Padding(padding: EdgeInsets.only(left: 15),),
Flexible(
flex: 8,
child: Text(
title,
style: TextStyle(
fontSize: 16, fontWeight: FontWeight.bold),
)),
],
),
);
});
} catch (e) {
print(e.toString());
}
}
when you want to show this just call
AppUtils.showProgressDialog(context, "Please wait");
when you have done task, just call Navigator.pop(context);
You can create a class as AppUtils and place this template in class, so you can reuse this anywhere in your app.
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.
I am currently making an app with lists of a Card widgets. This is a code snippet:
new GestureDetector(
onLongPress: () {
showAlert();
//pops up an AlertDialog
},
child: new Card(child: new Text("Hello"))
)
Is it possible to make the Card widget touchable, so the user can feel/see that the card is (long)tapped? I am searching for an 'InkWell-something result'.
Edit: I want to have a splash effect on my Card widgets when I longpress them.
An example of the splash effect that I mean is given in the following GIF:
Use this
return Card(
child: InkWell(
onTap: () {},
onLongPress: () {}
child: Container(),
));