diff --git a/packages/wyatt_form_bloc/example/lib/app/app.dart b/packages/wyatt_form_bloc/example/lib/app/app.dart
index 3f47e228..60d99e66 100644
--- a/packages/wyatt_form_bloc/example/lib/app/app.dart
+++ b/packages/wyatt_form_bloc/example/lib/app/app.dart
@@ -14,50 +14,48 @@
// 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/constants.dart';
+import 'package:form_bloc_example/cubit/custom_form_cubit.dart';
import 'package:form_bloc_example/sign_up/sign_up_page.dart';
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
- static FormData getNormalFormData() {
- return const FormData([
+ static List getNormalEntries() {
+ return const [
FormEntry(formFieldName, Name.pure()),
FormEntry(formFieldEmail, Email.pure()),
FormEntry(formFieldPhone, Phone.pure()),
+ FormEntry(
+ formFieldList, ListOption.pure(defaultValue: ['checkbox3'])),
+ FormEntry(formFieldRadio, TextString.pure()),
FormEntry(formFieldPro, Boolean.pure(), name: 'business'),
FormEntry(formFieldHidden, Boolean.pure(), export: false),
- ]);
+ ];
+ }
+
+ static List getBusinessEntries() {
+ const entries = [
+ FormEntry(formFieldSiren, Siren.pure()),
+ FormEntry(formFieldIban, Iban.pure()),
+ ];
+ return getNormalEntries() + entries;
+ }
+
+ static FormData getNormalFormData() {
+ return FormData(getNormalEntries());
}
static FormData getProFormData() {
- return const FormData([
- FormEntry(formFieldName, Name.pure()),
- FormEntry(formFieldEmail, Email.pure()),
- FormEntry(formFieldPhone, Phone.pure()),
- FormEntry(formFieldPro, Boolean.pure(), name: 'business'),
- FormEntry(formFieldHidden, Boolean.pure(), export: false),
- FormEntry(formFieldSiren, Siren.pure()),
- FormEntry(formFieldIban, Iban.pure()),
- ]);
- }
-
- Future onSubmit(FormDataState state) async {
- log(state.data.toMap().toString());
- return true;
+ return FormData(getBusinessEntries());
}
@override
Widget build(BuildContext context) {
- FormDataCubit _formCubit = FormDataCubit(
- entries: getNormalFormData(),
- onSubmit: onSubmit,
- );
+ FormDataCubit _formCubit = CustomFormCubit(entries: getNormalFormData());
return BlocProvider(
create: (context) => _formCubit,
diff --git a/packages/wyatt_form_bloc/example/lib/constants.dart b/packages/wyatt_form_bloc/example/lib/constants.dart
index d3bd1850..c1c52795 100644
--- a/packages/wyatt_form_bloc/example/lib/constants.dart
+++ b/packages/wyatt_form_bloc/example/lib/constants.dart
@@ -19,6 +19,8 @@ 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';
const String formFieldPro = 'isPro';
\ No newline at end of file
diff --git a/packages/wyatt_form_bloc/example/lib/cubit/custom_form_cubit.dart b/packages/wyatt_form_bloc/example/lib/cubit/custom_form_cubit.dart
new file mode 100644
index 00000000..6cdae37d
--- /dev/null
+++ b/packages/wyatt_form_bloc/example/lib/cubit/custom_form_cubit.dart
@@ -0,0 +1,33 @@
+// 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:developer';
+
+import 'package:meta/meta.dart';
+import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
+
+part 'custom_form_state.dart';
+
+class CustomFormCubit extends FormDataCubit {
+ CustomFormCubit({required FormData entries}) : super(entries: entries);
+
+ @override
+ Future submitForm() {
+ log(state.data.toMap().toString());
+
+ return Future.value();
+ }
+}
diff --git a/packages/wyatt_form_bloc/example/lib/cubit/custom_form_state.dart b/packages/wyatt_form_bloc/example/lib/cubit/custom_form_state.dart
new file mode 100644
index 00000000..f6993044
--- /dev/null
+++ b/packages/wyatt_form_bloc/example/lib/cubit/custom_form_state.dart
@@ -0,0 +1,22 @@
+// 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 .
+
+part of 'custom_form_cubit.dart';
+
+@immutable
+abstract class CustomFormState {}
+
+class CustomFormInitial extends CustomFormState {}
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 99fa68ae..82d28ed2 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
@@ -135,6 +135,112 @@ class _IbanInput extends StatelessWidget {
}
}
+class _CheckListInput extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return BlocBuilder(
+ builder: (context, state) {
+ final _input =
+ state.data.input>(formFieldList) as ListOption;
+ final _options = _input.value;
+
+ return Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ ListTile(
+ title: const Text('Checkbox1'),
+ trailing: Checkbox(
+ value: _options.contains('checkbox1'),
+ onChanged: (_) {
+ context.read().dataChanged(
+ formFieldList,
+ _input.select('checkbox1'),
+ );
+ }),
+ ),
+ ListTile(
+ title: const Text('Checkbox2'),
+ trailing: Checkbox(
+ value: _options.contains('checkbox2'),
+ onChanged: (_) {
+ context.read().dataChanged(
+ formFieldList,
+ _input.select('checkbox2'),
+ );
+ }),
+ ),
+ ListTile(
+ title: const Text('Checkbox3 (default)'),
+ trailing: Checkbox(
+ value: _options.contains('checkbox3'),
+ onChanged: (_) {
+ context.read().dataChanged(
+ formFieldList,
+ _input.select('checkbox3'),
+ );
+ }),
+ ),
+ ],
+ );
+ },
+ );
+ }
+}
+
+class _RadioListInput extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return BlocBuilder(
+ builder: (context, state) {
+ final _input =
+ state.data.input(formFieldRadio) as TextString;
+
+ return Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ ListTile(
+ title: const Text('Radio1'),
+ trailing: Radio(
+ groupValue: true,
+ value: _input.value == 'radio1',
+ onChanged: (_) {
+ context.read().dataChanged(
+ formFieldRadio,
+ const TextString.dirty('radio1'),
+ );
+ }),
+ ),
+ ListTile(
+ title: const Text('Radio2'),
+ trailing: Radio(
+ groupValue: true,
+ value: _input.value == 'radio2',
+ onChanged: (_) {
+ context.read().dataChanged(
+ formFieldRadio,
+ const TextString.dirty('radio2'),
+ );
+ }),
+ ),
+ ListTile(
+ title: const Text('Radio3'),
+ trailing: Radio(
+ groupValue: true,
+ value: _input.value == 'radio3',
+ onChanged: (_) {
+ context.read().dataChanged(
+ formFieldRadio,
+ const TextString.dirty('radio3'),
+ );
+ }),
+ ),
+ ],
+ );
+ },
+ );
+ }
+}
+
class _CheckHiddenInput extends StatelessWidget {
@override
Widget build(BuildContext context) {
@@ -201,8 +307,7 @@ class _SignUpButton extends StatelessWidget {
? const CircularProgressIndicator()
: ElevatedButton(
onPressed: state.status.isValidated
- ? () =>
- context.read().submitForm()
+ ? () => context.read().submitForm()
: null,
child: const Text('SIGN UP'),
);
@@ -244,42 +349,43 @@ class SignUpForm extends StatelessWidget {
);
}
},
- child: SizedBox(
- height: MediaQuery.of(context).size.height,
- child: Align(
- alignment: const Alignment(0, -1 / 3),
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- _NameInput(),
- const SizedBox(height: 8),
- _EmailInput(),
- const SizedBox(height: 8),
- _PhoneInput(),
- const SizedBox(height: 8),
- _CheckHiddenInput(),
- const SizedBox(height: 8),
- _CheckIsProInput(),
- const SizedBox(height: 8),
- BlocBuilder(
- builder: (context, state) {
- if (state.data.input(formFieldPro).value) {
- return Column(children: [
- _SirenInput(),
- const SizedBox(height: 8),
- _IbanInput(),
- const SizedBox(height: 8),
- ]);
- }
- return const SizedBox.shrink();
- },
- ),
- const SizedBox(height: 8),
- _SignUpButton(),
- const SizedBox(height: 8),
- _DebugButton(),
- ],
- ),
+ child: Align(
+ alignment: const Alignment(0, -1 / 3),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ _NameInput(),
+ const SizedBox(height: 8),
+ _EmailInput(),
+ const SizedBox(height: 8),
+ _PhoneInput(),
+ const SizedBox(height: 8),
+ _CheckListInput(),
+ const SizedBox(height: 8),
+ _RadioListInput(),
+ const SizedBox(height: 8),
+ _CheckHiddenInput(),
+ const SizedBox(height: 8),
+ _CheckIsProInput(),
+ const SizedBox(height: 8),
+ BlocBuilder(
+ builder: (context, state) {
+ if (state.data.input(formFieldPro).value) {
+ return Column(children: [
+ _SirenInput(),
+ const SizedBox(height: 8),
+ _IbanInput(),
+ const SizedBox(height: 8),
+ ]);
+ }
+ return const SizedBox.shrink();
+ },
+ ),
+ const SizedBox(height: 8),
+ _SignUpButton(),
+ const SizedBox(height: 8),
+ _DebugButton(),
+ ],
),
),
);
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
index 65706184..728c239d 100644
--- a/packages/wyatt_form_bloc/lib/src/cubit/form_data_cubit.dart
+++ b/packages/wyatt_form_bloc/lib/src/cubit/form_data_cubit.dart
@@ -23,16 +23,16 @@ import 'package:wyatt_form_bloc/src/form/form.dart';
part 'form_data_state.dart';
-class FormDataCubit extends Cubit {
- final Future Function(FormDataState state)? _onSubmit;
+abstract class FormDataCubit extends Cubit {
+ FormDataCubit({required FormData entries})
+ : super(FormDataState(data: entries));
- FormDataCubit({
- required FormData entries,
- Future Function(FormDataState state)? onSubmit,
- }) : _onSubmit = onSubmit,
- super(FormDataState(data: entries));
-
- void dataChanged(String field, FormInput dirtyValue) {
+ /// Change value of a field.
+ ///
+ /// Inputs:
+ /// - `field`: The key of the field to change.
+ /// - `dirtyValue`: The new value of the field.
+ void dataChanged(String field, FormInput dirtyValue) {
final _form = state.data.clone();
if (_form.contains(field)) {
@@ -49,6 +49,11 @@ class FormDataCubit extends Cubit {
);
}
+ /// 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,
@@ -78,13 +83,6 @@ class FormDataCubit extends Cubit {
);
}
- Future submitForm() async {
- unawaited(
- _onSubmit?.call(state).then((bool reemit) {
- if (reemit) {
- emit(state);
- }
- }),
- );
- }
+ /// Submit the form.
+ Future submitForm();
}
diff --git a/packages/wyatt_form_bloc/lib/src/validators/list_option.dart b/packages/wyatt_form_bloc/lib/src/validators/list_option.dart
new file mode 100644
index 00000000..13c7dc5c
--- /dev/null
+++ b/packages/wyatt_form_bloc/lib/src/validators/list_option.dart
@@ -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 .
+
+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 extends FormInput, ValidationError> {
+ /// {@macro list_option}
+ const ListOption.pure({List? defaultValue})
+ : super.pure(defaultValue ?? const []);
+
+ /// {@macro list_option}
+ const ListOption.dirty({List? value}) : super.dirty(value ?? const []);
+
+ ListOption select(T? v) {
+ if (v == null) {
+ return this;
+ }
+ if (value.contains(v)) {
+ final List newValue = List.from(value)..remove(v);
+ return ListOption.dirty(value: newValue);
+ } else {
+ final List newValue = List.from(value)..add(v);
+ return ListOption.dirty(value: newValue);
+ }
+ }
+
+ @override
+ ValidationError? validator(List? value) {
+ return value?.isNotEmpty == true ? null : ValidationError.invalid;
+ }
+}
diff --git a/packages/wyatt_form_bloc/lib/src/validators/validators.dart b/packages/wyatt_form_bloc/lib/src/validators/validators.dart
index 809c0005..170a7993 100644
--- a/packages/wyatt_form_bloc/lib/src/validators/validators.dart
+++ b/packages/wyatt_form_bloc/lib/src/validators/validators.dart
@@ -18,6 +18,7 @@ export 'boolean.dart';
export 'confirmed_password.dart';
export 'email.dart';
export 'iban.dart';
+export 'list_option.dart';
export 'name.dart';
export 'password.dart';
export 'phone.dart';