如何在 Flutter 中 check/uncheck 有状态 child WIdget 中的复选框?
How to check/uncheck a Checkbox inside a stateful child WIdget in Flutter?
我有一个名为 LockTimelogsList
的 parent 小部件,其中包含 ListView
个 LockTimelogBox
。每个 child 小部件/LockTimelogBox
包含一个 Checkbox
我需要从 parent LockTimelogsList
更新。当用户按下“Select all”或“Deselect all”时,LockTimelogsList
需要遍历 LockTimelogBoxes
并将每个内部的复选框设置为 true/false.
我遇到的问题是迭代列出的 child 小部件并更改和更新它们的复选框 bool 值,复选框不会更新它的视图,即使在调用 setState
之后也是如此。
我试过两种方法:
- 直接从 setState 调用中的 parent 更改复选框布尔值。
- 在每个 child 小部件中调用一个函数来更改 child 本身内部的复选框值。
更新
现在代码可以工作了。它正确更新所有复选框。查看已接受的答案,以防有人阅读本文时犯了与我相同的错误。我将代码更新为现在可以工作。您可以在下面看到工作代码。
[![在此处输入图片描述][2]][2]
当前parent(列表)代码:
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);
});
}
}
当前child(箱)代码:
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);
}
}
你做错的是改变了一些不可变的东西,这是一个非常糟糕的想法和做法。
状态 class 专门用于处理您的状态更改,因此您应该做的是在 class 内声明一个状态变量,并且由于有多个复选框,初始化一个 List<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});
},
),
),
),
);
}
类似的东西。
我有一个名为 LockTimelogsList
的 parent 小部件,其中包含 ListView
个 LockTimelogBox
。每个 child 小部件/LockTimelogBox
包含一个 Checkbox
我需要从 parent LockTimelogsList
更新。当用户按下“Select all”或“Deselect all”时,LockTimelogsList
需要遍历 LockTimelogBoxes
并将每个内部的复选框设置为 true/false.
我遇到的问题是迭代列出的 child 小部件并更改和更新它们的复选框 bool 值,复选框不会更新它的视图,即使在调用 setState
之后也是如此。
我试过两种方法:
- 直接从 setState 调用中的 parent 更改复选框布尔值。
- 在每个 child 小部件中调用一个函数来更改 child 本身内部的复选框值。
更新
现在代码可以工作了。它正确更新所有复选框。查看已接受的答案,以防有人阅读本文时犯了与我相同的错误。我将代码更新为现在可以工作。您可以在下面看到工作代码。
[![在此处输入图片描述][2]][2]
当前parent(列表)代码:
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);
});
}
}
当前child(箱)代码:
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);
}
}
你做错的是改变了一些不可变的东西,这是一个非常糟糕的想法和做法。
状态 class 专门用于处理您的状态更改,因此您应该做的是在 class 内声明一个状态变量,并且由于有多个复选框,初始化一个 List<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});
},
),
),
),
);
}
类似的东西。