Flutter awaiting result of dialog - flutter

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.

Related

How to close specific Dialog

I am opening a dialog from another dialog and trying to close the 1st dialog, but it is closing the recent dialog. Similar kind of git issue.
I've tried
putting ValueKey on AlertDialog
using rootNavigator:true while pop
keeping context into variable and doing Navigator.of(specifiqContext).pop()
But none of them is working.
Code to reproduce the issue on dartPad.
class MultiDialogTest extends StatefulWidget {
const MultiDialogTest({Key? key}) : super(key: key);
#override
State<MultiDialogTest> createState() => _MultiDialogTestState();
}
class _MultiDialogTestState extends State<MultiDialogTest> {
BuildContext? dialog1Context, dialog2Context;
Future<void> _showDialog1(BuildContext context) async {
await showDialog(
context: context,
barrierDismissible: false,
builder: (c) {
dialog1Context = c;
return AlertDialog(
key: const ValueKey("dialog 1"),
title: const Text("Dialog 1"),
content: ElevatedButton(
child: const Text("close dialog2"),
onPressed: () {
if (dialog2Context != null) {
Navigator.of(dialog2Context!,).pop();
}
},
),
actions: [
ElevatedButton(
child: const Text("close this"),
onPressed: () {
Navigator.of(c, rootNavigator: true).pop();
},
),
],
);
});
dialog1Context = null;
}
Future<void> _showDialog2(BuildContext context) async {
await showDialog(
context: context,
barrierDismissible: false,
builder: (c) {
dialog2Context = c;
return AlertDialog(
key: const ValueKey("dialog 2"),
title: const Text("Dialog 2"),
actions: [
ElevatedButton(
child: const Text("close this"),
onPressed: () {
Navigator.of(c, rootNavigator: true).pop();
},
),
],
content: Column(
children: [
ElevatedButton(
onPressed: () async {
await _showDialog1(context);
},
child: const Text("Open dialog 1"),
),
],
),
);
});
dialog2Context = null;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () {
_showDialog2(context);
},
child: const Text("show dialog 2"),
),
),
);
}
}
How can I close bellow Dialog(Dialog 2) without closing above(Dialog 1).
I don't like to close both and reopen the Dialog 1.
You need to pass context of the dialog you want to close (parentContext) and call:
Navigator.pop(parentContext); // close parent
Navigator.pop(context); // close current
Create a separate context and pass the correct context which one you want to close to the Navigator.pop(yourContextThatYouWishToClose)
Navigator.pop(dialogContext);
Here is the example code.
BuildContext dialogContext; // <<----
showDialog(
context: context, // <<----
barrierDismissible: false,
builder: (BuildContext context) {
dialogContext = context;
return Dialog(
child: new Row(
mainAxisSize: MainAxisSize.min,
children: [
new CircularProgressIndicator(),
new Text("Loading"),
],
),
);
},
);
await _longOperation();
Navigator.pop(dialogContext);
What you could do is pop twice in showDialog1 and then await for showDialog1 immediately.
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: const MultiDialogTest(),
);
}
}
class MultiDialogTest extends StatefulWidget {
const MultiDialogTest({Key? key}) : super(key: key);
#override
State<MultiDialogTest> createState() => _MultiDialogTestState();
}
class _MultiDialogTestState extends State<MultiDialogTest> {
Future<void> _showDialog1(BuildContext context) async {
await showDialog(
context: context,
barrierDismissible: false,
builder: (c) {
return AlertDialog(
key: const ValueKey("dialog 1"),
title: const Text("Dialog 1"),
content: ElevatedButton(
child: const Text("close dialog2"),
onPressed: () async {
Navigator.of(context).pop();
Navigator.of(context).pop();
await _showDialog1(context);
},
),
actions: [
ElevatedButton(
child: const Text("close this"),
onPressed: () {
Navigator.of(c).pop();
},
),
],
);
});
}
Future<void> _showDialog2(BuildContext context) async {
await showDialog(
context: context,
barrierDismissible: false,
builder: (c) {
return AlertDialog(
key: const ValueKey("dialog 2"),
title: const Text("Dialog 2"),
actions: [
ElevatedButton(
child: const Text("close this"),
onPressed: () {
Navigator.of(c).pop();
},
),
],
content: Column(
children: [
ElevatedButton(
onPressed: () async {
await _showDialog1(context);
},
child: const Text("Open dialog 1"),
),
],
),
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () {
_showDialog2(context);
},
child: const Text("show dialog 2"),
),
),
);
}
}
When you use showDialog what happens under the hood is a simple push on the Navigator, this will add the Dialog on the top of the current Navigator as a new Route.
All the pop methods in Navigator simply pop from the topmost route so this is not easily feasible.
A dirty hack may be to pop twice and show again the first dialog like in this sample that works in your dartpad sample
onPressed: () {
if (dialog2Context != null) {
Navigator.of(dialog2Context!).pop();
Navigator.of(dialog2Context!).pop();
_showDialog1(context);
}
},
In my opinion, having a dialog spawning another dialog its not the best UX you can provide to your user, but you can always check which routes are involved by using the inspector:
https://docs.flutter.dev/development/tools/devtools/inspector
in this case, you can quickly check that the dialog will be always on top (in this case the latest of the tree), the proper way to fix this should be to create several navigators and decide which one to use for showing your dialog, but that will complexity a lot of your code!

