Group Checkbox in ModalBottomSheet Flutter - 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.

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

Changing property of children don't trigger UI change on Flutter Getx

I'm trying to render a reactive widget, based on a object list.
I needed that when the children of this object, has his boolean property changes, the UI should be updated.
In this scenario, my controller has a list os Sections and each section has a list of Lessons.
To better understanding, i rewriter my code making a simple example, based on what i need.
I already try using .map, .forEach, and others approaches, but the children property never trigger the UI update.
GetX<LessonController>(
initState: (state) => controller.getLessonsByCourse(course.id),
builder: (_) {
return Expanded(
child: ListView.separated(
scrollDirection: Axis.vertical,
itemCount: _.sectionList.length,
itemBuilder: (BuildContext context, int index) {
return ExpansionTile(
title: Text(_.sectionList[index].title),
children: <Widget>[
for(var i = 0; i < _.sectionList[index].lessons.length; i++)
Padding(
padding: const EdgeInsets.all(8.0),
child:
Obx(() =>
GestureDetector(
child: Text((_.sectionList[index].lessons[i] as Lesson).isCompleted.toString()),
onTap: () {
(_.sectionList[index].lessons[i] as Lesson).isCompleted = !(_.sectionList[index].lessons[i]as Lesson).isCompleted;
print((_.sectionList[index].lessons[i]as Lesson).isCompleted.toString());
})
),
)
]);
},
separatorBuilder: (context, ind) => Divider(
height: 2,
color: Colors.grey[300],
),
),
);
})
Solution:
The GetX container monitores changes on the list items, so when you change a property from one of this items, the list itself doesn't change. To solved that, i change the item property, and after that i overrited it on the list.
onTap: (value) {
Section tappedSection = _.sectionList[index];
tappedSection.lessons[lessonIndex].isCompleted = value;
// This is the secret
// Making GetX detect a change on the list and rebuild UI
_.sectionList[index] = tappedSection;
}));
As far as I could tell, an observable list in Flutter GetX is not concerned with internal changes to objects in itself.
To force the UI update, I had to return the updated object to the same position in the list, in order to pretend that there was a change in the objects in the main list.
Something like that:
Section tappedSection = _.sectionList[index];
tappedSection.lessons[i].isCompleted = value;
_.notifyLessonProgress(tappedSection.lessons[i].id, value);
_.sectionList[index] = tappedSection;
It looks like the problem is here:
this._sectionList.value = value;
Try this instead:
this._sectionList = value;

DropdownButtonFormField not getting rebuilt

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

How to change state of a single object inside a List

I am building an app that shows three tabs, each one shows a list of places, each list is ordered by distance, importance and user votes. How do I update a single object inside those lists (Like if it's in user's liked list) without updating the entire list every time? I am currently using a bloc pattern.
You could create a StatefulWidget for that part so it can handle its own state, or you could wrap that part with StatefulBuilder, and use it like this:
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Column(
mainAxisSize: MainAxisSize.min,
children: List<Widget>.generate(4, (int index) {
return Radio<int>(
value: index,
groupValue: selectedRadio,
onChanged: (int value) {
setState(() => selectedRadio = value);
},
);
}),
);
},
)