Flutter refreshing a column inside an AlertDialog - flutter

I have an AlertDialog showed using the showDialog() method, inside it I have a Column that contains two Text widgets, one of which is invisible. What I'm trying to do here is change the visibility of the text using the AlertDialog action button.
What I initially is creating the Column like this:
bool textVisibility = false;
var column = Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text("Visible Text"),
Visibility(
visible: textVisibility,
child: Text("Invisible Text!"),
)
],
);
And then I include it inside my AlertDialog like this:
showDialog(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context,StateSetter dialogState) {
return AlertDialog(
content: column,
actions: <Widget>[
FlatButton(
child: Text("Yes"),
onPressed: () {
dialogState(() {
textVisibility = true
});
},
),
],
);
},
);
}
)
This obviously won't work because the dialogState() will update data of the dialog, not its Column child. So my question is how can I update the Column from inside the AlertDialog action button call?

One thing you could do is move the column initialization and declaration into the builder function because this is the only way the column will be rebuilt after the statesetter is called so you will have something like this.
showDialog(
context: context,
builder: (context) {
var column = Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text("Visible Text"),
Visibility(
visible: textVisibility,
child: Text("Invisible Text!"),
)
],
);
return StatefulBuilder(
builder: (context,StateSetter dialogState) {
return AlertDialog(
content: column,
actions: <Widget>[
FlatButton(
child: Text("Yes"),
onPressed: () {
dialogState(() {
textVisibility = true
});
},
),
],
);
},
);
}
)
Take note that the state variable has to stay outside the statefulbuilder's builder however.

One possible option could be to add an provider, transfer the changed boolean over the provider to the column and update it with notifylistener. Something like following could work.
//at the Action button of the AlertDialog
Provider.of<foo>(context).setBoolean(true)
//In the Provider
boolean isButtonVisible = false;
void setBoolean(bool visible){
isButtonVisible = visible;
notifylistener;
}
bool getBoolean()=>isButtonVisible;
//In the Column of the actionbutton
Visibility{
visible: `Provider.of<foo>(context).getBoolean,`

Related

Flutter showDialog

Can i showDialog inside a function without passing context?
void test(){
showDialog(context: context, builder: (_) => AlertDialog(
content: Column(
children: [
Row(
children: const [
Icon(Icons.check_circle, color: Colors.green,),
Text("Hi"),
],
)
],
),
));
}
Sorry i didn't explain very well, without passing context to function, not to showDialog
According to the doc (https://api.flutter.dev/flutter/material/showDialog.html) you can't, it's required.
the short answer is no, you can't.
the long answer:
first, the BuildContext is a object type, so in order to remove conflictions between the context property and the context value we're going to rename it to contextGotFromUI.
Note: contextGotFromUI here is just a BuildContext object sp we can rename it with whatever we want.
just to not get confused by the same names
void test(){
showDialog(context: contextGotFromUI, builder: (_) => AlertDialog(
content: Column(
children: [
Row(
children: const [
Icon(Icons.check_circle, color: Colors.green,),
Text("Hi"),
],
)
],
),
));}
the context property in the showDialog is required to set from it's implementation:
Future<T?> showDialog<T>({
required BuildContext context,
required WidgetBuilder builder,
bool barrierDismissible = true,
// more code
the BuildContext is an important topic to understand in flutter, to show a dialog widget on top of the screen the user is actually navigating in and seeing at any time, the BuildContext is what tells to show it on top of the widget with that specific context, and not other screens.
As from the showDialog official documentation:
The context argument is used to look up the Navigator and Theme for the dialog. It is only used when the method is called. Its corresponding widget can be safely removed from the tree before the dialog is closed.
so in order to show a dialog from an external method, you need to pass a context that belongs to a specific widget, then use it in the showDialog:
void test(BuildContext contextGotFromUI){
showDialog(context: contextGotFromUI, builder: (_) => AlertDialog(
content: Column(
children: [
Row(
children: const [
Icon(Icons.check_circle, color: Colors.green,),
Text("Hi"),
],
)
],
),
));}
then from your UI where you're calling that method, pass it:
Widget build(BuildContext) {
// widgets
//...
onPressed: () {
test(context); // will show an expected dialog on the screen
}
}
Yes, you can but you have to create the function inside a stateful widget not in the normal classes.
in case you create the function in a normal class the context will be required!
void test(BuildContext context){
showDialog(context: context, builder: (_) => AlertDialog(
content: Column(
children: [
Row(
children: const [
Icon(Icons.check_circle, color: Colors.green,),
Text("Hi"),
],
)
],
),
));
}

AppCenter.checkForUpdateAsync() method always returns null

In my mobile app I use the AppCenter.checkForUpdateAsync() (flutter_appcenter_bundle package) method to check for updates and prompt the user to update the application if needed. However, the method always returns null, even if the current version is not the latest.
I tried to change the versions of the package, and in accordance with the documentation, I published applications with forced updates enabled. But it didn't help.
I have no more ideas what can be done.
The method checkForUpdateAsync is called when the button is clicked.
Details:
Future<void> checkAppUpdate() async {
final result = await AppCenter.checkForUpdateAsync(); // always returns null
logger.i(LogMessage('result $result', name: 'checkAppUpdate'));
if (result == null) {
showDialog(
context: context,
builder: (BuildContext context) => AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children : <Widget>[
Container(
child: Text(
"Текущая версия приложения актуальна",
textAlign: TextAlign.center,
style: TextStyle(height: 1.44)
),
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('Ок'),
onPressed: () => Navigator.of(context).pop()),
])
],
),
)
);
}
}
Flutter version 1.12.13+hotfix.6
flutter_appcenter_bundle: 3.1.1+1

