feat!(form): migrate to wyatt architecture

This commit is contained in:
Hugo Pointcheval 2022-11-09 22:35:41 -05:00
parent 79fdd3c837
commit b89ef3de8a
Signed by: hugo
GPG Key ID: A9E8E9615379254F
62 changed files with 1387 additions and 818 deletions

View File

@ -16,16 +16,16 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
--> -->
# Dart - Form BLoC # Flutter - Form BLoC
<p align="left"> <p align="left">
<a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis"> <a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis">
<img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" /> <img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" />
</a> </a>
<img src="https://img.shields.io/badge/SDK-Dart%20%7C%20Flutter-blue?style=flat-square" alt="SDK: Dart & Flutter" /> <img src="https://img.shields.io/badge/SDK-Flutter-blue?style=flat-square" alt="SDK: Flutter" />
</p> </p>
Form Bloc for Dart & Flutter. Form Bloc for Flutter.
## Features ## Features

View File

@ -1,4 +1,4 @@
include: package:wyatt_analysis/analysis_options.yaml include: package:wyatt_analysis/analysis_options.flutter.yaml
analyzer: analyzer:
exclude: "!example/**" exclude: "!example/**"

View File

@ -21,6 +21,6 @@
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.0</string> <string>1.0</string>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>9.0</string> <string>11.0</string>
</dict> </dict>
</plist> </plist>

View File

@ -272,7 +272,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0; IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
@ -349,7 +349,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0; IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@ -398,7 +398,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0; IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;

View File

@ -43,5 +43,7 @@
</array> </array>
<key>UIViewControllerBasedStatusBarAppearance</key> <key>UIViewControllerBasedStatusBarAppearance</key>
<false/> <false/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
</dict> </dict>
</plist> </plist>

View File

@ -18,7 +18,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:form_bloc_example/app/metadata.dart'; import 'package:form_bloc_example/app/metadata.dart';
import 'package:form_bloc_example/constants.dart'; import 'package:form_bloc_example/constants.dart';
import 'package:form_bloc_example/cubit/custom_form_cubit.dart'; import 'package:form_bloc_example/simple_custom_form_cubit/simple_custom_form_cubit.dart';
import 'package:form_bloc_example/sign_up/sign_up_page.dart'; import 'package:form_bloc_example/sign_up/sign_up_page.dart';
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
@ -29,41 +29,43 @@ class App extends StatelessWidget {
const App({Key? key}) : super(key: key); const App({Key? key}) : super(key: key);
static List<FormInput> getNormalEntries() { static List<FormInput> getNormalEntries() {
return const [ return [
FormInput(formFieldName, Name.pure(), metadata: FormInputMetadata<Metadata>(extra: blue)), FormInput(formFieldName, const Name.pure(),
FormInput(formFieldEmail, Email.pure(), metadata: FormInputMetadata<Metadata>(extra: blue)), metadata: const FormInputMetadata<Metadata>(extra: blue)),
FormInput(formFieldPhone, Phone.pure(), metadata: FormInputMetadata<Metadata>(extra: red)), FormInput(formFieldEmail, const Email.pure(),
FormInput( metadata: const FormInputMetadata<Metadata>(extra: blue)),
formFieldList, ListOption<String>.pure(defaultValue: ['checkbox3'])), FormInput(formFieldList,
FormInput(formFieldRadio, TextString.pure()), const ListOption<String>.pure(defaultValue: 'c3')),
FormInput(formFieldPro, Boolean.pure(), FormInput(formFieldRadio, const TextString.pure()),
metadata: FormInputMetadata<Metadata>(name: 'business')), FormInput(formFieldPro, const Boolean.pure(),
FormInput(formFieldHidden, Boolean.pure(), metadata: const FormInputMetadata<Metadata>(name: 'business')),
metadata: FormInputMetadata<Metadata>(export: false, extra: blue)), FormInput(formFieldHidden, const Boolean.pure(),
metadata:
const FormInputMetadata<Metadata>(export: false, extra: blue)),
]; ];
} }
static List<FormInput> getBusinessEntries() { static List<FormInput> getBusinessEntries() {
const entries = [ final entries = [
FormInput(formFieldSiren, Siren.pure()), FormInput(formFieldPhone, const Phone.pure(),
FormInput(formFieldIban, Iban.pure()), metadata: const FormInputMetadata<Metadata>(extra: red)),
]; ];
return getNormalEntries() + entries; return getNormalEntries() + entries;
} }
static FormData getNormalFormData() { static WyattForm getNormalFormData() {
return FormData(getNormalEntries()); return WyattFormImpl(getNormalEntries());
} }
static FormData getProFormData() { static WyattForm getProFormData() {
return FormData(getBusinessEntries()); return WyattFormImpl(getBusinessEntries());
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
FormDataCubit formCubit = CustomFormCubit(inputs: getNormalFormData()); SimpleCustomFormCubit formCubit = SimpleCustomFormCubit(getNormalFormData());
return BlocProvider( return BlocProvider<SimpleCustomFormCubit>(
create: (context) => formCubit, create: (context) => formCubit,
child: const WidgetTree(), child: const WidgetTree(),
); );

View File

@ -28,7 +28,5 @@ class Metadata {
}); });
@override @override
String toString() { String toString() => 'Metadata(category: $category)';
return category.toString();
}
} }

View File

@ -17,8 +17,6 @@
const String formFieldName = 'name'; const String formFieldName = 'name';
const String formFieldPhone = 'phone'; const String formFieldPhone = 'phone';
const String formFieldEmail = 'email'; const String formFieldEmail = 'email';
const String formFieldSiren = 'siren';
const String formFieldIban = 'iban';
const String formFieldList = 'list'; const String formFieldList = 'list';
const String formFieldRadio = 'radio'; const String formFieldRadio = 'radio';
const String formFieldHidden = 'hidden'; const String formFieldHidden = 'hidden';

View File

