Flutter dismiss selected dialog with Getx - flutter

I am using flutter for quite some time and recently use Get to implement state management.
I am facing a problem when opening a loading dialog 1st and then message dialog. Then I want to dismiss the loading dialog, but the message dialog is the one that keep dismiss.
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class HomeController extends GetxController {
Future<void> openAndCloseLoadingDialog() async {
showDialog(
context: Get.overlayContext,
barrierDismissible: false,
builder: (_) => WillPopScope(
onWillPop: () async => false,
child: Center(
child: SizedBox(
width: 60,
height: 60,
child: CircularProgressIndicator(
strokeWidth: 10,
),
),
),
),
);
await Future.delayed(Duration(seconds: 3));
Get.dialog(
AlertDialog(
title: Text("This should not be closed automatically"),
content: Text("This should not be closed automatically"),
actions: <Widget>[
FlatButton(
child: Text("CLOSE"),
onPressed: () {
Get.back();
},
)
],
),
barrierDismissible: false,
);
await Future.delayed(Duration(seconds: 3));
Navigator.of(Get.overlayContext).pop();
}
}
The above code dismisses the 2nd dialog, not the 1st dialog which what I want.
Can anyone give advice on this matter.

The reason why the AlertDialog is being dismissed instead of CircularProgressIndicator is because AlertDialog is on the top of the stack. What you can do here is to call Navigator.of(Get.overlayContext).pop(); to dismiss CircularProgressIndicator prior to displaying the AlertDialog.
Sample code based from the snippets provided.
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
final HomeController c = Get.put(HomeController());
void _incrementCounter() {
c.openAndCloseLoadingDialog();
// setState(() {
// _counter++;
// });
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class HomeController extends GetxController {
Future<void> openAndCloseLoadingDialog() async {
showDialog(
context: Get.overlayContext,
barrierDismissible: false,
builder: (_) => WillPopScope(
onWillPop: () async => false,
child: Center(
child: SizedBox(
width: 60,
height: 60,
child: CircularProgressIndicator(
strokeWidth: 10,
),
),
),
),
);
await Future.delayed(Duration(seconds: 3));
// Dismiss CircularProgressIndicator
Navigator.of(Get.overlayContext).pop();
Get.dialog(
AlertDialog(
title: Text("This should not be closed automatically"),
content: Text("This should not be closed automatically"),
actions: <Widget>[
FlatButton(
child: Text("CLOSE"),
onPressed: () {
Get.back();
},
)
],
),
barrierDismissible: false,
);
// await Future.delayed(Duration(seconds: 3));
// Navigator.of(Get.overlayContext).pop();
}
}

I use it with bottomSheet(), but it will also work fine with Dialog. Just add an argument to Get.back(closeOverlays: true):
Get.bottomSheet(
WillPopScope(
onWillPop: () async {
Get.back(closeOverlays: true);
return false;
},
child: const QuestionWidget(),
);

Try to use closeOverlays param this way :
Get.back(closeOverlays: true);

Related

Flutter awaiting result of dialog

i have the following code which is called by a click of a FlatButton:
_performOrderCheck(BuildContext context) async {
bool _checksCompleted = await _performBundleCheck(context);
print("Sepp");
print(_checksCompleted);
if (_checksCompleted) {
_addArticleToOrder(_innerQty, _article);
Navigator.pop(context);
}
}
Future<bool> _performBundleCheck(BuildContext context) async {
//check bundles
if (!_article.checkBundeledArticles()) {
showDialog(
context: context,
builder: (_) => AlertDialog(
title: Text('Menü unvollständig'),
content: Text(
'Sie haben nicht alle möglichen Artikel gewählt. Wollen sie dennoch fortfahren?'),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.pop(_);
return false;
},
child: Text('Nein')),
FlatButton(
onPressed: () {
//_addArticleToOrder(_innerQty, _article);
Navigator.pop(_);
return true;
//Navigator.pop(context);
},
child: Text('Ja')),
],
elevation: 24,
),
barrierDismissible: false);
} else {
return true;
}
}
What i would like is that the could waits for the user decision and then it calls "_addArticleToOrder". Is that possible?
Thanks for any help.
You can add await keyword in-front of showdialog and return value at the end of show dialog.
added await.
await showDialog(
add return value
barrierDismissible: false);
return true; // added line
While the accepted answer is working the result is always returning true.
If you want to get the result of the dialog, which could be false by clicking 'Nein' and true by clicking 'Ja', here´s the code:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
_performOrderCheck(context);
},
// onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
_performOrderCheck(BuildContext context) async {
_incrementCounter();
print("_performOrderCheck called");
bool _checksCompleted = await _performBundleCheck(context);
print("_checksCompleted result: $_checksCompleted");
if (_checksCompleted) {
print("_checksCompleted");
}
}
Future<bool> _performBundleCheck(BuildContext context) async {
//check bundles
if (true) {
return await showDialog(
context: context,
builder: (_) => AlertDialog(
title: Text('Menü unvollständig'),
content: Text(
'Sie haben nicht alle möglichen Artikel gewählt. Wollen sie dennoch fortfahren?'),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.pop(context, false);
},
child: Text('Nein')),
FlatButton(
onPressed: () {
Navigator.pop(context, true);
},
child: Text('Ja')),
],
elevation: 24,
),
barrierDismissible: false,
);
} else {
return true;
}
}
}
Using Navigator.pop(context, false); and Navigator.pop(context, true); returns the result of the dialog to showDialog.
Using return await returns it then from the _performBundleCheck function to _performOrderCheck.

