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

juancastillo0 / json_form / 14147619926

29 Mar 2025 04:54PM UTC coverage: 81.034% (+0.4%) from 80.605%
14147619926

push

github

juancastillo0
fix example rendering test

1739 of 2146 relevant lines covered (81.03%)

1.32 hits per line

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

92.09
/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/models.dart';
8

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

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

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

29
  @override
1✔
30
  String get idKey => formValue.idKey;
2✔
31

32
  bool get isCheckboxes => schemaArray.uiSchema.widget == 'checkboxes';
5✔
33
  List<Object?>? _initialValue;
34

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

47
  @override
1✔
48
  void dispose() {
49
    // TODO: clean up
50
    // if (schemaArray.formField == this) {
51
    //   schemaArray.formField = null;
52
    // }
53
    super.dispose();
1✔
54
  }
55

56
  @override
1✔
57
  Widget build(BuildContext context) {
58
    final widgetBuilderInherited = WidgetBuilderInherited.of(context);
1✔
59
    final uiConfig = widgetBuilderInherited.uiConfig;
1✔
60

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

111
              int _index = 0;
112
              final items = formValue.children.map((item) {
4✔
113
                final index = _index++;
1✔
114
                final idKey = JsonFormKeyPath.appendId(this.idKey, item.id);
3✔
115

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

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

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

243
  void _addItem() {
1✔
244
    setState(() {
2✔
245
      formValue.addArrayChild(null);
2✔
246
    });
247
  }
248

249
  void _removeItem(int index) {
1✔
250
    setState(() {
2✔
251
      formValue.children.removeAt(index);
3✔
252

253
      /// cleans up the output data in the controller
254
      WidgetBuilderInherited.of(context).controller.updateDataInPlace(
4✔
255
            idKey,
1✔
256
            (a) => a is List && a.length > index ? (a..removeAt(index)) : a,
5✔
257
          );
258
    });
259
  }
260

261
  void _copyItem(int index) {
1✔
262
    setState(() {
2✔
263
      formValue.addArrayChild(null, baseValue: formValue.children[index]);
5✔
264
    });
265
  }
266

267
  void reorder(int oldIndex, int newIndex) {
×
268
    setState(() {
×
269
      final toRemove = newIndex > oldIndex ? oldIndex : oldIndex + 1;
×
270
      final array = formValue.children;
×
271
      array.insert(newIndex, array[oldIndex]);
×
272
      array.removeAt(toRemove);
×
273
      WidgetBuilderInherited.of(context).controller.updateDataInPlace(
×
274
        idKey,
×
275
        (a) {
×
276
          if (a is List) {
×
277
            a.insert(newIndex, a[oldIndex]);
×
278
            a.removeAt(toRemove);
×
279
          }
280
          return a;
281
        },
282
      );
283
    });
284
  }
285

286
  void selectCheckbox(Object? option) {
1✔
287
    setState(() {
2✔
288
      WidgetBuilderInherited.of(context).controller.updateDataInPlace(
4✔
289
        idKey,
1✔
290
        (a) {
1✔
291
          final valueList = (a as List?)?.toList() ?? [];
1✔
292
          final i = valueList.indexOf(option);
1✔
293
          if (i != -1) {
2✔
294
            valueList.removeAt(i);
1✔
295
          } else {
296
            valueList.add(option);
1✔
297
          }
298
          field.didChange(valueList);
2✔
299
          return valueList;
300
        },
301
      );
302
    });
303
  }
304

305
  @override
1✔
306
  List<Object?> get value =>
307
      isCheckboxes ? field.value! : formValue.toJson()! as List<Object?>;
5✔
308

309
  @override
310
  final focusNode = FocusNode();
311

312
  @override
1✔
313
  JsonSchemaInfo get property => schemaArray;
1✔
314

315
  @override
1✔
316
  set value(List<Object?> newValue) {
317
    WidgetBuilderInherited.get(context).controller.updateData(idKey, newValue);
5✔
318
    if (isCheckboxes) {
1✔
319
      field.didChange(newValue);
2✔
320
      formValue.value = newValue;
2✔
321
    } else {
322
      while (formValue.children.length != newValue.length) {
5✔
323
        if (formValue.children.length < newValue.length) {
5✔
324
          formValue.addArrayChild(null);
2✔
325
        } else {
326
          formValue.children.removeLast();
3✔
327
        }
328
      }
329
      for (var i = 0; i < newValue.length; i++) {
3✔
330
        final item = formValue.children[i];
3✔
331
        item.value = newValue[i];
2✔
332
        if (item.field != null) item.field!.value = newValue[i];
4✔
333
      }
334
    }
335
    setState(() {});
2✔
336
  }
337
}
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

© 2025 Coveralls, Inc