diff --git a/packages/wyatt_form_bloc/README.md b/packages/wyatt_form_bloc/README.md
index 74a53324..82fb439a 100644
--- a/packages/wyatt_form_bloc/README.md
+++ b/packages/wyatt_form_bloc/README.md
@@ -16,16 +16,16 @@
* along with this program. If not, see .
-->
-# Dart - Form BLoC
+# Flutter - Form BLoC
-
+
-Form Bloc for Dart & Flutter.
+Form Bloc for Flutter.
## Features
diff --git a/packages/wyatt_form_bloc/analysis_options.yaml b/packages/wyatt_form_bloc/analysis_options.yaml
index cc27c8ca..b0c6aced 100644
--- a/packages/wyatt_form_bloc/analysis_options.yaml
+++ b/packages/wyatt_form_bloc/analysis_options.yaml
@@ -1,4 +1,4 @@
-include: package:wyatt_analysis/analysis_options.yaml
+include: package:wyatt_analysis/analysis_options.flutter.yaml
analyzer:
exclude: "!example/**"
\ No newline at end of file
diff --git a/packages/wyatt_form_bloc/example/ios/Flutter/AppFrameworkInfo.plist b/packages/wyatt_form_bloc/example/ios/Flutter/AppFrameworkInfo.plist
index 8d4492f9..9625e105 100644
--- a/packages/wyatt_form_bloc/example/ios/Flutter/AppFrameworkInfo.plist
+++ b/packages/wyatt_form_bloc/example/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
CFBundleVersion1.0MinimumOSVersion
- 9.0
+ 11.0
diff --git a/packages/wyatt_form_bloc/example/ios/Runner.xcodeproj/project.pbxproj b/packages/wyatt_form_bloc/example/ios/Runner.xcodeproj/project.pbxproj
index abeae7fe..8a34f9c6 100644
--- a/packages/wyatt_form_bloc/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/packages/wyatt_form_bloc/example/ios/Runner.xcodeproj/project.pbxproj
@@ -272,7 +272,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -349,7 +349,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -398,7 +398,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
diff --git a/packages/wyatt_form_bloc/example/ios/Runner/Info.plist b/packages/wyatt_form_bloc/example/ios/Runner/Info.plist
index 7f67e595..18f4a4be 100644
--- a/packages/wyatt_form_bloc/example/ios/Runner/Info.plist
+++ b/packages/wyatt_form_bloc/example/ios/Runner/Info.plist
@@ -43,5 +43,7 @@
UIViewControllerBasedStatusBarAppearance
+ CADisableMinimumFrameDurationOnPhone
+
diff --git a/packages/wyatt_form_bloc/example/lib/app/app.dart b/packages/wyatt_form_bloc/example/lib/app/app.dart
index 611f20ac..f9000f08 100644
--- a/packages/wyatt_form_bloc/example/lib/app/app.dart
+++ b/packages/wyatt_form_bloc/example/lib/app/app.dart
@@ -18,7 +18,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:form_bloc_example/app/metadata.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:wyatt_form_bloc/wyatt_form_bloc.dart';
@@ -29,41 +29,43 @@ class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
static List getNormalEntries() {
- return const [
- FormInput(formFieldName, Name.pure(), metadata: FormInputMetadata(extra: blue)),
- FormInput(formFieldEmail, Email.pure(), metadata: FormInputMetadata(extra: blue)),
- FormInput(formFieldPhone, Phone.pure(), metadata: FormInputMetadata(extra: red)),
- FormInput(
- formFieldList, ListOption.pure(defaultValue: ['checkbox3'])),
- FormInput(formFieldRadio, TextString.pure()),
- FormInput(formFieldPro, Boolean.pure(),
- metadata: FormInputMetadata(name: 'business')),
- FormInput(formFieldHidden, Boolean.pure(),
- metadata: FormInputMetadata(export: false, extra: blue)),
+ return [
+ FormInput(formFieldName, const Name.pure(),
+ metadata: const FormInputMetadata(extra: blue)),
+ FormInput(formFieldEmail, const Email.pure(),
+ metadata: const FormInputMetadata(extra: blue)),
+ FormInput(formFieldList,
+ const ListOption.pure(defaultValue: 'c3')),
+ FormInput(formFieldRadio, const TextString.pure()),
+ FormInput(formFieldPro, const Boolean.pure(),
+ metadata: const FormInputMetadata(name: 'business')),
+ FormInput(formFieldHidden, const Boolean.pure(),
+ metadata:
+ const FormInputMetadata(export: false, extra: blue)),
];
}
static List getBusinessEntries() {
- const entries = [
- FormInput(formFieldSiren, Siren.pure()),
- FormInput(formFieldIban, Iban.pure()),
+ final entries = [
+ FormInput(formFieldPhone, const Phone.pure(),
+ metadata: const FormInputMetadata(extra: red)),
];
return getNormalEntries() + entries;
}
- static FormData getNormalFormData() {
- return FormData(getNormalEntries());
+ static WyattForm getNormalFormData() {
+ return WyattFormImpl(getNormalEntries());
}
- static FormData getProFormData() {
- return FormData(getBusinessEntries());
+ static WyattForm getProFormData() {
+ return WyattFormImpl(getBusinessEntries());
}
@override
Widget build(BuildContext context) {
- FormDataCubit formCubit = CustomFormCubit(inputs: getNormalFormData());
+ SimpleCustomFormCubit formCubit = SimpleCustomFormCubit(getNormalFormData());
- return BlocProvider(
+ return BlocProvider(
create: (context) => formCubit,
child: const WidgetTree(),
);
diff --git a/packages/wyatt_form_bloc/example/lib/app/metadata.dart b/packages/wyatt_form_bloc/example/lib/app/metadata.dart
index b4e68725..6d66f961 100644
--- a/packages/wyatt_form_bloc/example/lib/app/metadata.dart
+++ b/packages/wyatt_form_bloc/example/lib/app/metadata.dart
@@ -28,7 +28,5 @@ class Metadata {
});
@override
- String toString() {
- return category.toString();
- }
+ String toString() => 'Metadata(category: $category)';
}
diff --git a/packages/wyatt_form_bloc/example/lib/constants.dart b/packages/wyatt_form_bloc/example/lib/constants.dart
index c1c52795..573b5ef6 100644
--- a/packages/wyatt_form_bloc/example/lib/constants.dart
+++ b/packages/wyatt_form_bloc/example/lib/constants.dart
@@ -17,8 +17,6 @@
const String formFieldName = 'name';
const String formFieldPhone = 'phone';
const String formFieldEmail = 'email';
-const String formFieldSiren = 'siren';
-const String formFieldIban = 'iban';
const String formFieldList = 'list';
const String formFieldRadio = 'radio';
const String formFieldHidden = 'hidden';
diff --git a/packages/wyatt_form_bloc/example/lib/sign_up/widgets/sign_up_form.dart b/packages/wyatt_form_bloc/example/lib/sign_up/widgets/sign_up_form.dart
index 947faf6d..14ac7fbc 100644
--- a/packages/wyatt_form_bloc/example/lib/sign_up/widgets/sign_up_form.dart
+++ b/packages/wyatt_form_bloc/example/lib/sign_up/widgets/sign_up_form.dart
@@ -14,39 +14,33 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-import 'dart:developer';
-
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:form_bloc_example/app/app.dart';
import 'package:form_bloc_example/app/metadata.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';
-class CategoryIndicator extends StatelessWidget {
- const CategoryIndicator(
- {Key? key, required this.field, required this.builder})
- : super(key: key);
-
- final String field;
- final Function(BuildContext context, FormDataState state) builder;
-
- Color computeColor(Metadata meta) {
- switch (meta.category) {
- case Category.perso:
- return Colors.blue;
- case Category.business:
- return Colors.red;
- }
+Color computeColor(Metadata? meta) {
+ switch (meta?.category) {
+ case Category.perso:
+ return Colors.blue;
+ case Category.business:
+ return Colors.red;
+ case null:
+ return Colors.green;
}
+}
+class _NameInput extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return BlocBuilder(
- builder: ((context, state) {
- final meta = state.data.metadataOf(field).extra as Metadata;
+ return InputBuilderMetadata(
+ field: formFieldName,
+ builder: (context, cubit, state, field, inputValid, metadata) {
+ final meta = state.form.metadataOf(field).extra;
final color = computeColor(meta);
-
return Row(
children: [
Container(
@@ -57,58 +51,24 @@ class CategoryIndicator extends StatelessWidget {
borderRadius: BorderRadius.circular(8),
),
),
- const SizedBox(width: 20,),
+ const SizedBox(
+ width: 20,
+ ),
SizedBox(
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()
- .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()
- .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 {
@override
Widget build(BuildContext context) {
- return CategoryIndicator(
- field: formFieldPhone,
- builder: (context, state) {
- return TextField(
- onChanged: (phone) => context
- .read()
- .dataChanged(formFieldPhone, Phone.dirty(phone)),
- keyboardType: TextInputType.phone,
- decoration: InputDecoration(
- labelText: 'phone',
- helperText: '',
- errorText:
- state.data.isNotValid(formFieldPhone) ? 'invalid phone' : null,
- ),
+ return InputBuilderMetadata(
+ field: formFieldPhone,
+ builder: (context, cubit, state, field, inputValid, metadata) {
+ final meta = state.form.metadataOf(field).extra;
+ final color = computeColor(meta);
+ return Row(
+ children: [
+ Container(
+ height: 8,
+ width: 8,
+ decoration: BoxDecoration(
+ color: color,
+ 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
Widget build(BuildContext context) {
- return CategoryIndicator(
- field: formFieldName,
- builder: (context, state) {
- return TextField(
- onChanged: (siren) => context
- .read()
- .dataChanged(formFieldSiren, Siren.dirty(siren)),
- keyboardType: TextInputType.number,
- decoration: InputDecoration(
- labelText: 'siren',
- helperText: '',
- errorText:
- state.data.isNotValid(formFieldSiren) ? 'invalid SIREN' : null,
- ),
- );
- },
- );
- }
-}
-
-class _IbanInput extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return BlocBuilder(
- builder: (context, state) {
- return TextField(
- onChanged: (iban) => context
- .read()
- .dataChanged(formFieldIban, Iban.dirty(iban)),
- keyboardType: TextInputType.text,
- decoration: InputDecoration(
- labelText: 'iban',
- helperText: '',
- errorText:
- state.data.isNotValid(formFieldIban) ? 'invalid IBAN' : null,
- ),
- );
- },
+ return InputBuilder(
+ field: formFieldEmail,
+ builder: (context, cubit, state, field, inputValid) => TextField(
+ onChanged: (value) => cubit.dataChanged(field, Email.dirty(value)),
+ keyboardType: TextInputType.emailAddress,
+ decoration: InputDecoration(
+ labelText: 'email',
+ helperText: '',
+ errorText: !inputValid ? 'invalid email' : null,
+ ),
+ ),
);
}
}
@@ -185,11 +136,11 @@ class _IbanInput extends StatelessWidget {
class _CheckListInput extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return BlocBuilder(
- builder: (context, state) {
- final input =
- state.data.validatorOf>(formFieldList);
- final options = input.value;
+ return InputBuilder(
+ field: formFieldList,
+ builder: (context, cubit, state, field, inputValid) {
+ final input = state.form.validatorOf>(field);
+ final choices = ['c1', 'c2', 'c3'];
return Column(
mainAxisSize: MainAxisSize.min,
@@ -197,34 +148,34 @@ class _CheckListInput extends StatelessWidget {
ListTile(
title: const Text('Checkbox1'),
trailing: Checkbox(
- value: options.contains('checkbox1'),
+ value: input.value == choices[0],
onChanged: (_) {
- context.read().dataChanged(
- formFieldList,
- input.select('checkbox1'),
- );
+ cubit.dataChanged(
+ field,
+ ListOption.dirty(choices: choices, value: choices[0]),
+ );
}),
),
ListTile(
title: const Text('Checkbox2'),
trailing: Checkbox(
- value: options.contains('checkbox2'),
+ value: input.value == choices[1],
onChanged: (_) {
- context.read().dataChanged(
- formFieldList,
- input.select('checkbox2'),
- );
+ cubit.dataChanged(
+ field,
+ ListOption.dirty(choices: choices, value: choices[1]),
+ );
}),
),
ListTile(
title: const Text('Checkbox3 (default)'),
trailing: Checkbox(
- value: options.contains('checkbox3'),
+ value: input.value == choices[2],
onChanged: (_) {
- context.read().dataChanged(
- formFieldList,
- input.select('checkbox3'),
- );
+ cubit.dataChanged(
+ field,
+ ListOption.dirty(choices: choices, value: choices[2]),
+ );
}),
),
],
@@ -237,9 +188,10 @@ class _CheckListInput extends StatelessWidget {
class _RadioListInput extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return BlocBuilder(
- builder: (context, state) {
- final input = state.data.validatorOf(formFieldRadio);
+ return InputBuilder(
+ field: formFieldRadio,
+ builder: ((context, cubit, state, field, inputValid) {
+ final input = state.form.validatorOf(field);
return Column(
mainAxisSize: MainAxisSize.min,
@@ -250,10 +202,10 @@ class _RadioListInput extends StatelessWidget {
groupValue: true,
value: input.value == 'radio1',
onChanged: (_) {
- context.read().dataChanged(
- formFieldRadio,
- const TextString.dirty('radio1'),
- );
+ cubit.dataChanged(
+ field,
+ const TextString.dirty('radio1'),
+ );
}),
),
ListTile(
@@ -262,10 +214,10 @@ class _RadioListInput extends StatelessWidget {
groupValue: true,
value: input.value == 'radio2',
onChanged: (_) {
- context.read().dataChanged(
- formFieldRadio,
- const TextString.dirty('radio2'),
- );
+ cubit.dataChanged(
+ field,
+ const TextString.dirty('radio2'),
+ );
}),
),
ListTile(
@@ -274,15 +226,15 @@ class _RadioListInput extends StatelessWidget {
groupValue: true,
value: input.value == 'radio3',
onChanged: (_) {
- context.read().dataChanged(
- formFieldRadio,
- const TextString.dirty('radio3'),
- );
+ cubit.dataChanged(
+ field,
+ const TextString.dirty('radio3'),
+ );
}),
),
],
);
- },
+ }),
);
}
}
@@ -292,17 +244,18 @@ class _CheckHiddenInput extends StatelessWidget {
Widget build(BuildContext context) {
return ListTile(
title: const Text('This input is not exported'),
- trailing: BlocBuilder(
- builder: (context, state) {
+ trailing: InputBuilder(
+ field: formFieldHidden,
+ builder: (context, cubit, state, field, inputValid) {
return Checkbox(
- value: state.data.validatorOf(formFieldHidden).value,
+ value: state.form.validatorOf(field).value,
onChanged: (v) {
final value = v!; // state is false, so value can't be null
- context.read().dataChanged(
- formFieldHidden,
- Boolean.dirty(value: value),
- );
+ cubit.dataChanged(
+ field,
+ Boolean.dirty(value: value),
+ );
});
},
),
@@ -315,25 +268,24 @@ class _CheckIsProInput extends StatelessWidget {
Widget build(BuildContext context) {
return ListTile(
title: const Text('Are you a pro?'),
- trailing: BlocBuilder(
- builder: (context, state) {
+ trailing: InputBuilder(
+ field: formFieldPro,
+ builder: (context, cubit, state, field, inputValid) {
return Checkbox(
- value: state.data.validatorOf(formFieldPro).value,
- onChanged: (isPro) {
- final value = isPro!; // state is false, so value can't be null
+ value: state.form.validatorOf(field).value,
+ onChanged: (v) {
+ final value = v!; // state is false, so value can't be null
- context.read().dataChanged(
- formFieldPro,
- Boolean.dirty(value: value),
- );
+ cubit.dataChanged(
+ field,
+ Boolean.dirty(value: value),
+ );
if (value) {
- context.read().updateFormData(
- App.getProFormData(),
+ cubit.update(App.getProFormData(),
operation: SetOperation.union);
} else {
- context.read().updateFormData(
- App.getNormalFormData(),
+ cubit.update(App.getNormalFormData(),
operation: SetOperation.intersection);
}
});
@@ -346,49 +298,28 @@ class _CheckIsProInput extends StatelessWidget {
class _SignUpButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return BlocBuilder(
- buildWhen: (previous, current) => previous.status != current.status,
- builder: (context, state) {
- return state.status.isSubmissionInProgress
- ? const CircularProgressIndicator()
- : ElevatedButton(
- onPressed: state.status.isValidated
- ? () => context.read().submitForm()
- : null,
- child: const Text('SIGN UP'),
- );
- },
- );
- }
-}
-
-class _DebugButton extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return BlocBuilder(
- builder: (context, state) {
- return ElevatedButton(
- onPressed: () {
- log(state.toString());
- },
- child: const Text('DEBUG'),
- );
- },
- );
+ return SubmitBuilder(
+ builder: (context, cubit, status) {
+ return status.isSubmissionInProgress
+ ? const CircularProgressIndicator()
+ : ElevatedButton(
+ onPressed: status.isValidated ? () => cubit.submit() : null,
+ child: const Text('SIGN UP'),
+ );
+ });
}
}
class _ResetButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return BlocBuilder(
- builder: (context, state) {
- return ElevatedButton(
- onPressed: () => context.read().resetForm(),
- child: const Text('RESET'),
- );
- },
- );
+ return SubmitBuilder(
+ builder: (context, cubit, status) {
+ return ElevatedButton(
+ onPressed: () => cubit.reset(),
+ child: const Text('RESET'),
+ );
+ });
}
}
@@ -397,7 +328,7 @@ class SignUpForm extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return BlocListener(
+ return BlocListener(
listener: (context, state) {
if (state.status.isSubmissionSuccess) {
Navigator.of(context).pop(); // Never happens here
@@ -418,8 +349,6 @@ class SignUpForm extends StatelessWidget {
const SizedBox(height: 8),
_EmailInput(),
const SizedBox(height: 8),
- _PhoneInput(),
- const SizedBox(height: 8),
_CheckListInput(),
const SizedBox(height: 8),
_RadioListInput(),
@@ -428,13 +357,12 @@ class SignUpForm extends StatelessWidget {
const SizedBox(height: 8),
_CheckIsProInput(),
const SizedBox(height: 8),
- BlocBuilder(
+ BlocBuilder(
builder: (context, state) {
- if (state.data.validatorOf(formFieldPro).value) {
+ if (state.form.validatorOf(formFieldPro).value ??
+ false) {
return Column(children: [
- _SirenInput(),
- const SizedBox(height: 8),
- _IbanInput(),
+ _PhoneInput(),
const SizedBox(height: 8),
]);
}
@@ -444,8 +372,6 @@ class SignUpForm extends StatelessWidget {
const SizedBox(height: 8),
_SignUpButton(),
const SizedBox(height: 8),
- _DebugButton(),
- const SizedBox(height: 8),
_ResetButton(),
],
),
diff --git a/packages/wyatt_form_bloc/example/lib/cubit/custom_form_cubit.dart b/packages/wyatt_form_bloc/example/lib/simple_custom_form_cubit/simple_custom_form_cubit.dart
similarity index 80%
rename from packages/wyatt_form_bloc/example/lib/cubit/custom_form_cubit.dart
rename to packages/wyatt_form_bloc/example/lib/simple_custom_form_cubit/simple_custom_form_cubit.dart
index 3f211c60..b8b0dfca 100644
--- a/packages/wyatt_form_bloc/example/lib/cubit/custom_form_cubit.dart
+++ b/packages/wyatt_form_bloc/example/lib/simple_custom_form_cubit/simple_custom_form_cubit.dart
@@ -18,12 +18,13 @@ import 'dart:developer';
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
-class CustomFormCubit extends FormDataCubit {
- CustomFormCubit({required FormData inputs}) : super(inputs: inputs);
+class SimpleCustomFormCubit extends FormDataCubitImpl {
+ SimpleCustomFormCubit(super.form) : super();
@override
- Future submitForm() {
- log(state.data.toMap().toString());
+ Future submit() {
+ final value = FormJsonEncoder().encode(state.form);
+ log(value);
return Future.value();
}
diff --git a/packages/wyatt_form_bloc/lib/src/validators/validators.dart b/packages/wyatt_form_bloc/lib/src/core/core.dart
similarity index 60%
rename from packages/wyatt_form_bloc/lib/src/validators/validators.dart
rename to packages/wyatt_form_bloc/lib/src/core/core.dart
index b9eaa4b6..9d6c64d8 100644
--- a/packages/wyatt_form_bloc/lib/src/validators/validators.dart
+++ b/packages/wyatt_form_bloc/lib/src/core/core.dart
@@ -14,17 +14,4 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-export 'form/every_input_validator.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';
+export 'enums/enums.dart';
diff --git a/packages/wyatt_form_bloc/lib/src/enums/enums.dart b/packages/wyatt_form_bloc/lib/src/core/enums/enums.dart
similarity index 100%
rename from packages/wyatt_form_bloc/lib/src/enums/enums.dart
rename to packages/wyatt_form_bloc/lib/src/core/enums/enums.dart
diff --git a/packages/wyatt_form_bloc/lib/src/enums/form_input_status.dart b/packages/wyatt_form_bloc/lib/src/core/enums/form_input_status.dart
similarity index 100%
rename from packages/wyatt_form_bloc/lib/src/enums/form_input_status.dart
rename to packages/wyatt_form_bloc/lib/src/core/enums/form_input_status.dart
diff --git a/packages/wyatt_form_bloc/lib/src/enums/form_status.dart b/packages/wyatt_form_bloc/lib/src/core/enums/form_status.dart
similarity index 100%
rename from packages/wyatt_form_bloc/lib/src/enums/form_status.dart
rename to packages/wyatt_form_bloc/lib/src/core/enums/form_status.dart
diff --git a/packages/wyatt_form_bloc/lib/src/core/enums/set_operations.dart b/packages/wyatt_form_bloc/lib/src/core/enums/set_operations.dart
new file mode 100644
index 00000000..3e27f437
--- /dev/null
+++ b/packages/wyatt_form_bloc/lib/src/core/enums/set_operations.dart
@@ -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 .
+
+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);
+}
diff --git a/packages/wyatt_form_bloc/lib/src/enums/validation_error.dart b/packages/wyatt_form_bloc/lib/src/core/enums/validation_error.dart
similarity index 97%
rename from packages/wyatt_form_bloc/lib/src/enums/validation_error.dart
rename to packages/wyatt_form_bloc/lib/src/core/enums/validation_error.dart
index 5a0b436e..4966b6b8 100644
--- a/packages/wyatt_form_bloc/lib/src/enums/validation_error.dart
+++ b/packages/wyatt_form_bloc/lib/src/core/enums/validation_error.dart
@@ -23,5 +23,6 @@ abstract class ValidationError {}
enum ValidationStandardError implements ValidationError {
invalid,
- empty
+ empty,
+ notEqual,
}
diff --git a/packages/wyatt_form_bloc/lib/src/cubit/form_data_cubit.dart b/packages/wyatt_form_bloc/lib/src/cubit/form_data_cubit.dart
deleted file mode 100644
index 5434acac..00000000
--- a/packages/wyatt_form_bloc/lib/src/cubit/form_data_cubit.dart
+++ /dev/null
@@ -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 .
-
-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 {
- 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(
- String field,
- FormInputValidator 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 submitForm();
-}
diff --git a/packages/wyatt_form_bloc/lib/src/data/data.dart b/packages/wyatt_form_bloc/lib/src/data/data.dart
new file mode 100644
index 00000000..743bef59
--- /dev/null
+++ b/packages/wyatt_form_bloc/lib/src/data/data.dart
@@ -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 .
+
+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';
diff --git a/packages/wyatt_form_bloc/lib/src/data/form/wyatt_form_impl.dart b/packages/wyatt_form_bloc/lib/src/data/form/wyatt_form_impl.dart
new file mode 100644
index 00000000..235ce5c8
--- /dev/null
+++ b/packages/wyatt_form_bloc/lib/src/data/form/wyatt_form_impl.dart
@@ -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 .
+
+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>> _inputs;
+ final FormValidator _validator;
+
+ late List<
+ FormInput,
+ dynamic>> _inputsInitial;
+
+ WyattFormImpl(
+ this._inputs, {
+ FormValidator validationStrategy = const EveryInputValidator(),
+ }) : _validator = validationStrategy {
+ _inputsInitial = _inputs.map((input) => input.clone()).toList();
+ }
+
+ @override
+ List<
+ FormInput,
+ dynamic>> get inputs => _inputs;
+
+ @override
+ FormValidator get formValidationStrategy => _validator;
+
+ @override
+ bool containsKey(String key) => inputs.any((input) => input.key == key);
+
+ @override
+ List>
+ asValidatorList() => inputs
+ .map>(
+ (input) => input.validator as FormInputValidator,
+ )
+ .toList();
+
+ @override
+ FormInput, 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(String key) =>
+ (inputOf(key).validator as FormInputValidator).error;
+
+ @override
+ FormInputMetadata metadataOf(String key) =>
+ inputOf(key).metadata as FormInputMetadata;
+
+ @override
+ Validator validatorOf<
+ Validator extends FormInputValidator>(
+ String key,
+ ) =>
+ inputOf(key).validator as Validator;
+
+ @override
+ Value? valueOf(String key) =>
+ (inputOf(key).validator as FormInputValidator).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