TableField<T> constructor

TableField<T>({
  1. required List<T> initialValue,
  2. required String plural,
  3. required String single,
  4. required T create(),
  5. required List<Responsive> buildRow(
    1. BuildContext context,
    2. T row,
    3. int index,
    4. List<T> data, {
    5. required dynamic didChange(
      1. List<T> value
      ),
    6. required bool enabled,
    7. required bool isCard,
    }),
  6. List<Widget> columnHeaders = const <Widget>[],
  7. List<int>? columnsFlex,
  8. Future<bool> beforeAdd(
    1. BuildContext context,
    2. List<T> data
    )?,
  9. Future<bool> removeRow(
    1. BuildContext context,
    2. T row,
    3. int index,
    4. List<T> data,
    )?,
  10. String? validator(
    1. List<T>
    )?,
  11. void onSaved(
    1. List<T>
    )?,
  12. bool enabled = true,
  13. bool showAddButton = true,
  14. bool showTopAddButton = true,
  15. bool withDivider = true,
  16. AutovalidateMode autoValidateMode = AutovalidateMode.disabled,
  17. Widget buildFooter(
    1. BuildContext context,
    2. List<T> data, {
    3. required bool isCard,
    })?,
  18. InputDecoration? decoration,
  19. EdgeInsets padding = const EdgeInsets.all(8),
  20. int? sizeExtraSmall,
  21. int? sizeSmall,
  22. int? sizeMedium,
  23. int? sizeLarge,
  24. int? sizeExtraLarge,
  25. double? minHeight,
  26. ResponsiveSize? changeToCard,
  27. double? cardElevation,
  28. Color? cardColor,
  29. String emptyListText = 'Sem %s até o momento.',
  30. String removeText = 'Remover %s',
  31. String addText = 'Adicionar %s',
  32. Key? key,
})

Implementation