How to make a save changes button?

I have an AlertDialog that I use as a settings window, when the user opens it, the Apply button is not active, I would like that when the settings change, the button becomes active and saves the changes. How can I do this?
showAlertDialogSettings(BuildContext context, state) {
Widget okButton = TextButton(
child: Text("Apply"),
onPressed: null,
);
Widget cancelButton = TextButton(
child: Text("Close"),
onPressed:() => Navigator.pop(context),
);
AlertDialog alert = AlertDialog(
title: Center(child: Text("Settings")),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Sound:'),
SwitchWidget(),
],),
SizedBox(
height: 48,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Difficulty:'),
Padding(
padding: const EdgeInsets.only(right: 5),
child: DropDownButtonSettingsWidget()
),
],),
),
],
),
actions: [
okButton,
cancelButton,
],
);
showDialog(
context: context,
builder: (BuildContext context) {
return BackdropFilter (
filter: ImageFilter.blur(sigmaX: 6, sigmaY: 6),
child: alert );
},
);
}
You will need to maintain state of both the sound and the difficulty values (there are many ways to tackle this), though the simplest would be to split out the "body" of the AlertDialog to be a StatefulWidget to contain its state. From there, you can check whether values have changed and update the view state to enable the apply button.
It's highly recommended to not mix business logic with UI logic, so this widget shouldn't actually do any of the saving. Inputs can be encapsulated within a class, and then this class can be passed back via Navigator.of(context).pop(T) (where T is your value class, see docs) upon closing the dialog from the apply button callback.
// Input passed back via `pop(T)` can be retrieved via:
final input = await showDialog(MyAlertDialog());

having an error with context & provider when calling a showSlideDialog

