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
Related
I have a FutureBuilder that returns a ListViewBuilder in a class.
When loading the class the FutureBuilder loads the future which is a call to a API, then the ListView shows the received items inside Cards.
It is working fine, at this moment there are three items that should and are showed.
Then I am trying to verify if the class is updated when executing setState at a button click action. I am manually adding or removing items from the database that is called from the API, but clicking on the refres button after adding/removing items from the database, the list is not changing.
Here you have the code:
Container(
height: 120,
child:
FutureBuilder(
future: fetchFotosInformesIncidenciasTodos(
widget.informeActual.codigo),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<dynamic>? filteredList =
snapshot.data as List?;
filteredList ??= [];
listaFotosInformeIncidenciasActual =
filteredList;
WidgetsBinding.instance
.addPostFrameCallback((t) {
setState(() {
numeroFotosSubidas =
filteredList!.length +
numeroFotosSubidasAhora;
});
});
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: filteredList.length,
shrinkWrap: false,
itemBuilder: (BuildContext context, index) {
FotoInformeIncidenciasModelo foto =
filteredList![index];
var urlFoto = Constantes
.adminInformesIncidenciasUrl +
foto.archivo;
return GestureDetector(
onTap: () {
print("pulsada foto ${foto.id}");
},
child: Card(
elevation: 6,
child: (Column(
children: [
Image.network(
urlFoto,
width: 60,
height: 80,
),
],
)),
));
},
);
}
return Image.asset(
"imagenes/vacio.png",
fit: BoxFit.contain,
);
},
),
),
And here the refresh button:
InkWell(
onTap: (){
setState(() {
print("refrescando");
});
},
child: Text("refrescar")),
I would like to know why is the call to setState not forcing to update the FutureBuilder and the ListView Builder
The future function fetchFotosInformesIncidenciasTodos(widget.informeActual.codigo)
which is being called directly from the Future block. You need to make an instance of the future and invoke the same whenever you want a new request for the future eg.
Future<Response> _futureFun;
....
#override
void initState() {
super.initState();
_futureFun =
fetchFotosInformesIncidenciasTodos(widget.informeActual.codigo)
}
_futureFun = fetchFotosInformesIncidenciasTodos(widget.informeActual.codigo){}
#override
Widget build(BuildContext context) {
....
FutureBuilder<Response>(
future: _futureFun,
....
}
And to refresh the data again, just call the function fetchFotosInformesIncidenciasTodos(widget.informeActual.codigo) again and there is not need to setState.
I am making a growable Textfield widget that a user can add as many textfield as he wants.
I am using a list of textfields in this case. If user presses button to add textfield , it will be added to the list .
Also have created a function to delete the textfield if user wants and ima useing listName.removeAt() method for this . But when i delete a textfield which got some value there is a mismatch . I am deleting the textfield of that index but the value it holds shifts to another field.
Where i implement the code is :
Consumer(
builder: (ctx, ref, child) {
final customCourse =
ref.watch(customCourseTypeNotifierProvider);
return ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: customCourse.customCourses.length,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: MinimalInputField(
onChanged: (String value) {
},
hintText: "",
suffixIcon: IconButton(
icon: const Icon(Icons.remove),
color: Colors.red,
onPressed: () {
ref
.read(customCourseTypeNotifierProvider)
.removeCourse(index);
},
),
),
);
},
);
},
),
In type_controller.dart
class CourseTypeNotifier extends ChangeNotifier {
List<CustomCourseModel> customCourses = [];
void addCourse() {
customCourses.add(
const CustomCourseModel(title: "", description: ""),
);
notifyListeners();
}
void removeCourse(int index) {
customCourses.removeAt(index);
notifyListeners();
}
When text in MinimalInputField changes, the model is not updated and when the model is changed, text in MinimalInputField is not updated.
Using TextField could go something like this:
TextField(
controller: TextEditingController()..text = customCourse.customCourses[index].title!,
onChanged: (String value) {
ref.read(customCourseTypeNotifierProvider).updateCourse(index, value);
},
...
)
class CourseTypeNotifier extends ChangeNotifier {
...
void updateCourse(int index, String title) {
customCourses[index] = CustomCourseModel(title: title, description: "");
}
...
}
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
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.
Is there a way to give custom id to every item in flutter's listview?
I need to update a single item in flutter listview by its id or key.
Why I want to update by Id?
because I want to update a specific item without rebuilding the entire listview items, this may boost performance of my app.
I guess you can just use itemBuilder's index since it's not repetitive.
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, int index) {
return ListTile(key: new Key(index.toString()));...
},
);
You can do it like this:
return ListView.builder(
itemCount: list.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
title: Text(list[index]),
onTap: () { // <-- onTap
setState(() {
list.insert(index, 'updated value');
});
},
onLongPress: () { // <-- onLongPress
setState(() {
list.removeAt(index);
});
},
),
);
},
);
here as you can see you can easily update and delete.