TableField({
  required List<T> super.initialValue,
  required String plural,
  required String single,
  required T Function() create,
  required List<Responsive> Function(
    BuildContext context,
    T row,
    int index,
    List<T> data, {
    required bool isCard,
    required bool enabled,
    required Function(List<T> value) didChange,
  }) buildRow,
  List<Widget> columnHeaders = const <Widget>[],
  List<int>? columnsFlex,
  Future<bool> Function(BuildContext context, List<T> data)? beforeAdd,
  Future<bool> Function(BuildContext context, T row, int index, List<T> data)?
      removeRow,
  String? Function(List<T>)? validator,
  void Function(List<T>)? onSaved,
  super.enabled,
  bool showAddButton = true,
  bool showTopAddButton = true,
  bool withDivider = true,
  AutovalidateMode autoValidateMode = AutovalidateMode.disabled,
  Widget Function(
    BuildContext context,
    List<T> data, {
    required bool isCard,
  })? buildFooter,
  InputDecoration? decoration,
  EdgeInsets padding = const EdgeInsets.all(8),
  super.sizeExtraSmall,
  super.sizeSmall,
  super.sizeMedium,
  super.sizeLarge,
  super.sizeExtraLarge,
  super.minHeight,
  ResponsiveSize? changeToCard,
  double? cardElevation,
  Color? cardColor,
  String emptyListText = 'Sem %s até o momento.',
  String removeText = 'Remover %s',
  String addText = 'Adicionar %s',
  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: plural,
                    border: const OutlineInputBorder(),
                    counterText: '',
                  ))
              .applyDefaults(Theme.of(field.context).inputDecorationTheme);

          return FieldGroup(
            padding: padding,
            decoration: effectiveDecoration.copyWith(
              errorText: enabled ? field.errorText : null,
              enabled: enabled,
            ),
            children: <Widget>[
              /// Empty
              if (field.value?.isEmpty ?? true)
                SizedBox(
                  height: 75,
                  child: Center(
                    child: Text(sprintf(emptyListText, <dynamic>[plural])),
                  ),
                )

              /// Table
              else
                ResponsiveBuilder(
                  builder: (
                    BuildContext context,
                    ResponsiveSize responsiveSize,
                  ) {
                    bool isCard = changeToCard != null &&
                        responsiveSize <= changeToCard;

                    List<Widget> columnData = <Widget>[];

                    /// Card
                    if (isCard) {
                      /// Card data
                      for (final MapEntry<int, T>(:int key, :T value)
                          in field.value!.asMap().entries) {
                        if (withDivider || key == 0) {
                          columnData.add(FollyDivider(enabled: enabled));
                        }

                        columnData.add(
                          Padding(
                            padding: const EdgeInsets.symmetric(vertical: 8),
                            child: Card(
                              color: cardColor,
                              elevation: cardElevation,
                              child: ResponsiveGrid(
                                margin: const EdgeInsets.all(4),
                                children: <Responsive>[
                                  /// Fields
                                  ...buildRow(
                                    field.context,
                                    value,
                                    key,
                                    field.value!,
                                    isCard: isCard,
                                    enabled: enabled,
                                    didChange: field.didChange,
                                  ),

                                  /// Delete button
                                  if (removeRow != null)
                                    TableButton(
                                      enabled: enabled,
                                      iconData: FontAwesomeIcons.trashCan,
                                      padding: const EdgeInsets.all(8),
                                      label: sprintf(
                                        removeText,
                                        <dynamic>[single],
                                      ).toUpperCase(),
                                      onPressed: () async {
                                        bool go = await removeRow(
                                          field.context,
                                          value,
                                          key,
                                          field.value!,
                                        );
                                        if (!go) {
                                          return;
                                        }
                                        field.value!.removeAt(key);
                                        field.didChange(field.value);
                                      },
                                      sizeMedium: 12,
                                    ),
                                ],
                              ),
                            ),
                          ),
                        );
                      }

                      /// Table
                    } else {
                      /// Header
                      if (columnHeaders.isNotEmpty) {
                        columnData.add(
                          Row(
                            children: <Widget>[
                              /// Columns Names
                              ...columnHeaders.asMap().entries.map<Widget>(
                                (MapEntry<int, Widget> entry) {
                                  int flex = columnsFlex?[entry.key] ?? 1;

                                  if (flex < 1) {
                                    return entry.value;
                                  }

                                  return Flexible(
                                    flex: flex,
                                    child: SizedBox(
                                      width: double.infinity,
                                      child: entry.value,
                                    ),
                                  );
                                },
                              ),

                              /// Empty column to delete button
                              if (removeRow != null)
                                showTopAddButton
                                    ? TableIconButton(
                                        enabled: enabled,
                                        iconData: FontAwesomeIcons.plus,
                                        tooltip: sprintf(
                                          addText,
                                          <dynamic>[single],
                                        ),
                                        onPressed: () async {
                                          if (beforeAdd != null) {
                                            bool go = await beforeAdd(
                                              field.context,
                                              field.value!,
                                            );
                                            if (!go) {
                                              return;
                                            }
                                          }

                                          field.value!.insert(0, create());
                                          field.didChange(field.value);
                                        },
                                      )
                                    : const EmptyButton(),
                            ],
                          ),
                        );
                      }

                      /// Table data
                      for (final MapEntry<int, T>(:int key, :T value)
                          in field.value!.asMap().entries) {
                        columnData.addAll(
                          <Widget>[
                            /// Divider
                            if (withDivider ||
                                (key == 0 && columnHeaders.isNotEmpty))
                              FollyDivider(enabled: enabled),

                            /// Row
                            Row(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: <Widget>[
                                /// Cells
                                ...buildRow(
                                  field.context,
                                  value,
                                  key,
                                  field.value!,
                                  isCard: isCard,
                                  enabled: enabled,
                                  didChange: field.didChange,
                                ).asMap().entries.map<Widget>(
                                  (MapEntry<int, Widget> entry) {
                                    int flex = columnsFlex?[entry.key] ?? 1;

                                    if (flex < 1) {
                                      return entry.value;
                                    }

                                    return Flexible(
                                      flex: flex,
                                      child: SizedBox(
                                        width: double.infinity,
                                        child: entry.value,
                                      ),
                                    );
                                  },
                                ),

                                /// Delete button
                                if (removeRow != null)
                                  TableIconButton(
                                    enabled: enabled,
                                    iconData: FontAwesomeIcons.trashCan,
                                    tooltip: sprintf(
                                      removeText,
                                      <dynamic>[single],
                                    ),
                                    onPressed: () async {
                                      bool go = await removeRow(
                                        field.context,
                                        value,
                                        key,
                                        field.value!,
                                      );
                                      if (!go) {
                                        return;
                                      }
                                      field.value!.removeAt(key);
                                      field.didChange(field.value);
                                    },
                                  ),
                              ],
                            ),
                          ],
                        );
                      }
                    }

                    /// Footer
                    if (buildFooter != null) {
                      columnData.add(
                        buildFooter(
                          field.context,
                          field.value!,
                          isCard: isCard,
                        ),
                      );
                    }

                    return Column(children: columnData);
                  },
                ),

              /// Add button
              if (showAddButton)
                TableButton(
                  enabled: enabled,
                  iconData: FontAwesomeIcons.plus,
                  label: sprintf(addText, <dynamic>[single]).toUpperCase(),
                  onPressed: () async {
                    if (beforeAdd != null) {
                      bool go = await beforeAdd(field.context, field.value!);
                      if (!go) {
                        return;
                      }
                    }

                    field.value!.add(create());
                    field.didChange(field.value);
                  },
                ),
            ],
          );
        },
      );