Flutter, break out private method to a helper class. Arguments problem - flutter

I would like to break out this method and make it public in a helper class but I run into this error when trying and passing context to it? It wants two required contexts and BuildContext but I don't know how to implement this? I get this error. It seems to be a problem with two context arguments also:
"2 positional arguments required but found 0. Try to add missing arguments"
Or if I type the first context it is:
"1 positional argument required but found 0. Try to add missing arguments"
Private method:
_showDialog() {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Title'),
content: Text('Body'),
actions: <Widget>[
OutlinedButton(
child: Text('Close'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
Public method (with two context arguments):
class _LegendScreenState extends State<LegendScreen> {
showDialog(context, BuildContext context) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Title'),
content: Text('Body'),
actions: <Widget>[
OutlinedButton(
child: Text('Close'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
}

The issue is coming because, you are defining showDialog method that is already defined on material.dart. It is overlapping with name. Try to rename the helper class something else like appDialog and pass context.
appDialog(BuildContext context) {
showDialog(
context: context,
builder: (BuildContext context) {
More about showDialog

Remove one context argument. You only need one.
showDialog(context, BuildContext context) {
should be:
showDialog(BuildContext context) {

Related

How to dismiss an AlertDialog in Flutter?

I have a utility class that shows an alert dialog to which I'm passing a VoidCallback which usually contains Navigator.of(context).pop() to dismiss the alert dialog. However it throws a _CastError (Null check operator used on a null value) on button press.
class CustomAlertDialog {
static void show(
BuildContext context, {
Key? key,
required VoidCallback onPress,
}) {
showDialog(
context: context,
builder: (context) => AlertDialog(
content: ElevatedButton(
onPressed: onPress,
child: const Text(
"test",
),
),
),
);
}
}
If I explicitly pass Navigator.pop(context,true) to the onPress method it works just fine, however I need this to be a reusable static method with different onPress functionalities that does more than just pop the dialog.
class CustomAlertDialog {
static void show(
BuildContext context, {
Key? key,
required VoidCallback onPress,
}) {
showDialog(
context: context,
builder: (context) => AlertDialog(
content: ElevatedButton(
onPressed: () {
return Navigator.pop(context, true);
},
child: const Text(
"test",
),
),
),
);
}
}
Your Parent Context does not contain the CustomAlertDialog.
You should pass the context provide by the builder into the onPress function and call navigator.pop with the context provided in the builder function in showDialog.
class CustomAlertDialog {
static void show(
BuildContext context, {
Key? key,
required void Function(BuildContext context) onPress,
}) {
showDialog(
context: context,
builder: (context) => AlertDialog(
content: ElevatedButton(
onPressed: (){ onPress(context) },
child: const Text(
"test",
),
),
),
);
}
}
you can try this :
void show(BuildContext context, String title, {Function callback})
onPressed: () {
if (callback != null) {
Navigator.pop(context, true);
callback();
} else {
Navigator.pop(context, true);
}
},

How can I use "showDialog" in order to propagate data backwards in Flutter?

Future<bool> show(BuildContext context) async {
return Platform.isIOS
? await showCupertinoDialog<bool>
(context: context, builder: (context)=>this)
:await showDialog<bool>(
context: context,
builder: (context) => this,
);
}
Can anyone help me to understand the term 'this',what does 'this' refer to and how does showDialog works that it returns Future.I tried to read documentation but still couldn't understand it?Is it the same as AlertDialog widget?
well, it's pretty much what the documentation said, it shows a material dialog above the current content of your app, as for this it passes the current widget as child for the dialog, as for the returned value is just like normal page navigation that when you call pop(context, {value}) method you can also return a value, so that value that inside pop will be returned from the dialog.
here is an example below:
class DialogTest extends StatefulWidget {
#override
_DialogTestState createState() => _DialogTestState();
}
class _DialogTestState extends State<DialogTest> {
// the value that will be typed to the dialog
String dialogText;
// the value that will be returned from the dialog
String returnedFromDialog;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Sample Code'),
),
body: Center(
child:
Text('You got this value from the dialog => $returnedFromDialog'),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
returnedFromDialog = await showDialog<String>(
context: context,
builder: (context) {
return AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextField(
onChanged: (value) => dialogText = value,
),
FlatButton(
onPressed: () {
setState(() => Navigator.pop(context, dialogText));
},
child: Text(
'Close dialog',
style: TextStyle(color: Colors.red),
),
)
],
),
);
});
},
child: Icon(Icons.open_in_browser),
),
);
}
}

How to showDialog in FutureBuilder

In Flutter,
I used FutureBuilder to display data from API.
I would like to popup an error dialog when there's error.
However, it throws error when I called showDialog()
setState() or markNeedsBuild() called during build.
Here's my code
return FutureBuilder(
future: xxx,
builder: (context, snapshot) {
if (snapshot.hasData) {
//DO SOMETHING
} else if (snapshot.hasError) {
//TODO: Show Error
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Error"),
content: Text("HAHAHA"),
actions: <Widget>[
FlatButton(
child: Text("No"),
),
FlatButton(
child: Text("Yes")
),
],
);
}
);
return Container();
} else {
//DO SOMETHING
}
},
Please help to advise how could I showDialog in FutureBuilder.
We can not call setState, navigate or showDialog while build method is building widget. so, we can wait for a microsecond and meanwhile build method complete building widget, so we can show dialog.
Create a method like below.
showError() async {
await Future.delayed(Duration(microseconds: 1));
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Error"),
content: Text("HAHAHA"),
actions: <Widget>[
FlatButton(
child: Text("No"),
),
FlatButton(child: Text("Yes")),
],
);
});
}
And call this method Where you want to show dialog.