FLUTTER/DART - TEXT not displaying to home.dart file within a FloatingActionButton

Having a problem with getting text to display in my home.dart file when it's entered in the FloatingActionButton.
Below is the code sample. Any suggestions where I am getting it wrong. I believe that the 'String value;' line must be within the same MaterialButton function, though not sure how to do it without ruining it further.
}`
I'm using a simple app here to demonstrade the behavior. You can test by copy and running this:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SomeScreen(),
);
}
}
class SomeScreen extends StatefulWidget {
#override
_SomeScreenState createState() => _SomeScreenState();
}
class _SomeScreenState extends State<SomeScreen> {
String value = '';
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () async {
await _createDialog(context);
setState(() {});
},
child: Icon(Icons.add),
backgroundColor: Colors.red,
),
body: Container(
color: Colors.blue,
child: Center(
child: Text(value),
),
),
);
}
_createDialog(context) async {
await showDialog(
context: context,
builder: (BuildContext dialogContext) {
return AlertDialog(
title: Text('title'),
content: TextField(
onChanged: (text) {
value = text;
},
),
actions: <Widget>[
FlatButton(
child: Text('buttonText'),
onPressed: () {
Navigator.pop(context);
},
),
],
);
},
);
}
}

Flutter dismiss selected dialog with Getx

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);

Navigator.of(context).pop() Give me black screen

Noob here.
I've made a update checker with flutter, but if I choose any button, it give me black screen.
How can I fix this? Any ideas?
Code
Full Source : https://github.com/aroxu/LiteCalculator
Dialog Part Source :
import 'package:LiteCalculator/updater/bean/UpdaterBean.dart';
import 'package:flutter/material.dart';
class UpdateHolder extends StatelessWidget {
final List<Version> version;
UpdateHolder({Key key, this.version}) : super(key: key);
#override
Widget build(BuildContext context) {
return calculateResult(
version[0].latestVersion, version[1].currentVersion, context);
}
Widget calculateResult(latestVersion, currentVersion, context) {
print('Latest Version : ${int.parse(latestVersion)}');
print('Current Version : ${int.parse(currentVersion)}');
Widget data;
if ((int.parse(currentVersion) <= int.parse(latestVersion))) {
data = Center(
child: createAlert('Update Required', actions: <Widget>[
FlatButton(
child: Text('OK'),
onPressed: () {
print('OK Button Pressed.');
Navigator.of(context).pop();
},
),
FlatButton(
child: Text('Later'),
onPressed: () {
print('Later Button Pressed.');
Navigator.of(context).pop();
},
),
]),
);
} else
data = Center();
return data;
}
Widget createAlert(content, {List<Widget> actions, title}) {
AlertDialog snackBar;
snackBar = AlertDialog(
content: Text(content),
actions: actions,
);
return snackBar;
}
}
call this for your popup,
void showDialogPopup(){
showDialog(
context: context,
builder: (_)=>AlertDialog(
backgroundColor: Colors.transparent,
content: Container(
child: Center(
child: FlatButton(
onPressed: (){
Navigator.of(context).pop(null);
},
child: Center(
child: Text("close")
)
)
)
)
)
);
}
A black screen or a blank screen? If its a black screen, your are not wrapping your main widget (which goes in the runApp) with a MaterialApp.
You can refer this.
I used url_launcher 5.4.1 to open PlayStore web.
import "package:flutter/material.dart";
import 'package:url_launcher/url_launcher.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: FirstPage(),
debugShowCheckedModeBanner: false,
);
}
}
class FirstPage extends StatefulWidget {
#override
_FirstPageState createState() => _FirstPageState();
}
class _FirstPageState extends State<FirstPage> {
#override
void initState() {
_checkUpdate();
super.initState();
}
Future<void> _checkUpdate() async {
await Future.delayed(Duration.zero);
await showDialog(
context: context,
builder: (context) => UpdateDialog(),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text("First Page"),
),
);
}
}
class UpdateDialog extends StatefulWidget {
#override
_UpdateDialogState createState() => _UpdateDialogState();
}
class _UpdateDialogState extends State<UpdateDialog> {
Future<void> _updateFound;
#override
void initState() {
_updateFound = _checkForUpdate();
super.initState();
}
Future<bool> _checkForUpdate() async {
await Future.delayed(Duration.zero);
bool updateFound = false;
await Future.delayed(Duration(seconds: 3)); // Do Get call to server
updateFound = true;
if (!updateFound) Navigator.pop(context);
return updateFound;
}
Future<void> _openWebPage() async {
Navigator.pop(context);
launch("https://play.google.com"); //Your link `url_launcher` package
}
void _laterClicked(){
Navigator.pop(context);
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _updateFound,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting)
return AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
CircularProgressIndicator(),
const SizedBox(height: 12.0),
Text("Checking for Update"),
],
),
);
else if (snapshot.hasError)
return AlertDialog(
title: Text("Error Occured"),
content: Text("ERROR: ${snapshot.error}"),
);
else if(snapshot.data)
return AlertDialog(
title: Text("Update Required"),
content: Text(
"Latest version found. Need an update. bla bla bla bla bla bla"),
actions: <Widget>[
FlatButton(
child: Text("OK"),
onPressed: _openWebPage,
),
FlatButton(
child: Text("LATER"),
onPressed: _laterClicked,
),
],
);
else
return const SizedBox();
},
);
}
}
This happens whenever you try to pop using the Widget's context.
In the following code:
FlatButton(
child: Text('OK'),
onPressed: () {
print('OK Button Pressed.');
Navigator.of(context).pop();
},
)
context represents the context of the widget, itself (provided in the build method).
To resolve this issue instead of creating a Dialog widget and returning it as the main widget, just use showDialog and return a simple Container().
Use dialogContext to pop the dialog and not the widget itself.
for example:
if ((int.parse(currentVersion) <= int.parse(latestVersion))) {
showDialog(
builder: (dialogContext) => AlertDialog(
content: Text('Update Required'),
actions: <Widget>[
FlatButton(
child: Text('OK'),
onPressed: () {
print('OK Button Pressed.');
Navigator.of(dialogContext).pop();
},
),
FlatButton(
child: Text('Later'),
onPressed: () {
print('Later Button Pressed.');
Navigator.of(dialogContext).pop();
},
),
],
),
);
}
return Container();
I had a similar problem and my solution was something like that:
bool hasBeenShown = false;
if(!hasBeenShown) {
Navigator.pop(context);
}
hasBeenShown = true;
The problem for me was that for some reason Navigator.pop been invoked multiple times when it's supposed to be invoked only once.

Flutter v1.12.13+hotfix.5 FocusNode unexpected behaviour

I added focus node to textformfield. After i upgraded flutteer version to v1.12.13+hotfix.5 , flutter focusnode had unexpected bahaviour.
#override
void initState() {
super.initState();
focusNode.addListener(() async {
if(focusNode.hasFocus)
{
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
}
}
}
TextFormField(
style: TextStyle(color: Theme.of(context).brightness==Brightness.dark?Colors.white:Colors.grey),
readOnly: true,
validator: validateField,
focusNode: focusNode,
controller: controller,
);
I click to textformfield to open secondPage. but when i close secondPage, Second page is opened automatically. This behaviour occured after i upgraded version to v1.12.13+hotfix.5.
What is the true usage of focusNode in v1.12.13+hotfix.5?
You can copy paste run full code below
Because TextFormField still have foucs when you go back from SecondRoute, so you need to do unfocus
code snippet
focusNode.addListener(() async {
if (focusNode.hasFocus) {
await Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
FocusScope.of(context).requestFocus(new FocusNode());
}
});
working demo
full code
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#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;
final controller = TextEditingController();
FocusNode focusNode = new FocusNode();
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
void initState() {
super.initState();
focusNode.addListener(() async {
if (focusNode.hasFocus) {
await Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
FocusScope.of(context).requestFocus(new FocusNode());
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
style: TextStyle(
color: Theme.of(context).brightness == Brightness.dark
? Colors.white
: Colors.grey),
readOnly: true,
//validator: validateField,
focusNode: focusNode,
controller: controller,
),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class SecondRoute extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Route"),
),
body: Center(
child: RaisedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Go back!'),
),
),
);
}
}