Why is Navigator.pop failing to pass a parameter back?

I'm launching an alert dialog to confirm a user's action of saving or clearing a filter, and I want to return a boolean based on their selection. I'm trying to pass it through Navigator.pop() but keep getting this error:
The following _TypeError was thrown while handling a gesture:
type 'bool' is not a subtype of type 'AlertDialog' of 'result'
Anyone know why this is happening? Here is my code. The specific error is happening in the onPressed where I assign the result of showDialog to a var shouldClear.
import 'package:flutter/material.dart';
class FilterNavbar extends StatelessWidget {
final VoidCallback clearFilter;
const FilterNavbar({#required this.clearFilter});
#override
Widget build(BuildContext context) {
return Container(
height: 60,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width * .3,
child: RaisedButton(
onPressed: () async {
var shouldClear = await showDialog<AlertDialog>(
context: context,
builder: (context) {
return generateDialog(context, attemptSave: false);
}
);
},
child: const Text("Clear"),
),
),
Container(
width: MediaQuery.of(context).size.width * .3,
child: RaisedButton(
onPressed: () async {
await showDialog<AlertDialog>(
context: context,
builder: (context) {
return generateDialog(context, attemptSave: true);
}
);
Navigator.pop(context, true);
},
child: const Text("Save"),
),
)
]
),
);
}
}
AlertDialog generateDialog(BuildContext context, {bool attemptSave}){
return AlertDialog(
title: Center(child: Text("${attemptSave ? "Save": "Clear"} filter?")),
actions: [
FlatButton(
onPressed: () {
if (attemptSave) {
Navigator.pop(context, false);
}
else {
Navigator.pop(context, true);
}
},
child: Text("${attemptSave ? "Save": "Clear"}")
)
],
);
}
You can copy paste run full code below
Please change AlertDialog to bool
From
await showDialog<AlertDialog>
to
await showDialog<bool>
working demo
full code
import 'package:flutter/material.dart';
class FilterNavbar extends StatelessWidget {
final VoidCallback clearFilter;
const FilterNavbar({#required this.clearFilter});
#override
Widget build(BuildContext context) {
return Container(
height: 60,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width * .3,
child: RaisedButton(
onPressed: () async {
var shouldClear = await showDialog<bool>(
context: context,
builder: (context) {
return generateDialog(context, attemptSave: false);
});
},
child: const Text("Clear"),
),
),
Container(
width: MediaQuery.of(context).size.width * .3,
child: RaisedButton(
onPressed: () async {
await showDialog<bool>(
context: context,
builder: (context) {
return generateDialog(context, attemptSave: true);
});
Navigator.pop(context, true);
},
child: const Text("Save"),
),
)
]),
);
}
}
AlertDialog generateDialog(BuildContext context, {bool attemptSave}) {
return AlertDialog(
title: Center(child: Text("${attemptSave ? "Save" : "Clear"} filter?")),
actions: [
FlatButton(
onPressed: () {
if (attemptSave) {
Navigator.pop(context, false);
} else {
Navigator.pop(context, true);
}
},
child: Text("${attemptSave ? "Save" : "Clear"}"))
],
);
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: FilterNavbar(
clearFilter: () {},
)),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}

