Skip to content

Commit

Permalink
v4.0.0
Browse files Browse the repository at this point in the history
* v4.0.0

* v4.0.0
  • Loading branch information
udos86 authored Jan 13, 2022
1 parent bd7f573 commit dae5c35
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 159 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## [4.0.0] - 01/14/2022

* `FastChoiceChips` now exposes its value as `List<String>`
* `FastInputChips` can now scroll horizontally via `wrap` property
* renames `optionsMatcher` property of `FastAutocomplete` to `willAddOption`
* `FastFormFieldState<T>` is now `abstract` and its widget getter `@protected`

## [3.0.0] - 01/03/2022

* renames `id` property of `FastFormField` to `name`
Expand Down
97 changes: 95 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ It adds these missing pieces to the Flutter SDK to make Flutter form development

* `FormField<T>` wrappers for all [Material](https://flutter.dev/docs/development/ui/widgets/material#Input%20and%20selections) / [Cupertino](https://flutter.dev/docs/development/ui/widgets/cupertino) input widgets **according** to the already built-in [TextFormField](https://api.flutter.dev/flutter/material/TextFormField-class.html) / [DropdownButtonFormField](https://api.flutter.dev/flutter/material/DropdownButtonFormField-class.html)
* **adaptive** and highly **customizable** `FastFormControl<T>` widgets with support of [**validation states**](https://github.com/flutter/flutter/issues/18885).
* `FastForm` widget that provides current form field values `onChanged`
* `FastForm` widget that passes current form field values to `onChanged`
* `FastInputChips` widget that converts text input into chips as defined by [Material Design](https://material.io/components/chips#input-chips)
* common `FormFieldValidator<T>` functions

---
Expand Down Expand Up @@ -112,11 +113,103 @@ child: FastForm(
| `FastDatePicker` | `showDatePicker` | `CupertinoDatePicker` | no |
| `FastDateRangePicker` | `showDateRangePicker` | no | yes |
| `FastDropdown` | `DropdownButtonFormField`<br>`<String>` | no | yes |
| `FastInputChips` | `InputChip` | no | yes |
| `FastInputChips` | `Autocomplete` + `InputChip` | no | yes |
| `FastRadioGroup` | `RadioListTile` | no | yes |
| `FastRangeSlider` | `RangeSlider` | no | yes |
| `FastSegmentedControl` | no | `SlidingSegmenteControl`<br>`<String>` | no |
| `FastSlider` | `Slider.adaptive` | `CupertinoSlider` | no |
| `FastSwitch` | `SwitchListTile` | `CupertinoSwitch` | no |
| `FastTextField` | `TextFormField` | `CupertinoTextFormFieldRow` | no |
| `FastTimePicker` | `showTimePicker` | no / use `FastDatePicker`<br>with <br>`CupertinoDatePickerMode.time` | yes |


## Custom Form Field Widgets

With Flutter Fast Forms transforming any custom widget into a form field goes smoothly.

Let's assume a simple form field that provides a random integer whenever a button is pressed.

1. Create a minimal `FastFormField<T>` and a corresponding `FastFormFieldState<T>`:
```dart
class MyCustomField extends FastFormField<int> {
const MyCustomField({
Key? key,
required String name,
}) : super(
builder: _myCustomFormFieldBuilder,
key: key,
name: name,
);
@override
MyCustomFieldState createState() => MyCustomFieldState();
}
class MyCustomFieldState extends FastFormFieldState<int> {
@override
FastSimpleCustomField get widget => super.widget as FastSimpleCustomField;
}
```

2. Add optional parameters and pass them as you like:
```dart
class MyCustomField extends FastFormField<int> {
const MyCustomField({
InputDecoration? decoration,
String? helperText,
int? initialValue,
Key? key,
String? label,
required String name,
ValueChanged<int>? onChanged,
VoidCallback? onReset,
FormFieldSetter<int>? onSaved,
FormFieldValidator<int>? validator,
}) : super(
builder: _myCustomFormFieldBuilder,
helperText: helperText,
initialValue: initialValue,
key: key,
label: label,
name: name,
onChanged: onChanged,
onReset: onReset,
onSaved: onSaved,
validator: validator,
);
@override
MyCustomFieldState createState() => MyCustomFieldState();
}
class MyCustomFieldState extends FastFormFieldState<int> {
@override
FastSimpleCustomField get widget => super.widget as FastSimpleCustomField;
}
```

3. Implement the required `FormFieldBuilder<T>`:
```dart
Widget _myCustomFormFieldBuilder(FormFieldState<int> field) {
final decoration = (field as FastSimpleCustomFieldState)
.decoration
.copyWith(errorText: field.errorText);
return InputDecorator(
decoration: decoration,
child: Row(
children: [
ElevatedButton(
child: const Text('Create random number'),
onPressed: () => field.didChange(Random().nextInt(1 << 32)),
),
if (field.value is int)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: Text(field.value!.toString()),
)
],
),
);
}
```
2 changes: 1 addition & 1 deletion example/lib/custom_form_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class FastCustomField extends FastFormField<Map<String, bool>> {
final Widget? title;

@override
FormFieldState<Map<String, bool>> createState() => FastCustomFieldState();
FastCustomFieldState createState() => FastCustomFieldState();
}

class FastCustomFieldState extends FastFormFieldState<Map<String, bool>> {
Expand Down
4 changes: 1 addition & 3 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import 'package:flutter_fast_forms/flutter_fast_forms.dart';

import 'custom_form_field.dart';

void main() {
runApp(const ExampleApp());
}
void main() => runApp(const ExampleApp());

class ExampleApp extends StatelessWidget {
const ExampleApp({Key? key}) : super(key: key);
Expand Down
13 changes: 4 additions & 9 deletions lib/src/form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,13 @@ class FastFormState extends State<FastForm> {
final Set<FastFormFieldState<dynamic>> _fields = {};

UnmodifiableMapView<String, dynamic> get values {
return UnmodifiableMapView({
for (final fieldState in _fields) fieldState.widget.name: fieldState.value
});
return UnmodifiableMapView(
{for (final fieldState in _fields) fieldState.name: fieldState.value});
}

void register(FastFormFieldState field) {
_fields.add(field);
}
void register(FastFormFieldState field) => _fields.add(field);

void unregister(FastFormFieldState field) {
_fields.remove(field);
}
void unregister(FastFormFieldState field) => _fields.remove(field);

void updateValues() {
if (widget.onChanged != null) widget.onChanged!(values);
Expand Down
9 changes: 6 additions & 3 deletions lib/src/form_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ abstract class FastFormField<T> extends FormField<T> {
final VoidCallback? onReset;
}

class FastFormFieldState<T> extends FormFieldState<T> {
abstract class FastFormFieldState<T> extends FormFieldState<T> {
bool focused = false;
bool touched = false;

Expand All @@ -55,14 +55,17 @@ class FastFormFieldState<T> extends FormFieldState<T> {
FastFormScope? formScope;

@override
FastFormField<T> get widget => super.widget as FastFormField<T>;
@protected
FastFormField<T> get widget;

bool get adaptive => widget.adaptive ?? formScope?.adaptive ?? false;

String get name => widget.name;

InputDecoration get decoration {
final theme = Theme.of(context);
final decoration = widget.decoration ??
formScope?.inputDecorator.call(context, widget) ??
formScope?.inputDecorator(context, widget) ??
const InputDecoration();

return decoration.applyDefaults(theme.inputDecorationTheme);
Expand Down
3 changes: 0 additions & 3 deletions lib/src/form_scope.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ import 'package:flutter/material.dart';
import 'form.dart';
import 'form_field.dart';

typedef FastBoxDecorator = BoxDecoration Function(
BuildContext context, FastFormField field);

typedef FastInputDecorator = InputDecoration Function(
BuildContext context, FastFormField field);

Expand Down
16 changes: 8 additions & 8 deletions lib/src/options/autocomplete.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import 'package:flutter/material.dart';

import '../form_field.dart';

typedef FastOptionsMatcher<O extends Object> = bool Function(
TextEditingValue textEditingValue, O option);

typedef FastAutocompleteFieldViewBuilder<O extends Object>
= AutocompleteFieldViewBuilder Function(FastAutocompleteState<O> field);

typedef FastAutocompleteWillAddOption<O extends Object> = bool Function(
TextEditingValue textEditingValue, O option);

@immutable
class FastAutocomplete<O extends Object> extends FastFormField<String> {
FastAutocomplete({
Expand All @@ -31,9 +31,9 @@ class FastAutocomplete<O extends Object> extends FastFormField<String> {
this.onSelected,
this.options,
this.optionsBuilder,
this.optionsMatcher,
this.optionsMaxHeight = 200.00,
this.optionsViewBuilder,
this.willAddOption,
}) : assert(options != null || optionsBuilder != null),
_initialValue = initialValue,
super(
Expand All @@ -59,9 +59,9 @@ class FastAutocomplete<O extends Object> extends FastFormField<String> {
final AutocompleteOnSelected<O>? onSelected;
final Iterable<O>? options;
final AutocompleteOptionsBuilder<O>? optionsBuilder;
final FastOptionsMatcher<O>? optionsMatcher;
final double optionsMaxHeight;
final AutocompleteOptionsViewBuilder<O>? optionsViewBuilder;
final FastAutocompleteWillAddOption<O>? willAddOption;

@override
FastAutocompleteState<O> createState() => FastAutocompleteState<O>();
Expand All @@ -73,7 +73,7 @@ class FastAutocompleteState<O extends Object>
FastAutocomplete<O> get widget => super.widget as FastAutocomplete<O>;
}

bool _optionsMatcher<O extends Object>(TextEditingValue value, O option) {
bool _willAddOption<O extends Object>(TextEditingValue value, O option) {
return option.toString().toLowerCase().contains(value.text.toLowerCase());
}

Expand All @@ -83,8 +83,8 @@ AutocompleteOptionsBuilder<O> _optionsBuilder<O extends Object>(
if (value.text.isEmpty) {
return const Iterable.empty();
}
final optionsMatcher = field.widget.optionsMatcher ?? _optionsMatcher;
return options.where((O option) => optionsMatcher(value, option));
final willAddOption = field.widget.willAddOption ?? _willAddOption;
return options.where((O option) => willAddOption(value, option));
};
}

Expand Down
Loading

0 comments on commit dae5c35

Please sign in to comment.