ListField<T extends AbstractModel<ID>, B extends AbstractBuilder<T, ID>, ID> constructor

ListField<T extends AbstractModel<ID>, B extends AbstractBuilder<T, ID>, ID>({
  1. required List<T> initialValue,
  2. required B builder,
  3. required Widget routeAddBuilder(
    1. BuildContext context,
    2. B builder
    ),
  4. dynamic routeEditBuilder(
    1. BuildContext context,
    2. T model,
    3. B builder, {
    4. required bool edit,
    })?,
  5. void onSaved(
    1. List<T> value
    )?,
  6. String? validator(
    1. List<T> value
    )?,
  7. void onChanged(
    1. List<T> value
    )?,
  8. bool enabled = true,
  9. AutovalidateMode autoValidateMode = AutovalidateMode.disabled,
  10. Future<bool> beforeAdd(
    1. BuildContext context
    )?,
  11. Future<bool> beforeEdit(
    1. BuildContext context,
    2. int index,
    3. T model
    )?,
  12. Future<bool> beforeDelete(
    1. BuildContext context,
    2. int index,
    3. T model
    )?,
  13. String addText = 'Adicionar %s',
  14. String removeText = 'Deseja remover %s?',
  15. String clearText = 'Deseja remover todos itens da lista?',
  16. String emptyListText = 'Sem %s até o momento.',
  17. InputDecoration? decoration,
  18. EdgeInsets padding = const EdgeInsets.all(8),
  19. int listSort(
    1. T a,
    2. T b
    )?,
  20. bool expandable = false,
  21. bool initialExpanded = true,
  22. bool showClearAllButton = false,
  23. Icon clearAllIcon = const Icon(FontAwesomeIcons.solidTrashCan),
  24. Widget onCollapsed(
    1. BuildContext context,
    2. List<T> value
    )?,
  25. bool showCounter = false,
  26. bool showTopAddButton = false,
  27. Icon topAddIcon = const Icon(FontAwesomeIcons.plus),
  28. bool showDeleteButton = true,
  29. Icon deleteIcon = const Icon(FontAwesomeIcons.trashCan),
  30. bool showAddButton = true,
  31. String? hintText,
  32. EdgeInsets? contentPadding,
  33. Widget? prefix,
  34. Widget? prefixIcon,
  35. Widget? suffix,
  36. Widget? suffixIcon,
  37. bool sortable = false,
  38. int? sizeExtraSmall,
  39. int? sizeSmall,
  40. int? sizeMedium,
  41. int? sizeLarge,
  42. int? sizeExtraLarge,
  43. double? minHeight,
  44. Key? key,
})

Implementation

