• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

juancastillo0 / json_form / 10986779271

23 Sep 2024 02:41AM UTC coverage: 78.638% (+4.3%) from 74.365%
10986779271

Pull #6

github

web-flow
Merge 984528e5e into 419da37b9
Pull Request #6: Form value in controller recursive

479 of 585 new or added lines in 27 files covered. (81.88%)

26 existing lines in 6 files now uncovered.

1594 of 2027 relevant lines covered (78.64%)

1.27 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.12
/lib/src/builder/array_schema_builder.dart
1
import 'package:flutter/material.dart';
2
import 'package:json_form/json_form.dart';
3
import 'package:json_form/src/builder/general_subtitle_widget.dart';
4
import 'package:json_form/src/builder/logic/widget_builder_logic.dart';
5
import 'package:json_form/src/builder/widget_builder.dart';
6
import 'package:json_form/src/fields/shared.dart';
7
import 'package:json_form/src/models/json_form_schema_style.dart';
8
import 'package:json_form/src/models/models.dart';
9

10
class ArraySchemaBuilder extends StatefulWidget {
11
  const ArraySchemaBuilder({
1✔
12
    super.key,
13
    required this.mainSchema,
14
    required this.schemaArray,
15
  });
16
  final Schema mainSchema;
17
  final SchemaArray schemaArray;
18

19
  @override
1✔
20
  State<ArraySchemaBuilder> createState() => _ArraySchemaBuilderState();
1✔
21
}
22