@ -14,39 +14,33 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:developer';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:form_bloc_example/app/app.dart'; import 'package:form_bloc_example/app/app.dart';
import 'package:form_bloc_example/app/metadata.dart'; import 'package:form_bloc_example/app/metadata.dart';
import 'package:form_bloc_example/constants.dart'; import 'package:form_bloc_example/constants.dart';
import 'package:form_bloc_example/simple_custom_form_cubit/simple_custom_form_cubit.dart';
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
class CategoryIndicator extends StatelessWidget { Color computeColor(Metadata? meta) {
const CategoryIndicator( switch (meta?.category) {
{Key? key, required this.field, required this.builder}) case Category.perso:
: super(key: key); return Colors.blue;
case Category.business:
final String field; return Colors.red;
final Function(BuildContext context, FormDataState state) builder; case null:
return Colors.green;
Color computeColor(Metadata meta) {
switch (meta.category) {
case Category.perso:
return Colors.blue;
case Category.business:
return Colors.red;
}
} }
}
class _NameInput extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<FormDataCubit, FormDataState>( return InputBuilderMetadata<SimpleCustomFormCubit, Metadata>(
builder: ((context, state) { field: formFieldName,
final meta = state.data.metadataOf(field).extra as Metadata; builder: (context, cubit, state, field, inputValid, metadata) {
final meta = state.form.metadataOf<Metadata>(field).extra;
final color = computeColor(meta); final color = computeColor(meta);
return Row( return Row(
children: [ children: [
Container( Container(
@ -57,58 +51,24 @@ class CategoryIndicator extends StatelessWidget {
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
), ),
const SizedBox(width: 20,), const SizedBox(
width: 20,
),
SizedBox( SizedBox(
width: MediaQuery.of(context).size.width - 45, width: MediaQuery.of(context).size.width - 45,
child: builder(context, state), child: TextField(
onChanged: (value) =>
cubit.dataChanged(field, Name.dirty(value)),
keyboardType: TextInputType.name,
decoration: InputDecoration(
labelText: 'name',
helperText: '',
errorText: !inputValid ? 'invalid name' : null,
),
),
) )
], ],
); );
}),
);
}
}
class _NameInput extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CategoryIndicator(
field: formFieldName,
builder: (context, state) {
return TextField(
onChanged: (name) => context
.read<FormDataCubit>()
.dataChanged(formFieldName, Name.dirty(name)),
keyboardType: TextInputType.name,
decoration: InputDecoration(
labelText: 'name',
helperText: '',
errorText:
state.data.isNotValid(formFieldName) ? 'invalid name' : null,
),
);
});
}
}
class _EmailInput extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CategoryIndicator(
field: formFieldEmail,
builder: (context, state) {
return TextField(
onChanged: (email) => context
.read<FormDataCubit>()
.dataChanged(formFieldEmail, Email.dirty(email)),
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
labelText: 'email',
helperText: '',
errorText:
state.data.isNotValid(formFieldEmail) ? 'invalid email' : null,
),
);
}, },
); );
} }
@ -117,67 +77,58 @@ class _EmailInput extends StatelessWidget {
class _PhoneInput extends StatelessWidget { class _PhoneInput extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CategoryIndicator( return InputBuilderMetadata<SimpleCustomFormCubit, Metadata>(
field: formFieldPhone, field: formFieldPhone,
builder: (context, state) { builder: (context, cubit, state, field, inputValid, metadata) {
return TextField( final meta = state.form.metadataOf<Metadata>(field).extra;
onChanged: (phone) => context final color = computeColor(meta);
.read<FormDataCubit>() return Row(
.dataChanged(formFieldPhone, Phone.dirty(phone)), children: [
keyboardType: TextInputType.phone, Container(
decoration: InputDecoration( height: 8,
labelText: 'phone', width: 8,
helperText: '', decoration: BoxDecoration(
errorText: color: color,
state.data.isNotValid(formFieldPhone) ? 'invalid phone' : null, borderRadius: BorderRadius.circular(8),
), ),
),
const SizedBox(
width: 20,
),
SizedBox(
width: MediaQuery.of(context).size.width - 45,
child: TextField(
onChanged: (value) =>
cubit.dataChanged(field, Phone.dirty(value)),
keyboardType: TextInputType.phone,
decoration: InputDecoration(
labelText: 'phone',
helperText: '',
errorText: !inputValid ? 'invalid phone' : null,
),
),
)
],
); );
}, },
); );
} }
} }
class _SirenInput extends StatelessWidget { class _EmailInput extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CategoryIndicator( return InputBuilder<SimpleCustomFormCubit>(
field: formFieldName, field: formFieldEmail,
builder: (context, state) { builder: (context, cubit, state, field, inputValid) => TextField(
return TextField( onChanged: (value) => cubit.dataChanged(field, Email.dirty(value)),
onChanged: (siren) => context keyboardType: TextInputType.emailAddress,
.read<FormDataCubit>() decoration: InputDecoration(
.dataChanged(formFieldSiren, Siren.dirty(siren)), labelText: 'email',
keyboardType: TextInputType.number, helperText: '',
decoration: InputDecoration( errorText: !inputValid ? 'invalid email' : null,
labelText: 'siren', ),
helperText: '', ),
errorText:
state.data.isNotValid(formFieldSiren) ? 'invalid SIREN' : null,
),
);
},
);
}
}
class _IbanInput extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<FormDataCubit, FormDataState>(
builder: (context, state) {
return TextField(
onChanged: (iban) => context
.read<FormDataCubit>()
.dataChanged(formFieldIban, Iban.dirty(iban)),
keyboardType: TextInputType.text,
decoration: InputDecoration(
labelText: 'iban',
helperText: '',
errorText:
state.data.isNotValid(formFieldIban) ? 'invalid IBAN' : null,
),
);
},
); );
} }
} }
@ -185,11 +136,11 @@ class _IbanInput extends StatelessWidget {
class _CheckListInput extends StatelessWidget { class _CheckListInput extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<FormDataCubit, FormDataState>( return InputBuilder<SimpleCustomFormCubit>(
builder: (context, state) { field: formFieldList,
final input = builder: (context, cubit, state, field, inputValid) {
state.data.validatorOf<ListOption<String>>(formFieldList); final input = state.form.validatorOf<ListOption<String>>(field);
final options = input.value; final choices = ['c1', 'c2', 'c3'];
return Column( return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@ -197,34 +148,34 @@ class _CheckListInput extends StatelessWidget {
ListTile( ListTile(
title: const Text('Checkbox1'), title: const Text('Checkbox1'),
trailing: Checkbox( trailing: Checkbox(
value: options.contains('checkbox1'), value: input.value == choices[0],
onChanged: (_) { onChanged: (_) {
context.read<FormDataCubit>().dataChanged( cubit.dataChanged(
formFieldList, field,
input.select('checkbox1'), ListOption.dirty(choices: choices, value: choices[0]),
); );
}), }),
), ),
ListTile( ListTile(
title: const Text('Checkbox2'), title: const Text('Checkbox2'),
trailing: Checkbox( trailing: Checkbox(
value: options.contains('checkbox2'), value: input.value == choices[1],
onChanged: (_) { onChanged: (_) {
context.read<FormDataCubit>().dataChanged( cubit.dataChanged(
formFieldList, field,
input.select('checkbox2'), ListOption.dirty(choices: choices, value: choices[1]),
); );
}), }),
), ),
ListTile( ListTile(
title: const Text('Checkbox3 (default)'), title: const Text('Checkbox3 (default)'),
trailing: Checkbox( trailing: Checkbox(
value: options.contains('checkbox3'), value: input.value == choices[2],
onChanged: (_) { onChanged: (_) {
context.read<FormDataCubit>().dataChanged( cubit.dataChanged(
formFieldList, field,
input.select('checkbox3'), ListOption.dirty(choices: choices, value: choices[2]),
); );
}), }),
), ),
], ],
@ -237,9 +188,10 @@ class _CheckListInput extends StatelessWidget {
class _RadioListInput extends StatelessWidget { class _RadioListInput extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<FormDataCubit, FormDataState>( return InputBuilder<SimpleCustomFormCubit>(
builder: (context, state) { field: formFieldRadio,
final input = state.data.validatorOf<TextString>(formFieldRadio); builder: ((context, cubit, state, field, inputValid) {
final input = state.form.validatorOf<TextString>(field);
return Column( return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@ -250,10 +202,10 @@ class _RadioListInput extends StatelessWidget {
groupValue: true, groupValue: true,
value: input.value == 'radio1', value: input.value == 'radio1',
onChanged: (_) { onChanged: (_) {
context.read<FormDataCubit>().dataChanged( cubit.dataChanged(
formFieldRadio, field,
const TextString.dirty('radio1'), const TextString.dirty('radio1'),
); );
}), }),
), ),
ListTile( ListTile(
@ -262,10 +214,10 @@ class _RadioListInput extends StatelessWidget {
groupValue: true, groupValue: true,
value: input.value == 'radio2', value: input.value == 'radio2',
onChanged: (_) { onChanged: (_) {
context.read<FormDataCubit>().dataChanged( cubit.dataChanged(
formFieldRadio, field,
const TextString.dirty('radio2'), const TextString.dirty('radio2'),
); );
}), }),
), ),
ListTile( ListTile(
@ -274,15 +226,15 @@ class _RadioListInput extends StatelessWidget {
groupValue: true, groupValue: true,
value: input.value == 'radio3', value: input.value == 'radio3',
onChanged: (_) { onChanged: (_) {
context.read<FormDataCubit>().dataChanged( cubit.dataChanged(
formFieldRadio, field,
const TextString.dirty('radio3'), const TextString.dirty('radio3'),
); );
}), }),
), ),
], ],
); );
}, }),
); );
} }
} }
@ -292,17 +244,18 @@ class _CheckHiddenInput extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ListTile( return ListTile(
title: const Text('This input is not exported'), title: const Text('This input is not exported'),
trailing: BlocBuilder<FormDataCubit, FormDataState>( trailing: InputBuilder<SimpleCustomFormCubit>(
builder: (context, state) { field: formFieldHidden,
builder: (context, cubit, state, field, inputValid) {
return Checkbox( return Checkbox(
value: state.data.validatorOf<Boolean>(formFieldHidden).value, value: state.form.validatorOf<Boolean>(field).value,
onChanged: (v) { onChanged: (v) {
final value = v!; // state is false, so value can't be null final value = v!; // state is false, so value can't be null
context.read<FormDataCubit>().dataChanged( cubit.dataChanged(
formFieldHidden, field,
Boolean.dirty(value: value), Boolean.dirty(value: value),
); );
}); });
}, },
), ),
@ -315,25 +268,24 @@ class _CheckIsProInput extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ListTile( return ListTile(
title: const Text('Are you a pro?'), title: const Text('Are you a pro?'),
trailing: BlocBuilder<FormDataCubit, FormDataState>( trailing: InputBuilder<SimpleCustomFormCubit>(
builder: (context, state) { field: formFieldPro,
builder: (context, cubit, state, field, inputValid) {
return Checkbox( return Checkbox(
value: state.data.validatorOf<Boolean>(formFieldPro).value, value: state.form.validatorOf<Boolean>(field).value,
onChanged: (isPro) { onChanged: (v) {
final value = isPro!; // state is false, so value can't be null final value = v!; // state is false, so value can't be null
context.read<FormDataCubit>().dataChanged( cubit.dataChanged(
formFieldPro, field,
Boolean.dirty(value: value), Boolean.dirty(value: value),
); );
if (value) { if (value) {
context.read<FormDataCubit>().updateFormData( cubit.update(App.getProFormData(),
App.getProFormData(),
operation: SetOperation.union); operation: SetOperation.union);
} else { } else {
context.read<FormDataCubit>().updateFormData( cubit.update(App.getNormalFormData(),
App.getNormalFormData(),
operation: SetOperation.intersection); operation: SetOperation.intersection);
} }
}); });
@ -346,49 +298,28 @@ class _CheckIsProInput extends StatelessWidget {
class _SignUpButton extends StatelessWidget { class _SignUpButton extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<FormDataCubit, FormDataState>( return SubmitBuilder<SimpleCustomFormCubit>(
buildWhen: (previous, current) => previous.status != current.status, builder: (context, cubit, status) {
builder: (context, state) { return status.isSubmissionInProgress
return state.status.isSubmissionInProgress ? const CircularProgressIndicator()
? const CircularProgressIndicator() : ElevatedButton(
: ElevatedButton( onPressed: status.isValidated ? () => cubit.submit() : null,
onPressed: state.status.isValidated child: const Text('SIGN UP'),
? () => context.read<FormDataCubit>().submitForm() );
: null, });
child: const Text('SIGN UP'),
);
},
);
}
}
class _DebugButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<FormDataCubit, FormDataState>(
builder: (context, state) {
return ElevatedButton(
onPressed: () {
log(state.toString());
},
child: const Text('DEBUG'),
);
},
);
} }
} }
class _ResetButton extends StatelessWidget { class _ResetButton extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<FormDataCubit, FormDataState>( return SubmitBuilder<SimpleCustomFormCubit>(
builder: (context, state) { builder: (context, cubit, status) {
return ElevatedButton( return ElevatedButton(
onPressed: () => context.read<FormDataCubit>().resetForm(), onPressed: () => cubit.reset(),
child: const Text('RESET'), child: const Text('RESET'),
); );
}, });
);
} }
} }
@ -397,7 +328,7 @@ class SignUpForm extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocListener<FormDataCubit, FormDataState>( return BlocListener<SimpleCustomFormCubit, FormDataState>(
listener: (context, state) { listener: (context, state) {
if (state.status.isSubmissionSuccess) { if (state.status.isSubmissionSuccess) {
Navigator.of(context).pop(); // Never happens here Navigator.of(context).pop(); // Never happens here
@ -418,8 +349,6 @@ class SignUpForm extends StatelessWidget {
const SizedBox(height: 8), const SizedBox(height: 8),
_EmailInput(), _EmailInput(),
const SizedBox(height: 8), const SizedBox(height: 8),
_PhoneInput(),
const SizedBox(height: 8),
_CheckListInput(), _CheckListInput(),
const SizedBox(height: 8), const SizedBox(height: 8),
_RadioListInput(), _RadioListInput(),
@ -428,13 +357,12 @@ class SignUpForm extends StatelessWidget {
const SizedBox(height: 8), const SizedBox(height: 8),
_CheckIsProInput(), _CheckIsProInput(),
const SizedBox(height: 8), const SizedBox(height: 8),
BlocBuilder<FormDataCubit, FormDataState>( BlocBuilder<SimpleCustomFormCubit, FormDataState>(
builder: (context, state) { builder: (context, state) {
if (state.data.validatorOf<Boolean>(formFieldPro).value) { if (state.form.validatorOf<Boolean>(formFieldPro).value ??
false) {
return Column(children: [ return Column(children: [
_SirenInput(), _PhoneInput(),
const SizedBox(height: 8),
_IbanInput(),
const SizedBox(height: 8), const SizedBox(height: 8),
]); ]);
} }
@ -444,8 +372,6 @@ class SignUpForm extends StatelessWidget {
const SizedBox(height: 8), const SizedBox(height: 8),
_SignUpButton(), _SignUpButton(),
const SizedBox(height: 8), const SizedBox(height: 8),
_DebugButton(),
const SizedBox(height: 8),
_ResetButton(), _ResetButton(),
], ],
), ),

View File

@ -18,12 +18,13 @@ import 'dart:developer';
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
class CustomFormCubit extends FormDataCubit { class SimpleCustomFormCubit extends FormDataCubitImpl {
CustomFormCubit({required FormData inputs}) : super(inputs: inputs); SimpleCustomFormCubit(super.form) : super();
@override @override
Future<void> submitForm() { Future<void> submit() {
log(state.data.toMap().toString()); final value = FormJsonEncoder().encode(state.form);
log(value);
return Future.value(); return Future.value();
} }

View File

@ -14,17 +14,4 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'form/every_input_validator.dart'; export 'enums/enums.dart';
export 'inputs/base/regex_validator.dart';
export 'inputs/base/text_validator.dart';
export 'inputs/boolean.dart';
export 'inputs/confirmed_password.dart';
export 'inputs/email.dart';
export 'inputs/enum_validator.dart';
export 'inputs/iban.dart';
export 'inputs/list_option.dart';
export 'inputs/name.dart';
export 'inputs/password.dart';
export 'inputs/phone.dart';
export 'inputs/siren.dart';
export 'inputs/text_string.dart';

View File

@ -0,0 +1,39 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_form_bloc/src/data/form_operations/form_difference.dart';
import 'package:wyatt_form_bloc/src/data/form_operations/form_intersection.dart';
import 'package:wyatt_form_bloc/src/data/form_operations/form_replace.dart';
import 'package:wyatt_form_bloc/src/data/form_operations/form_union.dart';
import 'package:wyatt_form_bloc/src/domain/form_operations/form_operation.dart';
enum SetOperation {
/// Replace entire set with new set.
replace(FormReplace()),
/// Keep common elements between sets.
intersection(FormIntersection()),
/// Remove common elements between sets.
difference(FormDifference()),
/// Add new elements to set.
union(FormUnion());
final FormOperation operation;
const SetOperation(this.operation);
}

View File

@ -23,5 +23,6 @@ abstract class ValidationError {}
enum ValidationStandardError implements ValidationError { enum ValidationStandardError implements ValidationError {
invalid, invalid,
empty empty,
notEqual,
} }

View File

@ -1,110 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:wyatt_form_bloc/src/enums/enums.dart';
import 'package:wyatt_form_bloc/src/form/form.dart';
import 'package:wyatt_form_bloc/src/validators/form/every_input_validator.dart';
part 'form_data_state.dart';
abstract class FormDataCubit extends Cubit<FormDataState> {
FormValidator validationStrategy;
FormData formCopy;
FormDataCubit({
required FormData inputs,
FormValidator validator = const EveryInputValidator(),
}) : formCopy = inputs.clone(),
validationStrategy = validator,
super(FormDataState(data: inputs));
/// Change value of a field.
///
/// Inputs:
/// - `field`: The key of the field to change.
/// - `dirtyValue`: The new value of the field. (Wrapped in a dirty validator)
void dataChanged<V>(
String field,
FormInputValidator<V, ValidationError> dirtyValue,
) {
final form = state.data.clone();
if (form.contains(field)) {
form.updateValidator(field, dirtyValue);
} else {
throw Exception('Form field $field not found');
}
emit(
state.copyWith(
data: form,
status: validationStrategy.validate(form),
),
);
}
/// Just validate the form manually. (Useful if you just update the strategy)
void validate() {
final form = state.data;
emit(state.copyWith(status: validationStrategy.validate(form)));
}
/// Update entries list.
///
/// Inputs:
/// - `data`: The new entries list.
/// - `operation`: The operation to perform on the entries set.
void updateFormData(
FormData data, {
SetOperation operation = SetOperation.replace,
}) {
FormData form = data;
switch (operation) {
case SetOperation.replace:
form = data;
break;
case SetOperation.difference:
form = state.data.difference(data);
break;
case SetOperation.intersection:
form = state.data.intersection(data);
break;
case SetOperation.union:
form = state.data.union(data);
break;
}
emit(
state.copyWith(
data: form,
status: validationStrategy.validate(form),
),
);
}
/// Reset all form inputs
void resetForm() {
emit(FormDataState(data: formCopy));
}
/// Submit the form.
Future<void> submitForm();
}

View File

@ -0,0 +1,34 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'form/wyatt_form_impl.dart';
export 'form_encoders/form_json_encoder.dart';
export 'form_encoders/form_map_encoder.dart';
export 'form_operations/form_difference.dart';
export 'form_operations/form_intersection.dart';
export 'form_operations/form_replace.dart';
export 'form_operations/form_union.dart';
export 'form_validators/every_input_validator.dart';
export 'form_validators/only_required_input_validator.dart';
export 'input_validators/boolean.dart';
export 'input_validators/confirmed_password.dart';
export 'input_validators/email.dart';
export 'input_validators/enum_option.dart';
export 'input_validators/list_option.dart';
export 'input_validators/name.dart';
export 'input_validators/password.dart';
export 'input_validators/phone.dart';
export 'input_validators/text_string.dart';

View File

@ -0,0 +1,136 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_form_bloc/src/core/enums/form_status.dart';
import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart';
import 'package:wyatt_form_bloc/src/data/form_validators/every_input_validator.dart';
import 'package:wyatt_form_bloc/src/domain/entities/form_input.dart';
import 'package:wyatt_form_bloc/src/domain/entities/form_input_metadata.dart';
import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart';
import 'package:wyatt_form_bloc/src/domain/form_operations/form_operation.dart';
import 'package:wyatt_form_bloc/src/domain/form_validators/form_validator.dart';
import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart';
// ignore: must_be_immutable
class WyattFormImpl extends WyattForm {
final List<
FormInput<dynamic, FormInputValidator<dynamic, ValidationError>,
dynamic>> _inputs;
final FormValidator _validator;
late List<
FormInput<dynamic, FormInputValidator<dynamic, ValidationError>,
dynamic>> _inputsInitial;
WyattFormImpl(
this._inputs, {
FormValidator validationStrategy = const EveryInputValidator(),
}) : _validator = validationStrategy {
_inputsInitial = _inputs.map((input) => input.clone()).toList();
}
@override
List<
FormInput<dynamic, FormInputValidator<dynamic, ValidationError>,
dynamic>> get inputs => _inputs;
@override
FormValidator get formValidationStrategy => _validator;
@override
bool containsKey(String key) => inputs.any((input) => input.key == key);
@override
List<FormInputValidator<Value, Error>>
asValidatorList<Value, Error extends ValidationError>() => inputs
.map<FormInputValidator<Value, Error>>(
(input) => input.validator as FormInputValidator<Value, Error>,
)
.toList();
@override
FormInput<dynamic, FormInputValidator<dynamic, ValidationError>, dynamic>
inputOf(String key) {
if (containsKey(key)) {
return inputs.firstWhere((input) => input.key == key);
} else {
throw Exception('FormInput with key `$key` does not exist in form');
}
}
@override
Error? errorOf<Error extends ValidationError>(String key) =>
(inputOf(key).validator as FormInputValidator<dynamic, Error>).error;
@override
FormInputMetadata<Extra> metadataOf<Extra>(String key) =>
inputOf(key).metadata as FormInputMetadata<Extra>;
@override
Validator validatorOf<
Validator extends FormInputValidator<dynamic, ValidationError>>(
String key,
) =>
inputOf(key).validator as Validator;
@override
Value? valueOf<Value>(String key) =>
(inputOf(key).validator as FormInputValidator<Value, dynamic>).value;
@override
FormStatus validate() => formValidationStrategy.validate(this);
@override
WyattForm clone() => WyattFormImpl(
_inputs.map((input) => input.clone()).toList(),
validationStrategy: formValidationStrategy,
);
@override
WyattForm reset() => WyattFormImpl(
_inputsInitial,
validationStrategy: formValidationStrategy,
);
@override
bool? get stringify => true;
@override
List<Object?> get props => _inputs;
@override
void updateMetadata<Extra>(String key, FormInputMetadata<Extra> metadata) {
final index = _inputs.indexOf(
inputOf(key),
);
_inputs[index] = _inputs[index].copyWith(metadata: metadata);
}
@override
void updateValidator<Value>(
String key,
FormInputValidator<Value?, ValidationError> dirtyValue,
) {
final index = _inputs.indexOf(
inputOf(key),
);
_inputs[index] = _inputs[index].copyWith(validator: dirtyValue);
}
@override
WyattForm operationWith(FormOperation operation, WyattForm other) =>
operation.call(this, other);
}

View File

@ -0,0 +1,30 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:convert';
import 'package:wyatt_form_bloc/src/data/form_encoders/form_map_encoder.dart';
import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart';
import 'package:wyatt_form_bloc/src/domain/form_encoders/form_encoder.dart';
class FormJsonEncoder extends FormEncoder<String> {
@override
String encode(WyattForm form) {
final mapEncoder = FormMapEncoder();
final map = mapEncoder.encode(form);
return jsonEncode(map);
}
}

View File

@ -0,0 +1,32 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart';
import 'package:wyatt_form_bloc/src/domain/form_encoders/form_encoder.dart';
class FormMapEncoder extends FormEncoder<Map<String, dynamic>> {
@override
Map<String, dynamic> encode(WyattForm form) {
final inputs = form.inputs;
final map = <String, dynamic>{};
for (final input in inputs) {
if (input.metadata.export) {
map[input.name] = input.validator.value;
}
}
return map;
}
}

View File

@ -0,0 +1,50 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart';
import 'package:wyatt_form_bloc/src/data/form/wyatt_form_impl.dart';
import 'package:wyatt_form_bloc/src/domain/entities/form_input.dart';
import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart';
import 'package:wyatt_form_bloc/src/domain/form_operations/form_operation.dart';
import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart';
class FormDifference extends FormOperation {
const FormDifference();
@override
WyattForm call(WyattForm a, WyattForm b) {
final inputs = <
FormInput<dynamic, FormInputValidator<dynamic, ValidationError>,
dynamic>>[];
for (final i in b.inputs) {
if (!a.containsKey(i.key)) {
inputs.add(i);
}
}
for (final i in a.inputs) {
if (!b.containsKey(i.key)) {
inputs.add(i);
}
}
return WyattFormImpl(
inputs,
validationStrategy: a.formValidationStrategy,
);
}
}

View File

@ -0,0 +1,44 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart';
import 'package:wyatt_form_bloc/src/data/form/wyatt_form_impl.dart';
import 'package:wyatt_form_bloc/src/domain/entities/form_input.dart';
import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart';
import 'package:wyatt_form_bloc/src/domain/form_operations/form_operation.dart';
import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart';
class FormIntersection extends FormOperation {
const FormIntersection();
@override
WyattForm call(WyattForm a, WyattForm b) {
final inputs = <
FormInput<dynamic, FormInputValidator<dynamic, ValidationError>,
dynamic>>[];
for (final i in a.inputs) {
if (b.containsKey(i.key)) {
inputs.add(i);
}
}
return WyattFormImpl(
inputs,
validationStrategy: a.formValidationStrategy,
);
}
}

View File

@ -14,15 +14,12 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:convert'; import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart';
import 'package:wyatt_form_bloc/src/domain/form_operations/form_operation.dart';
import 'package:equatable/equatable.dart'; class FormReplace extends FormOperation {
import 'package:wyatt_form_bloc/src/enums/form_input_status.dart'; const FormReplace();
import 'package:wyatt_form_bloc/src/enums/form_status.dart';
import 'package:wyatt_form_bloc/src/enums/validation_error.dart';
part 'form_data.dart'; @override
part 'form_input.dart'; WyattForm call(WyattForm a, WyattForm b) => b;
part 'form_input_metadata.dart'; }
part 'form_input_validator.dart';
part 'form_validator.dart';

View File

@ -0,0 +1,48 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart';
import 'package:wyatt_form_bloc/src/data/form/wyatt_form_impl.dart';
import 'package:wyatt_form_bloc/src/domain/entities/form_input.dart';
import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart';
import 'package:wyatt_form_bloc/src/domain/form_operations/form_operation.dart';
import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart';
class FormUnion extends FormOperation {
const FormUnion();
@override
WyattForm call(WyattForm a, WyattForm b) {
final inputs = <
FormInput<dynamic, FormInputValidator<dynamic, ValidationError>,
dynamic>>[];
for (final i in a.inputs) {
inputs.add(i);
}
for (final i in b.inputs) {
if (!a.containsKey(i.key)) {
inputs.add(i);
}
}
return WyattFormImpl(
inputs,
validationStrategy: a.formValidationStrategy,
);
}
}

View File

@ -14,7 +14,10 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; import 'package:wyatt_form_bloc/src/core/enums/form_status.dart';
import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart';
import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart';
import 'package:wyatt_form_bloc/src/domain/form_validators/form_validator.dart';
/// {@template every} /// {@template every}
/// Check and validate every input of a form /// Check and validate every input of a form
@ -24,18 +27,13 @@ class EveryInputValidator extends FormValidator {
const EveryInputValidator() : super(); const EveryInputValidator() : super();
@override @override
FormStatus validate(FormData form) { FormStatus validate(WyattForm form) {
if (isPure(form)) { if (isPure(form)) {
return FormStatus.pure; return FormStatus.pure;
} }
return rawValidate(form.validators<dynamic, ValidationError>()); final validators = form.asValidatorList<dynamic, ValidationError>();
}
@override
FormStatus rawValidate(
List<FormInputValidator<dynamic, ValidationError>> validators,
) {
if (validators.any((validator) => validator.valid == false)) { if (validators.any((validator) => validator.valid == false)) {
return FormStatus.invalid; return FormStatus.invalid;
} }

View File

@ -0,0 +1,29 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_form_bloc/src/core/enums/form_status.dart';
import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart';
import 'package:wyatt_form_bloc/src/domain/form_validators/form_validator.dart';
class OnlyRequiredInputValidator extends FormValidator {
const OnlyRequiredInputValidator() : super();
@override
FormStatus validate(WyattForm form) {
// TODO(hpcl): use metadata to check if input is required or not.
return FormStatus.valid;
}
}

View File

@ -14,21 +14,25 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_form_bloc/src/enums/enums.dart'; import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart';
import 'package:wyatt_form_bloc/src/form/form.dart'; import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart';
/// {@template boolean} /// {@template boolean}
/// Form input for a bool input /// Form input for a bool input
/// {@endtemplate} /// {@endtemplate}
class Boolean extends FormInputValidator<bool, ValidationStandardError> { class Boolean extends NullableValidator<bool, ValidationStandardError> {
/// {@macro boolean} /// {@macro boolean}
const Boolean.pure({bool? defaultValue = false}) const Boolean.pure({bool? defaultValue = false})
: super.pure(defaultValue ?? false); : super.pure(defaultValue ?? false);
/// {@macro boolean} /// {@macro boolean}
const Boolean.dirty({bool value = false}) : super.dirty(value); const Boolean.dirty({bool value = false}) : super.dirty(value);
@override
ValidationStandardError get onNull => ValidationStandardError.invalid;
@override @override
ValidationStandardError? validator(bool? value) => ValidationStandardError? validator(bool? value) =>
value != null ? null : ValidationStandardError.invalid; value != null ? null : onNull;
} }

View File

@ -14,25 +14,31 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_form_bloc/src/enums/enums.dart'; import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart';
import 'package:wyatt_form_bloc/src/form/form.dart'; import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart';
/// {@template confirmed_password} /// {@template confirmed_password}
/// Form input for a confirmed password input. /// Form input for a confirmed password input.
/// {@endtemplate} /// {@endtemplate}
class ConfirmedPassword class ConfirmedPassword
extends FormInputValidator<String, ValidationStandardError> { extends EqualityValidator<String, ValidationStandardError> {
/// {@macro confirmed_password} /// {@macro confirmed_password}
const ConfirmedPassword.pure({this.password = ''}) : super.pure(''); const ConfirmedPassword.pure({this.password = '', String? defaultValue})
: super.pure(defaultValue);
/// {@macro confirmed_password} /// {@macro confirmed_password}
const ConfirmedPassword.dirty({required this.password, String value = ''}) const ConfirmedPassword.dirty({required this.password, String? value})
: super.dirty(value); : super.dirty(value);
/// The original password. /// The original password.
final String password; final String password;
@override @override
ValidationStandardError? validator(String? value) => String get another => password;
password == value ? null : ValidationStandardError.invalid;
@override
ValidationStandardError get onNotEqual => ValidationStandardError.notEqual;
@override
ValidationStandardError get onNull => ValidationStandardError.invalid;
} }

View File

@ -14,15 +14,15 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_form_bloc/src/enums/enums.dart'; import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart';
import 'package:wyatt_form_bloc/src/validators/inputs/base/regex_validator.dart'; import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart';
/// {@template email} /// {@template email}
/// Form input for an email input. /// Form input for an email input.
/// {@endtemplate} /// {@endtemplate}
class Email extends RegexValidator<ValidationStandardError> { class Email extends RegexValidator<ValidationStandardError> {
/// {@macro email} /// {@macro email}
const Email.pure() : super.pure(); const Email.pure([super.value]) : super.pure();
/// {@macro email} /// {@macro email}
const Email.dirty([super.value = '']) : super.dirty(); const Email.dirty([super.value = '']) : super.dirty();

View File

@ -14,24 +14,27 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_form_bloc/src/enums/enums.dart'; import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart';
import 'package:wyatt_form_bloc/src/validators/inputs/base/regex_validator.dart'; import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart';
/// {@template siren} class EnumOption<T> extends AnyValidator<T, List<T>, ValidationError> {
/// Form input for a SIREN input. const EnumOption.pure({this.enums = const [], T? defaultValue})
/// {@endtemplate} : super.pure(defaultValue);
class Siren extends RegexValidator<ValidationStandardError> {
/// {@macro siren}
const Siren.pure() : super.pure();
/// {@macro siren} const EnumOption.dirty({required this.enums, T? value})
const Siren.dirty([super.value = '']) : super.dirty(); : super.dirty(value);
final List<T> enums;
@override @override
ValidationStandardError get onEmpty => ValidationStandardError.empty; List<T> get allChoices => enums;
@override
ValidationStandardError get onError => ValidationStandardError.invalid;
@override @override
RegExp get regex => RegExp(r'(\d{9}|\d{3}[ ]\d{3}[ ]\d{3})$'); ValidationError get onError => ValidationStandardError.invalid;
@override
ValidationError get onNull => ValidationStandardError.invalid;
@override
bool test(T? element, T value) => element == value;
} }

View File

@ -0,0 +1,40 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart';
import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart';
class ListOption<T> extends AnyValidator<T, Iterable<T>, ValidationError> {
const ListOption.pure({this.choices = const [], T? defaultValue})
: super.pure(defaultValue);
const ListOption.dirty({required this.choices, T? value})
: super.dirty(value);
final Iterable<T> choices;
@override
Iterable<T> get allChoices => choices;
@override
ValidationError get onError => ValidationStandardError.invalid;
@override
ValidationError get onNull => ValidationStandardError.invalid;
@override
bool test(T? element, T value) => element == value;
}

View File

@ -14,15 +14,15 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_form_bloc/src/enums/enums.dart'; import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart';
import 'package:wyatt_form_bloc/src/validators/inputs/base/regex_validator.dart'; import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart';
/// {@template name} /// {@template name}
/// Form input for a name input. /// Form input for a name input.
/// {@endtemplate} /// {@endtemplate}
class Name extends RegexValidator<ValidationStandardError> { class Name extends RegexValidator<ValidationStandardError> {
/// {@macro name} /// {@macro name}
const Name.pure() : super.pure(); const Name.pure([super.value]) : super.pure();
/// {@macro name} /// {@macro name}
const Name.dirty([super.value = '']) : super.dirty(); const Name.dirty([super.value = '']) : super.dirty();

View File

@ -14,18 +14,18 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_form_bloc/src/enums/enums.dart'; import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart';
import 'package:wyatt_form_bloc/src/validators/inputs/base/regex_validator.dart'; import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart';
/// {@template password} /// {@template password}
/// Form input for a password input. /// Form input for a password input.
/// {@endtemplate} /// {@endtemplate}
class Password extends RegexValidator<ValidationStandardError> { class Password extends RegexValidator<ValidationStandardError> {
/// {@macro password} /// {@macro password}
const Password.pure() : super.pure(); const Password.pure([super.value]) : super.pure();
/// {@macro password} /// {@macro password}
const Password.dirty([super.value = '']) : super.dirty(); const Password.dirty([super.value]) : super.dirty();
@override @override
ValidationStandardError get onEmpty => ValidationStandardError.empty; ValidationStandardError get onEmpty => ValidationStandardError.empty;

View File

@ -14,15 +14,15 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_form_bloc/src/enums/enums.dart'; import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart';
import 'package:wyatt_form_bloc/src/validators/inputs/base/regex_validator.dart'; import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart';
/// {@template phone} /// {@template phone}
/// Form input for a phone input. /// Form input for a phone input.
/// {@endtemplate} /// {@endtemplate}
class Phone extends RegexValidator<ValidationStandardError> { class Phone extends RegexValidator<ValidationStandardError> {
/// {@macro phone} /// {@macro phone}
const Phone.pure() : super.pure(); const Phone.pure([super.value]) : super.pure();
/// {@macro phone} /// {@macro phone}
const Phone.dirty([super.value = '']) : super.dirty(); const Phone.dirty([super.value = '']) : super.dirty();

View File

@ -14,15 +14,15 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_form_bloc/src/enums/enums.dart'; import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart';
import 'package:wyatt_form_bloc/src/validators/inputs/base/text_validator.dart'; import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart';
/// {@template text_string} /// {@template text_string}
/// Form input for a text input /// Form input for a text input
/// {@endtemplate} /// {@endtemplate}
class TextString extends TextValidator<ValidationStandardError> { class TextString extends TextValidator<ValidationStandardError> {
/// {@macro text_string} /// {@macro text_string}
const TextString.pure() : super.pure(); const TextString.pure([super.value]) : super.pure();
/// {@macro text_string} /// {@macro text_string}
const TextString.dirty([super.value = '']) : super.dirty(); const TextString.dirty([super.value = '']) : super.dirty();

View File

@ -0,0 +1,23 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'entities/form_input.dart';
export 'entities/form_input_metadata.dart';
export 'form/wyatt_form.dart';
export 'form_encoders/form_encoder.dart';
export 'form_operations/form_operation.dart';
export 'form_validators/form_validator.dart';
export 'input_validators/form_input_validator.dart';

View File

@ -15,27 +15,36 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
part of 'form.dart'; import 'package:equatable/equatable.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart';
import 'package:wyatt_form_bloc/src/domain/entities/form_input_metadata.dart';
import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart';
class FormInput<Validator extends FormInputValidator<dynamic, ValidationError>> // ignore: must_be_immutable
extends Equatable { class FormInput<
Value,
Validator extends FormInputValidator<Value, ValidationError>,
Extra> extends Equatable implements Entity {
final String key; final String key;
final Validator validator; final Validator validator;
final FormInputMetadata<dynamic> metadata; late FormInputMetadata<Extra> metadata;
String get name => metadata._name ?? key; String get name => metadata.name ?? key;
const FormInput( FormInput(
this.key, this.key,
this.validator, { this.validator, {
// ignore: avoid_redundant_argument_values // ignore: avoid_redundant_argument_values
this.metadata = const FormInputMetadata<void>(export: true), FormInputMetadata<Extra>? metadata,
}); }) {
this.metadata = metadata ?? FormInputMetadata<Extra>();
}
FormInput copyWith({ FormInput<Value, Validator, Extra> copyWith({
String? key, String? key,
Validator? validator, Validator? validator,
FormInputMetadata? metadata, FormInputMetadata<Extra>? metadata,
}) => }) =>
FormInput( FormInput(
key ?? this.key, key ?? this.key,
@ -43,11 +52,12 @@ class FormInput<Validator extends FormInputValidator<dynamic, ValidationError>>
metadata: metadata ?? this.metadata, metadata: metadata ?? this.metadata,
); );
FormInput clone() => copyWith( FormInput<Value, Validator, Extra> clone() => copyWith(
key: key, key: key,
validator: validator, validator: validator,
metadata: metadata, metadata: metadata,
); );
@override @override
bool? get stringify => true; bool? get stringify => true;

View File

@ -15,33 +15,35 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
part of 'form.dart'; import 'package:equatable/equatable.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
class FormInputMetadata<T extends Object?> extends Equatable { class FormInputMetadata<Extra> extends Equatable
implements Entity {
final bool export; final bool export;
final String? _name; final String? name;
final T? extra; final Extra? extra;
const FormInputMetadata({ const FormInputMetadata({
this.export = true, this.export = true,
this.name,
this.extra, this.extra,
String? name, });
}) : _name = name;
FormInputMetadata<T> copyWith({ FormInputMetadata<Extra> copyWith({
bool? export, bool? export,
String? name, String? name,
T? extra, Extra? extra,
}) => }) =>
FormInputMetadata<T>( FormInputMetadata<Extra>(
export: export ?? this.export, export: export ?? this.export,
name: name ?? _name, name: name ?? this.name,
extra: extra ?? this.extra, extra: extra ?? this.extra,
); );
FormInputMetadata<T> clone() => copyWith( FormInputMetadata<Extra> clone() => copyWith(
export: export, export: export,
name: _name, name: name,
extra: extra, extra: extra,
); );
@ -49,5 +51,5 @@ class FormInputMetadata<T extends Object?> extends Equatable {
bool? get stringify => true; bool? get stringify => true;
@override @override
List<Object?> get props => [export, _name, extra]; List<Object?> get props => [export, name, extra];
} }

View File

@ -0,0 +1,60 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// ignore_for_file: strict_raw_type
import 'package:equatable/equatable.dart';
import 'package:wyatt_form_bloc/src/core/enums/form_status.dart';
import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart';
import 'package:wyatt_form_bloc/src/domain/entities/form_input.dart';
import 'package:wyatt_form_bloc/src/domain/entities/form_input_metadata.dart';
import 'package:wyatt_form_bloc/src/domain/form_operations/form_operation.dart';
import 'package:wyatt_form_bloc/src/domain/form_validators/form_validator.dart';
import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart';
abstract class WyattForm extends Equatable {
List<FormInput> get inputs;
FormValidator get formValidationStrategy;
bool containsKey(String key);
FormInput inputOf(String key);
Validator validatorOf<
Validator extends FormInputValidator<dynamic, ValidationError>>(
String key,
);
Error? errorOf<Error extends ValidationError>(String key);
Value? valueOf<Value>(String key);
FormInputMetadata<Extra> metadataOf<Extra>(String key);
List<FormInputValidator<Value, Error>>
asValidatorList<Value, Error extends ValidationError>();
void updateValidator<Value>(
String key,
FormInputValidator<Value?, ValidationError> dirtyValue,
);
void updateMetadata<Extra>(
String key,
FormInputMetadata<Extra> metadata,
);
FormStatus validate();
WyattForm clone();
WyattForm operationWith(FormOperation operation, WyattForm other);
WyattForm reset();
}

View File

@ -14,16 +14,9 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
enum SetOperation { import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart';
/// Replace entire set with new set.
replace, // ignore: one_member_abstracts
abstract class FormEncoder<Output> {
/// Keep common elements between sets. Output encode(WyattForm form);
intersection,
/// Remove common elements between sets.
difference,
/// Add new elements to set.
union
} }

View File

@ -0,0 +1,23 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart';
// ignore: one_member_abstracts
abstract class FormOperation {
const FormOperation();
WyattForm call(WyattForm a, WyattForm b);
}

View File

@ -14,7 +14,9 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
part of 'form.dart'; import 'package:wyatt_form_bloc/src/core/enums/form_status.dart';
import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart';
import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart';
/// {@template form_validator} /// {@template form_validator}
/// A [FormValidator] represents the global validaton state of a Form. /// A [FormValidator] represents the global validaton state of a Form.
@ -23,13 +25,9 @@ abstract class FormValidator {
/// {@macro form_validator} /// {@macro form_validator}
const FormValidator(); const FormValidator();
bool isPure(FormData form) => form bool isPure(WyattForm form) => form
.validators<dynamic, ValidationError>() .asValidatorList<dynamic, ValidationError>()
.every((validator) => validator.pure); .every((validator) => validator.pure);
FormStatus validate(FormData form); FormStatus validate(WyattForm form);
FormStatus rawValidate(
List<FormInputValidator<dynamic, ValidationError>> validators,
);
} }

View File

@ -0,0 +1,45 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
part of 'form_input_validator.dart';
abstract class AnyValidator<O, I extends Iterable<O?>,
E extends ValidationError> extends FormInputValidator<O?, E> {
const AnyValidator.pure(super.value) : super.pure();
const AnyValidator.dirty(super.value) : super.dirty();
I get allChoices;
bool test(O? element, O value);
E get onError;
/// If value is null.
E get onNull;
@override
E? validator(O? value) {
if (value == null) {
return onNull;
}
if (allChoices.any(
(element) => test(element, value),
)) {
return null;
}
return onError;
}
}

View File

@ -0,0 +1,45 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
part of 'form_input_validator.dart';
/// {@template equality_validator}
/// Abstract equality validator validating a value when equals to another.
/// {@endtemplate}
abstract class EqualityValidator<O extends Object, E extends ValidationError>
extends FormInputValidator<O?, E> {
const EqualityValidator.pure(super.value) : super.pure();
const EqualityValidator.dirty(super.value) : super.dirty();
O get another;
/// If values are different.
E get onNotEqual;
/// If value is null.
E get onNull;
@override
E? validator(O? value) {
if (value == null) {
return onNull;
}
if (value != another) {
return onNotEqual;
}
return null;
}
}

View File

@ -14,7 +14,15 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
part of 'form.dart'; import 'package:equatable/equatable.dart';
import 'package:wyatt_form_bloc/src/core/enums/form_input_status.dart';
import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart';
part 'regex_validator.dart';
part 'text_validator.dart';
part 'nullable_validator.dart';
part 'equality_validator.dart';
part 'any_validator.dart';
/// {@template form_input_validator} /// {@template form_input_validator}
/// A [FormInputValidator] represents the value of a single form input field. /// A [FormInputValidator] represents the value of a single form input field.
@ -39,21 +47,21 @@ part of 'form.dart';
/// } /// }
/// ``` /// ```
/// {@endtemplate} /// {@endtemplate}
abstract class FormInputValidator<V, E extends ValidationError> abstract class FormInputValidator<Value, Error extends ValidationError>
extends Equatable { extends Equatable {
const FormInputValidator._(this.value, [this.pure = true]); const FormInputValidator._(this.value, [this.pure = true]);
/// Constructor which create a `pure` [FormInputValidator] with a given value. /// Constructor which create a `pure` [FormInputValidator] with a given value.
const FormInputValidator.pure(V value) : this._(value); const FormInputValidator.pure(Value value) : this._(value);
/// Constructor which create a `dirty` [FormInputValidator] with a /// Constructor which create a `dirty` [FormInputValidator] with a
/// given value. /// given value.
const FormInputValidator.dirty(V value) : this._(value, false); const FormInputValidator.dirty(Value value) : this._(value, false);
/// The value of the given [FormInputValidator]. /// The value of the given [FormInputValidator].
/// For example, if you have a `FormInputValidator` for `FirstName`, /// For example, if you have a `FormInputValidator` for `FirstName`,
/// the value could be 'Joe'. /// the value could be 'Joe'.
final V value; final Value? value;
/// If the [FormInputValidator] is pure (has been touched/modified). /// If the [FormInputValidator] is pure (has been touched/modified).
/// Typically when the `FormInputValidator` is initially created, /// Typically when the `FormInputValidator` is initially created,
@ -80,7 +88,7 @@ abstract class FormInputValidator<V, E extends ValidationError>
/// Returns a validation error if the [FormInputValidator] is invalid. /// Returns a validation error if the [FormInputValidator] is invalid.
/// Returns `null` if the [FormInputValidator] is valid. /// Returns `null` if the [FormInputValidator] is valid.
E? get error => validator(value); Error? get error => validator(value);
/// Whether the [FormInputValidator] value is valid according to the /// Whether the [FormInputValidator] value is valid according to the
/// overridden `validator`. /// overridden `validator`.
@ -96,7 +104,7 @@ abstract class FormInputValidator<V, E extends ValidationError>
/// A function that must return a validation error if the provided /// A function that must return a validation error if the provided
/// [value] is invalid and `null` otherwise. /// [value] is invalid and `null` otherwise.
E? validator(V value); Error? validator(Value? value);
@override @override
bool? get stringify => true; bool? get stringify => true;

View File

@ -14,19 +14,24 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_form_bloc/src/enums/validation_error.dart'; part of 'form_input_validator.dart';
import 'package:wyatt_form_bloc/src/validators/inputs/base/text_validator.dart';
class EnumValidator<E> extends TextValidator<ValidationStandardError> { /// {@template nullable_validator}
/// {@macro text_string} /// Abstract nullable validator not validating null values.
const EnumValidator.pure() : super.pure(); /// {@endtemplate}
abstract class NullableValidator<O extends Object, E extends ValidationError>
extends FormInputValidator<O?, E> {
const NullableValidator.pure(super.value) : super.pure();
const NullableValidator.dirty(super.value) : super.dirty();
/// {@macro text_string} /// If value is null.
EnumValidator.dirty(E value) : super.dirty(value.toString()); E get onNull;
@override @override
ValidationStandardError get onEmpty => ValidationStandardError.empty; E? validator(O? value) {
if (value == null) {
@override return onNull;
ValidationStandardError get onNull => ValidationStandardError.invalid; }
return null;
}
} }

View File

@ -14,20 +14,21 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_form_bloc/src/enums/validation_error.dart'; part of 'form_input_validator.dart';
import 'package:wyatt_form_bloc/src/form/form.dart';
/// {@template regex_validator} /// {@template regex_validator}
/// Abstract regex validator for form input. /// Abstract regex validator for form input.
/// {@endtemplate} /// {@endtemplate}
abstract class RegexValidator<E extends ValidationError> abstract class RegexValidator<E extends ValidationError>
extends FormInputValidator<String, E> { extends FormInputValidator<String?, E> {
const RegexValidator.pure() : super.pure(''); const RegexValidator.pure(super.value) : super.pure();
const RegexValidator.dirty([super.value = '']) : super.dirty(); const RegexValidator.dirty(super.value) : super.dirty();
RegExp get regex; RegExp get regex;
/// If the value is **not** null, but empty. /// If the value is **not** null, but empty.
E get onEmpty; E get onEmpty;
/// If value does not conform to regex. /// If value does not conform to regex.
E get onError; E get onError;

View File

@ -14,13 +14,12 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_form_bloc/src/enums/validation_error.dart'; part of 'form_input_validator.dart';
import 'package:wyatt_form_bloc/src/form/form.dart';
abstract class TextValidator<E extends ValidationError> abstract class TextValidator<E extends ValidationError>
extends FormInputValidator<String, E> { extends FormInputValidator<String?, E> {
const TextValidator.pure() : super.pure(''); const TextValidator.pure(super.value) : super.pure();
const TextValidator.dirty([super.value = '']) : super.dirty(); const TextValidator.dirty(super.value) : super.dirty();
/// If the value is **not** null, but empty. /// If the value is **not** null, but empty.
E get onEmpty; E get onEmpty;

View File

@ -1,184 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
part of 'form.dart';
class FormData extends Equatable {
final List<FormInput> _inputs;
const FormData(this._inputs);
const FormData.empty() : this(const <FormInput>[]);
/// Returns all inputs as a list
List<FormInput> inputs() => _inputs;
/// Returns the input for the associated key
FormInput inputOf(String key) {
if (contains(key)) {
return _inputs.firstWhere((input) => input.key == key);
} else {
throw Exception('FormInput with key `$key` does not exist in form');
}
}
/// Updates a input (perform a replace at index).
void updateInput(String key, FormInput input) {
if (contains(key)) {
final index = _inputs.indexOf(
inputOf(key),
);
_inputs[index] = input;
}
}
/// Returns all associated validators as a list
List<FormInputValidator<V, E>> validators<V, E extends ValidationError>() =>
_inputs
.map<FormInputValidator<V, E>>(
(input) => input.validator as FormInputValidator<V, E>,
)
.toList();
/// A [FormInputValidator] represents the value of a single form input field.
/// It contains information about the [FormInputStatus], value, as well
/// as validation status.
T validatorOf<T>(String key) => inputOf(key).validator as T;
/// Updates validator of a given input. (perform copyWith)
void updateValidator<V>(
String key,
FormInputValidator<V, ValidationError> dirtyValue,
) {
if (contains(key)) {
final index = _inputs.indexOf(
inputOf(key),
);
_inputs[index] = _inputs[index].copyWith(validator: dirtyValue);
}
}
/// Returns a validation error if the [FormInputValidator] is invalid.
/// Returns null if the [FormInputValidator] is valid.
E? errorOf<E extends ValidationError>(String key) =>
(inputOf(key).validator as FormInputValidator<dynamic, E>).error;
/// The value of the associated [FormInputValidator]. For example,
/// if you have a FormInputValidator for FirstName, the value could be 'Joe'.
V valueOf<V>(String key) =>
(inputOf(key).validator as FormInputValidator<V, dynamic>).value;
/// Returns `true` if the [FormInputValidator] is not valid.
/// Same as `E? errorOf<V, E>(String key) != null`
bool isNotValid(String key) => !inputOf(key).validator.valid;
/// Returns all associated metadata as a list
List<FormInputMetadata<M>> metadata<M>() => _inputs
.map<FormInputMetadata<M>>(
(input) => input.metadata as FormInputMetadata<M>,
)
.toList();
/// Returns the metadata associated. With `M` the type of extra data.
FormInputMetadata<M> metadataOf<M>(String key) =>
inputOf(key).metadata as FormInputMetadata<M>;
/// Updates metadata of a given input. (perform copyWith)
void updateMetadata<M>(String key, FormInputMetadata<M> meta) {
if (contains(key)) {
final index = _inputs.indexOf(
inputOf(key),
);
_inputs[index] = _inputs[index].copyWith(metadata: meta);
}
}
/// Check if this contains an input with the given key.
bool contains(String key) => _inputs.any((input) => input.key == key);
/// Makes an intersection set operation and returns newly created [FormData]
FormData intersection(FormData other) {
final List<FormInput> inputs = <FormInput>[];
for (final FormInput i in _inputs) {
if (other.contains(i.key)) {
inputs.add(i);
}
}
return FormData(inputs);
}
/// Makes a difference set operation and returns newly created [FormData]
FormData difference(FormData other) {
final List<FormInput> inputs = <FormInput>[];
for (final FormInput i in other._inputs) {
if (!contains(i.key)) {
inputs.add(i);
}
}
for (final FormInput i in _inputs) {
if (!other.contains(i.key)) {
inputs.add(i);
}
}
return FormData(inputs);
}
/// Makes an union set operation and returns newly created [FormData]
FormData union(FormData other) {
final List<FormInput> inputs = <FormInput>[];
for (final FormInput i in _inputs) {
inputs.add(i);
}
for (final FormInput i in other._inputs) {
if (!contains(i.key)) {
inputs.add(i);
}
}
return FormData(inputs);
}
/// Deeply copy this.
FormData clone() => FormData(
_inputs.map((input) => input.clone()).toList(),
);
/// Export this to [Map] format.
Map<String, dynamic> toMap() {
final map = <String, dynamic>{};
for (final input in _inputs) {
if (input.metadata.export) {
map[input.name] = input.validator.value;
}
}
return map;
}
/// Export this to [String] format.
String toJson() => jsonEncode(toMap());
@override
bool? get stringify => true;
@override
List<Object?> get props => _inputs;
}

View File

@ -0,0 +1,47 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:async';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:wyatt_form_bloc/src/core/enums/form_status.dart';
import 'package:wyatt_form_bloc/src/core/enums/set_operations.dart';
import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart';
import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart';
import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart';
part 'form_data_state.dart';
abstract class FormDataCubit<State extends FormDataState> extends Cubit<State> {
FormDataCubit(super.initialState) : super();
FutureOr<void> dataChanged<Value>(
String key,
FormInputValidator<Value, ValidationError> dirtyValue,
);
FutureOr<void> update(
WyattForm form, {
SetOperation operation = SetOperation.replace,
});
FutureOr<void> validate();
FutureOr<void> reset();
FutureOr<void> submit();
}

View File

@ -16,35 +16,19 @@
part of 'form_data_cubit.dart'; part of 'form_data_cubit.dart';
class FormDataState extends Equatable { abstract class FormDataState extends Equatable {
/// Global status of a form. /// Global status of a form.
final FormStatus status; final FormStatus status;
/// FormData with all inputs, and associated metadata. /// FormData with all inputs, and associated metadata.
final FormData data; final WyattForm form;
/// Optional error message. /// Optional error message.
final String? errorMessage; final String? errorMessage;
const FormDataState({ const FormDataState({
required this.data, required this.form,
this.status = FormStatus.pure, this.status = FormStatus.pure,
this.errorMessage, this.errorMessage,
}); });
FormDataState copyWith({
FormStatus? status,
FormData? data,
String? errorMessage,
}) => FormDataState(
status: status ?? this.status,
data: data ?? this.data,
errorMessage: errorMessage ?? this.errorMessage,
);
@override
bool? get stringify => true;
@override
List<Object?> get props => [status, data, errorMessage];
} }

View File

@ -0,0 +1,86 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:async';
import 'package:wyatt_form_bloc/src/core/enums/form_status.dart';
import 'package:wyatt_form_bloc/src/core/enums/set_operations.dart';
import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart';
import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart';
import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart';
import 'package:wyatt_form_bloc/src/presentation/features/form_data/form_data_cubit.dart';
part 'form_data_state_impl.dart';
abstract class FormDataCubitImpl extends FormDataCubit<FormDataStateImpl> {
FormDataCubitImpl(WyattForm form) : super(FormDataStateImpl(form: form));
@override
FutureOr<void> dataChanged<Value>(
String key,
FormInputValidator<Value, ValidationError> dirtyValue,
) {
final form = state.form.clone();
try {
form.updateValidator(key, dirtyValue);
} catch (e) {
rethrow;
}
emit(
state.copyWith(
form: form,
status: form.validate(),
),
);
}
@override
FutureOr<void> update(
WyattForm form, {
SetOperation operation = SetOperation.replace,
}) {
final WyattForm newForm = operation.operation.call(state.form, form);
emit(
state.copyWith(
form: newForm,
status: newForm.validate(),
),
);
}
@override
FutureOr<void> reset() {
final form = state.form.reset();
emit(
state.copyWith(
form: form,
status: form.validate(),
),
);
}
@override
FutureOr<void> validate() {
emit(
state.copyWith(
status: state.form.validate(),
),
);
}
}

View File

@ -0,0 +1,42 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
part of 'form_data_cubit_impl.dart';
class FormDataStateImpl extends FormDataState {
const FormDataStateImpl({
required super.form,
super.status = FormStatus.pure,
super.errorMessage,
});
FormDataStateImpl copyWith({
FormStatus? status,
WyattForm? form,
String? errorMessage,
}) =>
FormDataStateImpl(
status: status ?? this.status,
form: form ?? this.form,
errorMessage: errorMessage ?? this.errorMessage,
);
@override
bool? get stringify => true;
@override
List<Object?> get props => [status, form, errorMessage];
}

View File

@ -0,0 +1,43 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
class InputBuilder<Cubit extends FormDataCubit> extends StatelessWidget {
const InputBuilder({required this.field, required this.builder, super.key});
final String field;
final Widget Function(
BuildContext context,
Cubit cubit,
FormDataState state,
String field,
bool inputValid,
) builder;
@override
Widget build(BuildContext context) =>
BlocBuilder<Cubit, FormDataState>(
builder: (context, state) {
final cubit = context.read<Cubit>();
final inputValid = state.form.validatorOf(field).valid;
return builder.call(context, cubit, state, field, inputValid);
},
);
}

View File

@ -0,0 +1,57 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:wyatt_form_bloc/src/domain/entities/form_input_metadata.dart';
import 'package:wyatt_form_bloc/src/presentation/presentation.dart';
class InputBuilderMetadata<Cubit extends FormDataCubit, Extra>
extends StatelessWidget {
const InputBuilderMetadata({
required this.field,
required this.builder,
super.key,
});
final String field;
final Widget Function(
BuildContext context,
Cubit cubit,
FormDataState state,
String field,
bool inputValid,
FormInputMetadata<Extra>? metadata,
) builder;
@override
Widget build(BuildContext context) => BlocBuilder<Cubit, FormDataState>(
builder: (context, state) {
final cubit = context.read<Cubit>();
final inputValid = state.form.validatorOf(field).valid;
final metadata = state.form.metadataOf<Extra>(field);
return builder.call(
context,
cubit,
state,
field,
inputValid,
metadata,
);
},
);
}

View File

@ -0,0 +1,37 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:wyatt_form_bloc/src/core/enums/form_status.dart';
import 'package:wyatt_form_bloc/src/presentation/features/form_data/form_data_cubit.dart';
class SubmitBuilder<Cubit extends FormDataCubit> extends StatelessWidget {
const SubmitBuilder({required this.builder, super.key});
final Widget Function(BuildContext context, Cubit cubit, FormStatus status)
builder;
@override
Widget build(BuildContext context) => BlocBuilder<Cubit, FormDataState>(
buildWhen: (previous, current) => previous.status != current.status,
builder: (context, state) => builder.call(
context,
context.read<Cubit>(),
state.status,
),
);
}

View File

@ -0,0 +1,21 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'features/form_data/form_data_cubit.dart';
export 'features/form_data_impl/form_data_cubit_impl.dart';
export 'features/widgets/input_builder.dart';
export 'features/widgets/input_builder_metadata.dart';
export 'features/widgets/submit_builder.dart';

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'cubit/form_data_cubit.dart'; export 'core/core.dart';
export 'enums/enums.dart'; export 'data/data.dart';
export 'form/form.dart'; export 'domain/domain.dart';
export 'validators/validators.dart'; export 'presentation/presentation.dart';

View File

@ -1,39 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_form_bloc/src/enums/enums.dart';
import 'package:wyatt_form_bloc/src/validators/inputs/base/regex_validator.dart';
/// {@template iban}
/// Form input for an IBAN input.
/// {@endtemplate}
class Iban extends RegexValidator<ValidationStandardError> {
/// {@macro iban}
const Iban.pure() : super.pure();
/// {@macro iban}
const Iban.dirty([super.value = '']) : super.dirty();
@override
ValidationStandardError get onEmpty => ValidationStandardError.empty;
@override
ValidationStandardError get onError => ValidationStandardError.invalid;
@override
RegExp get regex => RegExp(
r'^(?:((?:IT|SM)\d{2}[A-Z]{1}\d{22})|(NL\d{2}[A-Z]{4}\d{10})|(LV\d{2}[A-Z]{4}\d{13})|((?:BG|GB|IE)\d{2}[A-Z]{4}\d{14})|(GI\d{2}[A-Z]{4}\d{15})|(RO\d{2}[A-Z]{4}\d{16})|(MT\d{2}[A-Z]{4}\d{23})|(NO\d{13})|((?:DK|FI)\d{16})|((?:SI)\d{17})|((?:AT|EE|LU|LT)\d{18})|((?:HR|LI|CH)\d{19})|((?:DE|VA)\d{20})|((?:AD|CZ|ES|MD|SK|SE)\d{22})|(PT\d{23})|((?:IS)\d{24})|((?:BE)\d{14})|((?:FR|MC|GR)\d{25})|((?:PL|HU|CY)\d{26}))$',
);
}

View File

@ -1,50 +0,0 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_form_bloc/src/enums/enums.dart';
import 'package:wyatt_form_bloc/src/form/form.dart';
/// {@template list_option}
/// Form input for a list input
/// {@endtemplate}
class ListOption<T>
extends FormInputValidator<List<T>, ValidationStandardError> {
/// {@macro list_option}
const ListOption.pure({List<T>? defaultValue})
: super.pure(defaultValue ?? const []);
/// {@macro list_option}
const ListOption.dirty({List<T>? value}) : super.dirty(value ?? const []);
ListOption<T> select(T? v) {
if (v == null) {
return this;
}
if (value.contains(v)) {
final List<T> newValue = List.from(value)..remove(v);
return ListOption<T>.dirty(value: newValue);
} else {
final List<T> newValue = List.from(value)..add(v);
return ListOption<T>.dirty(value: newValue);
}
}
@override
ValidationStandardError? validator(List<T>? value) =>
value?.isNotEmpty ?? false == true
? null
: ValidationStandardError.invalid;
}

View File

@ -3,15 +3,33 @@ description: Manage forms in Dart & Flutter with Bloc
repository: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_form_bloc repository: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_form_bloc
version: 0.0.6 version: 0.0.6
publish_to: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub
environment: environment:
sdk: '>=2.17.2 <3.0.0' sdk: ">=2.17.2 <3.0.0"
dependencies: dependencies:
bloc: ^8.1.0 flutter:
sdk: flutter
flutter_bloc: ^8.1.0
equatable: ^2.0.5 equatable: ^2.0.5
wyatt_architecture:
git:
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages
ref: wyatt_architecture-v0.0.2-dev.0
path: packages/wyatt_architecture
wyatt_type_utils:
git:
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages
ref: wyatt_type_utils-v0.0.3+1
path: packages/wyatt_type_utils
dev_dependencies: dev_dependencies:
test: ^1.21.4 flutter_test:
sdk: flutter
wyatt_analysis: wyatt_analysis:
git: git: