flutter - 如何在 Flutter 中选中/取消选中有状态子 WIdget 中的复选框?
问题描述
我有一个名为 的父 Widget ,LockTimelogsList
其中包含一个ListView
. LockTimelogBox
每个子小部件/LockTimelogBox
包含一个Checkbox
我需要从父更新LockTimelogsList
。当用户按下“全选”或“取消全选”时,LockTimelogsList
需要迭代LockTimelogBoxes
并设置每个内部的复选框为真/假。
我遇到的麻烦是迭代列出的子小部件并更改和更新它们的复选框布尔值,复选框不会更新它的视图,即使在调用setState
.
我尝试了两种方法:
- 在 setState 调用中直接从父级更改复选框布尔值。
- 在每个子小部件内调用一个函数以从子小部件本身更改复选框值。
更新
现在代码可以工作了。它会正确更新所有复选框。看看接受的答案,以防万一读到这篇文章的人犯了同样的错误。我将代码更新为现在可以工作了。您可以在下面看到工作代码。
[![在此处输入图像描述][2]][2]
当前父(列表)代码:
class LockTimelogsList extends StatefulWidget {
LockTimelogsList({Key key}) : super(key: key);
@override
_LockTimelogsListState createState() => _LockTimelogsListState();
}
class _LockTimelogsListState extends State<LockTimelogsList> {
List<TimeLogModel> unlockedLogs;
List<ProjectModel> projects;
List<WorkOrderModel> workOrders;
List<TaskModel> tasks;
ProjectHttpService projectHttpService;
WorkOrderHttpService workOrderHttpService;
TaskHttpService taskHttpService;
TimeLogHttpService timeLogHttpService;
double displayHeight;
double displayWidth;
bool selectAllOptionActive;
List<LockTimelogBox> timelogBoxes;
List<Function> selectFunctions;
List<Function> deselectFunctions;
List<TimelogBoxData> boxDatas;
List<bool> checkboxes;
@override
void initState() {
initServices();
initValues();
loadInitData();
super.initState();
}
@override
Widget build(BuildContext context) {
setDisplayDimensions();
return SafeArea(
child: Scaffold(
backgroundColor: Colors.white,
appBar: buildAppBar(),
body: buildBody(),
));
}
buildAppBar() {
return AppBar(
backgroundColor: themeConfig.appBarBg,
title: Row(
children: [Icon(Icons.lock), Text(" Lås timmar")],
),
);
}
buildBody() {
return Stack(
children: [buildControlsLayer(), buildLoadDialogLayer()],
);
}
buildControlsLayer() {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [buildTopSelectBar(), buildTimelogList()],
);
}
buildLoadDialogLayer() {
return Container();
}
buildTimelogList() {
return Expanded(
flex: 100,
child: Scrollbar(
child: ListView(
children: buildTimelogBoxes(),
)));
}
buildTimelogBoxes() {
var boxDatas = TimelogBoxDataHelper.createList(unlockedLogs, workOrders, tasks, projects);
var widgets = new List<Widget>();
timelogBoxes.clear();
for (var i = 0; i < boxDatas.length; i++) {
var box = buildTimelogBox(boxDatas[i], checkboxes[i], i);
widgets.add(box);
timelogBoxes.add(box);
}
this.boxDatas = boxDatas;
return widgets;
}
LockTimelogBox buildTimelogBox(TimelogBoxData boxData, bool isChecked, int listIndex) {
var box = new LockTimelogBox(
data: boxData,
giveSelectFunction: addSelectFunction,
giveDeselectFunction: addDeselectFunction,
isChecked: isChecked,
checkChangeCallback: checkChangeCallback,
listIndex: listIndex,
);
return box;
}
void initValues() {
checkboxes = [];
boxDatas = [];
displayHeight = 1;
displayWidth = 1;
unlockedLogs = [];
projects = [];
workOrders = [];
tasks = [];
selectAllOptionActive = true;
timelogBoxes = [];
selectFunctions = [];
deselectFunctions = [];
}
Future loadInitData() async {
await loadTimelogs();
if (unlockedLogs.length > 0) await loadProjectData();
}
void initServices() {
projectHttpService = new ProjectHttpService();
workOrderHttpService = new WorkOrderHttpService();
taskHttpService = new TaskHttpService();
timeLogHttpService = new TimeLogHttpService();
}
void onLoadTimelogsError() {
MessageDialogHelper.show("Fel vid laddning av tidsloggar", "Fel uppstod vid laddning av tidsloggar.", context);
}
void onLoadTimelogsSuccess(Response response) {
var timelogs = TimelogHelper.getFromJson(response.body);
setTimelogs(timelogs);
}
setTimelogs(List<TimeLogModel> timelogs) {
setState(() {
// temp
timelogs.forEach((log) {
checkboxes.add(false);
});
// -
unlockedLogs = timelogs;
});
}
Future loadTimelogs() async {
try {
var response = await timeLogHttpService.getUnlockedLogs(globals.userId);
if (HttpHelper.isSuccess(response))
onLoadTimelogsSuccess(response);
else
onLoadTimelogsError();
} catch (e) {
onLoadTimelogsError();
}
}
Future loadProjectData() async {
var futures = new List<Future>();
futures.add(loadProjects(unlockedLogs));
futures.add(loadWorkOrders(unlockedLogs));
futures.add(loadTasks(unlockedLogs));
var results = await Future.wait(futures);
var projects = getFromResults<ProjectModel>(results);
var workOrders = getFromResults<WorkOrderModel>(results);
var tasks = getFromResults<TaskModel>(results);
setProjects(projects);
setWorkOrders(workOrders);
setTasks(tasks);
}
Future<List<ProjectModel>> loadProjects(List<TimeLogModel> timelogs) async {
var futures = new List<Future<ProjectModel>>();
var projectIds = TimelogHelper.getProjectIds(timelogs);
projectIds.forEach((id) {
futures.add(loadProject(id));
});
var projects = await Future.wait(futures);
return projects;
}
Future<ProjectModel> loadProject(String id) async {
try {
var response = projectHttpService.getProject(id);
return response.then<ProjectModel>((resp) {
if (HttpHelper.isSuccess(resp))
return ProjectHelper.getSingleFromJson(resp.body, true);
else
return null;
}).catchError((err) {
return null;
});
} catch (e) {
return null;
}
}
Future<List<WorkOrderModel>> loadWorkOrders(List<TimeLogModel> timelogs) async {
var futures = new List<Future<WorkOrderModel>>();
var workOrderIds = WorkOrderHelper.getWorkOrderIds(timelogs);
workOrderIds.forEach((id) {
futures.add(loadWorkOrder(id));
});
var workOrders = await Future.wait(futures);
return workOrders;
}
Future<WorkOrderModel> loadWorkOrder(String id) async {
try {
var response = workOrderHttpService.get(id);
return response.then<WorkOrderModel>((resp) {
if (HttpHelper.isSuccess(resp)) {
var list = WorkOrderHelper.getSingleFromJson(resp.body, true);
return list;
} else
return null;
}).catchError((err) {
return null;
});
} catch (e) {
return null;
}
}
Future<List<TaskModel>> loadTasks(List<TimeLogModel> timelogs) async {
var futures = new List<Future<TaskModel>>();
var taskIds = TaskHelper.getTaskIds(timelogs);
taskIds.forEach((id) {
futures.add(loadTask(id));
});
var tasks = await Future.wait(futures);
return tasks;
}
Future<TaskModel> loadTask(String id) async {
try {
var response = taskHttpService.getById(id);
return response.then<TaskModel>((resp) {
if (HttpHelper.isSuccess(resp)) {
var list = TaskHelper.getSingleFromJson(resp.body, true);
return list;
} else
return null;
}).catchError((err) {
return null;
});
} catch (e) {
return null;
}
}
List<T> getFromResults<T>(List<dynamic> results) {
List<T> result;
results.forEach((res) {
if (res is List<T>) result = res;
});
return result;
}
setProjects(List<ProjectModel> projects) {
setState(() {
this.projects = projects;
});
}
setWorkOrders(List<WorkOrderModel> workOrders) {
setState(() {
this.workOrders = workOrders;
});
}
setTasks(List<TaskModel> tasks) {
setState(() {
this.tasks = tasks;
});
}
buildTopSelectBar() {
return Expanded(
flex: 8,
child: Container(
decoration: BoxDecoration(
border: Border(bottom: BorderSide(width: displayHeight * 0.0005, color: Color(0x77FFFFFF))),
gradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Color(0xFF13313b), Color(0xFF11131a)]),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
children: [buildSelectAllButton(), buildSelectCount()],
),
));
}
setDisplayDimensions() {
if (displayWidth == 1 || displayWidth == null) displayWidth = DisplayHelper.getDisplayWidth(context);
if (displayHeight == 1 || displayHeight == null) displayHeight = DisplayHelper.getDisplayHeight(context);
}
buildSelectAllButton() {
return Expanded(
flex: 100,
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: onSelectAllTap,
child: Container(
padding: EdgeInsets.only(left: displayWidth * 0.03),
alignment: Alignment.center,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
child: Icon(
selectAllOptionActive ? Icons.check_box : Icons.cancel,
color: Colors.white,
size: displayWidth * 0.05,
),
),
Container(
margin: EdgeInsets.only(left: displayWidth * 0.015),
child: Text(
selectAllOptionActive ? "Markera alla" : "Avmarkera alla",
style: TextStyle(fontSize: displayWidth * 0.05, color: Colors.white),
),
)
],
),
),
),
));
}
buildSelectCount() {
return Expanded(
flex: 100,
child: Material(
color: Colors.transparent,
child: InkWell(
child: Container(
alignment: Alignment.center,
child: Text(
"",
style: TextStyle(fontSize: displayWidth * 0.05, color: Colors.white),
),
),
),
));
}
void onSelectAllTap() {
setState(() {
if (selectAllOptionActive)
selectAll();
else
deselectAll();
setSelectAllOption(!selectAllOptionActive);
});
}
setSelectAllOption(bool selectAllActive) {
setState(() {
selectAllOptionActive = selectAllActive;
});
}
void selectAll() {
print(selectFunctions);
print(timelogBoxes);
print(unlockedLogs);
for (var i = 0; i < checkboxes.length; i++) checkboxes[i] = true;
selectFunctions.forEach((func) {
func();
});
}
void deselectAll() {
for (var i = 0; i < checkboxes.length; i++) checkboxes[i] = false;
deselectFunctions.forEach((func) {
func();
});
}
addSelectFunction(Function f) {
selectFunctions.add(f);
}
addDeselectFunction(Function f) {
deselectFunctions.add(f);
}
checkChangeCallback(int listIndex, bool isChecked) {
setState(() {
checkboxes[listIndex] = isChecked;
selectAllOptionActive = !checkboxes.any((b) => b);
});
}
}
当前子(框)代码:
class LockTimelogBox extends StatefulWidget {
final TimelogBoxData data;
final Function giveSelectFunction;
final Function giveDeselectFunction;
final bool isChecked;
final Function checkChangeCallback;
final int listIndex;
LockTimelogBox({this.data, this.giveSelectFunction, this.giveDeselectFunction, this.isChecked, this.checkChangeCallback, this.listIndex});
@override
TimeLogBoxState createState() => TimeLogBoxState();
}
class TimeLogBoxState extends State<LockTimelogBox> {
double displayWidth = 1;
double displayHeight = 1;
double boxRowFontsizeFactor;
bool isChecked;
@override
void initState() {
initValues();
widget.giveSelectFunction(select);
widget.giveDeselectFunction(deselect);
super.initState();
}
@override
Widget build(BuildContext context) {
setDisplayDimensions();
return Container(
height: displayHeight * 0.2,
width: displayWidth,
decoration: BoxDecoration(
gradient: LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Color(0xFF13313b), Color(0xFF11131a)]),
border: Border(bottom: BorderSide(width: displayHeight * 0.0005, color: Color(0x77FFFFFF)))),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: onBoxTap,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
children: [buildCheckBoxContainer(), buildInfoContainer()],
),
),
),
);
}
buildCheckBoxContainer() {
return Expanded(
flex: 10,
child: Container(
alignment: Alignment.center,
child: buildCheckbox(),
));
}
buildInfoContainer() {
return Expanded(
flex: 70,
child: Container(
padding: EdgeInsets.only(top: displayHeight * 0.005, bottom: displayHeight * 0.005, left: displayWidth * 0.01, right: displayWidth * 0.01),
child: Column(
children: [buildDateRow(), buildProjectRow(), buildWorkOrderRow(), buildTaskRow()],
),
));
}
buildCheckbox() {
return Container(
child: Theme(
data: ThemeData(primarySwatch: Colors.green, unselectedWidgetColor: Colors.grey),
child: Transform.scale(
scale: 1.5,
child: Checkbox(
value: isChecked,
onChanged: onCheckChange,
),
),
),
);
}
void onCheckChange(bool isChecked) {
widget.checkChangeCallback(widget.listIndex, isChecked);
setIsChecked(isChecked);
}
setIsChecked(bool isChecked) {
if (this.mounted) {
setState(() {
this.isChecked = isChecked;
});
} else
this.isChecked = isChecked;
}
void onBoxTap() {
onCheckChange(!isChecked);
}
buildDateRow() {
return Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
children: [
Container(
alignment: Alignment.centerLeft,
child: Icon(
Icons.access_time,
color: Colors.white,
),
),
Container(
margin: EdgeInsets.only(left: displayWidth * 0.01),
alignment: Alignment.centerLeft,
child: Text(
DateFormat("yyyy-MM-dd HH:mm").format(widget.data.timeLog.start),
style: TextStyle(fontSize: displayWidth * boxRowFontsizeFactor, color: Colors.white),
),
)
],
));
}
buildProjectRow() {
return Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
children: [
Container(
alignment: Alignment.centerLeft,
child: Icon(
Icons.work,
color: Colors.white,
),
),
Container(
margin: EdgeInsets.only(left: displayWidth * 0.01),
alignment: Alignment.centerLeft,
child: Text(
widget.data.timeLog.projectName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: displayWidth * boxRowFontsizeFactor, color: Colors.white),
),
)
],
));
}
buildWorkOrderRow() {
return Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
children: [
Container(
alignment: Alignment.centerLeft,
child: Icon(
Icons.work,
color: Colors.white,
),
),
Container(
margin: EdgeInsets.only(left: displayWidth * 0.01),
alignment: Alignment.centerLeft,
child: Text(
widget.data.workOrder.name,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: TextStyle(fontSize: displayWidth * boxRowFontsizeFactor, color: Colors.white),
),
)
],
));
}
buildTaskRow() {
return Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
children: [
Container(
alignment: Alignment.centerLeft,
child: Icon(
Icons.playlist_add_check,
color: Colors.white,
),
),
Container(
margin: EdgeInsets.only(left: displayWidth * 0.01),
alignment: Alignment.centerLeft,
child: Text(
widget.data.timeLog.projectName,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: TextStyle(fontSize: displayWidth * boxRowFontsizeFactor, color: Colors.white),
),
)
],
));
}
void initValues() {
displayWidth = 1;
displayHeight = 1;
boxRowFontsizeFactor = 0.05;
isChecked = widget.isChecked;
}
setDisplayDimensions() {
if (displayWidth == 1 || displayWidth == null) displayWidth = DisplayHelper.getDisplayWidth(context);
if (displayHeight == 1 || displayHeight == null) displayHeight = DisplayHelper.getDisplayHeight(context);
}
select() {
setIsChecked(true);
}
deselect() {
setIsChecked(false);
}
}
解决方案
你做错了什么改变了应该是不可变的东西,这是一个非常糟糕的想法和做法。
状态类专门用于处理您的状态更改,因此您应该做的是在类中声明一个状态变量,并且由于有多个复选框,初始化 aList<bool> checkBoxes
并将它们映射到使用来自的传入数据 widget.data
或为每个单独的变量如果它们不多,然后在你的CheckBox()
class TimeLogBoxState extends State<LockTimelogBox> {
...
bool isChecked = widget.data.isChecked;
buildCheckbox() {
return Container(
child: Theme(
data: ThemeData(primarySwatch: Colors.green, unselectedWidgetColor: Colors.grey),
child: Transform.scale(
scale: 1.5,
child: Checkbox(
value: isChecked,
onChanged:(val){
setState((){ isChecked = val});
},
),
),
),
);
}
类似的东西。
推荐阅读
- html - HTML整个表格行作为复选框标签
- python - 如何使用 xlwt 将 python 列表导出到 excel 文件中?
- c++ - 为什么 a++(后自增运算符)不能是左值?
- node.js - 发布:npm - 如何在每个构建上设置反应 npm 组件位置
- javascript - How to show the order number in which the checkboxes are clicked?
- gcc - 如何使用 gcc 交叉编译器构建和安装“mlibc”?
- python-3.x - 通过python中的索引获取集合的具体值
- webpack - 将文件“.gql”与 webpack 一起使用时出现什么样的 graphql 错误?
- python - 带前缀的熊猫 loc
- java - 如何保存背景可绘制对象?