I am trying to dynamically create some checkboxes based on data pulled from an API. My checkboxes are created but when I click on them they are all being checked or unchecked. I am pretty sure I can identify why I am getting is, I am just unsure how to overcome it.
The variable I create is for one check box, so when my list is created from ListView.builder, it is using the same variable thereby making all boxes check and uncheck. I know that I need to create that variable based on how many items are in the list. I am just not sure how to do this and where within my code structure. I tried different methods of using .length or trying to use a .forEach but none of it was correct in the method I was implementing it. I have included my code that shows how I am creating my list of tags.
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:gateway_device/backend/api/api_services.dart';
import 'package:gateway_device/flutter_flow/flutter_flow_theme.dart';
import 'package:gateway_device/models/tag_list_model.dart';
class TagsListWidget extends StatefulWidget {
final int companyId;
const TagsListWidget({Key? key, required this.companyId}) : super(key: key);
@override
State<TagsListWidget> createState() => _TagsListWidgetState(companyId);
}
class _TagsListWidgetState extends State<TagsListWidget> {
final int companyId;
late bool checkboxListTileValue = false;
final scaffoldKey = GlobalKey<ScaffoldState>();
_TagsListWidgetState(this.companyId);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Tags",
textAlign: TextAlign.center,
style: FlutterFlowTheme.of(context).title2.override(
fontFamily: 'Heebo',
fontSize: 18.sp,
fontWeight: FontWeight.w500),
),
elevation: 0,
actions: <Widget>[
IconButton(
hoverColor: Colors.transparent,
iconSize: 40,
icon: Icon(
Icons.search,
color: Colors.black,
size: 20,
),
onPressed: () {
print("Test");
},
)
],
leading: IconButton(
hoverColor: Colors.transparent,
iconSize: 40,
icon: Icon(
Icons.keyboard_return_sharp,
color: Colors.black,
size: 30,
),
onPressed: () {
Navigator.pop(context);
},
),
centerTitle: true,
backgroundColor: Colors.white,
iconTheme: IconThemeData(color: Colors.black),
),
backgroundColor: Colors.white,
body: SafeArea(
child: FutureBuilder(
future: ApiService().getTagList(companyId),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
Tags tags = snapshot.data[index];
return GestureDetector(
onTap: (() {
FocusScope.of(context).unfocus();
}),
child: Center(
child: CheckboxListTile(
value: checkboxListTileValue,
onChanged: (newValue) => setState(() {
checkboxListTileValue = newValue!;
}),
title: Text(tags.tag,
textAlign: TextAlign.start,
style: FlutterFlowTheme.of(context)
.title3
.override(
fontFamily: 'Heebo',
color: Colors.black,
fontSize: 18.sp)),
),
),
);
});
}
return Center(
child: CircularProgressIndicator(),
);
}),
),
);
}
}
I appreciate the help!
CodePudding user response:
You can create List:
List<bool> checkboxValue = [];
then use it like this:
return StatefulBuilder(// add this
builder: (c, innerSetState) {
return GestureDetector(
onTap: (() {
FocusScope.of(context).unfocus();
}),
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
Tags tags = snapshot.data[index];
checkboxValue = List<bool>.generate(snapshot.data.length,(counter) => false); // add this
return GestureDetector(
onTap: (() {
FocusScope.of(context).unfocus();
}),
child: Center(
child: CheckboxListTile(
value: checkboxValue[index], //add this
onChanged: (newValue) {
innerSetState(() {// add this
checkboxValue[index] = newValue;
});
},
title: Text(tags.tag,
textAlign: TextAlign.start,
style: FlutterFlowTheme.of(context)
.title3
.override(
fontFamily: 'Heebo',
color: Colors.black,
fontSize: 18.sp)),
),
),
);
}),
);
},
);
note that this work when checkbox value not come from api, if it is let me know so I can Update my answer.
CodePudding user response:
Here is the problem, that you use varieble checkboxListTileValue in global scope. Then yo set new value in:
onChanged: (newValue) => setState(() {
checkboxListTileValue = newValue!;})
Global checkboxListTileValue applayed to all ListView items. You can made the List checkboxListTileValue, with defaul value set. And onChage set the new value only for item with index which was clicked.
onChanged: (newValue) => setState(() {
checkboxListTileValue[index] = newValue!;})
CodePudding user response:
You are using single bool
to control the checked status of a list. You can create a model class with a bool filed that will be like bool isCheked=false
, Once you click on item check it is already checked or not and change the value.
Or create a List that will hold tags.tag
value.
List<String> selected = [];
....
value: selected.contains(tags.tag),
onChanged: (newValue)
{
if(selected.contains(tags.tag)){
selected.remove(tags.tag); }
else{
selected.add(tags.tag);
}
setState((){});