How to pass context to second widget tree for provider

I am try pass context to second widget tree (in function) but I get error:
Tried to use Provider with a subtype of Listenable/Stream (Model2).
This is likely a mistake, as Provider will not automatically update
dependents when Model2 is updated. Instead, consider changing Provider
for more specific implementation that handles the update mecanism,
such as:
- ListenableProvider
- ChangeNotifierProvider
- ValueListenableProvider
- StreamProvider
Future<void> _neverSatisfied({Key key, #required BuildContext context}) async {
final model2 = Provider.of<Model2>(context, listen: false);
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return
Provider.value(value: model2, child:
AlertDialog(
title: Text('Rewind and remember'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('You will never be satisfied.'),
Text('You\’re like me. I’m never satisfied.'),
],
),
),
actions: <Widget>[
FlatButton(
child: Text('Regret'),
onPressed: () async {
await model2.getData();
Navigator.of(context).pop();
},
),
],
),
);
},
);
}
First widget in same StatefulWidget:
#override
Widget build(BuildContext context) {
return ChangeNotifierProxyProvider<Model1, Model2>(
initialBuilder: (_) => Model2(),
builder: (_, model1, model2) => model2
..string = model1.string,
),
child: Consumer<Model2>(
builder: (context, model2, _) =>
Second (I pass context from here):
#override
Widget buildStep(BuildContext context) {
Consumer<Model2>(
builder: (context, model2, _) =>
...
_neverSatisfied(context: context); //**pass context**
Instead of Provider.value you should use ChangeNotifierProvider.value

How to show a dialog inside a futurebuilder?

I want to show a dialog if I receive an error in a futurebuilder.
If I receiver an error, I want to show a dialog and force the user to click on the button, so that he can be redirected to another page.
The problems seems to be that it is not possible to show a dialog while widget is being built.
FutureBuilder(
future: ApiService.getPosts(),
builder: (BuildContext context, AsyncSnapshot snapShot) {
if (snapShot.connectionState == ConnectionState.done) {
if (snapShot.data.runtimeType == http.Response) {
var message =
json.decode(utf8.decode(snapShot.data.bodyBytes));
showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
return AlertDialog(
content: Text(message),
actions: <Widget>[
FlatButton(
child: Text("Ok"),
onPressed: () => null",
)
],
);
});
}
return ListView.separated(
separatorBuilder: (BuildContext context, int index) {
return Divider(
color: Colors.grey,
height: 1,
);
},
itemBuilder: (BuildContext context, int index) {
return _buildPostCard(index);
},
itemCount: snapShot.data.length,
);
return Center(
child: CircularProgressIndicator(),
);
},
)
If I return the AlertDialog alone, it works. But I need the showDialog because of the barrierDismissible property.
Does any one know if that is possible?
Also, is this a good way to handle what I want?
Thanks
UPDATE
For further reference, a friend at work had the solution.
In order to do what I was looking for, I had to decide which future I was going to pass to the futureBuilder.
Future<List<dynamic>> getPostsFuture() async {
try {
return await ApiService.getPosts();
} catch (e) {
await showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
return AlertDialog(
content: Text(message),
actions: <Widget>[
FlatButton(
child: Text("Ok"),
onPressed: () => null",
)
],
);
});
}
}
}
Then in the futureBuilder I would just call
FutureBuilder(
future: getPostsFuture(),
Thanks
To avoid setState() or markNeedsBuild() called during build error when using showDialog wrap it into Future.delayed like this:
Future.delayed(Duration.zero, () => showDialog(...));
setState() or markNeedsBuild() called during build.
This exception is allowed because the framework builds parent widgets before its children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
So to avoid that Future Callback is used, which adds a call like this EventQueue.
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
Future futureCall() async {
await Future.delayed(Duration(seconds: 2));
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: futureCall(),
builder: (_, dataSnapshot) {
if (dataSnapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else {
Future(() { // Future Callback
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Employee Data'),
content: Text('Do you want to show data?'),
actions: <Widget>[
FlatButton(
onPressed: () =>
Navigator.of(context).pop('No'),
child: Text('NO')),
FlatButton(
onPressed: () =>
Navigator.of(context).pop('Yes'),
child: Text('YES'))
],
));
});
return Container();
}
},
);
}
}
The builder param expects you to return a Widget. showDialog is a Future.
So you can't return that.
You show Dialog on top of other widgets, you can't return it from a build method that is expecting a widget.
What you want can be implemented the following way.
When you receive an error, show a dialog on the UI and return a Container for the builder. Modify your code to this:
if (snapShot.data.runtimeType == http.Response) {
var message =
json.decode(utf8.decode(snapShot.data.bodyBytes));
showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
return AlertDialog(
content: Text(message),
actions: <Widget>[
FlatButton(
child: Text("Ok"),
onPressed: () => null",
)
],
);
});
return Container();
}