Skip to content

Commit 36be580

Browse files
committed
2 parents 1c67b61 + 4e7b726 commit 36be580

File tree

3 files changed

+74
-46
lines changed

3 files changed

+74
-46
lines changed

lib/src/widgets/grouped_checkbox.dart

Lines changed: 29 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import 'package:flutter/material.dart';
22
import 'package:flutter_form_builder/flutter_form_builder.dart';
33

4-
class GroupedCheckbox<T> extends StatefulWidget {
4+
class GroupedCheckbox<T> extends StatelessWidget {
55
/// A list of string that describes each checkbox. Each item must be distinct.
66
final List<FormBuilderFieldOption<T>> options;
77

@@ -204,38 +204,22 @@ class GroupedCheckbox<T> extends StatefulWidget {
204204
this.controlAffinity = ControlAffinity.leading,
205205
}) : super(key: key);
206206

207-
@override
208-
_GroupedCheckboxState<T> createState() => _GroupedCheckboxState<T>();
209-
}
210-
211-
class _GroupedCheckboxState<T> extends State<GroupedCheckbox<T>> {
212-
final selectedListItems = <T>[];
213-
214-
@override
215-
void initState() {
216-
super.initState();
217-
218-
if (widget.value != null) {
219-
selectedListItems.addAll(widget.value!);
220-
}
221-
}
222-
223207
@override
224208
Widget build(BuildContext context) {
225209
final widgetList = <Widget>[];
226-
for (var i = 0; i < widget.options.length; i++) {
210+
for (var i = 0; i < options.length; i++) {
227211
widgetList.add(item(i));
228212
}
229213
Widget finalWidget;
230-
if (widget.orientation == OptionsOrientation.vertical) {
214+
if (orientation == OptionsOrientation.vertical) {
231215
finalWidget = SingleChildScrollView(
232216
scrollDirection: Axis.vertical,
233217
child: Column(
234218
crossAxisAlignment: CrossAxisAlignment.start,
235219
children: widgetList,
236220
),
237221
);
238-
} else if (widget.orientation == OptionsOrientation.horizontal) {
222+
} else if (orientation == OptionsOrientation.horizontal) {
239223
finalWidget = SingleChildScrollView(
240224
scrollDirection: Axis.horizontal,
241225
child: Row(
@@ -247,14 +231,14 @@ class _GroupedCheckboxState<T> extends State<GroupedCheckbox<T>> {
247231
} else {
248232
finalWidget = SingleChildScrollView(
249233
child: Wrap(
250-
spacing: widget.wrapSpacing,
251-
runSpacing: widget.wrapRunSpacing,
252-
textDirection: widget.wrapTextDirection,
253-
crossAxisAlignment: widget.wrapCrossAxisAlignment,
254-
verticalDirection: widget.wrapVerticalDirection,
255-
alignment: widget.wrapAlignment,
234+
spacing: wrapSpacing,
235+
runSpacing: wrapRunSpacing,
236+
textDirection: wrapTextDirection,
237+
crossAxisAlignment: wrapCrossAxisAlignment,
238+
verticalDirection: wrapVerticalDirection,
239+
alignment: wrapAlignment,
256240
direction: Axis.horizontal,
257-
runAlignment: widget.wrapRunAlignment,
241+
runAlignment: wrapRunAlignment,
258242
children: widgetList,
259243
),
260244
);
@@ -263,50 +247,49 @@ class _GroupedCheckboxState<T> extends State<GroupedCheckbox<T>> {
263247
}
264248

265249
Widget item(int index) {
266-
final option = widget.options[index];
250+
final option = options[index];
267251
final optionValue = option.value;
268-
final isOptionDisabled = true == widget.disabled?.contains(optionValue);
252+
final isOptionDisabled = true == disabled?.contains(optionValue);
269253
final control = Checkbox(
270-
activeColor: widget.activeColor,
271-
checkColor: widget.checkColor,
272-
focusColor: widget.focusColor,
273-
hoverColor: widget.hoverColor,
274-
materialTapTargetSize: widget.materialTapTargetSize,
275-
value: selectedListItems.contains(optionValue),
276-
tristate: widget.tristate,
254+
activeColor: activeColor,
255+
checkColor: checkColor,
256+
focusColor: focusColor,
257+
hoverColor: hoverColor,
258+
materialTapTargetSize: materialTapTargetSize,
259+
value: tristate
260+
? value?.contains(optionValue)
261+
: true == value?.contains(optionValue),
262+
tristate: tristate,
277263
onChanged: isOptionDisabled
278264
? null
279265
: (selected) {
266+
List<T> selectedListItems = value == null ? [] : List.of(value!);
280267
selected!
281268
? selectedListItems.add(optionValue)
282269
: selectedListItems.remove(optionValue);
283-
setState(() {
284-
widget.onChanged(selectedListItems);
285-
});
270+
onChanged(selectedListItems);
286271
},
287272
);
288273
final label = GestureDetector(
289274
onTap: isOptionDisabled
290275
? null
291276
: () {
277+
List<T> selectedListItems = value == null ? [] : List.of(value!);
292278
selectedListItems.contains(optionValue)
293279
? selectedListItems.remove(optionValue)
294280
: selectedListItems.add(optionValue);
295-
setState(() {
296-
widget.onChanged(selectedListItems);
297-
});
281+
onChanged(selectedListItems);
298282
},
299283
child: option,
300284
);
301285

302286
return Row(
303287
mainAxisSize: MainAxisSize.min,
304288
children: <Widget>[
305-
if (widget.controlAffinity == ControlAffinity.leading) control,
289+
if (controlAffinity == ControlAffinity.leading) control,
306290
Flexible(flex: 1, child: label),
307-
if (widget.controlAffinity == ControlAffinity.trailing) control,
308-
if (widget.separator != null && index != widget.options.length - 1)
309-
widget.separator!,
291+
if (controlAffinity == ControlAffinity.trailing) control,
292+
if (separator != null && index != options.length - 1) separator!,
310293
],
311294
);
312295
}

test/fields/form_builder_checkbox_group_test.dart

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,45 @@ void main() {
2727
expect(formSave(), isTrue);
2828
expect(formValue(widgetName), equals(const [1, 3]));
2929
});
30+
testWidgets('FormBuilderCheckboxGroup -- didChange',
31+
(WidgetTester tester) async {
32+
const fieldName = 'cbg1';
33+
final testWidget = FormBuilderCheckboxGroup<int>(
34+
name: fieldName,
35+
options: const [
36+
FormBuilderFieldOption(key: ValueKey('1'), value: 1),
37+
FormBuilderFieldOption(key: ValueKey('2'), value: 2),
38+
FormBuilderFieldOption(key: ValueKey('3'), value: 3),
39+
],
40+
);
41+
await tester.pumpWidget(buildTestableFieldWidget(testWidget));
42+
43+
expect(formSave(), isTrue);
44+
expect(formValue(fieldName), isNull);
45+
formFieldDidChange(fieldName, [1, 3]);
46+
await tester.pumpAndSettle();
47+
expect(formSave(), isTrue);
48+
expect(formValue(fieldName), [1, 3]);
49+
50+
Checkbox checkbox1 = tester
51+
.element(find.byKey(const ValueKey('1')))
52+
.findAncestorWidgetOfExactType<Row>()!
53+
.children
54+
.first as Checkbox;
55+
Checkbox checkbox2 = tester
56+
.element(find.byKey(const ValueKey('2')))
57+
.findAncestorWidgetOfExactType<Row>()!
58+
.children
59+
.first as Checkbox;
60+
Checkbox checkbox3 = tester
61+
.element(find.byKey(const ValueKey('3')))
62+
.findAncestorWidgetOfExactType<Row>()!
63+
.children
64+
.first as Checkbox;
65+
66+
// checkboxes should represent the state of the didChange value
67+
expect(checkbox1.value, true);
68+
expect(checkbox2.value, false);
69+
expect(checkbox3.value, true);
70+
});
3071
}

test/fields/form_builder_tester.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,8 @@ Widget buildTestableFieldWidget(Widget widget) {
1515
}
1616

1717
bool formSave() => _formKey.currentState!.saveAndValidate();
18+
void formFieldDidChange(String fieldName, dynamic value) {
19+
_formKey.currentState!.fields[fieldName]!.didChange(value);
20+
}
21+
1822
dynamic formValue(String name) => _formKey.currentState!.value[name];

0 commit comments

Comments
 (0)