DropdownButtonFormField not getting rebuilt - flutter

I'm trying to update the selected value programmatically.
I've used various method, including Consumer etc., and have made sure that the value is updated and the widget is called when the value changes, however, DropdownButtonFormField never got rebuilt with the latest value.
Currently I'm wrapping the DropdownButtonFormField in a StreamBuilder, which supposedly, should get rebuild whenever there's a new event sent through the stream. This is my code:
Declaration
final StreamController<String> _raceStreamController = new StreamController<String>();
DropdownButtonFormField
return
StreamBuilder<String>(
stream: _raceStreamController.stream,
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
return new DropdownButtonFormField<String>(
value: snapshot.data,
hint: new Text(hint,
textAlign: TextAlign.center),
isExpanded: true,
items: items.map((String value) {
return new DropdownMenuItem<String>(
child: new Text(value),
value: value
);
}).toList(),
validator: (value) => value == null ? 'field required' : null,
onChanged: (value) {} // no use for now,
);
});
Push data
onFocusChange: (focus) async {
if (!focus) {
try{
await userBloc.searchUser(controller.text.toUpperCase());
_raceStreamController.sink.add(userBloc.user.race);
} catch(e) {
if (e.toString() == ERROR_UNAUTHORISED)
navigateToRoot(context);
}
}
}
I've tried to remove as much redundant code as possible.
Thank you.

In Flutter version 1.17.2, that DropdownButtonFormField bug was fixed, so be sure to upgrade.
Github issue: https://github.com/flutter/flutter/issues/56898
Fixed in version 1.17.2: https://github.com/flutter/flutter/wiki/Hotfixes-to-the-Stable-Channel#1172-may-28-2020

Related

How to Obx in RxStatus.success() function