First I have created these Radio Buttons and they are working well
Widget buildRadioLanguageListTile(
Languages langvalue, String txt, BuildContext ctx2) {
return RadioListTile(
value: langvalue,
groupValue:
Provider.of<LanguageProvider>(ctx2, listen: true).currentLang,
onChanged: (langvalue) =>
Provider.of<LanguageProvider>(ctx2, listen: false)
.changeLanguage(langvalue),
title: Text(
txt,
style: Theme.of(ctx2).textTheme.bodyText1,
));
}
ListView(
Column(
mainAxisSize: MainAxisSize.min, children: <Widget>[
buildRadioLanguageListTile(
Languages.English, txt.getTexts("english"), context),
buildRadioLanguageListTile(
Languages.Arabic, txt.getTexts("arabic"), context),
buildRadioLanguageListTile(
Languages.Turkish, txt.getTexts("turkish"), context),
]);
Until now everything is working well, but I want to put those buttons inside a showSlideDialog as the following:
import 'package:slide_popup_dialog/slide_popup_dialog.dart' as slideDialog;
void _showDialog() { // here the problem begin
slideDialog.showSlideDialog(
context: context,
child: Column(
children: [
buildRadioLanguageListTile(
Languages.English, lan.getTexts("english"), context),
buildRadioLanguageListTile(
Languages.Arabic, lan.getTexts("arabic"), context),
buildRadioLanguageListTile(
Languages.Turkish, lan.getTexts("turkish"), context),
],
),
);}
.
.
.
InkWell(
child: Container(
child: Text("language"),
),
onTap: _showDialog, // calling _showDialog
)
Now I am getting this error :
Tried to listen to a value exposed with provider, from outside of the widget tree.
This is likely caused by an event handler (like a button's onPressed) that called
Provider.of without passing `listen: false`.
To fix, write:
Provider.of<LanguageProvider>(context, listen: false);
It is unsupported because may pointlessly rebuild the widget associated to the
event handler, when the widget tree doesn't care about the value.
The context used was: GeneralSetting(dependencies: [_InheritedProviderScope<LanguageProvider>, _LocalizationsScope-[GlobalKey#41a3d], _InheritedProviderScope<ThemeProvider>, _InheritedTheme])
I think there is an error in dealing with the provider or the context.
How can I fix this issue?
the problem was with the context for the parts of my code. surly there are many errors but I solved the problem by using the showModalBottomSheet which has a context and builder.
showModalBottomSheet(
context: context,
builder: (context) {
return Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
buildRadioLanguageListTile(
Languages.English, txt.getTexts("english"), context),
buildRadioLanguageListTile(
Languages.Arabic, txt.getTexts("arabic"), context),
buildRadioLanguageListTile(
Languages.Turkish, txt.getTexts("turkish"), context),
]);
});
and the rest of the code is the same.

Call SnackBar from within an AlertDialog

When a user clicks on delete for an employee, an AlertDialog shall pop up to warn the user.
If the user confirms the deletion, then the AlertDialog disappears and at the bottom of the Scaffold a SnackBar should appear with an Undo function.
Problem:
When I implement the SnackBar method showSnackBar(context, index, employee) within the AlertDialog class I get the following error:
he following assertion was thrown while handling a gesture:
Scaffold.of() called with a context that does not contain a Scaffold.
showDeleteDialog(BuildContext context, Employee employee, int index) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title:
Text('Are you sure you want to delete: ${employee.name} ?'),
actions: <Widget>[
Row(
children: <Widget>[
FlatButton(
child: Text('Yes'),
onPressed: () {
DatabaseProvider.db.deleteEmployee(employee.id).then(
(_) => BlocProvider.of<EmployeeBloc>(context)
.add(DeleteEmployee(index)));
Navigator.pop(context,employee);
showSnackBar(context, index, employee);
}),
FlatButton(
child: Text('No!'),
onPressed: () => Navigator.pop(context)),
],
)
],
));
}
Instead, I thought I could return an employee from the showDeleteDialog when I confirm the deletion. When the result is not null, then I should show the SnackBar. I tried to implement this with Future/Async but with no success.
onPressed: () async {
Employee deletedEmployee = await showDeleteDialog(context, employee, index);
await showSnackBar(context, index, deletedEmployee);
},
Edit: I would like to avoid using GlobalKey if possible, since I read it is not good for the App's performance.
As the error says Scaffold.of() called with a context that does not contain a Scaffold., that means the current context that you are passing to showSnackBar() method doesn't contain a Scaffold in the immediate parent.
We can fix this by using GlobalKey and assign it to the Scaffold. Declare a global key in your stateful widget and pass this as a key in your Scaffold, as below:
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
....
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text(widget.title),
),
I call a method _showSnackBar() after navigator.pop() on OK button click inside the alertDialog, as below:
return AlertDialog(
title: Text('Not in stock'),
content: const Text('This item is no longer available'),
actions: <Widget>[
FlatButton(
child: Text('Ok'),
onPressed: () {
Navigator.of(context).pop();
_showSnackBar();
},
),
],
);
Then in the _showSnackBar() method, use the key to show the snackbar, as below:
void _showSnackBar() {
_scaffoldKey.currentState.showSnackBar(
SnackBar(
content: Text('Snackbar is displayed'),
));
}
With this approach, once you tap on OK button on alertDialog, the dialog will close and you'll see the snackbar. You may need to customize this per your code as you shared above.
Hope this answers your question and resolves your issue.
Found the solution and it is super easy...
I only had to re-name one of the context to a dialogContext
showDeleteDialog(BuildContext context, Employee employee, int index) {
showDialog(
context: context,
builder: (dialogContext) => AlertDialog(
title:
Text('Are you sure you want to delete: ${employee.name} ?'),
actions: <Widget>[
Row(
children: <Widget>[
FlatButton(
child: Text('Yes'),
onPressed: () {
DatabaseProvider.db.deleteEmployee(employee.id).then(
(_) => BlocProvider.of<EmployeeBloc>(dialogContext)
.add(DeleteEmployee(index)));
Navigator.pop(dialogContext);
showSnackBar(context, index, employee);
}),
FlatButton(
child: Text('No!'),
onPressed: () => Navigator.pop(context)),
],
)
],
));
}