23
class _ArraySchemaBuilderState extends State<ArraySchemaBuilder>
24
    implements JsonFormField<List<Object?>> {
25
  late FormFieldState<List<Object?>> field;
26
  late final JsonFormValue formValue;
27
  SchemaArray get schemaArray => widget.schemaArray;
3✔
28
  int lastItemId = 1;
29
  bool showItems = true;
30

31
  @override
1✔
32
  String get idKey => formValue.idKey;
2✔
33

34
  String generateItemId() => (lastItemId++).toString();
4✔
35

36
  bool get isCheckboxes => schemaArray.uiSchema.widget == 'checkboxes';
5✔
37
  List<Object?>? _initialValue;
38

39
  @override
1✔
40
  void initState() {
41
    super.initState();
1✔
42
    formValue = PrivateJsonFormController.setField(context, schemaArray, this);
4✔
43
    formValue.value ??= [];
3✔
44
    _initialValue = formValue.value! as List;
3✔
45
    if (_initialValue!.isNotEmpty) {
2✔
46
      // update children
47
      value = _initialValue!;
2✔
48
    }
49
  }
50

51
  @override
1✔
52
  void dispose() {
53
    // TODO: clean up
54
    // if (schemaArray.formField == this) {
55
    //   schemaArray.formField = null;
56
    // }
57
    super.dispose();
1✔
58
  }
59

60
  @override
1✔
61
  Widget build(BuildContext context) {
62
    final widgetBuilderInherited = WidgetBuilderInherited.of(context);
1✔
63
    final uiConfig = widgetBuilderInherited.uiConfig;
1✔
64

65
    final widgetBuilder = FormField<List<Object?>>(
1✔
66
      validator: (_) {
1✔
67
        return uiConfig.localizedTexts
1✔
68
            .arrayPropertiesError(schemaArray.arrayProperties, value);
4✔
69
      },
70
      initialValue: _initialValue,
1✔
71
      builder: (field) {
1✔
72
        this.field = field;
1✔
73
        return Focus(
1✔
74
          focusNode: focusNode,
1✔
75
          autofocus: schemaArray.uiSchema.autofocus,
3✔
76
          child: Builder(
1✔
77
            builder: (context) {
1✔
78
              if (isCheckboxes) {
1✔
79
                final schema = schemaArray.itemsBaseSchema as SchemaProperty;
2✔
80
                final options =
81
                    schema.enumm ?? schema.numberProperties.options();
1✔
82
                int _index = 0;
83
                return Column(
1✔
84
                  crossAxisAlignment: CrossAxisAlignment.start,
85
                  children: [
1✔
86
                    GeneralSubtitle(
1✔
87
                      field: schemaArray,
1✔
88
                      mainSchema: widget.mainSchema,
2✔
89
                    ),
90
                    Wrap(
1✔
91
                      children: options.map((option) {
2✔
92
                        final index = _index++;
1✔
93
                        final title = schema.uiSchema.enumNames != null
2✔
94
                            ? schema.uiSchema.enumNames![index]
×
95
                            : option.toString();
1✔
96
                        return CheckboxListTile(
1✔
97
                          key: Key('JsonForm_item_${idKey}_$index'),
3✔
98
                          title: Text(
1✔
99
                            title,
100
                            style: uiConfig.fieldInput,
1✔
101
                          ),
102
                          value: field.value != null &&
1✔
103
                              field.value!.contains(option),
2✔
104
                          onChanged: (_) {
1✔
105
                            selectCheckbox(option);
1✔
106
                          },
107
                        );
108
                      }).toList(growable: false),
1✔
109
                    ),
110
                    if (field.hasError) CustomErrorText(text: field.errorText!),
1✔
111
                  ],
112
                );
113
              }
114

115
              int _index = 0;
116
              final items = formValue.children.map((item) {
4✔
117
                final index = _index++;
1✔
118
                final idKey = JsonFormKeyPath.appendId(this.idKey, item.id);
3✔
119

120
                return Column(
1✔
121
                  key: Key('JsonForm_item_$idKey'),
2✔
122
                  mainAxisSize: MainAxisSize.min,
123
                  crossAxisAlignment: CrossAxisAlignment.start,
124
                  children: [
1✔
125
                    Row(
1✔
126
                      mainAxisAlignment: MainAxisAlignment.end,
127
                      children: [
1✔
128
                        if (uiConfig.labelPosition == LabelPosition.table)
2✔
129
                          Text(
1✔
130
                            '${index + 1}.',
2✔
131
                            style: uiConfig.fieldLabel,
1✔
132
                          ),
133
                        const Spacer(),
1✔
134
                        const SizedBox(height: 5),
1✔
135
                        if (schemaArray.uiSchema.copyable)
3✔
136
                          uiConfig.copyItemWidget(
1✔
137
                            idKey,
138
                            () => _copyItem(index),
2✔
139
                          ),
140
                        if (schemaArray.uiSchema.removable)
3✔
141
                          uiConfig.removeItemWidget(
1✔
142
                            idKey,
143
                            () => _removeItem(index),
2✔
144
                          ),
145
                        if (schemaArray.uiSchema.orderable)
3✔
146
                          ReorderableDragStartListener(
1✔
147
                            index: index,
148
                            child: const Icon(Icons.drag_handle),
149
                          ),
150
                      ],
151
                    ),
152
                    Padding(
1✔
153
                      padding: const EdgeInsets.only(bottom: 5.0, left: 5.0),
154
                      child: WidgetBuilderInherited(
1✔
155
                        controller: widgetBuilderInherited.controller,
1✔
156
                        jsonForm: widgetBuilderInherited.jsonForm,
1✔
157
                        uiConfig: widgetBuilderInherited.uiConfig,
1✔
158
                        context: context,
159
                        child: FormFromSchemaBuilder(
1✔
160
                          mainSchema: widget.mainSchema,
2✔
161
                          formValue: item,
162
                        ),
163
                      ),
164
                    ),
165
                  ],
166
                );
167
              });
168

169
              return Column(
1✔
170
                crossAxisAlignment: CrossAxisAlignment.start,
171
                children: [
1✔
172
                  const SizedBox(width: double.infinity),
173
                  GeneralSubtitle(
1✔
174
                    field: schemaArray,
1✔
175
                    mainSchema: widget.mainSchema,
2✔
176
                    trailing: IconButton(
1✔
177
                      key: Key('JsonForm_showOrHideItems_$idKey'),
3✔
178
                      tooltip: showItems
1✔
179
                          ? uiConfig.localizedTexts.hideItems()
2✔
180
                          : uiConfig.localizedTexts.showItems(),
2✔
181
                      visualDensity: VisualDensity.compact,
182
                      onPressed: () {
1✔
183
                        setState(() {
2✔
184
                          showItems = !showItems;
2✔
185
                        });
186
                      },
187
                      icon: Row(
1✔
188
                        children: [
1✔
189
                          Text(
1✔
190
                            formValue.children.length.toString(),
4✔
191
                            style: uiConfig.subtitle,
1✔
192
                          ),
193
                          if (showItems)
1✔
194
                            const Icon(Icons.arrow_drop_up_outlined)
1✔
195
                          else
196
                            const Icon(Icons.arrow_drop_down_outlined),
1✔
197
                        ],
198
                      ),
199
                    ),
200
                  ),
201
                  if (!showItems)
1✔
202
                    const SizedBox()
1✔
203
                  else if (schemaArray.uiSchema.orderable)
3✔
204
                    ReorderableListView(
1✔
205
                      shrinkWrap: true,
206
                      buildDefaultDragHandles: false,
207
                      physics: const NeverScrollableScrollPhysics(),
208
                      onReorder: (oldIndex, newIndex) {
×
209
                        setState(() {
×
210
                          final toRemove =
211
                              newIndex > oldIndex ? oldIndex : oldIndex + 1;
×
NEW
212
                          final array = formValue.children;
×
NEW
213
                          formValue.children.insert(newIndex, array[oldIndex]);
×
NEW
214
                          array.removeAt(toRemove);
×
215
                        });
216
                      },
217
                      children: items.toList(growable: false),
1✔
218
                    )
219
                  else
220
                    ...items,
×
221
                  if (field.hasError) CustomErrorText(text: field.errorText!),
3✔
222
                ],
223
              );
224
            },
225
          ),
226
        );
227
      },
228
    );
229

230
    return FormSection(
1✔
231
      child: Column(
1✔
232
        children: [
1✔
233
          widgetBuilder,
234
          if (!schemaArray.isArrayMultipleFile() &&
2✔
235
              schemaArray.uiSchema.addable &&
3✔
236
              !isCheckboxes)
1✔
237
            Align(
1✔
238
              alignment: Alignment.centerRight,
239
              child: uiConfig.addItemWidget(formValue, _addItem),
3✔
240
            ),
241
        ],
242
      ),
243
    );
244
  }
245

246
  void _addItem() {
1✔
247
    setState(() {
2✔
248
      formValue.addArrayChild(null, generateItemId());
3✔
249
    });
250
  }
251

252
  void _removeItem(int index) {
1✔
253
    setState(() {
2✔
254
      formValue.children.removeAt(index);
3✔
255

256
      /// cleans up the output data in the controller
257
      WidgetBuilderInherited.of(context)
2✔
258
          .controller
1✔
259
          .updateDataInPlace(idKey, (a) => a);
3✔
260
    });
261
  }
262

263
  void _copyItem(int index) {
1✔
264
    setState(() {
2✔
265
      final newValue = formValue.children[index].copyWith(
4✔
266
        id: generateItemId(),
1✔
267
        parent: formValue,
1✔
268
      );
269
      formValue.children.add(newValue);
3✔
270
    });
271
  }
272

273
  void selectCheckbox(Object? option) {
1✔
274
    setState(() {
2✔
275
      WidgetBuilderInherited.of(context).controller.updateDataInPlace(
4✔
276
        idKey,
1✔
277
        (a) {
1✔
278
          final valueList = (a as List?)?.toList() ?? [];
1✔
279
          final i = valueList.indexOf(option);
1✔
280
          if (i != -1) {
2✔
281
            valueList.removeAt(i);
1✔
282
          } else {
283
            valueList.add(option);
1✔
284
          }
285
          field.didChange(valueList);
2✔
286
          return valueList;
287
        },
288
      );
289
    });
290
  }
291

292
  @override
1✔
293
  List<Object?> get value =>
294
      isCheckboxes ? field.value! : formValue.toJson()! as List<Object?>;
5✔
295

296
  @override
297
  final focusNode = FocusNode();
298

299
  @override
1✔
300
  JsonSchemaInfo get property => schemaArray;
1✔
301

302
  @override
1✔
303
  set value(List<Object?> newValue) {
304
    if (isCheckboxes) {
1✔
305
      field.didChange(newValue);
2✔
306
      formValue.value = newValue;
2✔
307
    } else {
308
      while (formValue.children.length != newValue.length) {
5✔
309
        if (formValue.children.length < newValue.length) {
5✔
310
          formValue.addArrayChild(null, generateItemId());
3✔
311
        } else {
NEW
312
          formValue.children.removeLast();
×
313
        }
314
      }
315
      for (var i = 0; i < newValue.length; i++) {
2✔
316
        final item = formValue.children[i];
3✔
317
        if (item.field == null) {
1✔
318
          if (mounted) setState(() {});
3✔
319
          break;
320
        }
NEW
321
        item.field!.value = newValue[i];
×
322
      }
323
    }
324
  }
325
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc