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.
Related
In my app, I have one JSON file with some static data which I use to produce List of a widgets. Then, I have one screen with ExpansionPanelRadio showing few items and each of them, when expanded, (in their bodies) are containing that list of a widgets made using JSON file.
I am using provider and I am able to display that list of widgets inside body of expansionpanel but the lists are somehow repeating.
For example, I expand one panel and in its body list is displayed few times, like in a loop. I guess, that the problem is in provider but I don't understand it quite well.
I am pretty new to flutter and would appreciate if someone could explain me why is this happening and what approach should I use to solve it.
here is part of a code where i make those expansion panels with provided JSON in a body:
SingleChildScrollView(
child: ExpansionPanelList.radio(
elevation: 0,
children: MyList.map<ExpansionPanelRadio>((Item item) {
return ExpansionPanelRadio(
value: MyList.indexOf(item),
headerBuilder: (BuildContext context, bool isExpanded) {
return Row(
children: [
Padding(
padding: const EdgeInsets.all(10),
child: SvgPicture.asset(
"assets/images/some_image.svg"
),
),
Text('some label'),
],
);
},
body: ChangeNotifierProvider(
create: (context) => FeaturesProvider(),
builder: (context, child) {
Provider.of<FeaturesProvider>(context, listen: false)
.readFeatures();
return SingleChildScrollView(
child: Container(
child: Features(item.objectId.toString()),
),
);
}));
}).toList(),
))
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"),
],
)
],
),
));
}
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
I'm using Provider to provide some data to my screen and an alert dialog. Knowing that AlertDialog is scooped outside the widget tree, I added a ChangeNotifierProvider as a wrapper widget of that dialog. But still, the UI is not changing even though I made sure the values are updated in the provider state.
Code Snippet:
showTextDialog(BuildContext context) {
final myModel = Provider.of<ServicesProvider>(context, listen: false);
return showDialog(
context: context,
builder: (_) => ChangeNotifierProvider.value(
value: myModel,
child: AlertDialog(
content: IntrinsicHeight(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
children: [
Radio(
value: '0',
groupValue: myModel.paymentMethod,
onChanged: (value) => myModel.setPaymentMethod('0'),
activeColor: ColorResources.PRIMARY_COLOR,
toggleable: true,
),
SizedBox(width: responsiveWidth(5)),
Text("Radio Button 1"),
],
),
SizedBox(height: responsiveHeight(10)),
Row(
children: [
Radio(
value: '1',
groupValue: myModel.paymentMethod,
onChanged: (value) => myModel.setPaymentMethod('1'),
activeColor: ColorResources.PRIMARY_COLOR,
toggleable: true,
),
SizedBox(width: responsiveWidth(5)),
Flexible(child: Text("Radio Button 2")),
],
),
],
),
),
),
),
);
}
Thanks for your help.
The main idea behind your problem is that you do not subscribe to the changes of your ServicesProvider. You can replace Provider.of<ServicesProvider>(context, listen: false); with context.read<ServicesProvider>(); that is a more concise syntax using extension methods.
Based on the Provider documentation:
context.read<T>(), which returns T without listening to it. <...> It's worth noting that context.read<T>() won't make a widget rebuild when the value changes and it cannot be called inside StatelessWidget.build/State.build. On the other hand, it can be freely called outside of these methods.
What you have done already with final myModel = Provider.of<ServicesProvider>(context, listen: false); is that you only retrieved the reference to ServicesProvider. However, this way you do not subscribe to the changes inside the model - this is exactly what the documentation explains.
To resolve this, you can move the AlertDialog into a separate widget, e.g. MyDialog. There is a practical reason - it's just easier to understand that now you are not using the same context and you should access the re-provided ServicesProvider model. Now, by using context.watch<ServicesProvider>() (you could use the Consumer widget as well if you would like to), you can subscribe to the changes of your model. Thus, when there is a change for the paymentMethod value inside the model (you can do it by calling the setPaymentMethod() method on model), it triggers UI rebuild (notifyListeners() does its work) and you get the expected output.
You could find the recreated and resolved issue here.
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,`