ListField({
  required List<T> super.initialValue,
  required B builder,
  required Widget Function(BuildContext context, B builder) routeAddBuilder,
  Function(BuildContext context, T model, B builder, {required bool edit})?
      routeEditBuilder,
  void Function(List<T> value)? onSaved,
  String? Function(List<T> value)? validator,
  void Function(List<T> value)? onChanged,
  super.enabled,
  AutovalidateMode autoValidateMode = AutovalidateMode.disabled,
  Future<bool> Function(BuildContext context)? beforeAdd,
  Future<bool> Function(BuildContext context, int index, T model)? beforeEdit,
  Future<bool> Function(BuildContext context, int index, T model)?
      beforeDelete,
  String addText = 'Adicionar %s',
  String removeText = 'Deseja remover %s?',
  String clearText = 'Deseja remover todos itens da lista?',
  String emptyListText = 'Sem %s até o momento.',
  InputDecoration? decoration,
  EdgeInsets padding = const EdgeInsets.all(8),
  int Function(T a, T b)? listSort,
  bool expandable = false,
  bool initialExpanded = true,
  bool showClearAllButton = false,
  Icon clearAllIcon = const Icon(FontAwesomeIcons.solidTrashCan),
  Widget Function(BuildContext context, List<T> value)? onCollapsed,
  bool showCounter = false,
  bool showTopAddButton = false,
  Icon topAddIcon = const Icon(FontAwesomeIcons.plus),
  bool showDeleteButton = true,
  Icon deleteIcon = const Icon(FontAwesomeIcons.trashCan),
  bool showAddButton = true,
  String? hintText,
  EdgeInsets? contentPadding,
  Widget? prefix,
  Widget? prefixIcon,
  Widget? suffix,
  Widget? suffixIcon,
  bool sortable = false,
  super.sizeExtraSmall,
  super.sizeSmall,
  super.sizeMedium,
  super.sizeLarge,
  super.sizeExtraLarge,
  super.minHeight,
  super.key,
}) : super(
        onSaved: enabled && onSaved != null
            ? (List<T>? value) => onSaved(value!)
            : null,
        validator: enabled && validator != null
            ? (List<T>? value) => validator(value!)
            : null,
        autovalidateMode: autoValidateMode,
        builder: (FormFieldState<List<T>> field) {
          InputDecoration effectiveDecoration = (decoration ??
                  InputDecoration(
                    labelText: builder.superPlural(field.context),
                    border: const OutlineInputBorder(),
                    counterText: '',
                    enabled: enabled,
                    errorText: field.errorText,
                    hintText: hintText,
                    contentPadding: contentPadding,
                    prefix: prefix,
                    prefixIcon: prefixIcon,
                    suffix: suffix,
                    suffixIcon: suffixIcon,
                  ))
              .applyDefaults(Theme.of(field.context).inputDecorationTheme);

          String emptyText = sprintf(
            emptyListText,
            <dynamic>[builder.superPlural(field.context)],
          );

          Future<void> add() async {
            if (beforeAdd != null) {
              bool go = await beforeAdd(field.context);
              if (!go) {
                return;
              }
            }

            dynamic selected = await Navigator.of(field.context).push(
              MaterialPageRoute<dynamic>(
                builder: (BuildContext context) =>
                    routeAddBuilder(context, builder),
              ),
            );

            if (selected != null) {
              bool changed = false;

              if (selected is List) {
                for (final T item in selected) {
                  if (item.id == null ||
                      !field.value!
                          .any((T element) => element.id == item.id)) {
                    field.value!.add(item);
                    changed = true;
                  }
                }
              } else {
                if ((selected as AbstractModel<ID>).id == null ||
                    !field.value!
                        .any((T element) => element.id == selected.id)) {
                  field.value!.add(selected as T);
                  changed = true;
                }
              }

              if (changed) {
                if (listSort != null && !sortable) {
                  field.value?.sort(listSort);
                }

                onChanged?.call(field.value!);

                field.didChange(field.value);
              }
            }
          }

          return FieldGroup(
            decoration: effectiveDecoration.copyWith(
              errorText: enabled ? field.errorText : null,
              enabled: enabled,
            ),
            padding: padding,
            children: <Widget>[
              ExpandableNotifier(
                initialExpanded: expandable ? initialExpanded : !expandable,
                child: Column(
                  children: <Widget>[
                    /// Top Bar
                    if (showCounter ||
                        showTopAddButton ||
                        expandable ||
                        showClearAllButton)
                      Padding(
                        padding: const EdgeInsets.fromLTRB(16, 0, 24, 0),
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                          children: <Widget>[
                            /// Left
                            if (showCounter || showTopAddButton)
                              Row(
                                children: <Widget>[
                                  /// Top Add Button
                                  if (showTopAddButton)
                                    IconButton(
                                      onPressed: add,
                                      icon: topAddIcon,
                                    ),

                                  /// Counter
                                  if (showCounter)
                                    Chip(
                                      label: Text(
                                        field.value!.length.toString(),
                                      ),
                                    ),
                                ],
                              ),

                            /// Right
                            Row(
                              children: <Widget>[
                                /// Expand Button
                                if (expandable)
                                  ExpandableButton(
                                    child: ExpandableIcon(
                                      theme: ExpandableThemeData(
                                        iconColor: Theme.of(field.context)
                                            .iconTheme
                                            .color,
                                        collapseIcon:
                                            FontAwesomeIcons.compress,
                                        expandIcon: FontAwesomeIcons.expand,
                                        iconSize: 24,
                                        iconPadding: const EdgeInsets.all(8),
                                      ),
                                    ),
                                  ),

                                /// Clear All Button
                                if (showClearAllButton)
                                  IconButton(
                                    onPressed: field.value!.isEmpty
                                        ? null
                                        : () {
                                            FollyDialogs.yesNoDialog(
                                              context: field.context,
                                              message: sprintf(
                                                clearText,
                                                <dynamic>[
                                                  builder.superSingle(
                                                    field.context,
                                                  ),
                                                ],
                                              ),
                                            ).then(
                                              (bool del) {
                                                if (del) {
                                                  field.value!.clear();
                                                  onChanged
                                                      ?.call(field.value!);
                                                  field
                                                      .didChange(field.value);
                                                }
                                              },
                                            );
                                          },
                                    icon: clearAllIcon,
                                  ),
                              ],
                            ),
                          ],
                        ),
                      ),
                    Expandable(
                      collapsed: field.value!.isEmpty
                          ? Padding(
                              padding: const EdgeInsets.only(bottom: 24),
                              child: Text(emptyText),
                            )
                          : onCollapsed == null
                              ? Padding(
                                  padding: const EdgeInsets.symmetric(
                                    vertical: 16,
                                    horizontal: 32,
                                  ),
                                  child: Text(
                                    field.value!.join(' - '),
                                    maxLines: 2,
                                    overflow: TextOverflow.ellipsis,
                                  ),
                                )
                              : onCollapsed(field.context, field.value!),
                      expanded: Column(
                        children: <Widget>[
                          if (field.value!.isEmpty)
                            Padding(
                              padding: const EdgeInsets.only(bottom: 16),
                              child: Text(emptyText),
                            )
                          else
                            ...field.value!.asMap().entries.map(
                                  (MapEntry<int, T> entry) =>
                                      _MyListTile<T, B, ID>(
                                    field: field,
                                    index: entry.key,
                                    model: entry.value,
                                    builder: builder,
                                    removeText: removeText,
                                    enabled: enabled,
                                    beforeEdit: beforeEdit,
                                    routeEditBuilder: routeEditBuilder,
                                    beforeDelete: beforeDelete,
                                    listSort: listSort,
                                    onChanged: onChanged,
                                    showDeleteButton: showDeleteButton,
                                    deleteIcon: deleteIcon,
                                    sortable: sortable,
                                  ),
                                ),

                          /// Add Button
                          if (showAddButton)
                            TableButton(
                              enabled: enabled,
                              iconData: FontAwesomeIcons.plus,
                              label: sprintf(
                                addText,
                                <dynamic>[
                                  builder.superSingle(field.context),
                                ],
                              ).toUpperCase(),
                              onPressed: add,
                            ),
                        ],
                      ),
                    ),
                  ],
                ),
              ),
            ],
          );
        },
      );