Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor specialised variants into subclasses #80

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions glade_forms/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,36 @@
## 4.0.0

**Breaking change release**

- Specialized versions of inputs such as `IntInput` or `StringInput` were renamed to `Glade*Input`.
- Removed specialized version factories. Now specialized versions are sub-classes of GladeInput
- This removes the weird possibility to create calls such as `StringInput.intInput()` which in the end threw a runtime exception due to type mismatch.
- Renamed `valueConverter` in `create()` factory to match internal name `stringToValueConverter` which is more explicit

- **Added** `GladeDateTimeInput` - specialized GladeInput for DateTime inputs.
- **Added** `inclusive` argument for `int` validations.
- `GladeIntInput` and `GladeDateTimeInput` offer *Nullable versions to support null values
- `StringInput` does not offer a nullable version as we believe that in most cases you don't really need to differentiate between a null string and an empty string. Feel free to open an issue if you disagree.
- **Added** Add `isPositive()` and `isNegative()` to Int validator.



## 3.1.1
- Add typedefs `IntInput` and `BooleanInput`
- Fix GladeModelDebugInfo colors in DarkMode.

## 3.1.0
- updated dependencies

# 3.0.1
## 3.0.1
- **[Fix]**: GladeFormProvider is missing key property [Fix 73](https://github.com/netglade/glade_forms/issues/73)
- **[Fix]**: enable value transform with text editing controller [Fix 72](https://github.com/netglade/glade_forms/issues/72)
- **[Fix]**: Input subscribed to its own changes in onDependencyChange [Fix 76](https://github.com/netglade/glade_forms/issues/76)


## 3.0.0

**Breaking release**
**Breaking change release**

- **[Add]**: Add `allowBlank` parameter to `isEmpty` string validator.
- **[Add]**: Add `IntInput` as a specialized variant of GladeInput<int> which has additional, int related, validations such as `isBetween`, `isMin`, `isMax`
Expand Down
31 changes: 18 additions & 13 deletions glade_forms/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ A universal way to define form validators with support of translations.
- [✨ Features](#-features)
- [GladeInput](#gladeinput)
- [String based input and TextEditingController](#string-based-input-and-texteditingcontroller)
- [StringInput](#stringinput)
- [GladeStringInput](#gladestringinput)
- [GladeIntInput](#gladeintinput)
- [More specialised variants](#more-specialised-variants)
- [Validation](#validation)
- [Skipping Specific Validation](#skipping-specific-validation)
- [Using validators without GladeInput](#using-validators-without-gladeinput)
Expand Down Expand Up @@ -55,18 +57,18 @@ setup a Model class that holds glade inputs together.

```dart
class _Model extends GladeModel {
late GladeInput<String> name;
late GladeInput<int> age;
late GladeInput<String> email;
late GladeStringInput name;
late GladeIntInput age;
late GladeStringInput email;

@override
List<GladeInput<Object?>> get inputs => [name, age, email];

@override
void initialize() {
name = GladeInput.stringInput();
age = GladeInput.intInput(value: 0, useTextEditingController: true);
email = GladeInput.stringInput(validator: (validator) => (validator..isEmail()).build());
name = GladeStringInput();
age = GladeIntInput(value: 0, useTextEditingController: true);
email = GladeStringInput(validator: (validator) => (validator..isEmail()).build());

super.initialize();
}
Expand Down Expand Up @@ -127,7 +129,7 @@ On each input we can define
- **initialValue** - Initial input's value. Used with valueComparator and for computing `isUnchanged`.
- **validator** - Input's value must satisfy validation to be *valid* input.
- **translateError** - If there are validation errors, this function is use to translate those errors.
- **dependencies** (WIP) - Each input can depend on another inputs for listening changes.
- **dependencies** - Each input can depend on another inputs for listening changes.
- **stringToValueConverter** - If input is used by TextField and `T` is not a `String`, value converter should be provided.
- **valueComparator** - Sometimes it is handy to provide `initialValue` which will be never updated after input is mutated. `valueComparator` should be provided to compare `initialValue` and `value` if `T` is not comparable type by default. Note that GladeForms handle deep equality of collections and assumes that complex types are comparable by values.
- **valueTransform** - transform `T` value into different `T` value. An example of usage can be sanitazation of string input (trim(),...).
Expand All @@ -147,7 +149,7 @@ NOTE: Prior to *GladeForms 2.0*, each input generated its own TextEditingControl

----

With the introduction of *GladeForms 2.0*, inputs by default (excluding the StringInput variant), do not create a TextEditingController. As a result, developers are required to use `updateValue()`, `updateValueWithString()` or directly set the `value` (via setter) to update the input's value.
With the introduction of *GladeForms 2.0*, inputs by default (excluding the `GladeStringInput` variant), do not create a TextEditingController. As a result, developers are required to use `updateValue()`, `updateValueWithString()` or directly set the `value` (via setter) to update the input's value.

If your implementation involves an input paired with a TextField (or any similar widget that utilizes a TextEditingController), you should set `useTextEditingController` to true.

Expand All @@ -158,13 +160,13 @@ Activating the useTextEditingController mode for a GladeInput results in a few b
- Consequently, developers are advised to provide only the controller property and a validator to the widget.
- While the use of updateValue (or similar methods) and resetToPure remains possible, be aware that these actions will override the text in the controller and reset text selection and other keyboard-related features.

#### StringInput
#### GladeStringInput

StringInput is specialized variant of GladeInput<String> which has additional, string related, validations such as `isEmail`, `isUrl`, `maxLength` and more.
GladeStringInput is specialized variant of GladeInput<String> which has additional, string related, validations such as `isEmail`, `isUrl`, `maxLength` and more.

Moreover `StringInput` by default uses TextEditingController under the hood.
Moreover `GladeStringInput` by default uses TextEditingController under the hood.

#### IntInput
#### GladeIntInput

IntInput is specialized variant of GladeInput<int> which has additional, int related, validations such as `isBetween`, `isMin`, `isMax` and more.

Expand All @@ -179,6 +181,9 @@ final validator = (IntValidator()..isMax(max: 10)).build();
final result = validator.validate(5); // valid
```

#### More specialised variants
Check out also `GladeBoolInput` and `GladeDateTimeInput` specialised variants.

### Validation

Validation is defined through part methods on ValidatorFactory such as `notNull()`, `satisfy()` and other parts.
Expand Down
16 changes: 8 additions & 8 deletions glade_forms/example/lib/example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@ import 'package:glade_forms/glade_forms.dart';

// ! When updating dont forget to update README.md quickstart as well
class _Model extends GladeModel {
late StringInput name;
late IntInput age;
late StringInput email;
late IntInput income;
late GladeStringInput name;
late GladeIntInput age;
late GladeStringInput email;
late GladeIntInput income;

@override
List<GladeInput<Object?>> get inputs => [name, age, email, income];

@override
void initialize() {
name = GladeInput.stringInput(inputKey: 'name');
age = GladeInput.intInput(value: 0, inputKey: 'age');
email = GladeInput.stringInput(validator: (validator) => (validator..isEmail()).build(), inputKey: 'email');
income = GladeInput.intInput(
name = GladeStringInput(inputKey: 'name');
age = GladeIntInput(value: 0, inputKey: 'age');
email = GladeStringInput(validator: (validator) => (validator..isEmail()).build(), inputKey: 'email');
income = GladeIntInput(
value: 10000,
validator: (validator) => (validator..isMin(min: 1000)).build(),
inputKey: 'income',
Expand Down
3 changes: 2 additions & 1 deletion glade_forms/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ resolution: workspace
dependencies:
flutter:
sdk: flutter
glade_forms: ^3.1.0
glade_forms:
path: ../
22 changes: 22 additions & 0 deletions glade_forms/lib/src/converters/glade_type_converters.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@ abstract final class GladeTypeConverters {
converterBack: (rawInput) => rawInput.toString(),
);

static final intConverterNullable = StringToTypeConverter<int?>(
converter: (rawValue, cantConvert) {
if (rawValue == null) {
return null;
}

return int.tryParse(rawValue) ?? cantConvert('Can not convert', rawValue: rawValue);
},
converterBack: (rawInput) => rawInput?.toString(),
);

static final boolConverter = StringToTypeConverter<bool>(
converter: (rawValue, cantConvert) {
if (rawValue == null) {
Expand All @@ -33,4 +44,15 @@ abstract final class GladeTypeConverters {
},
converterBack: (rawInput) => rawInput.toIso8601String(),
);

static final dateTimeIso8601Nullable = StringToTypeConverter<DateTime?>(
converter: (rawValue, cantConvert) {
if (rawValue == null) {
return null;
}

return DateTime.tryParse(rawValue) ?? cantConvert('Can not convert', rawValue: rawValue);
},
converterBack: (rawInput) => rawInput?.toIso8601String(),
);
}
8 changes: 2 additions & 6 deletions glade_forms/lib/src/core/core.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
export 'changes_info.dart';
export 'convert_error.dart';
export 'error_translator.dart';
export 'glade_error_keys.dart';
export 'glade_input.dart';
export 'glade_input_error.dart';
export 'error/error.dart';
export 'input/input.dart';
export 'input_dependencies.dart';
export 'string_to_type_converter.dart';
export 'type_helper.dart';
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:equatable/equatable.dart';
import 'package:glade_forms/src/core/glade_input_error.dart';
import 'package:glade_forms/src/core/error/glade_input_error.dart';

/// Before validation when converer from string to prpoer type failed.
typedef OnConvertError = String Function(String? rawInput, {Object? key});
Expand Down
4 changes: 4 additions & 0 deletions glade_forms/lib/src/core/error/error.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export 'convert_error.dart';
export 'error_translator.dart';
export 'glade_error_keys.dart';
export 'glade_input_error.dart';
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// ignore_for_file: prefer-match-file-name

import 'package:glade_forms/src/core/glade_input_error.dart';
import 'package:glade_forms/src/core/error/glade_input_error.dart';
import 'package:glade_forms/src/core/input_dependencies.dart';

typedef ErrorTranslator<T> = String Function(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,11 @@ abstract final class GladeErrorKeys {
static const String valueIsNull = 'value-is-null';
static const String valueIsEmpty = 'value-is-empty';
static const String intCompareError = 'int-compare-error';
static const String intCompareMaxError = 'int-compare-max-error';
static const String intCompareMinError = 'int-compare-min-error';
static const String intCompareIsPositiveError = 'int-compare-ispositive-error';
static const String intCompareIsNegativeError = 'int-compare-isnegative-error';
static const String dateTimeIsBetweenError = 'datetime-compare-isbetween-error';
static const String dateTimeIsAfterError = 'datetime-compare-isafter-error';
static const String dateTimeIsBeforeError = 'datetime-compare-isbefore-error';
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:glade_forms/src/core/convert_error.dart';
import 'package:glade_forms/src/core/glade_error_keys.dart';
import 'package:glade_forms/src/core/error/convert_error.dart';
import 'package:glade_forms/src/core/error/glade_error_keys.dart';
import 'package:glade_forms/src/validator/validator.dart';

abstract class GladeInputError<T> {
Expand Down
28 changes: 28 additions & 0 deletions glade_forms/lib/src/core/input/glade_bool_input.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import 'package:glade_forms/glade_forms.dart';

class GladeBoolInput extends GladeInput<bool> {
GladeBoolInput({
super.inputKey,
super.value,
super.initialValue,
ValidatorFactory<bool>? validator,
super.isPure,
super.translateError,
super.valueComparator,
StringToTypeConverter<bool>? stringToValueConverter,
super.dependencies,
super.onChange,
super.onDependencyChange,
super.textEditingController,
super.useTextEditingController = false,
super.valueTransform,
super.defaultTranslations,
super.trackUnchanged = true,
bool isRequired = true,
}) : super.internalCreate(
stringToValueConverter: stringToValueConverter ?? GladeTypeConverters.boolConverter,
validatorInstance: isRequired
? (validator?.call(GladeValidator<bool>()..notNull()) ?? (GladeValidator<bool>()..notNull()).build())
: (validator?.call(GladeValidator()) ?? GladeValidator<bool>().build()),
);
}
61 changes: 61 additions & 0 deletions glade_forms/lib/src/core/input/glade_date_time_input.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// ignore_for_file: prefer-single-declaration-per-file

import 'package:glade_forms/src/converters/converters.dart';
import 'package:glade_forms/src/core/input/glade_input.dart';
import 'package:glade_forms/src/core/string_to_type_converter.dart';
import 'package:glade_forms/src/validator/specialized/date_time_validator.dart';

class GladeDateTimeInput extends GladeInput<DateTime> {
GladeDateTimeInput({
super.inputKey,
super.value,
super.initialValue,
DateTimeValidatorFactory? validator,
super.isPure,
super.translateError,
super.valueComparator,
StringToTypeConverter<DateTime>? stringToValueConverter,
super.dependencies,
super.onChange,
super.onDependencyChange,
super.textEditingController,
super.useTextEditingController = false,
super.valueTransform,
super.defaultTranslations,
super.trackUnchanged = true,
bool isRequired = true,
}) : super.internalCreate(
stringToValueConverter: stringToValueConverter ?? GladeTypeConverters.dateTimeIso8601,
validatorInstance: isRequired
? (validator?.call(DateTimeValidator()..notNull()) ?? (DateTimeValidator()..notNull()).build())
: (validator?.call(DateTimeValidator()) ?? DateTimeValidator().build()),
);
}

class GladeDateTimeInputNullable extends GladeInput<DateTime?> {
GladeDateTimeInputNullable({
super.inputKey,
super.value,
super.initialValue,
DateTimeValidatorFactoryNullable? validator,
super.isPure,
super.translateError,
super.valueComparator,
StringToTypeConverter<DateTime?>? stringToValueConverter,
super.dependencies,
super.onChange,
super.onDependencyChange,
super.textEditingController,
super.useTextEditingController = false,
super.valueTransform,
super.defaultTranslations,
super.trackUnchanged = true,
bool isRquired = false,
}) : super.internalCreate(
stringToValueConverter: stringToValueConverter ?? GladeTypeConverters.dateTimeIso8601Nullable,
validatorInstance: isRquired
? (validator?.call(DateTimeValidatorNullable()..notNull()) ??
(DateTimeValidatorNullable()..notNull()).build())
: (validator?.call(DateTimeValidatorNullable()) ?? DateTimeValidatorNullable().build()),
);
}
Loading