I use CheckboxListTile to input checkbox.
this is a function to retrieve a list of data from firestore
void initDoctorCategory() {
DoctorCategoryService().getListDoctorCategory().then((doctorCategory) {
change(doctorCategory, status: RxStatus.success());
});
}
And this is my widget. i cant using obx for update value in doctorCategory[index].value:
body: controller.obx(
(doctorCategory) => Container(
child: ListView.builder(
itemCount: doctorCategory!.length,
itemBuilder: (BuildContext context, int index) {
return Obx(() => CheckboxListTile(
title: Text(doctorCategory[index].categoryName!),
value: doctorCategory[index].value,
onChanged: (value) {
doctorCategory[index].value = value!;
// Get.back();
},
));
},
),
),
),
Im get the error:
[Get] the improper use of a GetX has been detected.
You should only use GetX or Obx for the specific widget that will be updated.
If you are seeing this error, you probably did not insert any observable variables into GetX/Obx
or insert them outside the scope that GetX considers suitable for an update
(example: GetX => HeavyWidget => variableObservable).
If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX.
"""
How to solved this?
How to using Getx in RxStatus.success()?
Try this:
body: Container(
child: Obx(() {
return ListView.builder(
itemCount: doctorCategory!.length,
itemBuilder: (BuildContext context, int index) {
return CheckboxListTile(
title: Text(doctorCategory[index].categoryName!),
value: doctorCategory[index].value,
onChanged: (value) {
doctorCategory[index].value = value!;
},
);
},
);
}),
),
Be sure that you have Get.put or Get.lazyPut of your controller

Flutter DropDownButton using FutureBuilder value is not updating after selecting values from DropdownItems

I have been trying to code this app using Flutter and I want to make a Dropdown that displays the values received from a JSON response via provider. The response is successful from the service and correctly fetches the data. Dropdown is wrapped in a FutureBuilder, the information can be displayed without problems in the Dropdown, the problem is generated when I want to select an element, it is not updated, for this reason it is not reflected.
My code:
List<Datum> listDatumEnterprise;
Datum _selectEnterprise;
return FutureBuilder(
future: getInformationAdministrative.enterpriseDataGet(),
builder: (BuildContext context, AsyncSnapshot<List<Datum>> snapshot) {
if (snapshot.hasData) {
listDatumEnterprise = snapshot.data;
return CustomDropDown<Datum>(
title: 'Selecciona empresa',
value: _selectEnterprise,
onChanged: (Datum datum) {
setState(() {
_selectEnterprise = datum;
print(_selectEnterprise.id);
});
},
dropdownMenuItemList: listDatumEnterprise?.map((Datum item) {
return new DropdownMenuItem<Datum>(
child: Text(item.alias),
value: item,
);
})?.toList() ??
[],
);
} else {
return CircularProgressIndicator();
}
The problem is that you are initializing _selectEnterprise inside your build , calling setState() will call build and will re-initiate the value again to empty, move Datum _selectEnterprise outside your build. Also be sure to have distinct values for the DropdownMenuItem.
I have wrapped my DropdownButton with StatefulBuilder. This is the only way I could change the data of DropdownButton without recalling FutureBuilder.
Demo code:
StatefulBuilder(builder: (context, setState) {
return DropdownButton<String>(
borderRadius: BorderRadius.circular(
10,
),
hint: Text(
"Your Hint",
),
value: selectedValue, //your selected value
isExpanded: true,
items: your_list.map((String value) { //your list here
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (value) {
setState(() => selectedValue = value); //your selected value
},
);
}),
Learn more from: StatefulBuilder

Group Checkbox in ModalBottomSheet Flutter

How to apply group checkbox in modalbottomsheet? I have create group checkbox and its work fine. But when I put it in to the modalbottomsheet the state not instantly changes (need to reopen to see value changes). I do have wrap the checkboxListTile with StatefulBuilder and give StateSetter. Any Solution? Thank you
here my codes
Column(
children: [
StatefulBuilder(builder:
(BuildContext context,
StateSetter setState) {
return CheckboxListTile(
controlAffinity:
ListTileControlAffinity.leading,
value: checkAllPermit,
title: Text('Permit', style: TextStyle(fontWeight: FontWeight.bold)),
onChanged: (value) {
setState(() {
checkAllPermit = value;
_permitType.forEach((tipePermit){
tipePermit.value = value;
});
});
},
);
}),
ListView.builder(
shrinkWrap: true,
itemCount: _permitType.length,
itemBuilder: (context, index) {
return StatefulBuilder(builder:
(BuildContext context,
StateSetter state) {
return CheckboxListTile(
controlAffinity:
ListTileControlAffinity.leading,
value: _permitType[index].value,
title: Text(_permitType[index].title),
onChanged: (value) {
state(() {
_permitType[index].value = value;
});
},
);
});
},
),
],
),
List.forEach is useful in cases where you want to perform an operation using each element of the List.
But it is isn't a good approach when you want to update the List in your state.
When you do
_permitType.forEach((tipePermit){
tipePermit.value = value;
});
You are just updating the references of the individual elements inside your _permitType and not actually updating the reference of your _permitType, which is causing your UI to not update.
Instead do this,
_permitType = _permitType.map((_) => value);
With this, you are using map to not just update each element to value but are also changing the reference of _permitType to this new List that you are getting by calling .map.
Secondly,
When you call the StateSetter that you obtain from the StatefulBuilder, only the widget inside the builder is re-built and widgets outside the builder will not rebuild.
In your use case, there doesn't seems to be any need for StatefulBuilder since you are not using the context that it is giving you.
So, just remove the StatefulBuilder and use it normally.

Pulling data from QuerySnapshot in flutter

I need to populate a DropdownButton from a document in Firestore. I can retrieve the data. When I look into the snapshot.data I see 2 records which is what I expect to see. In the code below, everything works fine as long as I comment out the code snippet as you can see.
Container(
child: StreamBuilder(
//stream: _firestoreService.getAgency(),
stream: _db.collection('agency').snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Center(
child: CircularProgressIndicator(),
);
} else {
//var length = snapshot.data.docs.length;
//print('length: ' + length);
return new DropdownButton<String>(
hint: new Text("Select Agency"),
value: _currentAgency,
/* <<<< The code below is where I am having problems
//onChanged: changedDropDownState,
**items: snapshot.data.docs.map((Map map) {
return new DropdownMenuItem<String>(
value: map["name"].toString(),
child: new Text(
map["name"],
),
);
}).toList(),**
*/
);
}
;
}),
),
When I uncomment the code and run the app I get this error:
======== Exception caught by widgets library =======================================================
The following _TypeError was thrown building StreamBuilder<QuerySnapshot>(dirty, state: _StreamBuilderBaseState<QuerySnapshot, AsyncSnapshot<QuerySnapshot>>#d9273):
type '(Map<dynamic, dynamic>) => DropdownMenuItem<String>' is not a subtype of type '(QueryDocumentSnapshot) => dynamic' of 'f'
What I want to accomplish is to populate the value: attribute with the document ID but I don't see it in snapshot.data. The other thing I want to do is populate child: attribute with some of the values from the snapshot.data.
How do I do this?
Your issue is here
items: snapshot.data.docs.map(( ISSUE HERE===>Map map) {
return new DropdownMenuItem<String>(
value: map["name"].toString(),
child: new Text(
map["name"],
),
);
}).toList(),
The value for docs is QueryDocumentSnapshot and not type Map that is why you got the error message.
Instead, change it to this.
items: snapshot.data.docs.map((documentSnapshot) {
return new DropdownMenuItem<String>(
value: documentSnapshot["name"].toString(),
child: new Text(
documentSnapshot["name"],
),
);
}).toList(),
I found a post that says to use .forEach instead of map (Snapshot code inside StreamBuilder does not get executed when receiving data from Firebase FireCloud in Flutter).
This gets rid of the error but the DropdownButton is static and won't drop down or let me click on it. Here is my new code for the DropdownButton:
return new DropdownButton<String>(
hint: new Text("Select Agency"),
value: _currentAgency,
//onChanged: changedDropDownState,
items: snapshot.data.docs.forEach((document) {
return new DropdownMenuItem<String>(
value: document.data()['name'],
child: new Text(document.data()['name']),
);
}),
);
The data is there and I can see it when I debug. I want to get the documentId as well but I don't see that in the debugger.
How do I get the DropdownButton to be active and how do I add the documentId to the value: attribute?

ValueListenableBuilder not rebuilding a CheckboxListTile Flutter

I am trying to run a method that updates the value of a CheckboxListTile as I pass the new values at the end at Globals.data.updateFilterSelection(newFilters); the method runs fine and it do updates (tested it with prints), but the ValueListenableBuilder is not rebuilding the CheckboxListTile when I change its value.
I have three CheckboxListTile with the same code but different logic all of them listening to Globals.data.filterSelection,
What I am missing?
Hi here is the code:
ValueListenableBuilder<Map>(
valueListenable: Globals.data.filterSelection,
builder: (context, value, _) {
return CheckboxListTile(
activeColor: Theme.of(context).indicatorColor,
value: value['all_neighbors'],
secondary: Icon(
Icons.people,
color: Theme.of(context).indicatorColor,
),
title: Text(
'All Neighbors',
style: Theme.of(context).textTheme.bodyText1,
),
onChanged: (newValue) {
if (newValue) {
newFilters
..update('inactive', (value) => false)
..update('active', (value) => false)
..update('all_neighbors', (value) => true);
} else {
newFilters
..update('inactive', (value) => true)
..update('all_neighbors', (value) => false);
}
Globals.data.updateFilterSelection(newFilters);
},
);
}),
Here is also my ValueNotifier and the method called:
ValueNotifier<Map> filterSelection = ValueNotifier<Map>({
'inactive': true,
'active': false,
'all_neighbors': false,
});
/// Changes the filter selection
void updateFilterSelection(Map newFilter) {
filterSelection.value = newFilter;
print(filterSelection.value);
}
Thanks in advance
I found out that the ValueListenableBuilder does not rebuild on <Map> types it needs to be a single value that can be compared with the == operator, as described.
When value is replaced with something that is not equal to the old value as evaluated by the equality operator ==, this class notifies its listeners.