I ahve a parent Widget called LockTimelogsList
, which contains a ListView
of LockTimelogBox
. Each child widget/LockTimelogBox
contains a Checkbox
I need to update from the parent LockTimelogsList
. When the user presses "Select all" or "Deselect all", LockTimelogsList
needs to iterate though the LockTimelogBoxes
and set the checkbox inside each to true/false.
The trouble I'm having is that iterating the listed child widgets and changing the and updating their checkbox bool value, the checkbox does not update it's view, even after calling setState
.
I've tried two approaches:
- Changing the checkbox bool value directly from the parent within a setState call.
- Calling a function inside each child widget to change the checkbox value from within the child itself.
The parent widget code listing the child widgets:
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;
@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>();
var boxes = boxDatas.map((boxData) => buildTimelogBox(boxData)).toList();
timelogBoxes = boxes;
widgets.addAll(boxes);
return widgets;
}
LockTimelogBox buildTimelogBox(TimelogBoxData boxData) {
var box = new LockTimelogBox(data: boxData, giveSelectFunction: addSelectFunction, giveDeselectFunction: addDeselectFunction);
return box;
}
void initValues() {
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(() {
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() {
setSelectAllOption(!selectAllOptionActive);
if (selectAllOptionActive)
selectAll();
else
deselectAll();
}
setSelectAllOption(bool selectAllActive) {
setState(() {
selectAllOptionActive = selectAllActive;
});
}
void selectAll() {
print(selectFunctions);
// Call functions passed up from children for changing their Checkbox bool value.
selectFunctions.forEach((func) {
func();
});
// First approach:
// timelogBoxes.forEach((box) {
// First approach:
// timelogBoxes.forEach((box) {
// setState(() {
// box.data.isChecked = true;
// });
// });
}
void deselectAll() {
print(selectFunctions);
// Call functions passed up from children for changing their Checkbox bool value.
deselectFunctions.forEach((func) {
func();
});
// First approach:
// timelogBoxes.forEach((box) {
// First approach:
// timelogBoxes.forEach((box) {
// setState(() {
// box.data.isChecked = false;
// });
// });
}
addSelectFunction(Function f) {
selectFunctions.add(f);
}
addDeselectFunction(Function f) {
deselectFunctions.add(f);
}
}
The child widget code containing the Checkbox
:
class LockTimelogBox extends StatefulWidget {
final TimelogBoxData data;
final Function giveSelectFunction;
final Function giveDeselectFunction;
LockTimelogBox({this.data, this.giveSelectFunction, this.giveDeselectFunction});
@override
TimeLogBoxState createState() => TimeLogBoxState();
}
class TimeLogBoxState extends State<LockTimelogBox> {
double displayWidth = 1;
double displayHeight = 1;
double boxRowFontsizeFactor;
@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: widget.data.isChecked,
onChanged: onCheckChange,
),
),
),
);
}
void onCheckChange(bool isChecked) {
setIsChecked(isChecked);
}
setIsChecked(bool isChecked) {
setState(() {
widget.data.isChecked = isChecked;
});
}
void onBoxTap() {
setIsChecked(!widget.data.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;
}
setDisplayDimensions() {
if (displayWidth == 1 || displayWidth == null) displayWidth = DisplayHelper.getDisplayWidth(context);
if (displayHeight == 1 || displayHeight == null) displayHeight = DisplayHelper.getDisplayHeight(context);
}
select() {
onCheckChange(true);
// setState(() {
// widget.data.isChecked = true;
// });
}
deselect() {
onCheckChange(false);
// setState(() {
// widget.data.isChecked = false;
// });
}
}
Thanks!
Aucun commentaire:
Enregistrer un commentaire