Compare commits

...
Author SHA1 Message Date
hugo 74db784973 chore(release): publish packages
- wyatt_form_bloc@0.0.2
 - wyatt_authentication_bloc@0.2.0+1
2022-05-02 14:44:17 +02:00
hugo fe5fa692f7 feat(form): add list option validator 2022-05-02 14:41:24 +02:00
14 changed files with 327 additions and 83 deletions
+28
View File
@@ -3,6 +3,34 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## 2022-05-02
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`wyatt_form_bloc` - `v0.0.2`](#wyatt_form_bloc---v002)
- [`wyatt_authentication_bloc` - `v0.2.0+1`](#wyatt_authentication_bloc---v0201)
Packages with dependency updates only:
> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project.
- `wyatt_authentication_bloc` - `v0.2.0+1`
---
#### `wyatt_form_bloc` - `v0.0.2`
- **FEAT**: add list option validator.
## 2022-04-20 ## 2022-04-20
### Changes ### Changes
@@ -1,3 +1,7 @@
## 0.2.0+1
- Update a dependency to the latest release.
## 0.2.0 ## 0.2.0
- Graduate package to a stable release. See pre-releases prior to this version for changelog entries. - Graduate package to a stable release. See pre-releases prior to this version for changelog entries.
@@ -38,7 +38,7 @@ dependencies:
wyatt_form_bloc: wyatt_form_bloc:
git: git:
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages
ref: wyatt_form_bloc-v0.0.1 ref: wyatt_form_bloc-v0.0.2
path: packages/wyatt_form_bloc path: packages/wyatt_form_bloc
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
@@ -1,7 +1,7 @@
name: wyatt_authentication_bloc name: wyatt_authentication_bloc
description: Authentication BLoC for Flutter description: Authentication BLoC for Flutter
repository: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_authentication_bloc repository: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_authentication_bloc
version: 0.2.0 version: 0.2.0+1
environment: environment:
sdk: ">=2.15.1 <3.0.0" sdk: ">=2.15.1 <3.0.0"
@@ -18,7 +18,7 @@ dependencies:
wyatt_form_bloc: wyatt_form_bloc:
git: git:
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages
ref: wyatt_form_bloc-v0.0.1 ref: wyatt_form_bloc-v0.0.2
path: packages/wyatt_form_bloc path: packages/wyatt_form_bloc
+4
View File
@@ -1,3 +1,7 @@
## 0.0.2
- **FEAT**: add list option validator.
## 0.0.1 ## 0.0.1
- Graduate package to a stable release. See pre-releases prior to this version for changelog entries. - Graduate package to a stable release. See pre-releases prior to this version for changelog entries.
@@ -14,50 +14,48 @@
// 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/constants.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: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';
class App extends StatelessWidget { class App extends StatelessWidget {
const App({Key? key}) : super(key: key); const App({Key? key}) : super(key: key);
static FormData getNormalFormData() { static List<FormEntry> getNormalEntries() {
return const FormData([ return const [
FormEntry(formFieldName, Name.pure()), FormEntry(formFieldName, Name.pure()),
FormEntry(formFieldEmail, Email.pure()), FormEntry(formFieldEmail, Email.pure()),
FormEntry(formFieldPhone, Phone.pure()), FormEntry(formFieldPhone, Phone.pure()),
FormEntry(
formFieldList, ListOption<String>.pure(defaultValue: ['checkbox3'])),
FormEntry(formFieldRadio, TextString.pure()),
FormEntry(formFieldPro, Boolean.pure(), name: 'business'), FormEntry(formFieldPro, Boolean.pure(), name: 'business'),
FormEntry(formFieldHidden, Boolean.pure(), export: false), FormEntry(formFieldHidden, Boolean.pure(), export: false),
]); ];
}
static List<FormEntry> getBusinessEntries() {
const entries = [
FormEntry(formFieldSiren, Siren.pure()),
FormEntry(formFieldIban, Iban.pure()),
];
return getNormalEntries() + entries;
}
static FormData getNormalFormData() {
return FormData(getNormalEntries());
} }
static FormData getProFormData() { static FormData getProFormData() {
return const FormData([ return FormData(getBusinessEntries());
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<bool> onSubmit(FormDataState state) async {
log(state.data.toMap().toString());
return true;
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
FormDataCubit _formCubit = FormDataCubit( FormDataCubit _formCubit = CustomFormCubit(entries: getNormalFormData());
entries: getNormalFormData(),
onSubmit: onSubmit,
);
return BlocProvider( return BlocProvider(
create: (context) => _formCubit, create: (context) => _formCubit,
@@ -19,6 +19,8 @@ const String formFieldPhone = 'phone';
const String formFieldEmail = 'email'; const String formFieldEmail = 'email';
const String formFieldSiren = 'siren'; const String formFieldSiren = 'siren';
const String formFieldIban = 'iban'; const String formFieldIban = 'iban';
const String formFieldList = 'list';
const String formFieldRadio = 'radio';
const String formFieldHidden = 'hidden'; const String formFieldHidden = 'hidden';
const String formFieldPro = 'isPro'; const String formFieldPro = 'isPro';
@@ -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 <https://www.gnu.org/licenses/>.
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<void> submitForm() {
log(state.data.toMap().toString());
return Future.value();
}
}
@@ -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 <https://www.gnu.org/licenses/>.
part of 'custom_form_cubit.dart';
@immutable
abstract class CustomFormState {}
class CustomFormInitial extends CustomFormState {}
@@ -135,6 +135,112 @@ class _IbanInput extends StatelessWidget {
} }
} }
class _CheckListInput extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<FormDataCubit, FormDataState>(
builder: (context, state) {
final _input =
state.data.input<List<String>>(formFieldList) as ListOption<String>;
final _options = _input.value;
return Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
title: const Text('Checkbox1'),
trailing: Checkbox(
value: _options.contains('checkbox1'),
onChanged: (_) {
context.read<FormDataCubit>().dataChanged(
formFieldList,
_input.select('checkbox1'),
);
}),
),
ListTile(
title: const Text('Checkbox2'),
trailing: Checkbox(
value: _options.contains('checkbox2'),
onChanged: (_) {
context.read<FormDataCubit>().dataChanged(
formFieldList,
_input.select('checkbox2'),
);
}),
),
ListTile(
title: const Text('Checkbox3 (default)'),
trailing: Checkbox(
value: _options.contains('checkbox3'),
onChanged: (_) {
context.read<FormDataCubit>().dataChanged(
formFieldList,
_input.select('checkbox3'),
);
}),
),
],
);
},
);
}
}
class _RadioListInput extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<FormDataCubit, FormDataState>(
builder: (context, state) {
final _input =
state.data.input<String>(formFieldRadio) as TextString;
return Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
title: const Text('Radio1'),
trailing: Radio<bool>(
groupValue: true,
value: _input.value == 'radio1',
onChanged: (_) {
context.read<FormDataCubit>().dataChanged(
formFieldRadio,
const TextString.dirty('radio1'),
);
}),
),
ListTile(
title: const Text('Radio2'),
trailing: Radio<bool>(
groupValue: true,
value: _input.value == 'radio2',
onChanged: (_) {
context.read<FormDataCubit>().dataChanged(
formFieldRadio,
const TextString.dirty('radio2'),
);
}),
),
ListTile(
title: const Text('Radio3'),
trailing: Radio<bool>(
groupValue: true,
value: _input.value == 'radio3',
onChanged: (_) {
context.read<FormDataCubit>().dataChanged(
formFieldRadio,
const TextString.dirty('radio3'),
);
}),
),
],
);
},
);
}
}
class _CheckHiddenInput extends StatelessWidget { class _CheckHiddenInput extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -201,8 +307,7 @@ class _SignUpButton extends StatelessWidget {
? const CircularProgressIndicator() ? const CircularProgressIndicator()
: ElevatedButton( : ElevatedButton(
onPressed: state.status.isValidated onPressed: state.status.isValidated
? () => ? () => context.read<FormDataCubit>().submitForm()
context.read<FormDataCubit>().submitForm()
: null, : null,
child: const Text('SIGN UP'), child: const Text('SIGN UP'),
); );
@@ -244,42 +349,43 @@ class SignUpForm extends StatelessWidget {
); );
} }
}, },
child: SizedBox( child: Align(
height: MediaQuery.of(context).size.height, alignment: const Alignment(0, -1 / 3),
child: Align( child: Column(
alignment: const Alignment(0, -1 / 3), mainAxisSize: MainAxisSize.min,
child: Column( children: [
mainAxisSize: MainAxisSize.min, _NameInput(),
children: [ const SizedBox(height: 8),
_NameInput(), _EmailInput(),
const SizedBox(height: 8), const SizedBox(height: 8),
_EmailInput(), _PhoneInput(),
const SizedBox(height: 8), const SizedBox(height: 8),
_PhoneInput(), _CheckListInput(),
const SizedBox(height: 8), const SizedBox(height: 8),
_CheckHiddenInput(), _RadioListInput(),
const SizedBox(height: 8), const SizedBox(height: 8),
_CheckIsProInput(), _CheckHiddenInput(),
const SizedBox(height: 8), const SizedBox(height: 8),
BlocBuilder<FormDataCubit, FormDataState>( _CheckIsProInput(),
builder: (context, state) { const SizedBox(height: 8),
if (state.data.input<bool>(formFieldPro).value) { BlocBuilder<FormDataCubit, FormDataState>(
return Column(children: [ builder: (context, state) {
_SirenInput(), if (state.data.input<bool>(formFieldPro).value) {
const SizedBox(height: 8), return Column(children: [
_IbanInput(), _SirenInput(),
const SizedBox(height: 8), const SizedBox(height: 8),
]); _IbanInput(),
} const SizedBox(height: 8),
return const SizedBox.shrink(); ]);
}, }
), return const SizedBox.shrink();
const SizedBox(height: 8), },
_SignUpButton(), ),
const SizedBox(height: 8), const SizedBox(height: 8),
_DebugButton(), _SignUpButton(),
], const SizedBox(height: 8),
), _DebugButton(),
],
), ),
), ),
); );
@@ -23,16 +23,16 @@ import 'package:wyatt_form_bloc/src/form/form.dart';
part 'form_data_state.dart'; part 'form_data_state.dart';
class FormDataCubit extends Cubit<FormDataState> { abstract class FormDataCubit extends Cubit<FormDataState> {
final Future<bool> Function(FormDataState state)? _onSubmit; FormDataCubit({required FormData entries})
: super(FormDataState(data: entries));
FormDataCubit({ /// Change value of a field.
required FormData entries, ///
Future<bool> Function(FormDataState state)? onSubmit, /// Inputs:
}) : _onSubmit = onSubmit, /// - `field`: The key of the field to change.
super(FormDataState(data: entries)); /// - `dirtyValue`: The new value of the field.
void dataChanged(String field, FormInput dirtyValue) {
void dataChanged<T>(String field, FormInput dirtyValue) {
final _form = state.data.clone(); final _form = state.data.clone();
if (_form.contains(field)) { if (_form.contains(field)) {
@@ -49,6 +49,11 @@ class FormDataCubit extends Cubit<FormDataState> {
); );
} }
/// Update entries list.
///
/// Inputs:
/// - `data`: The new entries list.
/// - `operation`: The operation to perform on the entries set.
void updateFormData( void updateFormData(
FormData data, { FormData data, {
SetOperation operation = SetOperation.replace, SetOperation operation = SetOperation.replace,
@@ -78,13 +83,6 @@ class FormDataCubit extends Cubit<FormDataState> {
); );
} }
Future<void> submitForm() async { /// Submit the form.
unawaited( Future<void> submitForm();
_onSubmit?.call(state).then((bool reemit) {
if (reemit) {
emit(state);
}
}),
);
}
} }
@@ -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/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 FormInput<List<T>, ValidationError> {
/// {@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 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
ValidationError? validator(List<T>? value) {
return value?.isNotEmpty == true ? null : ValidationError.invalid;
}
}
@@ -18,6 +18,7 @@ export 'boolean.dart';
export 'confirmed_password.dart'; export 'confirmed_password.dart';
export 'email.dart'; export 'email.dart';
export 'iban.dart'; export 'iban.dart';
export 'list_option.dart';
export 'name.dart'; export 'name.dart';
export 'password.dart'; export 'password.dart';
export 'phone.dart'; export 'phone.dart';
+1 -1
View File
@@ -1,7 +1,7 @@
name: wyatt_form_bloc name: wyatt_form_bloc
description: Manage forms in Dart & Flutter with Bloc 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.1 version: 0.0.2
environment: environment:
sdk: '>=2.16.2 <3.0.0' sdk: '>=2.16.2 <3.0.0'