Flutter showDialog not working on a simple test

I am trying flutter and have problems in making a simple showdialog work. I tried a simple test with one button:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter Test',
home: Scaffold(
appBar: AppBar(
backgroundColor: Colors.teal,
title: Text('Flutter'),
),
body: Center(
child: ListView(
padding: EdgeInsets.all(8),
children: <Widget>[
Container(
child: RaisedButton(
child: Text('My Button'),
onPressed: () => {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
return AlertDialog(
title: Text('Test'),
content: Text('Dialog content'),
);
},
),
},
color: Colors.cyan,
textColor: Colors.white,
),
),
],
),
),
),
);
}
}
I expect the alert to pop on button tap. What am I missing? I also tried it with the showdialog in a separate custom function call, same result.
You need to use the showDialog method provided by Flutter, as seen on the example here. Check my example below with your button but using the showDialog method:
class DialogIssue extends StatefulWidget {
#override
_DialogIssueState createState() => _DialogIssueState();
}
class _DialogIssueState extends State<DialogIssue> {
#override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
child: Text('My Button'),
onPressed: () => _confirmDialog(),
color: Colors.cyan,
textColor: Colors.white,
),
);
}
Future<void> _confirmDialog() async {
switch (await showDialog<bool>(
context: context,
builder: (BuildContext context) {
return SimpleDialog(
title: const Text('True or false'),
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
SimpleDialogOption(
onPressed: () { Navigator.pop(context, true); },
child: const Text('Confirm',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
SimpleDialogOption(
onPressed: () { Navigator.pop(context, false); },
child: const Text('Cancel'),
),
],
),
],
);
}
)){
case true:
print('Confirmed');
break;
case false:
print('Canceled');
break;
default:
print('Canceled');
}
}
}
It can be done in a StatelessWidget, like in this DartPad pad.
Sidenote: I've had to use a Builder because the context in MyApp's build method doesn't have a MaterialApp ancestor.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
showAppDialog(BuildContext context) {
print("Showing app dialog");
showDialog(context: context,
builder: (context) {
return AlertDialog(
title: const Text(
"This is a dialog that works.",
),
icon: const Icon(Icons.delete),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text("OK"),
),
],
);
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(body: SafeArea(child: Builder(
builder: (context) {
return TextButton(child: Text("Show dialog"), onPressed: () => showAppDialog(context),);
}
))),
);
}
}
PS: You're already using showDialog, why does this answer suggest you to do that 🤔.

Is there a way to configure willPopScope to catch a custom pop navigation in flutter?

Is there a way to configure willPopScope to catch a custom pop navigation as follows? I have a custom Raisedbutton widget with onPressed to navigate back to the previous page.
But willPopScope doesn't catch the navigation as it does for the AppBar back button. I'm not sure if this is even possible. Please advise. Thanks!
WillPopScope(
child: Scaffold(
body: Container(
child: Center(
child: RaisedButton(
onPressed:(){
return Navigator.pop(context);
},
child: Text("Go Back),
),
),
),
),
onWillPop: () async {
// code to show a modal
}
);
Here is a full example to achieve your goal. WillPopScope needs a maybePop call in order to do your logic:
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: RaisedButton(
child: Text('Jump To Next Screen'),
onPressed: () => Navigator.of(context)
.push(MaterialPageRoute(builder: (_) => ModalScreen())),
),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class ModalScreen extends StatefulWidget {
#override
_ModalScreenState createState() => _ModalScreenState();
}
class _ModalScreenState extends State<ModalScreen> {
#override
Widget build(BuildContext context) {
return WillPopScope(
child: Scaffold(
appBar: AppBar(
leading: InkResponse(
child: Icon(Icons.arrow_back),
onTap: () => Navigator.of(context).maybePop(),
),
),
backgroundColor: Colors.blue,
body: Center(
child: RaisedButton(
child: Text('Let\'s go back'),
onPressed: () {
Navigator.of(context).maybePop();
},
),
),
),
onWillPop: () => _willPop(context),
);
}
Future<bool> _willPop(BuildContext context) {
final completer = Completer<bool>();
showModalBottomSheet(
context: context,
builder: (buildContext) {
return SizedBox(
height: 200,
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 20),
child: Text('Are you sure?'),
),
MaterialButton(
child: Text('YES'),
onPressed: () {
completer.complete(true);
Navigator.of(context).pop();
}),
MaterialButton(
child: Text('NO'),
onPressed: () {
completer.complete(true);
}),
],
),
);
});
return completer.future;
}
}
Once you return true for your modal, you also need to Pop the screen as the modal has his own context needed to be popped.
Final result is the following:

