Related
The app I'm building uses a Cupertino Picker that shows a list of items to select, in this case the names of the US States. The first item defaults to the first item in the list ('ak'), when the button to select the item is pressed, the app errors out. This only happens with the first item, when the picker isn't scrolled. If the picker is scrolled and the user goes back to the first item, it works fine.
class StateSelectScreen extends StatefulWidget {
static const String id = 'state_select_screen';
#override
_StateSelectScreenState createState() => _StateSelectScreenState();
}
class _StateSelectScreenState extends State<StateSelectScreen> {
String selectedState = 'ak';
bool showSpinner = false;
DropdownButton<String> androidDropdown() {
List<DropdownMenuItem<String>> dropdownItems = [];
for (String state in statesList) {
var newItem = DropdownMenuItem(
child: Text(
USStates.getName(state).toUpperCase(),
textAlign: TextAlign.center,
),
value: state,
);
dropdownItems.add(newItem);
}
return DropdownButton<String>(
dropdownColor: Colors.black26,
autofocus: true,
focusColor: Colors.black26,
style: TextStyle(
fontSize: k30PointFont,
),
value: selectedState,
items: dropdownItems,
onChanged: (value) {
setState(() {
selectedState = value;
getStateData();
});
},
);
}
CupertinoPicker iOSPicker() {
List<Text> pickerItems = [];
for (String state in statesList) {
pickerItems.add(Text(USStates.getName(state.toUpperCase())));
}
return CupertinoPicker(
backgroundColor: kCupertinoPickerBackgroundColor,
itemExtent: kCupertinoPickerItemExtent,
onSelectedItemChanged: (selectedIndex) {
setState(() {
selectedState = USStates.getName(statesList[selectedIndex]);
getStateData();
});
},
children: pickerItems,
);
}
Map<String, dynamic> selectedStateData = {};
bool isWaiting = false;
void getStateData() async {
isWaiting = true;
try {
var stateData = await GetData().getStateData(selectedState);
isWaiting = false;
setState(() {
selectedStateData = stateData;
});
} catch (e) {
print(e);
}
}
#override
void initState() {
super.initState();
getStateData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(kAppBarTitle),
),
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Flexible(
child: Padding(
padding: EdgeInsets.only(top: kStateSelectScreenFlexEdgeInsetsTop, bottom: kStateSelectScreenFlexEdgeBottom),
child: Hero(
tag: kHeroTag,
child: Container(
height: 200.0,
child: Image.asset(kHeroImageAsset),
),
),
),
),
Container(
child: Column(
children: <Widget>[
Container(
height: kStateSelectScreenContainerHeight,
alignment: Alignment.center,
padding: EdgeInsets.only(bottom: kStateSelectScreenContainerPaddingBottom),
child: Platform.isIOS ? iOSPicker() : androidDropdown(),
),
BottomButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return ResultsScreen(
covidData: selectedStateData,
location: selectedState,
);
},
),
);
},
buttonTitle: kCheckStateResultsButtonTitle,
),
SizedBox(
height: kHeight15,
),
BottomButton(
onPressed: () {
Navigator.pushNamed(context, MenuScreen.id);
},
buttonTitle: kMainMenuButtonTitle,
),
],
),
),
],
),
),
);
}
}
I have recreated your problem, but don't see any error.
https://codepen.io/flakerimi/pen/poRywLZ
You said when the button to select the item is pressed, the app errors out.
Which button ? I don't see any
I have added scrollController: FixedExtentScrollController(initialItem: 1), but I don't think thats the case.
return CupertinoPicker(
scrollController: FixedExtentScrollController(initialItem: 1),
backgroundColor: Colors.white,
itemExtent: 30,
onSelectedItemChanged: (selectedIndex) {
setState(() {
selectedState = statesList[selectedIndex];
print(selectedState);
});
},
children: pickerItems,
);
I need to pass the radio button value stored in variable radioValue from homepage.dart to class DialogFactory in dialogfactory.dart, where I will use it. The current radioValue value should be passed when I press a button which calls function _openDialog(), which is meant to open the alert dialog with the selected style.
======================================
homepage.dart
[...]
class _MyHomePageState extends State<MyHomePage> {
int radioValue = 1;
void _handleRadioValueChange(int value) {
setState(() {
radioValue = value;
});
}
void _openDialog() {
DialogFactory.showAlertDialog(
context,
title: Text('Alert Dialog!!!'),
content: Text(
'THIS IS AN ALERT DIALOG! IT MEANS YOU SHOULD BE IN ALERT STATE, RIGHT?'),
actions: [
DialogAction(
child: Text('YES'),
onPressed: () => print('YES'),
),
DialogAction(
child: Text('NO'),
onPressed: () => print('NO'),
)
],
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Radio(
value: 0,
groupValue: radioValue,
onChanged: _handleRadioValueChange,
),
new Text('IOS'),
Radio(
value: 1,
groupValue: radioValue,
onChanged: _handleRadioValueChange,
),
new Text('Android'),
RaisedButton(
child: Text('Show Alert'),
color: Colors.purple,
textColor: Colors.white,
onPressed: _openDialog,
),
],
),
),
);
}
}
dialogfactory.dart:
class DialogFactory {
static Future<T> showAlertDialog<T>(BuildContext context,
{Widget title, Widget content, List<DialogAction> actions}) {
IDialog dialogData;
int radioValue = -1;
// HOW TO GET RADIO VALUE?
if (radioValue == 0) {
// ios
dialogData = IosDialog();
} else if (radioValue == 1) {
//android
dialogData = AndroidDialog();
} else {
dialogData = AndroidDialog();
}
return showDialog(
context: context,
builder: (context) => dialogData.create(
context,
title ?? Text('Não informado'),
content ?? Text('Não informado'),
actions ?? [],
),
);
}
}
link to the repository with the code: https://github.com/gicraveiro/FactoryMethod
I passed the radioValue from the homepage.dart into the showAlertDialog.
Currently, I have an AlertDialog with an IconButton. The user can click on the IconButton, I have two colors for each click. The problem is that I need to close the AlertDialog and reopen to see the state change of the color icon. I want to change the IconButton color immediately when the user clicks it.
Here is the code:
bool pressphone = false;
//....
new IconButton(
icon: new Icon(Icons.phone),
color: pressphone ? Colors.grey : Colors.green,
onPressed: () => setState(() => pressphone = !pressphone),
),
Use StatefulBuilder to use setState inside Dialog and update Widgets only inside of it.
showDialog(
context: context,
builder: (context) {
String contentText = "Content of Dialog";
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: Text("Title of Dialog"),
content: Text(contentText),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context),
child: Text("Cancel"),
),
TextButton(
onPressed: () {
setState(() {
contentText = "Changed Content of Dialog";
});
},
child: Text("Change"),
),
],
);
},
);
},
);
Use a StatefulBuilder in the content section of the AlertDialog. Even the StatefulBuilder docs actually have an example with a dialog.
What it does is provide you with a new context, and setState function to rebuild when needed.
The sample code:
showDialog(
context: context,
builder: (BuildContext context) {
int selectedRadio = 0; // Declare your variable outside the builder
return AlertDialog(
content: StatefulBuilder( // You need this, notice the parameters below:
builder: (BuildContext context, StateSetter setState) {
return Column( // Then, the content of your dialog.
mainAxisSize: MainAxisSize.min,
children: List<Widget>.generate(4, (int index) {
return Radio<int>(
value: index,
groupValue: selectedRadio,
onChanged: (int value) {
// Whenever you need, call setState on your variable
setState(() => selectedRadio = value);
},
);
}),
);
},
),
);
},
);
And as I mentioned, this is what is said on the showDialog docs:
[...] The widget returned by the builder does not share a context with the location
that showDialog is originally called from. Use a StatefulBuilder or a
custom StatefulWidget if the dialog needs to update dynamically.
This is because you need to put your AlertDialog in its own StatefulWidget and move all state manipulation logic on the color there.
Update:
void main() => runApp(MaterialApp(home: Home()));
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton(
child: Text('Open Dialog'),
onPressed: () {
showDialog(
context: context,
builder: (_) {
return MyDialog();
});
},
)));
}
}
class MyDialog extends StatefulWidget {
#override
_MyDialogState createState() => new _MyDialogState();
}
class _MyDialogState extends State<MyDialog> {
Color _c = Colors.redAccent;
#override
Widget build(BuildContext context) {
return AlertDialog(
content: Container(
color: _c,
height: 20.0,
width: 20.0,
),
actions: <Widget>[
FlatButton(
child: Text('Switch'),
onPressed: () => setState(() {
_c == Colors.redAccent
? _c = Colors.blueAccent
: _c = Colors.redAccent;
}))
],
);
}
}
First you need to use StatefulBuilder. Then i am setting _setState variable, which even could be used outside StatefulBuilder, to set new state.
StateSetter _setState;
String _demoText = "test";
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: StatefulBuilder( // You need this, notice the parameters below:
builder: (BuildContext context, StateSetter setState) {
_setState = setState;
return Text(_demoText);
},
),
);
},
);
_setState is used same way as setState method. For example like this:
_setState(() {
_demoText = "new test text";
});
If you're separating your data from the UI via View Models and using the Provider package with ChangeNotifier, you'll need to include your current model like so within the widget calling the dialog:
showDialog(context: context, builder: (dialog) {
return ChangeNotifierProvider.value(
value: context.read<ViewModel>(),
child: CustomStatefulDialogWidget(),
);
},
Note that there may be a cleaner way to do this but this worked for me.
Additional info regarding Provider: https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple
showModalBottomSheet(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState /*You can rename this!*/) {
return Container(
height: heightOfModalBottomSheet,
child: RaisedButton(onPressed: () {
setState(() {
heightOfModalBottomSheet += 10;
});
}),
);
});
});
Not sure if this is best practice, but I solved the issue of updating both the dialog state and the content state by wrapping the setState functions, after using the top answer to add state to the dialog:
IconButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, newSetState) { // Create a "new" state variable
return AlertDialog(
content: DropdownButton(
value: listItem.type,
items: allItems
onChanged: (value) {
newSetState(() {
setState(() {
// HERE SET THE STATE TWICE
// Once with the "new" state, once with the "old"
});
});
})
),
);
}
);
}
),
In fact, you can use StatefullBuilder but the problem is that when you use this widget you cant change the state of the base screen! Prefer to navigate to a new screen in order to use setState.
I was stuck with this issue.You have to Change the name of setState to any Other name and pass this set state to all sub functions.
This will update your Dialog ui on time.
return StatefulBuilder(
builder: (context, setStateSB) {
return AlertDialog(
title: Text("Select Circle To Sync Data!" ,style: TextStyle(color: Colors.white),),
content: Column(
children: [
Text("Select Division!" ,style: TextStyle(color: Colors.white),),
Container(
height: 80,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InputDecorator(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0)),
contentPadding: EdgeInsets.all(5),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
isExpanded: true,
value: sync_DivisionName_firstValue,
items: _DivisionName_list.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value,style: TextStyle(color: Colors.black)),
);
}).toList(),
onChanged: (String? newValue) {
setStateSB(() {
sync_DivisionName_firstValue = newValue!;
if(sync_DivisionName_firstValue !="Select Division Name"){
print("sync_DivisionName_firstValue$sync_DivisionName_firstValue");
_getDistrictName(sync_DivisionName_firstValue,setStateSB);
}else{
refreashDivisionName(setStateSB);
}
});
},
)),
),
],
),
),
Text("Select District!" ,style: TextStyle(color: Colors.white),),
Container(
height: 80,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InputDecorator(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0)),
contentPadding: EdgeInsets.all(5),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
isExpanded: true,
value: sync_DistrictName_firstValue,
items: _DistrictName_list.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value,style: TextStyle(color: Colors.black),),
);
}).toList(),
onChanged: (String? newValue) {
setStateSB(() {
sync_DistrictName_firstValue = newValue!;
if(sync_DivisionName_firstValue != "Select Division Name" && sync_DistrictName_firstValue != "Select District Name"){
print("sync_DistrictName_firstValue$sync_DistrictName_firstValue");
_getTehsilName(sync_DivisionName_firstValue,sync_DistrictName_firstValue,setStateSB);
}else{
refreashDistrictName(setStateSB);
}
});
},
)),
),
],
),
),
Text("Select Tehsil!" ,style: TextStyle(color: Colors.white),),
Container(
height: 80,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InputDecorator(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0)),
contentPadding: EdgeInsets.all(5),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
isExpanded: true,
value: sync_TehsilName_firstValue,
items: _TehsilName_list.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value,style: TextStyle(color: Colors.black),),
);
}).toList(),
onChanged: (String? newValue) {
setStateSB(() {
sync_TehsilName_firstValue = newValue!;
if(sync_DivisionName_firstValue != "Select Division Name" && sync_DistrictName_firstValue != "Select District Name" && sync_TehsilName_firstValue != "Select Tehsil Name"){
print("sync_TehsilName_firstValue$sync_TehsilName_firstValue");
_getRatingAreaName(sync_DivisionName_firstValue,sync_DistrictName_firstValue,sync_TehsilName_firstValue,setStateSB);
}else{
refreashTehsilName(setStateSB);
}
});
},
)),
),
],
),
),
Text("Select Rating Area Name!" ,style: TextStyle(color: Colors.white),),
Container(
height: 80,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InputDecorator(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0)),
contentPadding: EdgeInsets.all(5),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
isExpanded: true,
value: sync_RatingAreaName_firstValue,
items: _RatingAreaName_list.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value,style: TextStyle(color: Colors.black),),
);
}).toList(),
onChanged: (String? newValue) {
setStateSB(() {
sync_RatingAreaName_firstValue = newValue!;
if(sync_DivisionName_firstValue != "Select Division Name" && sync_DistrictName_firstValue != "Select District Name" && sync_TehsilName_firstValue != "Select Tehsil Name" && sync_RatingAreaName_firstValue != "Select Rating Area Name"){
print("sync_RatingAreaName_firstValue$sync_RatingAreaName_firstValue");
_getWardCircleName(sync_DivisionName_firstValue,sync_DistrictName_firstValue,sync_TehsilName_firstValue,sync_RatingAreaName_firstValue,setStateSB);
}else{
refreashWardCircleName(setStateSB);
}
});
},
)),
),
],
),
),
Text("Select Ward Circle Name!" ,style: TextStyle(color: Colors.white),),
Container(
height: 80,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InputDecorator(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0)),
contentPadding: EdgeInsets.all(5),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
isExpanded: true,
value: sync_circle_name_firstValue,
items: _circle_name_list.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value,style: TextStyle(color: Colors.black),),
);
}).toList(),
onChanged: (String? newValue) {
setStateSB(() {
sync_circle_name_firstValue = newValue!;
print("sync_circle_name_firstValue$sync_circle_name_firstValue");
// if(sync_circle_name_firstValue != "Select Ward Circle Name"){
//
// _getWardCircleName(sync_RatingAreaName_firstValue);
// }else{
//
// }
});
},
)),
),
],
),
),
]),
backgroundColor:Color(0xFFEC9F46),
actions: [
okButton,SyncButton
],
);
},
);
One of the Inner Funciton is like this.
Future<void> refreashDivisionName( StateSetter setInnerState) async {
final List<String> _division_name = await getDivisionNameList();
final List<String> _district_name_list = await getDistrictName(sync_DivisionName_firstValue);
final List<String> _tehsil_name_list = await getTehsilName(sync_DivisionName_firstValue,sync_DistrictName_firstValue);
final List<String> _rating_area_name_list = await getRatingAreaName(sync_DivisionName_firstValue,sync_DistrictName_firstValue,sync_TehsilName_firstValue);
final List<String> _ward_circle_name_list = await getWardCircleName(sync_DivisionName_firstValue,sync_DistrictName_firstValue,sync_TehsilName_firstValue,sync_RatingAreaName_firstValue);
setInnerState(() {
_division_name.insert(0, "Select Division Name");
_DivisionName_list = _division_name;
sync_DivisionName_firstValue = _DivisionName_list[0];
_district_name_list.insert(0, "Select District Name");
_DistrictName_list = _district_name_list;
sync_DistrictName_firstValue = _DistrictName_list[0];
_tehsil_name_list.insert(0, "Select Tehsil Name");
_TehsilName_list = _tehsil_name_list;
sync_TehsilName_firstValue = _TehsilName_list[0];
_rating_area_name_list.insert(0, "Select Rating Area Name");
_RatingAreaName_list = _rating_area_name_list;
sync_RatingAreaName_firstValue = _RatingAreaName_list[0];
_ward_circle_name_list.insert(0, "Select Ward Circle Name");
_circle_name_list = _ward_circle_name_list;
sync_circle_name_firstValue = _circle_name_list[0];
});
}
I hope you under Stand.
base on Andris's answer.
when dialog share the same state with parent widget, you can override parent widget's method setState to invoke StatefulBuilder's setState, so you don't need to call setState twice.
StateSetter? _setState;
Dialog dialog = showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: StatefulBuilder( // You need this, notice the parameters below:
builder: (BuildContext context, StateSetter setState) {
_setState = setState;
return Text(_demoText);
},
),
);
},
);
// set the function to null when dialo is dismiss.
dialogFuture.whenComplete(() => {_stateSetter = null});
#override
void setState(VoidCallback fn) {
// invoke dialog setState to refresh dialog content when need
_stateSetter?.call(fn);
super.setState(fn);
}
Currently to retrieve the value of Dialog I use
showDialog().then((val){
setState (() {});
print (val);
});
Example
1st screen
onPressed: () {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
return AddDespesa();
}).then((val) {
setState(() {});
print(val);
}
);
}
2nd screen
AlertDialog(
title: Text("Sucesso!"),
content: Text("Gasto resgristrado com sucesso"),
actions: <Widget>[
FlatButton(
child: Text("OK"),
onPressed: () {
Navigator.pop(context, true);
},
),
],
);
Will be printed true,
I want to get an integer value in Future function which must be compared in a if condition when i'm pressing my fab button but instead of getting the number of documents in the collection it's giving in the log console 'Instance of Future' but I need my method to return a number.
this is my code:
class MembresPage extends StatefulWidget {
#override
_MembresPageState createState() => _MembresPageState();
}
class _MembresPageState extends State<MembresPage> {
Map<String, String> sublingData = {};
String nomComplet;
String adresse;
String ldNais;
QuerySnapshot subling;
CrudMethods crudObj = new CrudMethods();
Future<bool> addDialog(BuildContext context) async{
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context){
return AlertDialog(
title: Text('Ajouter Membre', style: TextStyle(fontSize: 15.0)),
content: Column(
children: <Widget>[
TextField(
decoration: InputDecoration(hintText: 'Nom Complet'),
onChanged: (value){
this.nomComplet =value;
},
),
SizedBox(height: 8.0),
TextField(
decoration: InputDecoration(hintText: 'Ex: Kinshasa, le 21/12/1960'),
onChanged: (value){
this.ldNais = value;
},
),
SizedBox(height: 8.0),
TextField(
decoration: InputDecoration(hintText: 'Adresse'),
onChanged: (value){
this.adresse = value;
},
),
],
),
actions: <Widget>[
FlatButton(
child: Text('Ajouter'),
textColor: Colors.deepOrangeAccent[400],
onPressed: () async {
Navigator.of(context).pop();
sublingData = {
'nomComplet':this.nomComplet,
'ldNais': this.ldNais,
'adresse':this.adresse
};
PaiementStripe().addMembers(sublingData).then((result){
dialogTrigger(context);
}).catchError((e){
print(e);
});
},
)
],
);
});
}
Future<bool> updateDialog(BuildContext context, selectedDoc) async{
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context){
return AlertDialog(
title: Text('Modifier Membre', style: TextStyle(fontSize: 15.0)),
content: Column(
children: <Widget>[
TextField(
decoration: InputDecoration(hintText: 'Nom Complet'),
onChanged: (value){
this.nomComplet =value;
},
),
SizedBox(height: 8.0),
TextField(
decoration: InputDecoration(hintText: 'Ex: Kinshasa, le 21/12/1960'),
onChanged: (value){
this.ldNais = value;
},
),
SizedBox(height: 8.0),
TextField(
decoration: InputDecoration(hintText: 'Adresse'),
onChanged: (value){
this.adresse = value;
},
),
],
),
actions: <Widget>[
FlatButton(
child: Text('Modifier'),
textColor: Colors.deepOrangeAccent[400],
onPressed: (){
Navigator.of(context).pop();
/*sublingData = {
'nomComplet':this.nomSubling,
'lieuNais': this.lieuNais,
'dateNais':this.dateNais
};*/
PaiementStripe().updateMembers(selectedDoc,{
'nomComplet':this.nomComplet,
'ldNais': this.ldNais,
'adresse':this.adresse
}).then((result){
//dialogTrigger(context);
}).catchError((e){
print(e);
});
PaiementStripe().getMembers().then((result){
setState(() {
subling = result;
});
});
},
)
],
);
});
}
Future<bool> dialogTrigger(BuildContext context) async{
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context){
return AlertDialog(
title: Text('Info', style: TextStyle(fontSize: 15.0)),
content: Text('Membre ajouté'),
actions: <Widget>[
FlatButton(
child: Text('OK'),
textColor: Colors.deepOrangeAccent[400],
onPressed: (){
PaiementStripe().getMembers().then((result){
setState(() {
subling = result;
});
});
Navigator.of(context, rootNavigator: true).pop();
},
)
],
);
});
}
void showSnackBar(BuildContext context, docID){
var snackBar = SnackBar(
content: Text("Voulez vous Supprimer le membre?"),
action: SnackBarAction(
label: "OUI",
onPressed: (){
PaiementStripe().deleteMembers(docID);
PaiementStripe().getMembers().then((result){
setState(() {
subling = result;
});
});
}
),
);
Scaffold.of(context).showSnackBar(snackBar);
}
void seeSnackBar(BuildContext context){
var snackBar = SnackBar(
content: Text("Vous avez le maximum de membres"),
);
Scaffold.of(context).showSnackBar(snackBar);
}
#override
void initState(){
PaiementStripe().getMembers().then((result){
setState(() {
subling = result;
});
});
super.initState();
}
// the method that I need to return an Integer value
Future<int> countDocuments() async {
FirebaseUser user = await FirebaseAuth.instance.currentUser();
QuerySnapshot _myDoc = await Firestore.instance.collection('users').document(user.uid).collection('sublings').getDocuments();
List<DocumentSnapshot> _myDocCount = _myDoc.documents;
return _myDocCount.length;
//Count of Documents in Collection
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton:Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FloatingActionButton(
heroTag: 'fab1',
onPressed: (){
//the test comparison
if (countDocuments() < 5){
addDialog(context);
print(countDocuments());
}
else{
seeSnackBar(context);
}
},
child: Icon(
Icons.add,
color: Colors.white,
),
backgroundColor: Colors.deepOrangeAccent,
),
]
),
body:_sublingList(),
);
}
Widget _sublingList(){
if(subling!=null){
return ListView.separated(
itemCount: subling.documents.length,
padding: EdgeInsets.all(5.0),
itemBuilder: (context, i){
return ListTile(
leading: Icon(Icons.account_circle,
size: 60.0,),
title: Text(subling.documents[i].data['nomComplet'],
style: TextStyle(
fontWeight: FontWeight.bold,
fontFamily: 'OpenSans'
),),
subtitle: Text(subling.documents[i].data['ldNais'] +'\n'+subling.documents[i].data['adresse']),
onTap: (){
updateDialog(context, subling.documents[i].documentID);
PaiementStripe().getMembers().then((result){
setState(() {
subling = result;
});
});
},
onLongPress: (){
showSnackBar(context,subling.documents[i].documentID);
},
);
},
separatorBuilder: (context, i){
return Divider();
},
);
}
else{
return Center(
child: CircularProgressIndicator(),
);
}
}
}
when I am pressing the fab button, I am getting this message:
Instance of Future'dynamic'
5 //which is the number of documents
that's why it's not possible to compare it
you need to await the Future function to finish
onPressed: () async {
//the test comparison
int count = await countDocuments();
if (count < 5){
addDialog(context);
print(countDocuments());
}
else{
seeSnackBar(context);
}
},
you can do that
void getCount(){
countDocuments().then((count ) {
// print(result);
if (count < 5){
addDialog(context);
print(countDocuments());
}
else{
seeSnackBar(context);
}
});
}
Currently, I have an AlertDialog with an IconButton. The user can click on the IconButton, I have two colors for each click. The problem is that I need to close the AlertDialog and reopen to see the state change of the color icon. I want to change the IconButton color immediately when the user clicks it.
Here is the code:
bool pressphone = false;
//....
new IconButton(
icon: new Icon(Icons.phone),
color: pressphone ? Colors.grey : Colors.green,
onPressed: () => setState(() => pressphone = !pressphone),
),
Use StatefulBuilder to use setState inside Dialog and update Widgets only inside of it.
showDialog(
context: context,
builder: (context) {
String contentText = "Content of Dialog";
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: Text("Title of Dialog"),
content: Text(contentText),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context),
child: Text("Cancel"),
),
TextButton(
onPressed: () {
setState(() {
contentText = "Changed Content of Dialog";
});
},
child: Text("Change"),
),
],
);
},
);
},
);
Use a StatefulBuilder in the content section of the AlertDialog. Even the StatefulBuilder docs actually have an example with a dialog.
What it does is provide you with a new context, and setState function to rebuild when needed.
The sample code:
showDialog(
context: context,
builder: (BuildContext context) {
int selectedRadio = 0; // Declare your variable outside the builder
return AlertDialog(
content: StatefulBuilder( // You need this, notice the parameters below:
builder: (BuildContext context, StateSetter setState) {
return Column( // Then, the content of your dialog.
mainAxisSize: MainAxisSize.min,
children: List<Widget>.generate(4, (int index) {
return Radio<int>(
value: index,
groupValue: selectedRadio,
onChanged: (int value) {
// Whenever you need, call setState on your variable
setState(() => selectedRadio = value);
},
);
}),
);
},
),
);
},
);
And as I mentioned, this is what is said on the showDialog docs:
[...] The widget returned by the builder does not share a context with the location
that showDialog is originally called from. Use a StatefulBuilder or a
custom StatefulWidget if the dialog needs to update dynamically.
This is because you need to put your AlertDialog in its own StatefulWidget and move all state manipulation logic on the color there.
Update:
void main() => runApp(MaterialApp(home: Home()));
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton(
child: Text('Open Dialog'),
onPressed: () {
showDialog(
context: context,
builder: (_) {
return MyDialog();
});
},
)));
}
}
class MyDialog extends StatefulWidget {
#override
_MyDialogState createState() => new _MyDialogState();
}
class _MyDialogState extends State<MyDialog> {
Color _c = Colors.redAccent;
#override
Widget build(BuildContext context) {
return AlertDialog(
content: Container(
color: _c,
height: 20.0,
width: 20.0,
),
actions: <Widget>[
FlatButton(
child: Text('Switch'),
onPressed: () => setState(() {
_c == Colors.redAccent
? _c = Colors.blueAccent
: _c = Colors.redAccent;
}))
],
);
}
}
First you need to use StatefulBuilder. Then i am setting _setState variable, which even could be used outside StatefulBuilder, to set new state.
StateSetter _setState;
String _demoText = "test";
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: StatefulBuilder( // You need this, notice the parameters below:
builder: (BuildContext context, StateSetter setState) {
_setState = setState;
return Text(_demoText);
},
),
);
},
);
_setState is used same way as setState method. For example like this:
_setState(() {
_demoText = "new test text";
});
If you're separating your data from the UI via View Models and using the Provider package with ChangeNotifier, you'll need to include your current model like so within the widget calling the dialog:
showDialog(context: context, builder: (dialog) {
return ChangeNotifierProvider.value(
value: context.read<ViewModel>(),
child: CustomStatefulDialogWidget(),
);
},
Note that there may be a cleaner way to do this but this worked for me.
Additional info regarding Provider: https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple
showModalBottomSheet(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState /*You can rename this!*/) {
return Container(
height: heightOfModalBottomSheet,
child: RaisedButton(onPressed: () {
setState(() {
heightOfModalBottomSheet += 10;
});
}),
);
});
});
Not sure if this is best practice, but I solved the issue of updating both the dialog state and the content state by wrapping the setState functions, after using the top answer to add state to the dialog:
IconButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, newSetState) { // Create a "new" state variable
return AlertDialog(
content: DropdownButton(
value: listItem.type,
items: allItems
onChanged: (value) {
newSetState(() {
setState(() {
// HERE SET THE STATE TWICE
// Once with the "new" state, once with the "old"
});
});
})
),
);
}
);
}
),
In fact, you can use StatefullBuilder but the problem is that when you use this widget you cant change the state of the base screen! Prefer to navigate to a new screen in order to use setState.
I was stuck with this issue.You have to Change the name of setState to any Other name and pass this set state to all sub functions.
This will update your Dialog ui on time.
return StatefulBuilder(
builder: (context, setStateSB) {
return AlertDialog(
title: Text("Select Circle To Sync Data!" ,style: TextStyle(color: Colors.white),),
content: Column(
children: [
Text("Select Division!" ,style: TextStyle(color: Colors.white),),
Container(
height: 80,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InputDecorator(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0)),
contentPadding: EdgeInsets.all(5),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
isExpanded: true,
value: sync_DivisionName_firstValue,
items: _DivisionName_list.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value,style: TextStyle(color: Colors.black)),
);
}).toList(),
onChanged: (String? newValue) {
setStateSB(() {
sync_DivisionName_firstValue = newValue!;
if(sync_DivisionName_firstValue !="Select Division Name"){
print("sync_DivisionName_firstValue$sync_DivisionName_firstValue");
_getDistrictName(sync_DivisionName_firstValue,setStateSB);
}else{
refreashDivisionName(setStateSB);
}
});
},
)),
),
],
),
),
Text("Select District!" ,style: TextStyle(color: Colors.white),),
Container(
height: 80,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InputDecorator(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0)),
contentPadding: EdgeInsets.all(5),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
isExpanded: true,
value: sync_DistrictName_firstValue,
items: _DistrictName_list.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value,style: TextStyle(color: Colors.black),),
);
}).toList(),
onChanged: (String? newValue) {
setStateSB(() {
sync_DistrictName_firstValue = newValue!;
if(sync_DivisionName_firstValue != "Select Division Name" && sync_DistrictName_firstValue != "Select District Name"){
print("sync_DistrictName_firstValue$sync_DistrictName_firstValue");
_getTehsilName(sync_DivisionName_firstValue,sync_DistrictName_firstValue,setStateSB);
}else{
refreashDistrictName(setStateSB);
}
});
},
)),
),
],
),
),
Text("Select Tehsil!" ,style: TextStyle(color: Colors.white),),
Container(
height: 80,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InputDecorator(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0)),
contentPadding: EdgeInsets.all(5),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
isExpanded: true,
value: sync_TehsilName_firstValue,
items: _TehsilName_list.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value,style: TextStyle(color: Colors.black),),
);
}).toList(),
onChanged: (String? newValue) {
setStateSB(() {
sync_TehsilName_firstValue = newValue!;
if(sync_DivisionName_firstValue != "Select Division Name" && sync_DistrictName_firstValue != "Select District Name" && sync_TehsilName_firstValue != "Select Tehsil Name"){
print("sync_TehsilName_firstValue$sync_TehsilName_firstValue");
_getRatingAreaName(sync_DivisionName_firstValue,sync_DistrictName_firstValue,sync_TehsilName_firstValue,setStateSB);
}else{
refreashTehsilName(setStateSB);
}
});
},
)),
),
],
),
),
Text("Select Rating Area Name!" ,style: TextStyle(color: Colors.white),),
Container(
height: 80,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InputDecorator(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0)),
contentPadding: EdgeInsets.all(5),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
isExpanded: true,
value: sync_RatingAreaName_firstValue,
items: _RatingAreaName_list.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value,style: TextStyle(color: Colors.black),),
);
}).toList(),
onChanged: (String? newValue) {
setStateSB(() {
sync_RatingAreaName_firstValue = newValue!;
if(sync_DivisionName_firstValue != "Select Division Name" && sync_DistrictName_firstValue != "Select District Name" && sync_TehsilName_firstValue != "Select Tehsil Name" && sync_RatingAreaName_firstValue != "Select Rating Area Name"){
print("sync_RatingAreaName_firstValue$sync_RatingAreaName_firstValue");
_getWardCircleName(sync_DivisionName_firstValue,sync_DistrictName_firstValue,sync_TehsilName_firstValue,sync_RatingAreaName_firstValue,setStateSB);
}else{
refreashWardCircleName(setStateSB);
}
});
},
)),
),
],
),
),
Text("Select Ward Circle Name!" ,style: TextStyle(color: Colors.white),),
Container(
height: 80,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InputDecorator(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0)),
contentPadding: EdgeInsets.all(5),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
isExpanded: true,
value: sync_circle_name_firstValue,
items: _circle_name_list.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value,style: TextStyle(color: Colors.black),),
);
}).toList(),
onChanged: (String? newValue) {
setStateSB(() {
sync_circle_name_firstValue = newValue!;
print("sync_circle_name_firstValue$sync_circle_name_firstValue");
// if(sync_circle_name_firstValue != "Select Ward Circle Name"){
//
// _getWardCircleName(sync_RatingAreaName_firstValue);
// }else{
//
// }
});
},
)),
),
],
),
),
]),
backgroundColor:Color(0xFFEC9F46),
actions: [
okButton,SyncButton
],
);
},
);
One of the Inner Funciton is like this.
Future<void> refreashDivisionName( StateSetter setInnerState) async {
final List<String> _division_name = await getDivisionNameList();
final List<String> _district_name_list = await getDistrictName(sync_DivisionName_firstValue);
final List<String> _tehsil_name_list = await getTehsilName(sync_DivisionName_firstValue,sync_DistrictName_firstValue);
final List<String> _rating_area_name_list = await getRatingAreaName(sync_DivisionName_firstValue,sync_DistrictName_firstValue,sync_TehsilName_firstValue);
final List<String> _ward_circle_name_list = await getWardCircleName(sync_DivisionName_firstValue,sync_DistrictName_firstValue,sync_TehsilName_firstValue,sync_RatingAreaName_firstValue);
setInnerState(() {
_division_name.insert(0, "Select Division Name");
_DivisionName_list = _division_name;
sync_DivisionName_firstValue = _DivisionName_list[0];
_district_name_list.insert(0, "Select District Name");
_DistrictName_list = _district_name_list;
sync_DistrictName_firstValue = _DistrictName_list[0];
_tehsil_name_list.insert(0, "Select Tehsil Name");
_TehsilName_list = _tehsil_name_list;
sync_TehsilName_firstValue = _TehsilName_list[0];
_rating_area_name_list.insert(0, "Select Rating Area Name");
_RatingAreaName_list = _rating_area_name_list;
sync_RatingAreaName_firstValue = _RatingAreaName_list[0];
_ward_circle_name_list.insert(0, "Select Ward Circle Name");
_circle_name_list = _ward_circle_name_list;
sync_circle_name_firstValue = _circle_name_list[0];
});
}
I hope you under Stand.
base on Andris's answer.
when dialog share the same state with parent widget, you can override parent widget's method setState to invoke StatefulBuilder's setState, so you don't need to call setState twice.
StateSetter? _setState;
Dialog dialog = showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: StatefulBuilder( // You need this, notice the parameters below:
builder: (BuildContext context, StateSetter setState) {
_setState = setState;
return Text(_demoText);
},
),
);
},
);
// set the function to null when dialo is dismiss.
dialogFuture.whenComplete(() => {_stateSetter = null});
#override
void setState(VoidCallback fn) {
// invoke dialog setState to refresh dialog content when need
_stateSetter?.call(fn);
super.setState(fn);
}
Currently to retrieve the value of Dialog I use
showDialog().then((val){
setState (() {});
print (val);
});
Example
1st screen
onPressed: () {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
return AddDespesa();
}).then((val) {
setState(() {});
print(val);
}
);
}
2nd screen
AlertDialog(
title: Text("Sucesso!"),
content: Text("Gasto resgristrado com sucesso"),
actions: <Widget>[
FlatButton(
child: Text("OK"),
onPressed: () {
Navigator.pop(context, true);
},
),
],
);
Will be printed true,