flutter: Another exception was thrown: No MaterialLocalizations found

I am trying to show an Alert Dialog on press of a button in Flutter.
Following is my code
main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "Different Widgets",
debugShowCheckedModeBanner: false,
home: showAlertDialog()
);
}
void _dialogResult(String value) {
if (value == "YES") {
print("YES");
} else {
print("NO");
}
Navigator.pop(context);
}
Widget showAlertDialog() {
TextEditingController textEditingController = TextEditingController();
return Scaffold(
appBar: AppBar(
title: Text("Different Widgets"),
),
body: Container(
child: Center(
child: Column(
children: <Widget>[
TextField(
controller: textEditingController,
),
RaisedButton(
onPressed: () {
print("Hi");
AlertDialog dialog = AlertDialog(
title: Text("Hi"),
content: Text(
textEditingController.text,
style: TextStyle(fontSize: 30.0),
),
actions: <Widget>[
FlatButton(
onPressed: () {
_dialogResult("YES");
},
child: Text("YES")),
FlatButton(
onPressed: () {
_dialogResult("NO");
},
child: Text("NO")),
],
);
showDialog(context: context, builder: (BuildContext context) => dialog);
},
child: Text("Click Me"),
)
],
),
),
),
);
}
What does this has to do with Localisation, I cannot follow. I did the same steps as per the docs. I am able to see the button but on click of that button I keep getting error. I tried writing print statement inside of button click and the print statement appears in the log, definitely something wrong with AlertDialog.
You may get No MaterialLocalizations found error while showing dialog using showDialog() class in Flutter. The issue is putting child widget on home property of MaterialApp() widget without creating new widget class.
One way to solve is putting MaterialApp() inside runApp() and create new class for home property.
import 'package:flutter/material.dart';
main() {
runApp(
MaterialApp(
home: MyApp(),
title: "Different Widgets",
debugShowCheckedModeBanner: false,
),
);
}
/*
place MaterialApp() widget on runApp() and create
new class for its 'home' property
to escape 'No MaterialLocalizations found' error
*/
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return showAlertDialog();
}
void _dialogResult(String value) {
if (value == "YES") {
print("YES");
} else {
print("NO");
}
Navigator.pop(context);
}
Widget showAlertDialog() {
TextEditingController textEditingController = TextEditingController();
return Scaffold(
appBar: AppBar(
title: Text("Different Widgets"),
),
body: Container(
child: Center(
child: Column(
children: <Widget>[
TextField(
controller: textEditingController,
),
RaisedButton(
onPressed: () {
print("Hi");
AlertDialog dialog = AlertDialog(
title: Text("Hi"),
content: Text(
textEditingController.text,
style: TextStyle(fontSize: 30.0),
),
actions: <Widget>[
FlatButton(
onPressed: () {
_dialogResult("YES");
},
child: Text("YES")),
FlatButton(
onPressed: () {
_dialogResult("NO");
},
child: Text("NO")),
],
);
showDialog(
context: context,
builder: (BuildContext context) => dialog);
},
child: Text("Click Me"),
)
],
),
),
),
);
}
}