diff --git a/form_bloc_web/lib/examples/all_fields_form.dart b/form_bloc_web/lib/examples/all_fields_form.dart index eddd32e8..ee9882c2 100644 --- a/form_bloc_web/lib/examples/all_fields_form.dart +++ b/form_bloc_web/lib/examples/all_fields_form.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart'; void main() => runApp(const App()); @@ -44,7 +45,7 @@ class AllFieldsFormBloc extends FormBloc { final time1 = InputFieldBloc(initialValue: null); AllFieldsFormBloc() { - addFieldBlocs(fieldBlocs: [ + addStep(ListFieldBloc(fieldBlocs: [ text1, boolean1, boolean2, @@ -54,7 +55,7 @@ class AllFieldsFormBloc extends FormBloc { date1, dateAndTime1, time1, - ]); + ])); } @override @@ -90,7 +91,8 @@ class AllFieldsForm extends StatelessWidget { ), child: Scaffold( appBar: AppBar(title: const Text('Built-in Widgets')), - body: FormBlocListener( + body: FormBlocListener( + formBloc: formBloc, onSubmitting: (context, state) { LoadingDialog.show(context); }, diff --git a/form_bloc_web/lib/examples/async_field_validation_form.dart b/form_bloc_web/lib/examples/async_field_validation_form.dart index c7204201..c01faa6b 100644 --- a/form_bloc_web/lib/examples/async_field_validation_form.dart +++ b/form_bloc_web/lib/examples/async_field_validation_form.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart'; void main() => runApp(const App()); @@ -25,7 +26,7 @@ class AsyncFieldValidationFormBloc extends FormBloc { ); AsyncFieldValidationFormBloc() { - addFieldBlocs(fieldBlocs: [username]); + addStep(username); username.addAsyncValidators( [_checkUsername], @@ -75,8 +76,8 @@ class AsyncFieldValidationForm extends StatelessWidget { return Scaffold( resizeToAvoidBottomInset: false, appBar: AppBar(title: const Text('Async Field Validation')), - body: - FormBlocListener( + body: FormBlocListener( + formBloc: formBloc, onSubmitting: (context, state) { LoadingDialog.show(context); }, diff --git a/form_bloc_web/lib/examples/conditional_fields_form.dart b/form_bloc_web/lib/examples/conditional_fields_form.dart index 818eff9f..501fed61 100644 --- a/form_bloc_web/lib/examples/conditional_fields_form.dart +++ b/form_bloc_web/lib/examples/conditional_fields_form.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart'; +import 'package:form_bloc_web/widgets/app_form_bloc_provider.dart'; void main() => runApp(const App()); @@ -16,6 +17,8 @@ class App extends StatelessWidget { } class ConditionalFieldsFormBloc extends FormBloc { + final _step = ListFieldBloc(); + final doYouLikeFormBloc = SelectFieldBloc( validators: [FieldBlocValidators.required], items: ['No', 'Yes'], @@ -32,38 +35,30 @@ class ConditionalFieldsFormBloc extends FormBloc { ); ConditionalFieldsFormBloc() { - addFieldBlocs( - fieldBlocs: [ - doYouLikeFormBloc, - ], - ); + addStep(_step..addFieldBloc(doYouLikeFormBloc)); showSecretField.onValueChanges( onData: (previous, current) async* { if (current.value) { - addFieldBlocs(fieldBlocs: [secretField]); + _step.addFieldBloc(secretField); } else { - removeFieldBlocs(fieldBlocs: [secretField]); + _step.removeFieldBloc(secretField); } }, ); doYouLikeFormBloc.onValueChanges( onData: (previous, current) async* { - removeFieldBlocs( - fieldBlocs: [ - whyNotYouLikeFormBloc, - showSecretField, - secretField, - ], - ); + _step.removeFieldBlocs([ + whyNotYouLikeFormBloc, + showSecretField, + secretField, + ]); if (current.value == 'No') { - addFieldBlocs(fieldBlocs: [ - whyNotYouLikeFormBloc, - ]); + _step.addFieldBloc(whyNotYouLikeFormBloc); } else if (current.value == 'Yes') { - addFieldBlocs(fieldBlocs: [ + _step.addFieldBlocs([ showSecretField, if (showSecretField.value) secretField, ]); @@ -88,13 +83,9 @@ class ConditionalFieldsFormBloc extends FormBloc { debugPrint(showSecretField.value.toString()); debugPrint(secretField.value); - try { - await Future.delayed(const Duration(milliseconds: 500)); + await Future.delayed(const Duration(milliseconds: 500)); - emitSuccess(); - } catch (e) { - emitFailure(); - } + emitSuccess(); } } @@ -103,98 +94,97 @@ class ConditionalFieldsForm extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocProvider( + return AppFormBlocProvider( create: (context) => ConditionalFieldsFormBloc(), - child: Builder( - builder: (context) { - final formBloc = BlocProvider.of(context); - - return Theme( - data: Theme.of(context).copyWith( - inputDecorationTheme: InputDecorationTheme( - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(20), - ), + builder: (context, formBloc) { + return Theme( + data: Theme.of(context).copyWith( + inputDecorationTheme: InputDecorationTheme( + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(20), ), ), - child: Scaffold( - appBar: AppBar(title: const Text('Conditional Fields')), - body: FormBlocListener( - onSubmitting: (context, state) { - LoadingDialog.show(context); - }, - onSubmissionFailed: (context, state) { - LoadingDialog.hide(context); - }, - onSuccess: (context, state) { - LoadingDialog.hide(context); - - Navigator.of(context).pushReplacement( - MaterialPageRoute(builder: (_) => const SuccessScreen())); - }, - onFailure: (context, state) { - LoadingDialog.hide(context); - - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(state.failureResponse!))); - }, - child: SingleChildScrollView( - physics: const ClampingScrollPhysics(), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - RadioButtonGroupFieldBlocBuilder( - selectFieldBloc: formBloc.doYouLikeFormBloc, - itemBuilder: (context, dynamic value) => - FieldItem(child: Text(value)), - decoration: const InputDecoration( - labelText: 'Do you like form bloc?', - prefixIcon: SizedBox(), - ), - ), - TextFieldBlocBuilder( - textFieldBloc: formBloc.whyNotYouLikeFormBloc, - keyboardType: TextInputType.multiline, - maxLines: 1, - decoration: const InputDecoration( - labelText: 'Why?', - prefixIcon: Icon(Icons.sentiment_very_dissatisfied), - ), + ), + child: Scaffold( + appBar: AppBar(title: const Text('Conditional Fields')), + body: FormBlocListener( + formBloc: formBloc, + onSubmitting: (context, state) { + LoadingDialog.show(context); + }, + onSubmissionFailed: (context, state) { + LoadingDialog.hide(context); + }, + onSuccess: (context, state) { + LoadingDialog.hide(context); + + Navigator.of(context).pushReplacement( + MaterialPageRoute(builder: (_) => const SuccessScreen())); + }, + onFailure: (context, state) { + LoadingDialog.hide(context); + + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('${state.failureResponse}'), + )); + }, + child: SingleChildScrollView( + physics: const ClampingScrollPhysics(), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + RadioButtonGroupFieldBlocBuilder( + selectFieldBloc: formBloc.doYouLikeFormBloc, + itemBuilder: (context, dynamic value) => + FieldItem(child: Text(value)), + decoration: const InputDecoration( + labelText: 'Do you like form bloc?', + prefixIcon: SizedBox(), ), - SizedBox( - width: 200, - child: CheckboxFieldBlocBuilder( - booleanFieldBloc: formBloc.showSecretField, - controlAffinity: - FieldBlocBuilderControlAffinity.trailing, - body: Container( - alignment: Alignment.center, - child: const Text('Do you want to see a secret field?'), - ), - ), + ), + TextFieldBlocBuilder( + textFieldBloc: formBloc.whyNotYouLikeFormBloc, + keyboardType: TextInputType.multiline, + maxLines: 1, + decoration: const InputDecoration( + labelText: 'Why?', + prefixIcon: Icon(Icons.sentiment_very_dissatisfied), ), - TextFieldBlocBuilder( - textFieldBloc: formBloc.secretField, - keyboardType: TextInputType.multiline, - decoration: const InputDecoration( - labelText: 'Secret field', - prefixIcon: Icon(Icons.sentiment_very_satisfied), + ), + SizedBox( + width: 200, + child: CheckboxFieldBlocBuilder( + booleanFieldBloc: formBloc.showSecretField, + controlAffinity: + FieldBlocBuilderControlAffinity.trailing, + body: Container( + alignment: Alignment.center, + child: const Text( + 'Do you want to see a secret field?'), ), ), - ElevatedButton( - onPressed: formBloc.submit, - child: const Text('SUBMIT'), + ), + TextFieldBlocBuilder( + textFieldBloc: formBloc.secretField, + keyboardType: TextInputType.multiline, + decoration: const InputDecoration( + labelText: 'Secret field', + prefixIcon: Icon(Icons.sentiment_very_satisfied), ), - ], - ), + ), + ElevatedButton( + onPressed: formBloc.submit, + child: const Text('SUBMIT'), + ), + ], ), ), ), ), - ); - }, - ), + ), + ); + }, ); } } @@ -249,7 +239,8 @@ class SuccessScreen extends StatelessWidget { const SizedBox(height: 10), ElevatedButton.icon( onPressed: () => Navigator.of(context).pushReplacement( - MaterialPageRoute(builder: (_) => const ConditionalFieldsForm())), + MaterialPageRoute( + builder: (_) => const ConditionalFieldsForm())), icon: const Icon(Icons.replay), label: const Text('AGAIN'), ), diff --git a/form_bloc_web/lib/examples/list_fields_form.dart b/form_bloc_web/lib/examples/list_fields_form.dart index 031f740b..e6b7c88d 100644 --- a/form_bloc_web/lib/examples/list_fields_form.dart +++ b/form_bloc_web/lib/examples/list_fields_form.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart'; void main() => runApp(const App()); @@ -19,25 +20,22 @@ class App extends StatelessWidget { } class ListFieldFormBloc extends FormBloc { - final clubName = TextFieldBloc(name: 'clubName'); + final clubName = TextFieldBloc(); - final members = ListFieldBloc(name: 'members'); + final members = ListFieldBloc(); ListFieldFormBloc() { - addFieldBlocs( - fieldBlocs: [ - clubName, - members, - ], - ); + addStep(ListFieldBloc(fieldBlocs: [ + clubName, + members, + ])); } void addMember() { members.addFieldBloc(MemberFieldBloc( - name: 'member', - firstName: TextFieldBloc(name: 'firstName'), - lastName: TextFieldBloc(name: 'lastName'), - hobbies: ListFieldBloc(name: 'hobbies'), + firstName: TextFieldBloc(), + lastName: TextFieldBloc(), + hobbies: ListFieldBloc(), )); } @@ -82,7 +80,7 @@ class ListFieldFormBloc extends FormBloc { debugPrint(clubV1.toJson().toString()); // With Serialization - final clubV2 = Club.fromJson(state.toJson()); + final clubV2 = Club.fromJson(state.toJson()[0]!); debugPrint('clubV2'); debugPrint(clubV2.toJson().toString()); @@ -105,35 +103,34 @@ class MemberFieldBloc extends GroupFieldBloc { required this.firstName, required this.lastName, required this.hobbies, - String? name, - }) : super(name: name, fieldBlocs: [firstName, lastName, hobbies]); + }) : super( + fieldBlocs: { + 'firstName': firstName, + 'lastName': lastName, + 'hobbies': hobbies, + }, + ); } class Club { - String? clubName; - List? members; - - Club({this.clubName, this.members}); - - Club.fromJson(Map json) { - clubName = json['clubName']; - if (json['members'] != null) { - members = []; - json['members'].forEach((v) { - members!.add(Member.fromJson(v)); - }); - } - } + final String clubName; + final List members; + + Club({required this.clubName, required this.members}); Map toJson() { - final Map data = {}; - data['clubName'] = clubName; - if (members != null) { - data['members'] = members!.map((v) => v.toJson()).toList(); - } - return data; + return { + 'clubName': clubName, + 'members': members, + }; } + factory Club.fromJson(Map map) { + return Club( + clubName: map['clubName'] as String, + members: map['members'] as List, + ); + } @override String toString() => '''Club { clubName: $clubName, @@ -142,24 +139,30 @@ class Club { } class Member { - String? firstName; - String? lastName; - List? hobbies; + final String firstName; + final String lastName; + final List hobbies; - Member({this.firstName, this.lastName, this.hobbies}); + Member({ + required this.firstName, + required this.lastName, + required this.hobbies, + }); - Member.fromJson(Map json) { - firstName = json['firstName']; - lastName = json['lastName']; - hobbies = json['hobbies'].cast(); + Map toJson() { + return { + 'firstName': firstName, + 'lastName': lastName, + 'hobbies': hobbies, + }; } - Map toJson() { - final Map data = {}; - data['firstName'] = firstName; - data['lastName'] = lastName; - data['hobbies'] = hobbies; - return data; + factory Member.fromJson(Map map) { + return Member( + firstName: map['firstName'] as String, + lastName: map['lastName'] as String, + hobbies: map['hobbies'] as List, + ); } @override @@ -196,7 +199,8 @@ class ListFieldsForm extends StatelessWidget { onPressed: formBloc.submit, child: const Icon(Icons.send), ), - body: FormBlocListener( + body: FormBlocListener( + formBloc: formBloc, onSubmitting: (context, state) { LoadingDialog.show(context); }, diff --git a/form_bloc_web/lib/examples/loading_and_initializing_form.dart b/form_bloc_web/lib/examples/loading_and_initializing_form.dart index f561a655..8e292919 100644 --- a/form_bloc_web/lib/examples/loading_and_initializing_form.dart +++ b/form_bloc_web/lib/examples/loading_and_initializing_form.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart'; void main() => runApp(const App()); @@ -21,9 +22,9 @@ class LoadingFormBloc extends FormBloc { final select = SelectFieldBloc(); LoadingFormBloc() : super(isLoading: true) { - addFieldBlocs( + addStep(ListFieldBloc( fieldBlocs: [text, select], - ); + )); } var _throwException = true; @@ -88,7 +89,8 @@ class LoadingForm extends StatelessWidget { ), child: Scaffold( appBar: AppBar(title: const Text('Loading and Initializing')), - body: FormBlocListener( + body: FormBlocListener( + formBloc: loadingFormBloc, onSubmitting: (context, state) { LoadingDialog.show(context); }, @@ -116,10 +118,12 @@ class LoadingForm extends StatelessWidget { child: SingleChildScrollView( child: Column( children: [ - const Icon(Icons.sentiment_dissatisfied, size: 70), + const Icon(Icons.sentiment_dissatisfied, + size: 70), const SizedBox(height: 20), Container( - padding: const EdgeInsets.symmetric(horizontal: 12), + padding: + const EdgeInsets.symmetric(horizontal: 12), alignment: Alignment.center, child: Text( state.failureResponse ?? diff --git a/form_bloc_web/lib/examples/serialized_form.dart b/form_bloc_web/lib/examples/serialized_form.dart index 40264e8d..e949f6c6 100644 --- a/form_bloc_web/lib/examples/serialized_form.dart +++ b/form_bloc_web/lib/examples/serialized_form.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart'; void main() => runApp(const App()); @@ -18,29 +19,25 @@ class App extends StatelessWidget { } class SerializedFormBloc extends FormBloc { - final name = TextFieldBloc( - name: 'name', - ); + final name = TextFieldBloc(); final gender = SelectFieldBloc( - name: 'gender', items: ['male', 'female'], ); final birthDate = InputFieldBloc( - name: 'birthDate', initialValue: null, toJson: (value) => value!.toUtc().toIso8601String(), ); SerializedFormBloc() { - addFieldBlocs( + addStep(ListFieldBloc( fieldBlocs: [ name, gender, birthDate, ], - ); + )); } @override @@ -80,7 +77,8 @@ class SerializedForm extends StatelessWidget { onPressed: formBloc.submit, child: const Icon(Icons.send), ), - body: FormBlocListener( + body: FormBlocListener( + formBloc: formBloc, onSuccess: (context, state) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text(state.successResponse!), diff --git a/form_bloc_web/lib/examples/simple_form.dart b/form_bloc_web/lib/examples/simple_form.dart index a688ee5c..901a539d 100644 --- a/form_bloc_web/lib/examples/simple_form.dart +++ b/form_bloc_web/lib/examples/simple_form.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart'; void main() => runApp(const App()); @@ -32,13 +33,13 @@ class LoginFormBloc extends FormBloc { final showSuccessResponse = BooleanFieldBloc(); LoginFormBloc() { - addFieldBlocs( + addStep(ListFieldBloc( fieldBlocs: [ email, password, showSuccessResponse, ], - ); + )); } @override @@ -71,7 +72,8 @@ class LoginForm extends StatelessWidget { return Scaffold( resizeToAvoidBottomInset: false, appBar: AppBar(title: const Text('Login')), - body: FormBlocListener( + body: FormBlocListener( + formBloc: loginFormBloc, onSubmitting: (context, state) { LoadingDialog.show(context); }, diff --git a/form_bloc_web/lib/examples/submission_error_to_field_form.dart b/form_bloc_web/lib/examples/submission_error_to_field_form.dart index 728c7b7b..503e7327 100644 --- a/form_bloc_web/lib/examples/submission_error_to_field_form.dart +++ b/form_bloc_web/lib/examples/submission_error_to_field_form.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart'; void main() => runApp(const App()); @@ -19,11 +20,7 @@ class SubmissionErrorToFieldFormBloc extends FormBloc { final username = TextFieldBloc(); SubmissionErrorToFieldFormBloc() { - addFieldBlocs( - fieldBlocs: [ - username, - ], - ); + addStep(username); } @override @@ -69,8 +66,8 @@ class SubmissionErrorToFieldForm extends StatelessWidget { ), child: Scaffold( appBar: AppBar(title: const Text('Submission Error to Field')), - body: FormBlocListener( + body: FormBlocListener( + formBloc: formBloc, onSubmitting: (context, state) { LoadingDialog.show(context); }, diff --git a/form_bloc_web/lib/examples/submission_progress_form.dart b/form_bloc_web/lib/examples/submission_progress_form.dart index 68a9e2ff..fa47edb1 100644 --- a/form_bloc_web/lib/examples/submission_progress_form.dart +++ b/form_bloc_web/lib/examples/submission_progress_form.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart'; import 'package:liquid_progress_indicator/liquid_progress_indicator.dart'; import 'package:rxdart/rxdart.dart'; @@ -26,9 +27,7 @@ class SubmissionProgressFormBloc extends FormBloc { ); SubmissionProgressFormBloc() { - addFieldBlocs( - fieldBlocs: [username], - ); + addStep(username); } final List _fakeUploads = []; @@ -120,7 +119,8 @@ class SubmissionProgressForm extends StatelessWidget { return Scaffold( resizeToAvoidBottomInset: false, appBar: AppBar(title: const Text('Submission Progress')), - body: FormBlocListener( + body: FormBlocListener( + formBloc: formBloc, onSuccess: (context, state) { Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (_) => const SuccessScreen())); @@ -298,7 +298,8 @@ class SuccessScreen extends StatelessWidget { const SizedBox(height: 10), ElevatedButton.icon( onPressed: () => Navigator.of(context).pushReplacement( - MaterialPageRoute(builder: (_) => const SubmissionProgressForm())), + MaterialPageRoute( + builder: (_) => const SubmissionProgressForm())), icon: const Icon(Icons.replay), label: const Text('AGAIN'), ), diff --git a/form_bloc_web/lib/examples/validation_based_on_other_field.dart b/form_bloc_web/lib/examples/validation_based_on_other_field.dart index a7c95472..76047961 100644 --- a/form_bloc_web/lib/examples/validation_based_on_other_field.dart +++ b/form_bloc_web/lib/examples/validation_based_on_other_field.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart'; void main() => runApp(const App()); @@ -35,9 +36,9 @@ class ValidationBasedOnOtherFieldFormBloc extends FormBloc { } ValidationBasedOnOtherFieldFormBloc() { - addFieldBlocs( + addStep(ListFieldBloc( fieldBlocs: [password, confirmPassword], - ); + )); confirmPassword ..addValidators([_confirmPassword(password)]) @@ -74,9 +75,10 @@ class ValidationBasedOnOtherFieldForm extends StatelessWidget { return Scaffold( resizeToAvoidBottomInset: false, - appBar: AppBar(title: const Text('Validation based on other field')), - body: FormBlocListener( + appBar: + AppBar(title: const Text('Validation based on other field')), + body: FormBlocListener( + formBloc: loginFormBloc, onSubmitting: (context, state) { LoadingDialog.show(context); }, diff --git a/form_bloc_web/lib/examples/wizard_form.dart b/form_bloc_web/lib/examples/wizard_form.dart index eb229135..cbd9f55e 100644 --- a/form_bloc_web/lib/examples/wizard_form.dart +++ b/form_bloc_web/lib/examples/wizard_form.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart'; void main() => runApp(const App()); @@ -54,18 +55,15 @@ class WizardFormBloc extends FormBloc { final facebook = TextFieldBloc(); WizardFormBloc() { - addFieldBlocs( - step: 0, + addStep(ListFieldBloc( fieldBlocs: [username, email, password], - ); - addFieldBlocs( - step: 1, + )); + addStep(ListFieldBloc( fieldBlocs: [firstName, lastName, gender, birthDate], - ); - addFieldBlocs( - step: 2, + )); + addStep(ListFieldBloc( fieldBlocs: [github, twitter, facebook], - ); + )); } bool _showEmailTakenError = true; @@ -120,6 +118,8 @@ class _WizardFormState extends State { create: (context) => WizardFormBloc(), child: Builder( builder: (context) { + final formBloc = context.read(); + return Theme( data: Theme.of(context).copyWith( inputDecorationTheme: InputDecorationTheme( @@ -141,15 +141,17 @@ class _WizardFormState extends State { ], ), body: SafeArea( - child: FormBlocListener( + child: FormBlocListener( + formBloc: formBloc, onSubmitting: (context, state) => LoadingDialog.show(context), - onSubmissionFailed: (context, state) => LoadingDialog.hide(context), + onSubmissionFailed: (context, state) => + LoadingDialog.hide(context), onSuccess: (context, state) { LoadingDialog.hide(context); if (state.stepCompleted == state.lastStep) { - Navigator.of(context).pushReplacement( - MaterialPageRoute(builder: (_) => const SuccessScreen())); + Navigator.of(context).pushReplacement(MaterialPageRoute( + builder: (_) => const SuccessScreen())); } }, onFailure: (context, state) { diff --git a/form_bloc_web/lib/main.dart b/form_bloc_web/lib/main.dart index 1dcb2311..e9fd6c2b 100644 --- a/form_bloc_web/lib/main.dart +++ b/form_bloc_web/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart'; import 'package:form_bloc_web/pages/home_page.dart'; import 'package:form_bloc_web/routes.dart'; diff --git a/form_bloc_web/lib/widgets/app_form_bloc_provider.dart b/form_bloc_web/lib/widgets/app_form_bloc_provider.dart new file mode 100644 index 00000000..d1704619 --- /dev/null +++ b/form_bloc_web/lib/widgets/app_form_bloc_provider.dart @@ -0,0 +1,32 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_bloc/flutter_form_bloc.dart'; +import 'package:provider/provider.dart'; + +class AppFormBlocProvider extends StatelessWidget { + final Create create; + final Widget Function(BuildContext context, TFormBloc formBloc) builder; + + const AppFormBlocProvider({ + Key? key, + required this.create, + required this.builder, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: create, + child: Builder( + builder: (context) { + final formBloc = context.select((e) => e); + + return FormBlocProvider( + formBloc: formBloc, + child: builder(context, formBloc), + ); + }, + ), + ); + } +} diff --git a/form_bloc_web/pubspec.yaml b/form_bloc_web/pubspec.yaml index e07d02ad..aef054f3 100644 --- a/form_bloc_web/pubspec.yaml +++ b/form_bloc_web/pubspec.yaml @@ -10,9 +10,10 @@ dependencies: sdk: flutter # form_bloc: # path: ../packages/form_bloc -# flutter_form_bloc: -# path: ../packages/flutter_form_bloc - flutter_form_bloc: ^0.30.1 + flutter_form_bloc: + path: ../packages/flutter_form_bloc +# flutter_form_bloc: ^0.30.1 + flutter_bloc: ^8.0.1 animations: ^2.0.2 flutter_syntax_view: ^4.0.0 flash: ^1.5.2 diff --git a/packages/flutter_form_bloc/example/android/app/build.gradle b/packages/flutter_form_bloc/example/android/app/build.gradle index 79c473f4..98de8876 100644 --- a/packages/flutter_form_bloc/example/android/app/build.gradle +++ b/packages/flutter_form_bloc/example/android/app/build.gradle @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 28 + compileSdkVersion 31 sourceSets { main.java.srcDirs += 'src/main/kotlin' @@ -39,8 +39,8 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.example.form_bloc_example" - minSdkVersion 16 - targetSdkVersion 28 + minSdkVersion 24 + targetSdkVersion 31 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -61,7 +61,4 @@ flutter { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } diff --git a/packages/flutter_form_bloc/example/android/app/src/main/AndroidManifest.xml b/packages/flutter_form_bloc/example/android/app/src/main/AndroidManifest.xml index c73176e1..98a5c543 100644 --- a/packages/flutter_form_bloc/example/android/app/src/main/AndroidManifest.xml +++ b/packages/flutter_form_bloc/example/android/app/src/main/AndroidManifest.xml @@ -6,7 +6,7 @@ additional functionality it is fine to subclass or reimplement FlutterApplication and put your custom class here. --> + android:windowSoftInputMode="adjustResize" + android:exported="true"> diff --git a/packages/flutter_form_bloc/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt b/packages/flutter_form_bloc/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt deleted file mode 100644 index e793a000..00000000 --- a/packages/flutter_form_bloc/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.example.example - -import io.flutter.embedding.android.FlutterActivity - -class MainActivity: FlutterActivity() { -} diff --git a/packages/flutter_form_bloc/example/android/app/src/main/kotlin/com/example/form_bloc_example/MainActivity.kt b/packages/flutter_form_bloc/example/android/app/src/main/kotlin/com/example/form_bloc_example/MainActivity.kt index 19b4902f..30fed964 100644 --- a/packages/flutter_form_bloc/example/android/app/src/main/kotlin/com/example/form_bloc_example/MainActivity.kt +++ b/packages/flutter_form_bloc/example/android/app/src/main/kotlin/com/example/form_bloc_example/MainActivity.kt @@ -1,12 +1,5 @@ package com.example.form_bloc_example -import androidx.annotation.NonNull; -import io.flutter.embedding.android.FlutterActivity -import io.flutter.embedding.engine.FlutterEngine -import io.flutter.plugins.GeneratedPluginRegistrant +import io.flutter.embedding.android.FlutterActivity; -class MainActivity: FlutterActivity() { - override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { - GeneratedPluginRegistrant.registerWith(flutterEngine); - } -} +class MainActivity: FlutterActivity() {} diff --git a/packages/flutter_form_bloc/example/android/build.gradle b/packages/flutter_form_bloc/example/android/build.gradle index 3100ad2d..a57760a8 100644 --- a/packages/flutter_form_bloc/example/android/build.gradle +++ b/packages/flutter_form_bloc/example/android/build.gradle @@ -1,12 +1,12 @@ buildscript { - ext.kotlin_version = '1.3.50' + ext.kotlin_version = '1.6.21' repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:4.2.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/packages/flutter_form_bloc/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/flutter_form_bloc/example/android/gradle/wrapper/gradle-wrapper.properties index 296b146b..939efa29 100644 --- a/packages/flutter_form_bloc/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/flutter_form_bloc/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip diff --git a/packages/flutter_form_bloc/example/lib/main.dart b/packages/flutter_form_bloc/example/lib/main.dart index 37f26b17..d02d35a0 100644 --- a/packages/flutter_form_bloc/example/lib/main.dart +++ b/packages/flutter_form_bloc/example/lib/main.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart'; void main() { @@ -40,6 +41,21 @@ class App extends StatelessWidget { } class AllFieldsFormBloc extends FormBloc { + late final step = ListFieldBloc( + fieldBlocs: [ + text1, + boolean1, + boolean2, + select1, + select2, + multiSelect1, + date1, + dateAndTime1, + time1, + double1, + ], + ); + final text1 = TextFieldBloc(); final boolean1 = BooleanFieldBloc(); @@ -78,18 +94,7 @@ class AllFieldsFormBloc extends FormBloc { ); AllFieldsFormBloc() : super(autoValidate: false) { - addFieldBlocs(fieldBlocs: [ - text1, - boolean1, - boolean2, - select1, - select2, - multiSelect1, - date1, - dateAndTime1, - time1, - double1, - ]); + addStep(step); } void addErrors() { @@ -147,7 +152,8 @@ class AllFieldsForm extends StatelessWidget { ), ], ), - body: FormBlocListener( + body: FormBlocListener( + formBloc: formBloc, onSubmitting: (context, state) { LoadingDialog.show(context); }, @@ -251,13 +257,13 @@ class AllFieldsForm extends StatelessWidget { Row( children: [ IconButton( - onPressed: () => formBloc.addFieldBloc( - fieldBloc: formBloc.select1), + onPressed: () => + formBloc.step.addFieldBloc(formBloc.select1), icon: const Icon(Icons.add), ), IconButton( - onPressed: () => formBloc.removeFieldBloc( - fieldBloc: formBloc.select1), + onPressed: () => + formBloc.step.removeFieldBloc(formBloc.select1), icon: const Icon(Icons.delete), ), ], diff --git a/packages/flutter_form_bloc/example/pubspec.yaml b/packages/flutter_form_bloc/example/pubspec.yaml index 4fbb5270..e917e5f5 100644 --- a/packages/flutter_form_bloc/example/pubspec.yaml +++ b/packages/flutter_form_bloc/example/pubspec.yaml @@ -9,6 +9,8 @@ environment: dependencies: flutter: sdk: flutter + + flutter_bloc: flutter_form_bloc: path: ../ diff --git a/packages/flutter_form_bloc/lib/flutter_form_bloc.dart b/packages/flutter_form_bloc/lib/flutter_form_bloc.dart index 4420ee81..2a34d652 100644 --- a/packages/flutter_form_bloc/lib/flutter_form_bloc.dart +++ b/packages/flutter_form_bloc/lib/flutter_form_bloc.dart @@ -1,6 +1,5 @@ library flutter_form_bloc; -export 'package:flutter_bloc/flutter_bloc.dart'; export 'package:form_bloc/form_bloc.dart'; export 'src/checkbox_field_bloc_builder.dart'; @@ -15,6 +14,7 @@ export 'src/features/scroll/scrollable_field_bloc_target.dart'; export 'src/features/scroll/scrollable_form_bloc_manager.dart'; export 'src/field_bloc_builder.dart'; export 'src/fields/simple_field_bloc_builder.dart'; +export 'src/form/form_bloc_provider.dart'; export 'src/form_bloc_listener.dart'; export 'src/groups/fields/checkbox_group_field_bloc_builder.dart'; export 'src/groups/fields/radio_button_group_field_bloc.dart'; diff --git a/packages/flutter_form_bloc/lib/src/checkbox_field_bloc_builder.dart b/packages/flutter_form_bloc/lib/src/checkbox_field_bloc_builder.dart index 1ce2408c..10027f63 100644 --- a/packages/flutter_form_bloc/lib/src/checkbox_field_bloc_builder.dart +++ b/packages/flutter_form_bloc/lib/src/checkbox_field_bloc_builder.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_form_bloc/src/features/appear/can_show_field_bloc_builder.dart'; +import 'package:flutter_form_bloc/src/fields/simple_field_bloc_builder.dart'; import 'package:flutter_form_bloc/src/theme/field_theme_resolver.dart'; import 'package:flutter_form_bloc/src/theme/form_bloc_theme.dart'; import 'package:flutter_form_bloc/src/utils/utils.dart'; @@ -127,68 +126,65 @@ class CheckboxFieldBlocBuilder extends StatelessWidget { data: Theme.of(context).copyWith( checkboxTheme: fieldTheme.checkboxTheme, ), - child: CanShowFieldBlocBuilder( + child: SimpleFieldBlocBuilder( fieldBloc: booleanFieldBloc, - animate: animateWhenCanShow, - builder: (_, __) { - return BlocBuilder( - bloc: booleanFieldBloc, - builder: (context, state) { - final isEnabled = fieldBlocIsEnabled( - isEnabled: this.isEnabled, - enableOnlyWhenFormBlocCanSubmit: - enableOnlyWhenFormBlocCanSubmit, - fieldBlocState: state, - ); - - return DefaultFieldBlocBuilderPadding( - padding: padding, - child: InputDecorator( - decoration: Style.inputDecorationWithoutBorder.copyWith( - prefixIcon: fieldTheme.controlAffinity! == - FieldBlocBuilderControlAffinity.leading - ? _buildCheckbox(context, state) - : null, - suffixIcon: fieldTheme.controlAffinity! == - FieldBlocBuilderControlAffinity.trailing - ? _buildCheckbox(context, state) - : null, - errorText: Style.getErrorText( - context: context, - errorBuilder: errorBuilder, - fieldBlocState: state, - fieldBloc: booleanFieldBloc, - ), - ), - child: DefaultTextStyle( - style: Style.resolveTextStyle( - isEnabled: isEnabled, - style: fieldTheme.textStyle!, - color: fieldTheme.textColor!, - ), - child: Container( - constraints: const BoxConstraints( - minHeight: kMinInteractiveDimension, - ), - alignment: alignment, - child: body, - ), + animateWhenCanShow: animateWhenCanShow, + enableOnlyWhenFormBlocCanSubmit: enableOnlyWhenFormBlocCanSubmit, + isEnabled: isEnabled, + // TODO: Implement readOnly + readOnly: false, + nextFocusNode: nextFocusNode, + builder: (context, state, data) { + final isEnabled = data.isEnabled; + + return DefaultFieldBlocBuilderPadding( + padding: padding, + child: InputDecorator( + decoration: Style.inputDecorationWithoutBorder.copyWith( + prefixIcon: fieldTheme.controlAffinity! == + FieldBlocBuilderControlAffinity.leading + ? _buildCheckbox(context, state, data) + : null, + suffixIcon: fieldTheme.controlAffinity! == + FieldBlocBuilderControlAffinity.trailing + ? _buildCheckbox(context, state, data) + : null, + errorText: Style.getErrorText( + context: context, + errorBuilder: errorBuilder, + fieldBlocState: state, + fieldBloc: booleanFieldBloc, + ), + ), + child: DefaultTextStyle( + style: Style.resolveTextStyle( + isEnabled: isEnabled, + style: fieldTheme.textStyle!, + color: fieldTheme.textColor!, + ), + child: Container( + constraints: const BoxConstraints( + minHeight: kMinInteractiveDimension, ), + alignment: alignment, + child: body, ), - ); - }, + ), + ), ); }, ), ); } - Checkbox _buildCheckbox(BuildContext context, BooleanFieldBlocState state) { + Checkbox _buildCheckbox( + BuildContext context, + BooleanFieldBlocState state, + FieldBlocBuilderData data, + ) { return Checkbox( value: state.value, - onChanged: fieldBlocBuilderOnChange( - isEnabled: isEnabled, - nextFocusNode: nextFocusNode, + onChanged: data.buildOnChange( onChanged: booleanFieldBloc.changeValue as void Function(bool?), ), ); diff --git a/packages/flutter_form_bloc/lib/src/chip/choice_chip_field_bloc_builder.dart b/packages/flutter_form_bloc/lib/src/chip/choice_chip_field_bloc_builder.dart index eae9b5d3..14d4924d 100644 --- a/packages/flutter_form_bloc/lib/src/chip/choice_chip_field_bloc_builder.dart +++ b/packages/flutter_form_bloc/lib/src/chip/choice_chip_field_bloc_builder.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_bloc/src/chip/chip_field_item_builder.dart'; import 'package:flutter_form_bloc/src/fields/simple_field_bloc_builder.dart'; import 'package:flutter_form_bloc/src/theme/form_bloc_theme.dart'; @@ -191,78 +190,70 @@ class ChoiceChipFieldBlocBuilder extends StatelessWidget { Widget build(BuildContext context) { final fieldTheme = themeOf(context); - final current = SimpleFieldBlocBuilder( - singleFieldBloc: selectFieldBloc, + final current = SimpleFieldBlocBuilder>( + fieldBloc: selectFieldBloc, animateWhenCanShow: animateWhenCanShow, - builder: (context, canShow) { - return BlocBuilder, - SelectFieldBlocState>( - bloc: selectFieldBloc, - builder: (context, state) { - final isEnabled = fieldBlocIsEnabled( - isEnabled: this.isEnabled, - enableOnlyWhenFormBlocCanSubmit: enableOnlyWhenFormBlocCanSubmit, - fieldBlocState: state, - ); - - final value = state.value; - final items = state.items; - - return DefaultFieldBlocBuilderPadding( - padding: padding, - child: InputDecorator( - decoration: _buildDecoration(context, state, isEnabled), - isEmpty: false, - child: Wrap( - direction: direction, - alignment: alignment, - spacing: fieldTheme.wrapTheme.spacing!, - runAlignment: runAlignment, - runSpacing: fieldTheme.wrapTheme.runSpacing!, - crossAxisAlignment: crossAxisAlignment, - textDirection: textDirection, - verticalDirection: verticalDirection, - children: items.map((item) { - final fieldItem = itemBuilder(context, item); - - return ChoiceChip( - focusNode: focusNode, - autofocus: autofocus, - selected: value == item, - onSelected: fieldBlocBuilderOnChange( - isEnabled: isEnabled && fieldItem.isEnabled, - readOnly: readOnly, - nextFocusNode: nextFocusNode, - onChanged: (isSelected) { - selectFieldBloc.changeValue(isSelected ? item : null); - fieldItem.onTap?.call(); - }, - ), - labelStyle: labelStyle, - labelPadding: labelPadding, - pressElevation: pressElevation, - disabledColor: disabledColor, - selectedColor: selectedColor, - side: side, - shape: shape, - clipBehavior: clipBehavior, - backgroundColor: backgroundColor, - padding: chipPadding, - visualDensity: visualDensity, - materialTapTargetSize: materialTapTargetSize, - elevation: elevation, - shadowColor: shadowColor, - selectedShadowColor: selectedShadowColor, - avatarBorder: avatarBorder, - tooltip: fieldItem.tooltip, - avatar: fieldItem.avatar, - label: fieldItem.label, - ); - }).toList(), - ), - ), - ); - }, + enableOnlyWhenFormBlocCanSubmit: enableOnlyWhenFormBlocCanSubmit, + isEnabled: isEnabled, + readOnly: readOnly, + nextFocusNode: nextFocusNode, + builder: (context, state, data) { + final isEnabled = data.isEnabled; + + final value = state.value; + final items = state.items; + + return DefaultFieldBlocBuilderPadding( + padding: padding, + child: InputDecorator( + decoration: _buildDecoration(context, state, isEnabled), + isEmpty: false, + child: Wrap( + direction: direction, + alignment: alignment, + spacing: fieldTheme.wrapTheme.spacing!, + runAlignment: runAlignment, + runSpacing: fieldTheme.wrapTheme.runSpacing!, + crossAxisAlignment: crossAxisAlignment, + textDirection: textDirection, + verticalDirection: verticalDirection, + children: items.map((item) { + final fieldItem = itemBuilder(context, item); + + return ChoiceChip( + focusNode: focusNode, + autofocus: autofocus, + selected: value == item, + onSelected: data.buildOnChange( + isEnabled: fieldItem.isEnabled, + onChanged: (isSelected) { + selectFieldBloc.changeValue(isSelected ? item : null); + fieldItem.onTap?.call(); + }, + ), + labelStyle: labelStyle, + labelPadding: labelPadding, + pressElevation: pressElevation, + disabledColor: disabledColor, + selectedColor: selectedColor, + side: side, + shape: shape, + clipBehavior: clipBehavior, + backgroundColor: backgroundColor, + padding: chipPadding, + visualDensity: visualDensity, + materialTapTargetSize: materialTapTargetSize, + elevation: elevation, + shadowColor: shadowColor, + selectedShadowColor: selectedShadowColor, + avatarBorder: avatarBorder, + tooltip: fieldItem.tooltip, + avatar: fieldItem.avatar, + label: fieldItem.label, + ); + }).toList(), + ), + ), ); }, ); diff --git a/packages/flutter_form_bloc/lib/src/chip/filter_chip_field_bloc_builder.dart b/packages/flutter_form_bloc/lib/src/chip/filter_chip_field_bloc_builder.dart index 8fdd2d41..55bcaddd 100644 --- a/packages/flutter_form_bloc/lib/src/chip/filter_chip_field_bloc_builder.dart +++ b/packages/flutter_form_bloc/lib/src/chip/filter_chip_field_bloc_builder.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_bloc/src/chip/chip_field_item_builder.dart'; import 'package:flutter_form_bloc/src/fields/simple_field_bloc_builder.dart'; import 'package:flutter_form_bloc/src/theme/form_bloc_theme.dart'; @@ -186,84 +185,77 @@ class FilterChipFieldBlocBuilder extends StatelessWidget { Widget build(BuildContext context) { final fieldTheme = themeOf(context); - final current = SimpleFieldBlocBuilder( - singleFieldBloc: multiSelectFieldBloc, + final current = + SimpleFieldBlocBuilder>( + fieldBloc: multiSelectFieldBloc, animateWhenCanShow: animateWhenCanShow, - builder: (_, __) { - return BlocBuilder, - MultiSelectFieldBlocState>( - bloc: multiSelectFieldBloc, - builder: (context, state) { - final isEnabled = fieldBlocIsEnabled( - isEnabled: this.isEnabled, - enableOnlyWhenFormBlocCanSubmit: enableOnlyWhenFormBlocCanSubmit, - fieldBlocState: state, - ); - - final values = state.value; - final items = state.items; - - return DefaultFieldBlocBuilderPadding( - padding: padding, - child: InputDecorator( - decoration: _buildDecoration(context, state, isEnabled), - isEmpty: false, - child: Wrap( - direction: direction, - alignment: alignment, - spacing: fieldTheme.wrapTheme.spacing!, - runAlignment: runAlignment, - runSpacing: fieldTheme.wrapTheme.runSpacing!, - crossAxisAlignment: crossAxisAlignment, - textDirection: textDirection, - verticalDirection: verticalDirection, - children: items.map((item) { - final fieldItem = itemBuilder(context, item); - - return FilterChip( - focusNode: focusNode, - autofocus: autofocus, - selected: values.contains(item), - onSelected: fieldBlocBuilderOnChange( - isEnabled: isEnabled && fieldItem.isEnabled, - readOnly: readOnly, - nextFocusNode: nextFocusNode, - onChanged: (isSelected) { - if (isSelected) { - multiSelectFieldBloc.select(item); - } else { - multiSelectFieldBloc.deselect(item); - } - fieldItem.onTap?.call(); - }, - ), - labelStyle: labelStyle, - labelPadding: labelPadding, - pressElevation: pressElevation, - disabledColor: disabledColor, - selectedColor: selectedColor, - side: side, - shape: shape, - clipBehavior: clipBehavior, - backgroundColor: backgroundColor, - padding: chipPadding, - visualDensity: visualDensity, - materialTapTargetSize: materialTapTargetSize, - elevation: elevation, - shadowColor: shadowColor, - selectedShadowColor: selectedShadowColor, - showCheckmark: showCheckmark, - checkmarkColor: checkmarkColor, - avatarBorder: avatarBorder, - tooltip: fieldItem.tooltip, - avatar: fieldItem.avatar, - label: fieldItem.label, - ); - }).toList(), - ), - ), - ); - }, + enableOnlyWhenFormBlocCanSubmit: enableOnlyWhenFormBlocCanSubmit, + isEnabled: isEnabled, + readOnly: readOnly, + nextFocusNode: nextFocusNode, + builder: (context, state, data) { + final isEnabled = data.isEnabled; + + final values = state.value; + final items = state.items; + + return DefaultFieldBlocBuilderPadding( + padding: padding, + child: InputDecorator( + decoration: _buildDecoration(context, state, isEnabled), + isEmpty: false, + child: Wrap( + direction: direction, + alignment: alignment, + spacing: fieldTheme.wrapTheme.spacing!, + runAlignment: runAlignment, + runSpacing: fieldTheme.wrapTheme.runSpacing!, + crossAxisAlignment: crossAxisAlignment, + textDirection: textDirection, + verticalDirection: verticalDirection, + children: items.map((item) { + final fieldItem = itemBuilder(context, item); + + return FilterChip( + focusNode: focusNode, + autofocus: autofocus, + selected: values.contains(item), + onSelected: data.buildOnChange( + isEnabled: fieldItem.isEnabled, + onChanged: (isSelected) { + if (isSelected) { + multiSelectFieldBloc.select(item); + } else { + multiSelectFieldBloc.deselect(item); + } + fieldItem.onTap?.call(); + }, + ), + labelStyle: labelStyle, + labelPadding: labelPadding, + pressElevation: pressElevation, + disabledColor: disabledColor, + selectedColor: selectedColor, + side: side, + shape: shape, + clipBehavior: clipBehavior, + backgroundColor: backgroundColor, + padding: chipPadding, + visualDensity: visualDensity, + materialTapTargetSize: materialTapTargetSize, + elevation: elevation, + shadowColor: shadowColor, + selectedShadowColor: selectedShadowColor, + showCheckmark: showCheckmark, + checkmarkColor: checkmarkColor, + avatarBorder: avatarBorder, + tooltip: fieldItem.tooltip, + avatar: fieldItem.avatar, + label: fieldItem.label, + ); + }).toList(), + ), + ), ); }, ); diff --git a/packages/flutter_form_bloc/lib/src/cubit_consumer.dart b/packages/flutter_form_bloc/lib/src/cubit_consumer.dart new file mode 100644 index 00000000..a7e27472 --- /dev/null +++ b/packages/flutter_form_bloc/lib/src/cubit_consumer.dart @@ -0,0 +1,128 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:flutter/widgets.dart'; + +typedef CubitListener = void Function( + BuildContext context, TState state); + +typedef CubitBuilder = Widget Function( + BuildContext context, TState state, Widget child); + +typedef CubitCondition = bool Function(TState prev, TState curr); + +class SourceConsumer extends StatefulWidget { + final StateStreamable source; + final CubitCondition? listenWhen; + final CubitListener? listener; + final CubitCondition? buildWhen; + final Widget? child; + final CubitBuilder? builder; + + const SourceConsumer({ + Key? key, + required this.source, + this.listenWhen, + this.listener, + this.buildWhen, + this.child, + this.builder, + }) : super(key: key); + + bool _when(CubitCondition? condition, TState prev, TState curr) { + final source = this.source; + // Simulated the behavior of BlocBase + if (source is _StateStreamableSelector) { + if (prev == curr) return false; + } + return condition?.call(prev, curr) ?? true; + } + + @override + _SourceConsumerState createState() => _SourceConsumerState(); +} + +class _SourceConsumerState extends State> { + late StreamSubscription _sub; + late TState _listenState; + late TState _buildState; + + @override + void initState() { + super.initState(); + _initListener(); + } + + @override + void didUpdateWidget(covariant SourceConsumer oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.source != oldWidget.source) { + _sub.cancel(); + _initListener(); + } + } + + @override + void dispose() { + _sub.cancel(); + super.dispose(); + } + + void _initListener() { + _listenState = widget.source.state; + _buildState = widget.source.state; + + _sub = widget.source.stream.listen((state) { + if (widget._when(widget.listenWhen, _listenState, state)) { + _listenState = state; + widget.listener?.call(context, state); + } + + if (widget._when(widget.buildWhen, _buildState, state)) { + _buildState = state; + if (widget.builder != null) { + setState(() { + _buildState = state; + }); + } + } + }); + } + + @override + Widget build(BuildContext context) { + final child = widget.child ?? const SizedBox.shrink(); + + return widget.builder?.call(context, _listenState, child) ?? child; + } +} + +class _StateStreamableSelector + implements StateStreamable { + final StateStreamable source; + final TResult Function(TState state) selector; + + _StateStreamableSelector(this.source, this.selector); + + @override + TResult get state => selector(source.state); + + @override + Stream get stream => source.stream.map(selector); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is _StateStreamableSelector && + runtimeType == other.runtimeType && + source == other.source; + + @override + int get hashCode => source.hashCode; +} + +extension SelectStateStreamableExtension on StateStreamable { + StateStreamable select(R Function(TState state) selector) { + return _StateStreamableSelector(this, selector); + } +} diff --git a/packages/flutter_form_bloc/lib/src/date_time/date_time_field_bloc_builder_base.dart b/packages/flutter_form_bloc/lib/src/date_time/date_time_field_bloc_builder_base.dart index f3181d24..7727c5f2 100644 --- a/packages/flutter_form_bloc/lib/src/date_time/date_time_field_bloc_builder_base.dart +++ b/packages/flutter_form_bloc/lib/src/date_time/date_time_field_bloc_builder_base.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_bloc/src/fields/simple_field_bloc_builder.dart'; import 'package:flutter_form_bloc/src/suffix_buttons/clear_suffix_button.dart'; import 'package:flutter_form_bloc/src/theme/field_theme_resolver.dart'; @@ -162,6 +161,8 @@ class _DateTimeFieldBlocBuilderBaseState } } + late FieldBlocBuilderData _data; + void _showPicker(BuildContext context) async { FocusScope.of(context).requestFocus(FocusNode()); dynamic result; @@ -177,16 +178,10 @@ class _DateTimeFieldBlocBuilderBaseState } else if (widget.type == DateTimeFieldBlocBuilderBaseType.time) { result = await _showTimePicker(context); } - if (result != null) { - fieldBlocBuilderOnChange( - isEnabled: widget.isEnabled, - nextFocusNode: widget.nextFocusNode, - onChanged: (value) { - widget.dateTimeFieldBloc.changeValue(value); - // Used for hide keyboard - // FocusScope.of(context).requestFocus(FocusNode()); - }, - )!(result); + if (result != null && _data.canChange(isEnabled: widget.isEnabled)) { + widget.dateTimeFieldBloc.changeValue(result); + // Used for hide keyboard + // FocusScope.of(context).requestFocus(FocusNode()); } } @@ -196,66 +191,61 @@ class _DateTimeFieldBlocBuilderBaseState return Focus( focusNode: _effectiveFocusNode, - child: SimpleFieldBlocBuilder( - singleFieldBloc: widget.dateTimeFieldBloc, + child: SimpleFieldBlocBuilder>( + fieldBloc: widget.dateTimeFieldBloc, animateWhenCanShow: widget.animateWhenCanShow, - builder: (_, __) { - return BlocBuilder, - InputFieldBlocState>( - bloc: widget.dateTimeFieldBloc, - builder: (context, state) { - final isEnabled = fieldBlocIsEnabled( - isEnabled: widget.isEnabled, - enableOnlyWhenFormBlocCanSubmit: - widget.enableOnlyWhenFormBlocCanSubmit, - fieldBlocState: state, - ); - - Widget child; - - if (state.value == null && widget.decoration.hintText != null) { - child = Text( - widget.decoration.hintText!, - maxLines: widget.decoration.hintMaxLines, - overflow: TextOverflow.ellipsis, - textAlign: fieldTheme.textAlign, - style: Style.resolveTextStyle( - isEnabled: isEnabled, - style: widget.decoration.hintStyle ?? fieldTheme.textStyle!, - color: fieldTheme.textColor!, - ), - ); - } else { - child = Text( - state.value != null - ? _tryFormat(state.value, widget.format) - : '', - maxLines: 1, - overflow: TextOverflow.ellipsis, - softWrap: true, - textAlign: fieldTheme.textAlign, - style: Style.resolveTextStyle( - isEnabled: isEnabled, - style: fieldTheme.textStyle!, - color: fieldTheme.textColor!, - ), - ); - } - - return DefaultFieldBlocBuilderPadding( - padding: widget.padding, - child: GestureDetector( - onTap: !isEnabled ? null : () => _showPicker(context), - child: InputDecorator( - decoration: - _buildDecoration(context, fieldTheme, state, isEnabled), - isEmpty: state.value == null && - widget.decoration.hintText == null, - child: child, - ), - ), - ); - }, + enableOnlyWhenFormBlocCanSubmit: widget.enableOnlyWhenFormBlocCanSubmit, + isEnabled: widget.isEnabled, + // TODO: Implement readOnly + readOnly: false, + nextFocusNode: widget.nextFocusNode, + builder: (context, state, data) { + // TODO: Improve the handling of the data field + _data = data; + + final isEnabled = data.isEnabled; + + Widget child; + + if (state.value == null && widget.decoration.hintText != null) { + child = Text( + widget.decoration.hintText!, + maxLines: widget.decoration.hintMaxLines, + overflow: TextOverflow.ellipsis, + textAlign: fieldTheme.textAlign, + style: Style.resolveTextStyle( + isEnabled: isEnabled, + style: widget.decoration.hintStyle ?? fieldTheme.textStyle!, + color: fieldTheme.textColor!, + ), + ); + } else { + child = Text( + state.value != null ? _tryFormat(state.value, widget.format) : '', + maxLines: 1, + overflow: TextOverflow.ellipsis, + softWrap: true, + textAlign: fieldTheme.textAlign, + style: Style.resolveTextStyle( + isEnabled: isEnabled, + style: fieldTheme.textStyle!, + color: fieldTheme.textColor!, + ), + ); + } + + return DefaultFieldBlocBuilderPadding( + padding: widget.padding, + child: GestureDetector( + onTap: !isEnabled ? null : () => _showPicker(context), + child: InputDecorator( + decoration: + _buildDecoration(context, fieldTheme, state, isEnabled), + isEmpty: + state.value == null && widget.decoration.hintText == null, + child: child, + ), + ), ); }, ), diff --git a/packages/flutter_form_bloc/lib/src/dropdown_field_bloc_builder.dart b/packages/flutter_form_bloc/lib/src/dropdown_field_bloc_builder.dart index 5bf34df5..83988f80 100644 --- a/packages/flutter_form_bloc/lib/src/dropdown_field_bloc_builder.dart +++ b/packages/flutter_form_bloc/lib/src/dropdown_field_bloc_builder.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_bloc/src/fields/simple_field_bloc_builder.dart'; import 'package:flutter_form_bloc/src/flutter_typeahead.dart'; import 'package:flutter_form_bloc/src/theme/field_theme_resolver.dart'; @@ -155,79 +154,72 @@ class DropdownFieldBlocBuilder extends StatelessWidget { Widget build(BuildContext context) { final fieldTheme = themeStyleOf(context); - return SimpleFieldBlocBuilder( - singleFieldBloc: selectFieldBloc, + return SimpleFieldBlocBuilder>( + fieldBloc: selectFieldBloc, animateWhenCanShow: animateWhenCanShow, - builder: (context, canShow) { - return BlocBuilder, - SelectFieldBlocState>( - bloc: selectFieldBloc, - builder: (context, fieldState) { - final isEnabled = fieldBlocIsEnabled( - isEnabled: this.isEnabled, - enableOnlyWhenFormBlocCanSubmit: enableOnlyWhenFormBlocCanSubmit, - fieldBlocState: fieldState, - ); - - final decoration = - _buildDecoration(context, fieldTheme, fieldState, isEnabled); - - return DefaultFieldBlocBuilderPadding( - padding: padding, - child: InputDecorator( - decoration: decoration, - textAlign: textAlign, - isEmpty: - fieldState.value == null && decoration.hintText == null, - child: DropdownButtonHideUnderline( - child: DropdownButton( - value: fieldState.value, - focusNode: focusNode, - hint: hint, - isExpanded: isExpanded, - isDense: true, - disabledHint: disabledHint ?? - (decoration.hintText != null - ? DefaultTextStyle( - style: Style.resolveTextStyle( - isEnabled: isEnabled, - style: decoration.hintStyle ?? - fieldTheme.textStyle!, - color: fieldTheme.textColor!, - ), - child: Text(decoration.hintText!), - ) - : null), - onChanged: fieldBlocBuilderOnChange( - isEnabled: isEnabled, - nextFocusNode: nextFocusNode, - onChanged: (value) { - selectFieldBloc.changeValue(value); - onChanged?.call(value); - }, - ), - items: _buildItems( - context: context, - fieldTheme: fieldTheme, - items: fieldState.items, - isEnabled: isEnabled, - isSelected: false, - ), - selectedItemBuilder: (context) => _buildItems( - context: context, - fieldTheme: fieldTheme, - items: fieldState.items, - isEnabled: isEnabled, - isSelected: true, - ), - icon: this.decoration.suffixIcon ?? - fieldTheme.moreIcon ?? - const Icon(Icons.arrow_drop_down), - ), + enableOnlyWhenFormBlocCanSubmit: enableOnlyWhenFormBlocCanSubmit, + isEnabled: isEnabled, + // TODO: implement readOnly + readOnly: false, + nextFocusNode: nextFocusNode, + builder: (context, fieldState, data) { + final isEnabled = data.isEnabled; + + final decoration = + _buildDecoration(context, fieldTheme, fieldState, isEnabled); + + return DefaultFieldBlocBuilderPadding( + padding: padding, + child: InputDecorator( + decoration: decoration, + textAlign: textAlign, + isEmpty: fieldState.value == null && decoration.hintText == null, + child: DropdownButtonHideUnderline( + child: DropdownButton( + value: fieldState.value, + focusNode: focusNode, + hint: hint, + isExpanded: isExpanded, + isDense: true, + disabledHint: disabledHint ?? + (decoration.hintText != null + ? DefaultTextStyle( + style: Style.resolveTextStyle( + isEnabled: isEnabled, + style: + decoration.hintStyle ?? fieldTheme.textStyle!, + color: fieldTheme.textColor!, + ), + child: Text(decoration.hintText!), + ) + : null), + onChanged: data.buildOnChange( + isEnabled: isEnabled, + onChanged: (value) { + selectFieldBloc.changeValue(value); + onChanged?.call(value); + }, + ), + items: _buildItems( + context: context, + fieldTheme: fieldTheme, + items: fieldState.items, + isEnabled: isEnabled, + isSelected: false, + ), + selectedItemBuilder: (context) => _buildItems( + context: context, + fieldTheme: fieldTheme, + items: fieldState.items, + isEnabled: isEnabled, + isSelected: true, ), + icon: this.decoration.suffixIcon ?? + fieldTheme.moreIcon ?? + const Icon(Icons.arrow_drop_down), ), - ); - }, + ), + ), ); }, ); diff --git a/packages/flutter_form_bloc/lib/src/features/appear/can_show_field_bloc_builder.dart b/packages/flutter_form_bloc/lib/src/features/appear/can_show_field_bloc_builder.dart index ab580d0e..b9f6c8f3 100644 --- a/packages/flutter_form_bloc/lib/src/features/appear/can_show_field_bloc_builder.dart +++ b/packages/flutter_form_bloc/lib/src/features/appear/can_show_field_bloc_builder.dart @@ -1,16 +1,18 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_bloc/src/cubit_consumer.dart'; import 'package:form_bloc/form_bloc.dart'; class CanShowFieldBlocBuilder extends StatefulWidget { const CanShowFieldBlocBuilder({ Key? key, + required this.formBloc, required this.fieldBloc, required this.builder, this.animate = true, }) : super(key: key); - final FieldBloc fieldBloc; + final FieldBloc formBloc; + final FieldBloc fieldBloc; final bool animate; final Widget Function(BuildContext context, bool canShow) builder; @@ -36,7 +38,7 @@ class _CanShowFieldBlocBuilderState extends State duration: const Duration(milliseconds: 300), ); - if (widget.fieldBloc.state.hasFormBloc) { + if (widget.formBloc.state.contains(widget.fieldBloc)) { _showOnFirstFrame = true; _initVisibility(); } else { @@ -59,8 +61,8 @@ class _CanShowFieldBlocBuilderState extends State if (widget.animate != oldWidget.animate) { _initVisibility(); } - if (widget.fieldBloc.state.hasFormBloc != - oldWidget.fieldBloc.state.hasFormBloc) { + if (widget.formBloc.state.contains(widget.fieldBloc) != + widget.formBloc.state.contains(oldWidget.fieldBloc)) { _initVisibility(); } } @@ -72,7 +74,7 @@ class _CanShowFieldBlocBuilderState extends State } void _initVisibility() { - final canShow = widget.fieldBloc.state.hasFormBloc; + final canShow = widget.formBloc.state.contains(widget.fieldBloc); _canShow = canShow; @@ -126,10 +128,12 @@ class _CanShowFieldBlocBuilderState extends State ); } - return BlocListener( - bloc: widget.fieldBloc, - listenWhen: (prev, curr) => prev.hasFormBloc != curr.hasFormBloc, - listener: (context, state) => _changeVisibility(state.hasFormBloc), + return SourceConsumer( + source: widget.formBloc, + listenWhen: (prev, curr) => + prev.contains(widget.fieldBloc) != curr.contains(widget.fieldBloc), + listener: (context, state) => + _changeVisibility(state.contains(widget.fieldBloc)), child: child, ); } diff --git a/packages/flutter_form_bloc/lib/src/features/scroll/scrollable_field_bloc_target.dart b/packages/flutter_form_bloc/lib/src/features/scroll/scrollable_field_bloc_target.dart index 8ae6c6e7..7785f1bf 100644 --- a/packages/flutter_form_bloc/lib/src/features/scroll/scrollable_field_bloc_target.dart +++ b/packages/flutter_form_bloc/lib/src/features/scroll/scrollable_field_bloc_target.dart @@ -1,10 +1,10 @@ import 'package:flutter/widgets.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_bloc/src/cubit_consumer.dart'; import 'package:form_bloc/form_bloc.dart'; /// Mark the widget as a possible scroll target class ScrollableFieldBlocTarget extends StatefulWidget { - final SingleFieldBloc singleFieldBloc; + final FieldBloc singleFieldBloc; /// Enable auto scroll when the field has an error final bool canScroll; @@ -43,8 +43,7 @@ class ScrollableFieldBlocTarget extends StatefulWidget { } @override - State createState() => - ScrollableFieldBlocTargetState(); + State createState() => ScrollableFieldBlocTargetState(); } class ScrollableFieldBlocTargetState extends State { @@ -56,8 +55,8 @@ class ScrollableFieldBlocTargetState extends State { @override Widget build(BuildContext context) { - return BlocListener( - bloc: widget.singleFieldBloc, + return SourceConsumer( + source: widget.singleFieldBloc, listenWhen: (prev, curr) => prev.hasError != curr.hasError, listener: (context, state) { _hasError = state.hasError; diff --git a/packages/flutter_form_bloc/lib/src/features/scroll/scrollable_form_bloc_manager.dart b/packages/flutter_form_bloc/lib/src/features/scroll/scrollable_form_bloc_manager.dart index a42897e9..3bb923b9 100644 --- a/packages/flutter_form_bloc/lib/src/features/scroll/scrollable_form_bloc_manager.dart +++ b/packages/flutter_form_bloc/lib/src/features/scroll/scrollable_form_bloc_manager.dart @@ -1,5 +1,5 @@ import 'package:flutter/widgets.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_bloc/src/cubit_consumer.dart'; import 'package:flutter_form_bloc/src/features/scroll/scrollable_field_bloc_target.dart'; import 'package:flutter_form_bloc/src/theme/form_bloc_theme.dart'; import 'package:form_bloc/form_bloc.dart'; @@ -62,8 +62,8 @@ class ScrollableFormBlocManager extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocListener( - bloc: formBloc, + return SourceConsumer( + source: formBloc, listenWhen: (prev, curr) => prev.runtimeType != curr.runtimeType, listener: _onFormBlocState, child: child, diff --git a/packages/flutter_form_bloc/lib/src/fields/multi_field_bloc_consumer.dart b/packages/flutter_form_bloc/lib/src/fields/multi_field_bloc_consumer.dart new file mode 100644 index 00000000..f8e10952 --- /dev/null +++ b/packages/flutter_form_bloc/lib/src/fields/multi_field_bloc_consumer.dart @@ -0,0 +1,92 @@ +import 'package:collection/collection.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_form_bloc/src/cubit_consumer.dart'; +import 'package:form_bloc/form_bloc.dart'; + +class MultiFieldBlocConsumer extends StatelessWidget { + final MultiFieldBloc multiFieldBloc; + final CubitCondition? listenWhen; + final CubitListener? listener; + final CubitCondition? buildWhen; + final Widget? child; + final CubitBuilder? builder; + + const MultiFieldBlocConsumer({ + Key? key, + required this.multiFieldBloc, + this.listenWhen, + this.listener, + this.buildWhen, + this.child, + this.builder, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return SourceConsumer( + source: multiFieldBloc, + listenWhen: listenWhen, + listener: listener, + buildWhen: buildWhen, + child: child, + builder: builder, + ); + } +} + +class ListFieldBlocConsumer + extends MultiFieldBlocConsumer> { + const ListFieldBlocConsumer({ + Key? key, + required ListFieldBloc listFieldBloc, + CubitCondition>? listenWhen, + CubitListener>? listener, + CubitCondition>? buildWhen = + fieldBlocsChanges, + Widget? child, + CubitBuilder>? builder, + }) : super( + key: key, + multiFieldBloc: listFieldBloc, + listener: listener, + builder: builder, + child: child, + ); + + static bool fieldBlocsChanges( + ListFieldBlocState prev, ListFieldBlocState curr) { + return !prev.fieldBlocs.equals(curr.fieldBlocs); + } +} + +class MapFieldBlocConsumer + extends MultiFieldBlocConsumer> { + const MapFieldBlocConsumer({ + Key? key, + required MapFieldBloc mapFieldBloc, + CubitCondition>? listenWhen, + CubitListener>? listener, + CubitCondition>? buildWhen = + fieldBlocsChanges, + Widget? child, + CubitBuilder>? builder, + }) : super( + key: key, + multiFieldBloc: mapFieldBloc, + listener: listener, + builder: builder, + child: child, + ); + + static bool fieldBlocsChanges( + MapFieldBlocState prev, MapFieldBlocState curr) { + return !prev.fieldBlocs.equals(curr.fieldBlocs); + } +} + +extension on Map { + bool equals(Map other) => const MapEquality().equals(this, other); +} diff --git a/packages/flutter_form_bloc/lib/src/fields/simple_field_bloc_builder.dart b/packages/flutter_form_bloc/lib/src/fields/simple_field_bloc_builder.dart index 2b044004..3e7b8ee8 100644 --- a/packages/flutter_form_bloc/lib/src/fields/simple_field_bloc_builder.dart +++ b/packages/flutter_form_bloc/lib/src/fields/simple_field_bloc_builder.dart @@ -1,41 +1,159 @@ +import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_form_bloc/src/cubit_consumer.dart'; import 'package:flutter_form_bloc/src/features/appear/can_show_field_bloc_builder.dart'; import 'package:flutter_form_bloc/src/features/scroll/scrollable_field_bloc_target.dart'; +import 'package:flutter_form_bloc/src/form/form_bloc_provider.dart'; import 'package:form_bloc/form_bloc.dart'; +class FieldBlocBuilderData extends Equatable { + final bool canShow; + final bool isReadonly; + final bool isEnabled; + final FocusNode? nextFocusNode; + + const FieldBlocBuilderData({ + required this.canShow, + required this.isReadonly, + required this.isEnabled, + required this.nextFocusNode, + }); + + bool canChange({bool isEnabled = true}) => + !isReadonly && this.isEnabled && isEnabled; + + ValueChanged? buildOnChange({ + bool isEnabled = true, + required void Function(T value) onChanged, + }) { + if (isReadonly) return null; + if (!this.isEnabled || !isEnabled) return null; + final nextFocusNode = this.nextFocusNode; + if (nextFocusNode != null) { + return (value) { + onChanged(value); + nextFocusNode.nextFocus(); + }; + } + return onChanged; + } + + @override + List get props => [canShow, isReadonly, isEnabled]; +} + /// Use these widgets: /// - [CanShowFieldBlocBuilder] /// - [ScrollableFieldBlocTarget] -class SimpleFieldBlocBuilder extends StatelessWidget { - final SingleFieldBloc singleFieldBloc; +class SimpleFieldBlocBuilder + extends StatelessWidget { + final FieldBloc fieldBloc; final bool animateWhenCanShow; final bool focusOnValidationFailed; - final Widget Function(BuildContext context, bool canShow) builder; + final bool enableOnlyWhenFormBlocCanSubmit; + final bool isEnabled; + final bool readOnly; + final FocusNode? nextFocusNode; + final Widget Function( + BuildContext context, + TState state, + FieldBlocBuilderData data, + ) builder; const SimpleFieldBlocBuilder({ Key? key, - required this.singleFieldBloc, + required this.fieldBloc, this.animateWhenCanShow = true, this.focusOnValidationFailed = true, + this.enableOnlyWhenFormBlocCanSubmit = true, + this.isEnabled = true, + this.readOnly = false, + this.nextFocusNode, required this.builder, }) : super(key: key); @override Widget build(BuildContext context) { - return CanShowFieldBlocBuilder( - fieldBloc: singleFieldBloc, - animate: animateWhenCanShow, - builder: (context, canShow) { - final field = builder(context, canShow); - - if (!canShow) { - return field; + Widget _buildField({ + bool canShow = true, + bool isSubmitting = false, + required TState state, + }) { + final data = FieldBlocBuilderData( + canShow: canShow, + isReadonly: readOnly || isSubmitting, + isEnabled: isEnabled, + nextFocusNode: nextFocusNode, + ); + final child = builder(context, state, data); + + if (!canShow) { + return child; + } + + return ScrollableFieldBlocTarget( + singleFieldBloc: fieldBloc, + canScroll: focusOnValidationFailed, + child: child, + ); + } + + Widget _buildAnimatedField({ + required FieldBloc formBloc, + bool isSubmitting = false, + required TState state, + }) { + return CanShowFieldBlocBuilder( + formBloc: formBloc, + fieldBloc: fieldBloc, + animate: animateWhenCanShow, + builder: (context, canShow) { + return _buildField( + canShow: canShow, + isSubmitting: isSubmitting, + state: state, + ); + }, + ); + } + + Widget _buildLockableField({ + required FormBloc formBloc, + required TState state, + }) { + return SourceConsumer( + source: formBloc.select((state) => state is FormBlocSubmitting), + builder: (context, isSubmitting, _) { + return _buildAnimatedField( + formBloc: formBloc, + isSubmitting: isSubmitting, + state: state, + ); + }, + ); + } + + return SourceConsumer( + source: fieldBloc, + builder: (context, state, _) { + final formBloc = FormBlocProvider.maybeOf(context); + + if (formBloc == null) { + return _buildField( + state: state, + ); + } + + if (!enableOnlyWhenFormBlocCanSubmit || formBloc is! FormBloc) { + return _buildAnimatedField( + formBloc: formBloc, + state: state, + ); } - return ScrollableFieldBlocTarget( - singleFieldBloc: singleFieldBloc, - canScroll: focusOnValidationFailed, - child: field, + return _buildLockableField( + formBloc: formBloc, + state: state, ); }, ); diff --git a/packages/flutter_form_bloc/lib/src/form/form_bloc_provider.dart b/packages/flutter_form_bloc/lib/src/form/form_bloc_provider.dart new file mode 100644 index 00000000..17eaadec --- /dev/null +++ b/packages/flutter_form_bloc/lib/src/form/form_bloc_provider.dart @@ -0,0 +1,22 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_form_bloc/flutter_form_bloc.dart'; + +class FormBlocProvider extends InheritedWidget { + final FieldBloc formBloc; + + const FormBlocProvider({ + Key? key, + required this.formBloc, + required Widget child, + }) : super(key: key, child: child); + + static FieldBloc? maybeOf(BuildContext context) { + return context + .dependOnInheritedWidgetOfExactType() + ?.formBloc; + } + + @override + bool updateShouldNotify(FormBlocProvider oldWidget) => + formBloc != oldWidget.formBloc; +} diff --git a/packages/flutter_form_bloc/lib/src/form_bloc_listener.dart b/packages/flutter_form_bloc/lib/src/form_bloc_listener.dart index c159292e..1b34b16b 100644 --- a/packages/flutter_form_bloc/lib/src/form_bloc_listener.dart +++ b/packages/flutter_form_bloc/lib/src/form_bloc_listener.dart @@ -1,6 +1,7 @@ import 'package:flutter/widgets.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_bloc/src/cubit_consumer.dart'; import 'package:form_bloc/form_bloc.dart' as form_bloc; +import 'package:form_bloc/form_bloc.dart'; typedef FormBlocListenerCallback< FormBlocState extends form_bloc @@ -10,18 +11,13 @@ typedef FormBlocListenerCallback< = void Function(BuildContext context, FormBlocState state); /// [BlocListener] that reacts to the state changes of the FormBloc. -class FormBlocListener< - FormBloc extends form_bloc.FormBloc, - SuccessResponse, - ErrorResponse> - extends BlocListener> { +class FormBlocListener extends SourceConsumer< + form_bloc.FormBlocState> { /// [BlocListener] that reacts to the state changes of the FormBloc. /// {@macro bloclistener} FormBlocListener({ Key? key, - this.formBloc, - Widget? child, + required this.formBloc, this.onLoading, this.onLoaded, this.onLoadFailed, @@ -33,59 +29,49 @@ class FormBlocListener< this.onDeleting, this.onDeleteFailed, this.onDeleteSuccessful, + required Widget child, }) : super( key: key, - child: child, - bloc: formBloc, + source: formBloc, listenWhen: (previousState, state) => previousState.runtimeType != state.runtimeType, listener: (context, state) { - if (state is form_bloc - .FormBlocLoading && - onLoading != null) { - onLoading(context, state); - } else if (state is form_bloc - .FormBlocLoaded && - onLoaded != null) { - onLoaded(context, state); + if (state + is form_bloc.FormBlocLoading) { + onLoading?.call(context, state); + } else if (state + is form_bloc.FormBlocLoaded) { + onLoaded?.call(context, state); } else if (state is form_bloc - .FormBlocLoadFailed && - onLoadFailed != null) { - onLoadFailed(context, state); + .FormBlocLoadFailed) { + onLoadFailed?.call(context, state); } else if (state is form_bloc - .FormBlocSubmitting && - onSubmitting != null) { - onSubmitting(context, state); + .FormBlocSubmitting) { + onSubmitting?.call(context, state); + } else if (state + is form_bloc.FormBlocSuccess) { + onSuccess?.call(context, state); + } else if (state + is form_bloc.FormBlocFailure) { + onFailure?.call(context, state); } else if (state is form_bloc - .FormBlocSuccess && - onSuccess != null) { - onSuccess(context, state); + .FormBlocSubmissionCancelled) { + onSubmissionCancelled?.call(context, state); } else if (state is form_bloc - .FormBlocFailure && - onFailure != null) { - onFailure(context, state); - } else if (state is form_bloc.FormBlocSubmissionCancelled< - SuccessResponse, ErrorResponse> && - onSubmissionCancelled != null) { - onSubmissionCancelled(context, state); + .FormBlocSubmissionFailed) { + onSubmissionFailed?.call(context, state); + } else if (state + is form_bloc.FormBlocDeleting) { + onDeleting?.call(context, state); } else if (state is form_bloc - .FormBlocSubmissionFailed && - onSubmissionFailed != null) { - onSubmissionFailed(context, state); + .FormBlocDeleteFailed) { + onDeleteFailed?.call(context, state); } else if (state is form_bloc - .FormBlocDeleting && - onDeleting != null) { - onDeleting(context, state); - } else if (state is form_bloc - .FormBlocDeleteFailed && - onDeleteFailed != null) { - onDeleteFailed(context, state); - } else if (state is form_bloc - .FormBlocDeleteSuccessful && - onDeleteSuccessful != null) { - onDeleteSuccessful(context, state); + .FormBlocDeleteSuccessful) { + onDeleteSuccessful?.call(context, state); } }, + child: child, ); /// {@macro form_bloc.form_state.FormBlocLoading} @@ -157,7 +143,7 @@ class FormBlocListener< /// If the [formBloc] parameter is omitted, [FormBlocListener] /// will automatically perform a lookup using /// [BlocProvider].of<[FormBloc]> and the current [BuildContext]. - final FormBloc? formBloc; + final FormBloc formBloc; /// The [Widget] which will be rendered as a descendant of the [BlocListener]. @override diff --git a/packages/flutter_form_bloc/lib/src/groups/fields/checkbox_group_field_bloc_builder.dart b/packages/flutter_form_bloc/lib/src/groups/fields/checkbox_group_field_bloc_builder.dart index 823d47f2..9003b188 100644 --- a/packages/flutter_form_bloc/lib/src/groups/fields/checkbox_group_field_bloc_builder.dart +++ b/packages/flutter_form_bloc/lib/src/groups/fields/checkbox_group_field_bloc_builder.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_bloc/src/fields/simple_field_bloc_builder.dart'; import 'package:flutter_form_bloc/src/groups/widgets/group_view.dart'; import 'package:flutter_form_bloc/src/groups/widgets/item_group_tile.dart'; @@ -130,31 +129,21 @@ class CheckboxGroupFieldBlocBuilder extends StatelessWidget { data: Theme.of(context).copyWith( checkboxTheme: fieldTheme.checkboxTheme, ), - child: SimpleFieldBlocBuilder( - singleFieldBloc: multiSelectFieldBloc, + child: SimpleFieldBlocBuilder>( + fieldBloc: multiSelectFieldBloc, animateWhenCanShow: animateWhenCanShow, - builder: (_, __) { - return BlocBuilder, - MultiSelectFieldBlocState>( - bloc: multiSelectFieldBloc, - builder: (context, state) { - final isEnabled = fieldBlocIsEnabled( - isEnabled: this.isEnabled, - enableOnlyWhenFormBlocCanSubmit: - enableOnlyWhenFormBlocCanSubmit, - fieldBlocState: state, - ); - - return DefaultFieldBlocBuilderPadding( - padding: padding, - child: GroupInputDecorator( - decoration: - _buildDecoration(context, state, isEnabled, fieldTheme), - child: - _buildCheckboxes(context, state, isEnabled, fieldTheme), - ), - ); - }, + enableOnlyWhenFormBlocCanSubmit: enableOnlyWhenFormBlocCanSubmit, + isEnabled: isEnabled, + // TODO: Implement readOnly + readOnly: false, + nextFocusNode: nextFocusNode, + builder: (context, state, data) { + return DefaultFieldBlocBuilderPadding( + padding: padding, + child: GroupInputDecorator( + decoration: _buildDecoration(context, state, data, fieldTheme), + child: _buildCheckboxes(context, state, data, fieldTheme), + ), ); }, ), @@ -164,7 +153,7 @@ class CheckboxGroupFieldBlocBuilder extends StatelessWidget { Widget _buildCheckboxes( BuildContext context, MultiSelectFieldBlocState state, - bool isFieldEnabled, + FieldBlocBuilderData data, CheckboxFieldTheme fieldTheme, ) { return DefaultTextStyle( @@ -183,11 +172,9 @@ class CheckboxGroupFieldBlocBuilder extends StatelessWidget { builder: (context, index) { final item = state.items[index]; final fieldItem = itemBuilder(context, item); - final isEnabled = isFieldEnabled && fieldItem.isEnabled; - final onChanged = fieldBlocBuilderOnChange( + final onChanged = data.buildOnChange( isEnabled: isEnabled, - nextFocusNode: nextFocusNode, onChanged: (isChecked) { if (!isChecked!) { multiSelectFieldBloc.deselect(item); @@ -220,13 +207,13 @@ class CheckboxGroupFieldBlocBuilder extends StatelessWidget { InputDecoration _buildDecoration( BuildContext context, MultiSelectFieldBlocState state, - bool isEnabled, + FieldBlocBuilderData data, CheckboxFieldTheme fieldTheme, ) { var decoration = this.decoration; decoration = decoration.copyWith( - enabled: isEnabled, + enabled: data.isEnabled, errorText: Style.getErrorText( context: context, errorBuilder: errorBuilder, diff --git a/packages/flutter_form_bloc/lib/src/groups/fields/radio_button_group_field_bloc.dart b/packages/flutter_form_bloc/lib/src/groups/fields/radio_button_group_field_bloc.dart index a84c6546..38f07497 100644 --- a/packages/flutter_form_bloc/lib/src/groups/fields/radio_button_group_field_bloc.dart +++ b/packages/flutter_form_bloc/lib/src/groups/fields/radio_button_group_field_bloc.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_form_bloc/src/features/appear/can_show_field_bloc_builder.dart'; +import 'package:flutter_form_bloc/src/fields/simple_field_bloc_builder.dart'; import 'package:flutter_form_bloc/src/groups/widgets/group_view.dart'; import 'package:flutter_form_bloc/src/groups/widgets/item_group_tile.dart'; import 'package:flutter_form_bloc/src/theme/field_theme_resolver.dart'; @@ -122,31 +121,21 @@ class RadioButtonGroupFieldBlocBuilder extends StatelessWidget { data: Theme.of(context).copyWith( radioTheme: fieldTheme.radioTheme, ), - child: CanShowFieldBlocBuilder( + child: SimpleFieldBlocBuilder>( fieldBloc: selectFieldBloc, - animate: animateWhenCanShow, - builder: (_, __) { - return BlocBuilder, - SelectFieldBlocState>( - bloc: selectFieldBloc, - builder: (context, state) { - final isEnabled = fieldBlocIsEnabled( - isEnabled: this.isEnabled, - enableOnlyWhenFormBlocCanSubmit: - enableOnlyWhenFormBlocCanSubmit, - fieldBlocState: state, - ); - - return DefaultFieldBlocBuilderPadding( - padding: padding, - child: GroupInputDecorator( - decoration: - _buildDecoration(context, fieldTheme, state, isEnabled), - child: - _buildRadioButtons(context, state, fieldTheme, isEnabled), - ), - ); - }, + animateWhenCanShow: animateWhenCanShow, + enableOnlyWhenFormBlocCanSubmit: enableOnlyWhenFormBlocCanSubmit, + isEnabled: isEnabled, + // TODO: Implement readOnly + readOnly: false, + nextFocusNode: nextFocusNode, + builder: (context, state, data) { + return DefaultFieldBlocBuilderPadding( + padding: padding, + child: GroupInputDecorator( + decoration: _buildDecoration(context, state, data, fieldTheme), + child: _buildRadioButtons(context, state, data, fieldTheme), + ), ); }, ), @@ -156,8 +145,8 @@ class RadioButtonGroupFieldBlocBuilder extends StatelessWidget { Widget _buildRadioButtons( BuildContext context, SelectFieldBlocState state, + FieldBlocBuilderData data, RadioFieldTheme fieldTheme, - bool isFieldEnabled, ) { return DefaultTextStyle( style: Style.resolveTextStyle( @@ -175,11 +164,8 @@ class RadioButtonGroupFieldBlocBuilder extends StatelessWidget { builder: (context, index) { final item = state.items[index]; final fieldItem = itemBuilder(context, item); - final isEnabled = isFieldEnabled && fieldItem.isEnabled; - final onChanged = fieldBlocBuilderOnChange( - isEnabled: isEnabled, - nextFocusNode: nextFocusNode, + final onChanged = data.buildOnChange( onChanged: (value) { selectFieldBloc.changeValue(value); fieldItem.onTap?.call(); @@ -213,14 +199,14 @@ class RadioButtonGroupFieldBlocBuilder extends StatelessWidget { InputDecoration _buildDecoration( BuildContext context, - RadioFieldTheme fieldTheme, SelectFieldBlocState state, - bool isEnable, + FieldBlocBuilderData data, + RadioFieldTheme fieldTheme, ) { var decoration = this.decoration; decoration = decoration.copyWith( - enabled: isEnable, + enabled: data.isEnabled, errorText: Style.getErrorText( context: context, errorBuilder: errorBuilder, diff --git a/packages/flutter_form_bloc/lib/src/slider/slider_field_bloc_builder.dart b/packages/flutter_form_bloc/lib/src/slider/slider_field_bloc_builder.dart index 1bb2e6ae..fddd5ddb 100644 --- a/packages/flutter_form_bloc/lib/src/slider/slider_field_bloc_builder.dart +++ b/packages/flutter_form_bloc/lib/src/slider/slider_field_bloc_builder.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_bloc/src/fields/simple_field_bloc_builder.dart'; import 'package:flutter_form_bloc/src/theme/form_bloc_theme.dart'; import 'package:flutter_form_bloc/src/utils/utils.dart'; @@ -96,47 +95,37 @@ class SliderFieldBlocBuilder extends StatelessWidget { data: Theme.of(context).copyWith( sliderTheme: fieldTheme.sliderTheme, ), - child: SimpleFieldBlocBuilder( - singleFieldBloc: inputFieldBloc, + child: SimpleFieldBlocBuilder>( + fieldBloc: inputFieldBloc, animateWhenCanShow: animateWhenCanShow, - builder: (context, _) { - return BlocBuilder, - InputFieldBlocState>( - bloc: inputFieldBloc, - builder: (context, state) { - final isEnabled = fieldBlocIsEnabled( - isEnabled: this.isEnabled, - enableOnlyWhenFormBlocCanSubmit: - enableOnlyWhenFormBlocCanSubmit, - fieldBlocState: state, - ); - final value = state.value; - - return DefaultFieldBlocBuilderPadding( - padding: padding, - child: InputDecorator( - decoration: _buildDecoration(context, state, isEnabled), - isEmpty: false, - child: Slider( - value: value, - min: min, - max: max, - focusNode: focusNode, - divisions: divisions, - onChanged: fieldBlocBuilderOnChange( - isEnabled: isEnabled, - readOnly: readOnly, - nextFocusNode: nextFocusNode, - onChanged: inputFieldBloc.changeValue, - ), - label: labelBuilder?.call(context, value), - activeColor: activeColor, - inactiveColor: inactiveColor, - mouseCursor: mouseCursor, - ), + enableOnlyWhenFormBlocCanSubmit: enableOnlyWhenFormBlocCanSubmit, + isEnabled: isEnabled, + readOnly: readOnly, + nextFocusNode: nextFocusNode, + builder: (context, state, data) { + final value = state.value; + + return DefaultFieldBlocBuilderPadding( + padding: padding, + child: InputDecorator( + decoration: _buildDecoration(context, state, data), + isEmpty: false, + child: Slider( + value: value, + min: min, + max: max, + focusNode: focusNode, + divisions: divisions, + onChanged: data.buildOnChange( + isEnabled: isEnabled, + onChanged: inputFieldBloc.changeValue, ), - ); - }, + label: labelBuilder?.call(context, value), + activeColor: activeColor, + inactiveColor: inactiveColor, + mouseCursor: mouseCursor, + ), + ), ); }, ), @@ -146,7 +135,7 @@ class SliderFieldBlocBuilder extends StatelessWidget { InputDecoration _buildDecoration( BuildContext context, FieldBlocState state, - bool isEnabled, + FieldBlocBuilderData data, ) { return decoration.copyWith( enabled: isEnabled, diff --git a/packages/flutter_form_bloc/lib/src/stepper/stepper_form_bloc_builder.dart b/packages/flutter_form_bloc/lib/src/stepper/stepper_form_bloc_builder.dart index ce78d7c8..fba225db 100644 --- a/packages/flutter_form_bloc/lib/src/stepper/stepper_form_bloc_builder.dart +++ b/packages/flutter_form_bloc/lib/src/stepper/stepper_form_bloc_builder.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart' hide Stepper, Step; -import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_bloc/src/cubit_consumer.dart'; import 'package:flutter_form_bloc/src/stepper/stepper.dart'; import 'package:form_bloc/form_bloc.dart'; @@ -51,10 +51,11 @@ class FormBlocStep { final bool? isActive; } -class StepperFormBlocBuilder extends StatelessWidget { +class StepperFormBlocBuilder + extends StatelessWidget { const StepperFormBlocBuilder({ Key? key, - this.formBloc, + required this.formBloc, required this.stepsBuilder, this.physics, this.type = StepperType.vertical, @@ -64,12 +65,12 @@ class StepperFormBlocBuilder extends StatelessWidget { this.controlsBuilder, }) : super(key: key); - final T? formBloc; + final TFormBloc formBloc; /// The steps of the stepper whose titles, subtitles, icons always get shown. /// /// The length of [stepsBuilder] must not change. - final List Function(T? formBloc) stepsBuilder; + final List Function(TFormBloc? formBloc) stepsBuilder; /// How the stepper's scroll view should respond to user input. /// @@ -158,12 +159,12 @@ class StepperFormBlocBuilder extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( - bloc: formBloc, + return SourceConsumer( + source: formBloc, buildWhen: (p, c) => p.numberOfSteps != c.numberOfSteps || p.currentStep != c.currentStep, - builder: (context, state) { - final formBloc = this.formBloc ?? context.read(); + builder: (context, state, _) { + final formBloc = this.formBloc; final formBlocSteps = stepsBuilder(formBloc); return Stepper( diff --git a/packages/flutter_form_bloc/lib/src/suffix_buttons/suffix_button_bloc_builder.dart b/packages/flutter_form_bloc/lib/src/suffix_buttons/suffix_button_bloc_builder.dart index 597c0747..8ce27901 100644 --- a/packages/flutter_form_bloc/lib/src/suffix_buttons/suffix_button_bloc_builder.dart +++ b/packages/flutter_form_bloc/lib/src/suffix_buttons/suffix_button_bloc_builder.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_form_bloc/src/utils/functions.dart'; +import 'package:flutter_form_bloc/src/cubit_consumer.dart'; +import 'package:flutter_form_bloc/src/form/form_bloc_provider.dart'; import 'package:form_bloc/form_bloc.dart'; typedef BlocChildBuilder = Widget Function( @@ -33,10 +33,10 @@ class SuffixButtonBuilderBase extends StatelessWidget { } Widget _buildButton(BuildContext context, FieldBlocState state) { - final isEnabled = fieldBlocIsEnabled( - isEnabled: this.isEnabled, - fieldBlocState: state, - ); + final formBloc = FormBlocProvider.maybeOf(context); + + final isEnabled = + this.isEnabled && !(formBloc?.state.isValidating ?? false); return InkWell( borderRadius: const BorderRadius.all(Radius.circular(25.0)), @@ -48,9 +48,9 @@ class SuffixButtonBuilderBase extends StatelessWidget { @override Widget build(BuildContext context) { return ExcludeFocus( - child: BlocBuilder( - bloc: singleFieldBloc, - builder: (context, state) { + child: SourceConsumer( + source: singleFieldBloc, + builder: (context, state, _) { Widget current = _buildButton(context, state); if (!visibleWithoutValue) { diff --git a/packages/flutter_form_bloc/lib/src/switch_field_bloc_builder.dart b/packages/flutter_form_bloc/lib/src/switch_field_bloc_builder.dart index 37a3880b..eb870c52 100644 --- a/packages/flutter_form_bloc/lib/src/switch_field_bloc_builder.dart +++ b/packages/flutter_form_bloc/lib/src/switch_field_bloc_builder.dart @@ -1,6 +1,5 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_bloc/src/fields/simple_field_bloc_builder.dart'; import 'package:flutter_form_bloc/src/theme/field_theme_resolver.dart'; import 'package:flutter_form_bloc/src/theme/form_bloc_theme.dart'; @@ -136,56 +135,51 @@ class SwitchFieldBlocBuilder extends StatelessWidget { data: Theme.of(context).copyWith( switchTheme: fieldTheme.switchTheme!, ), - child: SimpleFieldBlocBuilder( - singleFieldBloc: booleanFieldBloc, + child: SimpleFieldBlocBuilder( + fieldBloc: booleanFieldBloc, animateWhenCanShow: animateWhenCanShow, - builder: (_, __) { - return BlocBuilder( - bloc: booleanFieldBloc, - builder: (context, state) { - final isEnabled = fieldBlocIsEnabled( - isEnabled: this.isEnabled, - enableOnlyWhenFormBlocCanSubmit: - enableOnlyWhenFormBlocCanSubmit, - fieldBlocState: state, - ); - - return DefaultFieldBlocBuilderPadding( - padding: padding, - child: InputDecorator( - decoration: Style.inputDecorationWithoutBorder.copyWith( - prefixIcon: fieldTheme.controlAffinity == - FieldBlocBuilderControlAffinity.leading - ? _buildSwitch(context, state) - : null, - suffixIcon: fieldTheme.controlAffinity == - FieldBlocBuilderControlAffinity.trailing - ? _buildSwitch(context, state) - : null, - errorText: Style.getErrorText( - context: context, - errorBuilder: errorBuilder, - fieldBlocState: state, - fieldBloc: booleanFieldBloc, - ), - ), - child: DefaultTextStyle( - style: Style.resolveTextStyle( - isEnabled: isEnabled, - style: fieldTheme.textStyle!, - color: fieldTheme.textColor!, - ), - child: Container( - constraints: const BoxConstraints( - minHeight: kMinInteractiveDimension, - ), - alignment: alignment, - child: body, - ), + enableOnlyWhenFormBlocCanSubmit: enableOnlyWhenFormBlocCanSubmit, + isEnabled: isEnabled, + // TODO: Implement readOnly + readOnly: false, + nextFocusNode: nextFocusNode, + builder: (context, state, data) { + final isEnabled = data.isEnabled; + + return DefaultFieldBlocBuilderPadding( + padding: padding, + child: InputDecorator( + decoration: Style.inputDecorationWithoutBorder.copyWith( + prefixIcon: fieldTheme.controlAffinity == + FieldBlocBuilderControlAffinity.leading + ? _buildSwitch(context, state, data) + : null, + suffixIcon: fieldTheme.controlAffinity == + FieldBlocBuilderControlAffinity.trailing + ? _buildSwitch(context, state, data) + : null, + errorText: Style.getErrorText( + context: context, + errorBuilder: errorBuilder, + fieldBlocState: state, + fieldBloc: booleanFieldBloc, + ), + ), + child: DefaultTextStyle( + style: Style.resolveTextStyle( + isEnabled: isEnabled, + style: fieldTheme.textStyle!, + color: fieldTheme.textColor!, + ), + child: Container( + constraints: const BoxConstraints( + minHeight: kMinInteractiveDimension, ), + alignment: alignment, + child: body, ), - ); - }, + ), + ), ); }, ), @@ -195,12 +189,11 @@ class SwitchFieldBlocBuilder extends StatelessWidget { Switch _buildSwitch( BuildContext context, BooleanFieldBlocState state, + FieldBlocBuilderData data, ) { return Switch( value: state.value, - onChanged: fieldBlocBuilderOnChange( - isEnabled: isEnabled, - nextFocusNode: nextFocusNode, + onChanged: data.buildOnChange( onChanged: booleanFieldBloc.changeValue, ), activeThumbImage: activeThumbImage, diff --git a/packages/flutter_form_bloc/lib/src/text_field_bloc_builder.dart b/packages/flutter_form_bloc/lib/src/text_field_bloc_builder.dart index ed01c7a0..908eb2d0 100644 --- a/packages/flutter_form_bloc/lib/src/text_field_bloc_builder.dart +++ b/packages/flutter_form_bloc/lib/src/text_field_bloc_builder.dart @@ -1,9 +1,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_form_bloc/flutter_form_bloc.dart'; -import 'package:flutter_form_bloc/src/features/appear/can_show_field_bloc_builder.dart'; +import 'package:flutter_form_bloc/src/fields/simple_field_bloc_builder.dart'; import 'package:flutter_form_bloc/src/flutter_typeahead.dart'; import 'package:flutter_form_bloc/src/suffix_buttons/clear_suffix_button.dart'; import 'package:flutter_form_bloc/src/suffix_buttons/obscure_suffix_button.dart'; @@ -770,12 +768,13 @@ class _TextFieldBlocBuilderState extends State { super.dispose(); } + late FieldBlocBuilderData _data; + /// Disable editing when the state of the FormBloc is [FormBlocSubmitting]. void _textControllerListener() { - if (widget.textFieldBloc.state.formBloc?.state is FormBlocSubmitting) { - if (_controller.text != (widget.textFieldBloc.value)) { - _fixControllerTextValue(widget.textFieldBloc.value); - } + if (!_data.isReadonly) return; + if (_controller.text != (widget.textFieldBloc.value)) { + _fixControllerTextValue(widget.textFieldBloc.value); } } @@ -807,33 +806,26 @@ class _TextFieldBlocBuilderState extends State { Widget build(BuildContext context) { final fieldTheme = widget.themeStyleOf(context); - return SimpleFieldBlocBuilder( - singleFieldBloc: widget.textFieldBloc, + return SimpleFieldBlocBuilder( + fieldBloc: widget.textFieldBloc, animateWhenCanShow: widget.animateWhenCanShow, focusOnValidationFailed: widget.focusOnValidationFailed, - builder: (_, __) { - return BlocBuilder( - bloc: widget.textFieldBloc, - builder: (context, state) { - final isEnabled = fieldBlocIsEnabled( - isEnabled: widget.isEnabled, - enableOnlyWhenFormBlocCanSubmit: - widget.enableOnlyWhenFormBlocCanSubmit, - fieldBlocState: state, - ); - - if (_controller.text != state.value) { - _fixControllerTextValue(state.value); - } - return DefaultFieldBlocBuilderPadding( - padding: widget.padding, - child: _buildTextField( - state: state, - isEnabled: isEnabled, - fieldTheme: fieldTheme, - ), - ); - }, + enableOnlyWhenFormBlocCanSubmit: widget.enableOnlyWhenFormBlocCanSubmit, + isEnabled: widget.isEnabled, + readOnly: widget.readOnly, + nextFocusNode: widget.nextFocusNode, + builder: (context, state, data) { + _data = data; + if (_controller.text != state.value) { + _fixControllerTextValue(state.value); + } + return DefaultFieldBlocBuilderPadding( + padding: widget.padding, + child: _buildTextField( + state: state, + data: data, + fieldTheme: fieldTheme, + ), ); }, ); @@ -913,7 +905,7 @@ class _TextFieldBlocBuilderState extends State { Widget _buildTextField({ required TextFieldTheme fieldTheme, required TextFieldBlocState state, - required bool isEnabled, + required FieldBlocBuilderData data, }) { return TypeAheadField( textFieldConfiguration: TextFieldConfiguration( @@ -925,7 +917,7 @@ class _TextFieldBlocBuilderState extends State { (widget.nextFocusNode != null ? TextInputAction.next : null), textCapitalization: widget.textCapitalization, style: Style.resolveTextStyle( - isEnabled: isEnabled, + isEnabled: data.isEnabled, style: fieldTheme.textStyle!, color: fieldTheme.textColor!, ), @@ -947,7 +939,7 @@ class _TextFieldBlocBuilderState extends State { onEditingComplete: widget.onEditingComplete, onSubmitted: _onSubmitted, inputFormatters: widget.inputFormatters, - enabled: isEnabled, + enabled: data.isEnabled, cursorWidth: widget.cursorWidth, cursorRadius: widget.cursorRadius, cursorColor: widget.cursorColor, diff --git a/packages/flutter_form_bloc/lib/src/utils/functions.dart b/packages/flutter_form_bloc/lib/src/utils/functions.dart index 8963fdff..2a393fa7 100644 --- a/packages/flutter_form_bloc/lib/src/utils/functions.dart +++ b/packages/flutter_form_bloc/lib/src/utils/functions.dart @@ -1,36 +1,35 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:form_bloc/form_bloc.dart'; -ValueChanged? fieldBlocBuilderOnChange({ - required bool isEnabled, - bool readOnly = false, - required FocusNode? nextFocusNode, - required void Function(T value) onChanged, -}) { - if (isEnabled) { - return (T value) { - if (readOnly) return; - onChanged(value); - if (nextFocusNode != null) { - nextFocusNode.requestFocus(); - } - }; - } - return null; -} +// ValueChanged? fieldBlocBuilderOnChange({ +// required bool isEnabled, +// bool readOnly = false, +// required FocusNode? nextFocusNode, +// required void Function(T value) onChanged, +// }) { +// if (isEnabled) { +// return (T value) { +// if (readOnly) return; +// onChanged(value); +// if (nextFocusNode != null) { +// nextFocusNode.requestFocus(); +// } +// }; +// } +// return null; +// } -bool fieldBlocIsEnabled({ - required bool isEnabled, - bool? enableOnlyWhenFormBlocCanSubmit, - required FieldBlocState fieldBlocState, -}) { - return isEnabled - ? (enableOnlyWhenFormBlocCanSubmit ?? false) - ? fieldBlocState.formBloc?.state.canSubmit ?? true - : true - : false; -} +// bool fieldBlocIsEnabled({ +// required bool isEnabled, +// bool? enableOnlyWhenFormBlocCanSubmit, +// required FieldBlocState fieldBlocState, +// }) { +// return isEnabled +// ? (enableOnlyWhenFormBlocCanSubmit ?? false) +// ? fieldBlocState.formBloc?.state.canSubmit ?? true +// : true +// : false; +// } Widget widgetBasedOnPlatform({ required Widget mobile, diff --git a/packages/flutter_form_bloc/pubspec.yaml b/packages/flutter_form_bloc/pubspec.yaml index c473c489..e3098a68 100644 --- a/packages/flutter_form_bloc/pubspec.yaml +++ b/packages/flutter_form_bloc/pubspec.yaml @@ -12,10 +12,15 @@ dependencies: flutter: sdk: flutter - flutter_bloc: ^8.0.1 -# form_bloc: + bloc: ^8.0.3 +# flutter_bloc: ^8.0.1 + form_bloc: # path: ../form_bloc - form_bloc: ^0.30.0 + git: + url: https://github.com/BreX900/form_bloc.git + path: ./packages/form_bloc + ref: next +# form_bloc: ^0.30.0 equatable: ^2.0.3 rxdart: ^0.27.3 flutter_keyboard_visibility: ^5.0.2 diff --git a/packages/form_bloc/example/main.dart b/packages/form_bloc/example/main.dart index 1ba9b2f4..dc44c0d5 100644 --- a/packages/form_bloc/example/main.dart +++ b/packages/form_bloc/example/main.dart @@ -15,11 +15,13 @@ class LoginFormBloc extends FormBloc { ); LoginFormBloc() { - addFieldBlocs( - fieldBlocs: [ - email, - password, - ], + addStep( + fieldBloc: ListFieldBloc( + fieldBlocs: [ + email, + password, + ], + ), ); } diff --git a/packages/form_bloc/lib/src/blocs/boolean_field/boolean_field_bloc.dart b/packages/form_bloc/lib/src/blocs/boolean_field/boolean_field_bloc.dart index c383baee..07daea39 100644 --- a/packages/form_bloc/lib/src/blocs/boolean_field/boolean_field_bloc.dart +++ b/packages/form_bloc/lib/src/blocs/boolean_field/boolean_field_bloc.dart @@ -35,7 +35,6 @@ class BooleanFieldBloc extends SingleFieldBloc>? validators, List>? asyncValidators, @@ -69,7 +68,6 @@ class BooleanFieldBloc extends SingleFieldBloc value, extraData: extraData, ), diff --git a/packages/form_bloc/lib/src/blocs/boolean_field/boolean_field_state.dart b/packages/form_bloc/lib/src/blocs/boolean_field/boolean_field_state.dart index b96e659d..92ce6319 100644 --- a/packages/form_bloc/lib/src/blocs/boolean_field/boolean_field_state.dart +++ b/packages/form_bloc/lib/src/blocs/boolean_field/boolean_field_state.dart @@ -1,7 +1,6 @@ part of '../field/field_bloc.dart'; -class BooleanFieldBlocState - extends FieldBlocState { +class BooleanFieldBlocState extends FieldBlocState { BooleanFieldBlocState({ required bool isValueChanged, required bool initialValue, @@ -12,8 +11,6 @@ class BooleanFieldBlocState required Suggestions? suggestions, required bool isValidated, required bool isValidating, - FormBloc? formBloc, - required String name, List additionalProps = const [], dynamic Function(bool value)? toJson, ExtraData? extraData, @@ -27,8 +24,6 @@ class BooleanFieldBlocState suggestions: suggestions, isValidated: isValidated, isValidating: isValidating, - formBloc: formBloc, - name: name, toJson: toJson, extraData: extraData, ); @@ -44,7 +39,6 @@ class BooleanFieldBlocState Param?>? suggestions, bool? isValidated, bool? isValidating, - Param? formBloc, Param? extraData, }) { return BooleanFieldBlocState( @@ -57,8 +51,6 @@ class BooleanFieldBlocState isDirty: isDirty ?? this.isDirty, isValidated: isValidated ?? this.isValidated, isValidating: isValidating ?? this.isValidating, - formBloc: formBloc == null ? this.formBloc : formBloc.value, - name: name, toJson: _toJson, extraData: extraData == null ? this.extraData : extraData.value, ); diff --git a/packages/form_bloc/lib/src/blocs/field/field_bloc.dart b/packages/form_bloc/lib/src/blocs/field/field_bloc.dart index 67b0b40c..d308a45f 100644 --- a/packages/form_bloc/lib/src/blocs/field/field_bloc.dart +++ b/packages/form_bloc/lib/src/blocs/field/field_bloc.dart @@ -10,7 +10,6 @@ import 'package:form_bloc/src/extension/extension.dart'; import 'package:form_bloc/src/utils.dart'; import 'package:meta/meta.dart'; import 'package:rxdart/rxdart.dart'; -import 'package:uuid/uuid.dart'; part '../boolean_field/boolean_field_bloc.dart'; part '../boolean_field/boolean_field_state.dart'; @@ -52,23 +51,13 @@ typedef Suggestions = Future> Function(String pattern); /// * [GroupFieldBloc]. /// * [ListFieldBloc]. mixin FieldBloc on BlocBase { - String get name => state.name; - /// {@template form_bloc.FieldBloc.validate} /// Validate the field. If it contains more fields, it validates all children. /// Returns `true` if the field is valid otherwise `false` /// {@endtemplate} Future validate(); - /// {@template form_bloc.FieldBloc.updateFormBloc} - /// Update the [formBloc] and [autoValidate] to the fieldBloc - /// {@endtemplate} - void updateFormBloc(FormBloc formBloc, {bool autoValidate = false}); - - /// {@template form_bloc.FieldBloc.removeFormBloc} - /// Remove the [formBloc] to the fieldBloc - /// {@endtemplate} - void removeFormBloc(FormBloc formBloc); + void updateAutoValidation(bool isEnabled); } /// The base class with the common behavior @@ -84,7 +73,7 @@ abstract class SingleFieldBloc< Suggestion, State extends FieldBlocState, ExtraData> extends Cubit with FieldBloc { - bool _autoValidate = true; + bool _autoValidate; List> _validators; @@ -108,11 +97,13 @@ abstract class SingleFieldBloc< SingleFieldBloc({ Equality equality = const DefaultEquality(), + bool autoValidate = true, required List>? validators, required List>? asyncValidators, required Duration asyncValidatorDebounceTime, required State initialState, - }) : _validators = validators ?? [], + }) : _autoValidate = autoValidate, + _validators = validators ?? [], _asyncValidators = asyncValidators ?? [], _asyncValidatorDebounceTime = asyncValidatorDebounceTime, super(initialState) { @@ -327,16 +318,12 @@ abstract class SingleFieldBloc< _revalidateFieldBlocsSubscription?.cancel(); // TODO: When async validation is in progress and auto validate is off this method is broken // because it emit a completed async validation - // TODO: It does not manage MultiFieldBloc fields if (fieldBlocs.isNotEmpty) { - _revalidateFieldBlocsSubscription = Rx.combineLatest( - fieldBlocs.whereType().toList().map( - (state) { - return state.stream.map((state) => state.value).distinct(); - }, - ), - (_) {}, - ).listen((_) { + _revalidateFieldBlocsSubscription = Rx.merge(fieldBlocs.map((e) { + return e.stream + .map((state) => state.value) + .distinct(DeepCollectionEquality().equals); + })).listen((_) { if (_autoValidate) { _validate(); } else { @@ -546,34 +533,13 @@ abstract class SingleFieldBloc< } } - /// {@macro form_bloc.FieldBloc.updateFormBloc} - /// See [FieldBloc.updateFormBloc] + // Launch validate if previous auto validation is disable and current is enabled @override - void updateFormBloc(FormBloc formBloc, {bool autoValidate = false}) { - _autoValidate = autoValidate; - if (!_autoValidate) { - emit(state.copyWith( - error: Param(null), - isValidated: false, - isValidating: false, - formBloc: Param(formBloc), - ) as State); - } else { - emit(state.copyWith( - formBloc: Param(formBloc), - ) as State); - } - } - - /// {@macro form_bloc.FieldBloc.removeFormBloc} - /// See [FieldBloc.removeFormBloc] - @override - void removeFormBloc(FormBloc formBloc) { - if (state.formBloc == formBloc) { - emit(state.copyWith( - formBloc: Param(null), - ) as State); - } + void updateAutoValidation(bool isEnabled) { + if (_autoValidate == isEnabled) return; + _autoValidate = isEnabled; + if (!_autoValidate || state.isValidated) return; + _validate(); } /// {@template form_bloc.field_bloc.itemsWithoutDuplicates} @@ -624,42 +590,13 @@ abstract class SingleFieldBloc< } } -class ValidationStatus extends Equatable { - final bool isValidating; - final bool isValid; - - const ValidationStatus({ - required this.isValidating, - required this.isValid, - }); - - @override - List get props => [isValidating, isValid]; - - @override - String toString() { - return 'ValidationStatus{isValidating: $isValidating, isValid: $isValid}'; - } -} - class MultiFieldBloc> extends Cubit with FieldBloc { - late final StreamSubscription _onValidationStatus; - - bool _autoValidate = false; + bool _autoValidate; - bool get autoValidate => _autoValidate; - - MultiFieldBloc(TState initialState) : super(initialState) { - _onValidationStatus = stream.switchMap((state) { - return MultiFieldBloc.onValidationStatus(state.flatFieldBlocs); - }).listen((validationStatus) { - emit(state.copyWith( - isValidating: validationStatus.isValidating, - isValid: validationStatus.isValid, - ) as TState); - }); - } + MultiFieldBloc(TState initialState, {required bool autoValidate}) + : _autoValidate = autoValidate, + super(initialState); Iterable get flatFieldBlocs => state.flatFieldBlocs; @@ -701,59 +638,21 @@ class MultiFieldBloc> // INTERNAL // =========================================================================== - /// See [FieldBloc.updateFormBloc] @override - void updateFormBloc(FormBloc formBloc, {bool autoValidate = false}) { - _autoValidate = autoValidate; - - emit(state.copyWith( - formBloc: Param(formBloc), - ) as TState); - - FormBlocUtils.updateFormBloc( - fieldBlocs: state.flatFieldBlocs, - formBloc: formBloc, - autoValidate: _autoValidate, - ); - } - - /// See [FieldBloc.removeFormBloc] - @override - void removeFormBloc(FormBloc formBloc) { - if (state.formBloc == formBloc) { - emit(state.copyWith( - formBloc: Param(null), - ) as TState); - - FormBlocUtils.removeFormBloc( - fieldBlocs: state.flatFieldBlocs, - formBloc: formBloc, - ); + void updateAutoValidation(bool isEnabled) { + if (_autoValidate == isEnabled) return; + _autoValidate = isEnabled; + for (final fieldBloc in state.flatFieldBlocs) { + fieldBloc.updateAutoValidation(isEnabled); } } @override - Future close() { - _onValidationStatus.cancel(); - for (final fieldBloc in state.flatFieldBlocs) { - fieldBloc.close(); - } + Future close() async { + await Future.wait(state.flatFieldBlocs.map((e) => e.close())); return super.close(); } - static Stream onValidationStatus( - Iterable fieldBlocs, - ) { - return Rx.combineLatestList(fieldBlocs.map((fieldBloc) { - return Rx.merge([Stream.value(fieldBloc.state), fieldBloc.stream]); - })).map((fieldStates) { - return ValidationStatus( - isValidating: fieldStates.any((fieldState) => fieldState.isValidating), - isValid: fieldStates.every((fieldState) => fieldState.isValid), - ); - }).distinct(); - } - static Future validateAll(Iterable fieldBlocs) async { // Force validation if field bloc is not valid fieldBlocs = fieldBlocs.where((element) => !element.state.isValid); @@ -766,32 +665,4 @@ class MultiFieldBloc> return areValid.every((isValid) => isValid); }); } - - static bool deepContains(Iterable fieldBlocs, FieldBloc target) { - if (fieldBlocs.isEmpty) return false; - - for (final fieldBloc in fieldBlocs) { - if (fieldBloc is MultiFieldBloc) { - final contains = - MultiFieldBloc.deepContains(fieldBloc.state.flatFieldBlocs, target); - if (contains) { - return true; - } - } else if (fieldBloc.state.name == target.state.name) { - return true; - } - } - return false; - } - - static bool areFieldBlocsValid(Iterable fieldBlocs) => - fieldBlocs.every(_isFieldBlocValid); - - static bool areFieldBlocsValidating(Iterable fieldBlocs) => - fieldBlocs.every(_isFieldBlocValidating); - - static bool _isFieldBlocValid(FieldBloc field) => field.state.isValid; - - static bool _isFieldBlocValidating(FieldBloc field) => - field.state.isValidating; } diff --git a/packages/form_bloc/lib/src/blocs/field/field_state.dart b/packages/form_bloc/lib/src/blocs/field/field_state.dart index 951c1b87..56d84c22 100644 --- a/packages/form_bloc/lib/src/blocs/field/field_state.dart +++ b/packages/form_bloc/lib/src/blocs/field/field_state.dart @@ -2,25 +2,34 @@ part of 'field_bloc.dart'; /// The common state interface of all field blocs mixin FieldBlocStateBase { - // TODO: Rename to `FieldBlocState` - /// It is the string that identifies the [FieldBloc]. - String get name; + // TODO: Set value Type + dynamic get value; - bool get isValidating; + bool get isValueChanged; + bool get hasInitialValue; + bool get hasUpdatedValue; + + bool get hasError; + bool get isDirty; + // TODO: Implement autoValidate + bool get isValidating; bool get isValid; - /// Identifies whether the FieldBloc has been added to the FormBloc - FormBloc? get formBloc; + // TODO: Implement isReadOnly. You can't call changeValue method - bool get hasFormBloc => formBloc != null; + bool contains(FieldBloc fieldBloc) => false; + + dynamic toJson(); } +// TODO: Implement disabled values/items abstract class FieldBlocState extends Equatable with FieldBlocStateBase { /// {@template flutter_field_bloc.FieldBloc.isValueChanged} /// Returns true when the value has been changed by [FieldBloc.changeValue]. /// {@endtemplate} + @override final bool isValueChanged; final Value initialValue; @@ -37,19 +46,13 @@ abstract class FieldBlocState extends Equatable /// Indicate if this field was [value] updated by [SingleFieldBloc.changeValue] / [SingleFieldBloc.updateValue] /// or receive a external validation by [SingleFieldBloc.validate]. + @override final bool isDirty; - @Deprecated('In favour of [isValueChanged]') - bool get isInitial => - !hasInitialValue && (isValueChanged || !hasUpdatedValue); /// Function that returns a list of suggestions /// which can be used to update the value. final Suggestions? suggestions; - /// It is the string that identifies the [FieldBloc]. - @override - final String name; - /// Indicate if [value] was checked with the validators /// of the [FieldBloc]. final bool isValidated; @@ -59,16 +62,13 @@ abstract class FieldBlocState extends Equatable @override final bool isValidating; - /// The current [FormBloc] that contains this `FieldBloc`. - @override - final FormBloc? formBloc; - /// Transform [value] in a JSON value. /// By default returns [value], but you can /// set in the constructor of the `FieldBloc` /// /// This method is called when you use [FormBlocState.toJson] - Object? toJson() => value == null ? null : _toJson(value); + @override + dynamic toJson() => _toJson(value); /// Implementation of [toJson] final dynamic Function(Value value) _toJson; @@ -88,8 +88,6 @@ abstract class FieldBlocState extends Equatable required this.suggestions, required this.isValidated, required this.isValidating, - required this.formBloc, - required this.name, required dynamic Function(Value value)? toJson, required this.extraData, }) : _toJson = toJson ?? ((value) => value); @@ -102,6 +100,7 @@ abstract class FieldBlocState extends Equatable bool get isValid => !hasError && !isValidating && isValidated; /// Indicates if [error] is not `null`. + @override bool get hasError => error != null; /// Indicates if [value] is not `null`. @@ -110,28 +109,30 @@ abstract class FieldBlocState extends Equatable /// {@template flutter_field_bloc.FieldBloc.hasInitialValue} /// Indicate if this field has [value] from [FieldBloc.updateInitialValue] method. /// {@endtemplate} + @override bool get hasInitialValue => initialValue == value; /// {@template flutter_field_bloc.FieldBloc.hasUpdatedValue} /// Indicate if this field has [value] from [FieldBloc.updateValue] method. /// {@endtemplate} + @override bool get hasUpdatedValue => updatedValue == value; - bool get hasDirt => isDirty || isValueChanged || !hasInitialValue; - /// Indicates if this state has error and is not initial. /// /// Used for not show the error when [hasInitialValue] is `false`. /// Which mean that [value] was updated /// after be instantiate by the [FieldBloc] and has an error. - bool get canShowError => hasDirt && hasError; + bool get canShowError => _hasDirt && hasError; /// Indicates if this state is validating and is not initial. /// /// Used for not show the is validating when [isDirty] is `true`. /// Which mean that [value] was updated /// after be instantiate by the [FieldBloc] and is validating. - bool get canShowIsValidating => hasDirt && isValidating; + bool get canShowIsValidating => _hasDirt && isValidating; + + bool get _hasDirt => isDirty || isValueChanged || !hasInitialValue; /// Returns a copy of the current state by changing /// the values that are passed as parameters. @@ -145,7 +146,6 @@ abstract class FieldBlocState extends Equatable Param?>? suggestions, bool? isValidated, bool? isValidating, - Param formBloc, Param? extraData, }); @@ -154,7 +154,6 @@ abstract class FieldBlocState extends Equatable var _toString = ''; _toString += '$runtimeType {'; - _toString += '\n name: $name'; _toString += ',\n isValueChanged: $isValueChanged'; _toString += ',\n updatedValue: $updatedValue'; _toString += ',\n initialValue: $initialValue'; @@ -166,7 +165,6 @@ abstract class FieldBlocState extends Equatable _toString += ',\n isValid: $isValid'; _toString += ',\n extraData: $extraData'; _toString += extra; - _toString += ',\n formBloc: $formBloc'; _toString += '\n}'; return _toString; @@ -184,51 +182,64 @@ abstract class FieldBlocState extends Equatable isValidated, isValidating, extraData, - formBloc, ]; } abstract class MultiFieldBlocState extends Equatable with FieldBlocStateBase { + final ExtraData? extraData; + + MultiFieldBlocState({ + required this.extraData, + }); + @override - final FormBloc? formBloc; + late final bool isValueChanged = + flatFieldStates.any((fs) => fs.isValueChanged); @override - final String name; + late final bool hasInitialValue = + flatFieldStates.every((fs) => fs.hasInitialValue); @override - final bool isValidating; + late final bool hasUpdatedValue = + flatFieldStates.every((fs) => fs.hasUpdatedValue); @override - final bool isValid; + late final bool hasError = flatFieldStates.any((fs) => fs.hasError); - final ExtraData? extraData; + @override + late final bool isDirty = flatFieldStates.any((fs) => fs.isDirty); - const MultiFieldBlocState({ - required this.formBloc, - required this.name, - required this.isValidating, - required this.isValid, - required this.extraData, - }); + @override + late final bool isValid = flatFieldStates.every((fs) => fs.isValid); + + @override + late final bool isValidating = flatFieldStates.any((fs) => fs.isValidating); + + /// Returns `true` if the [FormBloc] contains [fieldBloc] + @override + bool contains(FieldBloc fieldBloc) { + if (flatFieldBlocs.contains(fieldBloc)) return true; + return flatFieldStates.any((fb) => fb.contains(fieldBloc)); + } + @internal Iterable get flatFieldBlocs; + @internal + Iterable get flatFieldStates; + MultiFieldBlocState copyWith({ - Param? formBloc, - bool? isValidating, - bool? isValid, Param? extraData, }); @override - List get props => [formBloc, name, isValidating, isValid, extraData]; + List get props => [extraData]; @override String toString([Object? other]) { return '$runtimeType {' - ',\n formBloc: $formBloc' - ',\n name: $name' ',\n isValidating: $isValidating' ',\n isValid: $isValid' ',\n extraData: $extraData' diff --git a/packages/form_bloc/lib/src/blocs/form/form_bloc.dart b/packages/form_bloc/lib/src/blocs/form/form_bloc.dart index 661e7372..300bb8ee 100644 --- a/packages/form_bloc/lib/src/blocs/form/form_bloc.dart +++ b/packages/form_bloc/lib/src/blocs/form/form_bloc.dart @@ -1,8 +1,10 @@ import 'dart:async'; import 'package:bloc/bloc.dart'; +import 'package:collection/collection.dart'; import 'package:equatable/equatable.dart'; import 'package:form_bloc/src/blocs/field/field_bloc.dart'; +import 'package:form_bloc/src/extension/extension.dart'; import 'package:rxdart/rxdart.dart'; part 'form_state.dart'; @@ -11,8 +13,9 @@ part 'form_state.dart'; /// /// See complex examples here: https://github.com/GiancarloCode/form_bloc/tree/master/packages/flutter_form_bloc/example/lib/forms abstract class FormBloc - extends Cubit> { - /// See: [_setupStepValidationSubs]. + extends Cubit> + implements FieldBloc> { + /// See: [_mapFieldStates]. /// Each [FormBlocState.currentStep] has its own subscription final Map _stepValidationSubs = {}; @@ -32,7 +35,7 @@ abstract class FormBloc bool _canSubmit = true; /// Indicates if the [_fieldBlocs] must be autoValidated. - final bool _autoValidate; + bool _autoValidate; late final StreamSubscription _setupAreAllFieldsValidSubscriptionSubscription; @@ -59,7 +62,7 @@ abstract class FormBloc _formBlocStateSubscription?.cancel(); _isValidDone = null; - for (final fieldBloc in state.flatFieldBlocs()!) { + for (final fieldBloc in state.fieldBlocs.values) { fieldBloc.close(); } @@ -70,34 +73,32 @@ abstract class FormBloc void _initStepValidationSubs() { _setupAreAllFieldsValidSubscriptionSubscription = stream - .map((state) => state._flatFieldBlocsStepped()) + .map((state) => state._fieldBlocs) .debounceTime(const Duration(milliseconds: 5)) - .distinct() - .listen(_setupStepValidationSubs); + .distinct(const MapEquality().equals) + .switchMap(_mapFieldStates) + .listen((fieldStates) { + emit(state._copyWith( + fieldStates: fieldStates, + )); + }); } /// Init the subscription to the state of each /// `fieldBloc` in [FieldBlocs] to update [FormBlocState._isValidByStep] /// when any `fieldBloc` changes it state. - void _setupStepValidationSubs( - Map> allFieldBlocs, - ) { - for (final sub in _stepValidationSubs.values) { - sub.cancel(); + Stream> _mapFieldStates( + Map fieldBlocs, + ) async* { + final fieldStatesStream = Rx.combineLatestList(fieldBlocs.entries.map((e) { + final step = e.key; + final fb = e.value; + return fb.hotStream.map((fs) => MapEntry(step, fs)); + })).skip(1); + + await for (final fieldStates in fieldStatesStream) { + yield Map.fromEntries(fieldStates.map((e) => MapEntry(e.key, e.value))); } - - allFieldBlocs.forEach((step, fieldBlocs) { - _stepValidationSubs[step] = - MultiFieldBloc.onValidationStatus(fieldBlocs).listen((status) { - if (_autoValidate) { - _canSubmit = !status.isValidating; - } - _updateValidStep( - isValid: status.isValid, - step: step, - ); - }); - }); } /// If [isLoading] is `true`, [OnLoading] @@ -158,6 +159,11 @@ abstract class FormBloc /// [onSubmitting] will be called. void submit() => _onSubmit(); + @override + Future validate() async { + return await state.fieldBloc(state.currentStep).validate(); + } + /// Call `clear` method for each [FieldBloc] in [FieldBlocs]. void clear() => _onClearFormBloc(); @@ -178,15 +184,22 @@ abstract class FormBloc /// Adds [fieldBloc] to the [FormBloc]. /// - /// You can set [step] of this fields, by default is `0`. - void addFieldBloc({int step = 0, required FieldBloc fieldBloc}) => - _onAddFieldBlocs(step: step, fieldBlocs: [fieldBloc]); + /// TODO: Fix + /// You can set [insertAt] of this fields, by default is `0`. + void addStep(FieldBloc fieldBloc, {int? insertAt}) => + _onAddStep(step: insertAt, fieldBloc: fieldBloc); - /// Adds [fieldBlocs] to the [FormBloc]. + /// Adds [fieldBloc] to the [FormBloc]. /// - /// You can set [step] of this fields, by default is `0`. - void addFieldBlocs({int step = 0, required List fieldBlocs}) => - _onAddFieldBlocs(step: step, fieldBlocs: fieldBlocs); + /// You can set [at] of this fields, by default is `0`. + void updateStep(int at, FieldBloc fieldBloc) => + _onUpdateStep(step: at, fieldBloc: fieldBloc); + + // /// Adds [fieldBlocs] to the [FormBloc]. + // /// + // /// You can set [step] of this fields, by default is `0`. + // void addFieldBlocs({int step = 0, required List fieldBlocs}) => + // _onAddFieldBlocs(step: step, fieldBlocs: fieldBlocs); void previousStep() => _onPreviousStep(); @@ -195,12 +208,19 @@ abstract class FormBloc void updateCurrentStep(int step) => _onUpdateCurrentStep(step: step); /// Removes a [FieldBloc] from the [FormBloc] - void removeFieldBloc({int? step, required FieldBloc fieldBloc}) => - _onRemoveFieldBlocs(step: step, fieldBlocs: [fieldBloc]); + void removeStep(int at) => _onRemoveStep(step: at); - /// Removes a [FieldBlocs] from the [FormBloc] - void removeFieldBlocs({int? step, required List fieldBlocs}) => - _onRemoveFieldBlocs(step: step, fieldBlocs: fieldBlocs); + /// Removes a [FieldBloc] from the [FormBloc] + void removeStepBy(FieldBloc fieldBloc, {int? at}) => + _onRemoveStepBy(at: at, fieldBlocs: fieldBloc); + + @override + void updateAutoValidation(bool isEnabled) { + if (_autoValidate == isEnabled) return; + for (final fieldBloc in state._fieldBlocs.values) { + fieldBloc.updateAutoValidation(isEnabled); + } + } // =========================================================================== // METHODS TO UPDATE STATE @@ -298,19 +318,19 @@ abstract class FormBloc /// Check if all field blocs and their children have undergone a change in values /// {@endtemplate} bool isValuesChanged({int? step}) => - FormBlocUtils.isValuesChanged(state.flatFieldBlocs(step) ?? const []); + FormBlocUtils.isValuesChanged(state.fieldBlocs.values); /// {@template form_bloc.hasInitialValues} /// Check if all field blocs and their children have initial values /// {@endtemplate} bool hasInitialValues({int? step}) => - FormBlocUtils.hasInitialValues(state.flatFieldBlocs(step) ?? const []); + FormBlocUtils.hasInitialValues(state.fieldBlocs.values); /// {@template form_bloc.hasUpdatedValues} /// Check if all field blocs and their children have updated values /// {@endtemplate} bool hasUpdatedValues({int? step}) => - FormBlocUtils.hasUpdatedValues(state.flatFieldBlocs(step) ?? const []); + FormBlocUtils.hasUpdatedValues(state.fieldBlocs.values); // =========================================================================== // toString @@ -331,14 +351,10 @@ abstract class FormBloc notValidStep != null && notValidStep != state.lastStep) { // go to the first step invalid - emit(FormBlocSubmissionFailed( - isValidByStep: state._isValidByStep, - isEditing: state.isEditing, - fieldBlocs: state._fieldBlocs, - currentStep: state.currentStep, - )); - emit(state.toLoaded()._copyWith(currentStep: notValidStep)); - } else if (_autoValidate && state.isValid(state.currentStep)) { + + emit(state.toSubmissionFailed()); + emit(state.toLoaded()); + } else if (_autoValidate && state.isValidStep(state.currentStep)) { // Auto validate is enabled, required validate all field blocs // if step isn't valid to show a error (if value is initial) @@ -354,54 +370,30 @@ abstract class FormBloc void _validateAndSubmit() async { // get field blocs of the current step and validate - final currentFieldBlocs = state.fieldBlocs(state.currentStep)?.values ?? []; - final isValidDone = - _isValidDone = MultiFieldBloc.validateAll(currentFieldBlocs); + _isValidDone = state.fieldBloc(state.currentStep).validate(); final isValid = await isValidDone; if (_isValidDone != isValidDone) return; if (!isValid) { - emit(FormBlocSubmissionFailed( - isValidByStep: { - ...state._isValidByStep, - state.currentStep: false, - }, - isEditing: state.isEditing, + emit(state.toFailure( fieldBlocs: state._fieldBlocs, - currentStep: state.currentStep, )); emit(state.toLoaded()); return; } emit(state._copyWith( - isValidByStep: { - ...state._isValidByStep, - state.currentStep: true, - }, + fieldBlocs: state._fieldBlocs, )); _callInBlocContext(onSubmitting); } - void _updateValidStep({ - required bool isValid, - required int step, - }) async { - emit(state._copyWith( - isValidByStep: { - ...state._isValidByStep, - step: isValid, - }, - fieldBlocs: state._fieldBlocs, - )); - } - void _onClearFormBloc() { final allSingleFieldBlocs = - FormBlocUtils.getAllSingleFieldBlocs(state.fieldBlocs()!.values); + FormBlocUtils.getAllSingleFieldBlocs(state.fieldBlocs.values); for (var fieldBloc in allSingleFieldBlocs) { fieldBloc.clear(); @@ -413,13 +405,8 @@ abstract class FormBloc if (stateSnapshot is FormBlocSubmitting && !stateSnapshot.isCanceling) { _isValidDone = null; - emit(FormBlocSubmitting( - isCanceling: true, - isValidByStep: stateSnapshot._isValidByStep, - progress: stateSnapshot.progress, - isEditing: stateSnapshot.isEditing, - fieldBlocs: stateSnapshot._fieldBlocs, - currentStep: stateSnapshot.currentStep, + emit(state.toSubmitting( + isCancelling: true, )); _callInBlocContext(onCancelingSubmission); @@ -427,13 +414,7 @@ abstract class FormBloc } void _onDeleteFormBloc() { - emit(FormBlocDeleting( - isValidByStep: state._isValidByStep, - isEditing: state.isEditing, - fieldBlocs: state._fieldBlocs, - currentStep: state.currentStep, - progress: 0.0, - )); + emit(state.toDeleting()); _callInBlocContext(onDeleting); } @@ -450,84 +431,68 @@ abstract class FormBloc } } - void _onAddFieldBlocs({ - int step = 0, - required Iterable fieldBlocs, + void _onAddStep({ + int? step, + required FieldBloc fieldBloc, }) { - fieldBlocs = fieldBlocs.where((fieldBloc) { - return !state.contains(fieldBloc, step: step, deep: false); - }); + assert(step == null || step > 0 && step <= state._fieldBlocs.length); - if (fieldBlocs.isEmpty) return; + if (state._fieldBlocs[step] == fieldBloc) return; - for (final fieldBloc in fieldBlocs) { - fieldBloc.updateFormBloc(this, autoValidate: _autoValidate); - } + fieldBloc.updateAutoValidation(_autoValidate); emit(state._copyWith( fieldBlocs: { ...state._fieldBlocs, - step: { - ...?state._fieldBlocs[step], - for (final fieldBloc in fieldBlocs) fieldBloc.state.name: fieldBloc, - }, - }, - isValidByStep: { - ...state._isValidByStep, - step: MultiFieldBloc.areFieldBlocsValid( - fieldBlocs.followedBy(state.flatFieldBlocs(step) ?? const [])), + step ?? state._fieldStates.length: fieldBloc, }, )); } - void _onRemoveFieldBlocs({ - int? step, - required Iterable fieldBlocs, - }) async { - fieldBlocs = fieldBlocs.where((fieldBloc) { - return state.contains(fieldBloc, step: step, deep: false); - }); + void _onUpdateStep({ + required int step, + required FieldBloc fieldBloc, + }) { + assert(step > 0 && step <= state._fieldBlocs.length); - if (fieldBlocs.isEmpty) return; + if (state._fieldBlocs[step] == fieldBloc) return; - for (final fieldBloc in fieldBlocs) { - fieldBloc.removeFormBloc(this); - } + fieldBloc.updateAutoValidation(_autoValidate); - Map> nextFieldBlocs; - if (step == null) { - nextFieldBlocs = { - ...state._fieldBlocs, - for (final step in state._fieldBlocs.keys) - step: { - for (final fieldBloc in state.flatFieldBlocs(step)!) - if (!fieldBlocs - .any((fb) => fieldBloc.state.name == fb.state.name)) - fieldBloc.state.name: fieldBloc, - }, - }; - } else { - final fieldBlocsInStep = state.flatFieldBlocs(step); - if (fieldBlocsInStep == null) return; - nextFieldBlocs = { + emit(state._copyWith( + fieldBlocs: { ...state._fieldBlocs, - step: { - for (final fieldBloc in fieldBlocsInStep) - if (!fieldBlocs.any((fb) => fieldBloc.state.name == fb.state.name)) - fieldBloc.state.name: fieldBloc, - }, - }; - } + step: fieldBloc, + }, + )); + } + + void _onRemoveStep({required int step}) async { + if (!state._fieldBlocs.containsKey(step)) return; - // Remove empty steps - nextFieldBlocs.removeWhere((_, fieldBlocs) => fieldBlocs.isEmpty); + final nextFieldBlocs = state._fieldBlocs.entries + .where((element) => element.key != step) + .map((e) => e.value) + .toList() + .asMap(); + + emit(state._copyWith( + fieldBlocs: nextFieldBlocs, + )); + } + + void _onRemoveStepBy({ + int? at, + required FieldBloc fieldBlocs, + }) async { + if (!state._fieldBlocs.containsValue(fieldBlocs)) return; + + final nextFieldBlocs = state._fieldBlocs.where((key, value) { + return !((at == null || at == key) && fieldBlocs == value); + }); emit(state._copyWith( fieldBlocs: nextFieldBlocs, - isValidByStep: { - for (final e in nextFieldBlocs.entries) - e.key: MultiFieldBloc.areFieldBlocsValid(e.value.values), - }, )); } } diff --git a/packages/form_bloc/lib/src/blocs/form/form_bloc_utils.dart b/packages/form_bloc/lib/src/blocs/form/form_bloc_utils.dart index 00144123..bc0de651 100644 --- a/packages/form_bloc/lib/src/blocs/form/form_bloc_utils.dart +++ b/packages/form_bloc/lib/src/blocs/form/form_bloc_utils.dart @@ -3,8 +3,7 @@ part of '../field/field_bloc.dart'; class FormBlocUtils { FormBlocUtils._(); - static Iterable getAllSingleFieldBlocs( - Iterable fieldBlocs) { + static Iterable getAllSingleFieldBlocs(Iterable fieldBlocs) { return fieldBlocs.expand((fieldBloc) { if (fieldBloc is SingleFieldBloc) { return [fieldBloc]; @@ -21,8 +20,7 @@ class FormBlocUtils { if (fieldBloc is SingleFieldBloc) { return [fieldBloc]; } else if (fieldBloc is MultiFieldBloc) { - return [fieldBloc] - .followedBy(getAllFieldBlocs(fieldBloc.flatFieldBlocs)); + return [fieldBloc].followedBy(getAllFieldBlocs(fieldBloc.flatFieldBlocs)); } } return const []; @@ -33,7 +31,7 @@ class FormBlocUtils { /// if it does not exist, return `null`. static FieldBloc? getFieldBlocFromPath({ required String path, - required Map fieldBlocs, + required Map fieldBlocs, }) { var names = path.split('/'); @@ -189,8 +187,7 @@ class FormBlocUtils { } } */ - static Map fieldBlocsStatesToJson( - Map fieldBlocsStates) { + static Map fieldBlocsStatesToJson(Map fieldBlocsStates) { final json = {}; fieldBlocsStates.forEach((name, dynamic fieldBlocState) { @@ -206,8 +203,7 @@ class FormBlocUtils { return json; } - static List fieldBlocsStatesListToJsonList( - List fieldBlocsStatesList) { + static List fieldBlocsStatesListToJsonList(List fieldBlocsStatesList) { final list = []; for (var fieldBlocState in fieldBlocsStatesList) { @@ -222,8 +218,7 @@ class FormBlocUtils { return list; } - static Map fieldBlocsToFieldBlocsStates( - Map fieldBlocs) { + static Map fieldBlocsToFieldBlocsStates(Map fieldBlocs) { final json = {}; fieldBlocs.forEach((name, fieldBloc) { @@ -239,8 +234,7 @@ class FormBlocUtils { return json; } - static List fieldBlocListToFieldBlocsStatesList( - ListFieldBloc fieldBlocList) { + static List fieldBlocListToFieldBlocsStatesList(ListFieldBloc fieldBlocList) { final list = []; for (var fieldBloc in fieldBlocList.state.fieldBlocs) { @@ -255,8 +249,7 @@ class FormBlocUtils { return list; } - static Iterable flattenFieldBlocsStateList( - Iterable fieldBlocStateList) { + static Iterable flattenFieldBlocsStateList(Iterable fieldBlocStateList) { final list = []; for (var fieldBlocState in fieldBlocStateList) { @@ -306,8 +299,7 @@ class FormBlocUtils { currentFieldBlocState = fieldBlocsStates[name]; } } else { - if (currentFieldBlocState == null || - currentFieldBlocState is FieldBlocState) { + if (currentFieldBlocState == null || currentFieldBlocState is FieldBlocState) { return null; } @@ -338,8 +330,8 @@ class FormBlocUtils { required String path, required Map fieldBlocsStates, }) { - final dynamic fieldBlocState = FormBlocUtils.getFieldBlocStateFromPath( - path: path, fieldBlocsStates: fieldBlocsStates); + final dynamic fieldBlocState = + FormBlocUtils.getFieldBlocStateFromPath(path: path, fieldBlocsStates: fieldBlocsStates); if (fieldBlocState is FieldBlocState) { return fieldBlocState.value; @@ -352,28 +344,28 @@ class FormBlocUtils { } } - static void updateFormBloc({ - required Iterable fieldBlocs, - required FormBloc? formBloc, - bool autoValidate = false, - }) { - if (formBloc == null) return; - - for (final fieldBloc in fieldBlocs) { - fieldBloc.updateFormBloc(formBloc, autoValidate: autoValidate); - } - } - - static void removeFormBloc({ - required Iterable fieldBlocs, - required FormBloc? formBloc, - }) { - if (formBloc == null) return; - - for (final fieldBloc in fieldBlocs) { - fieldBloc.removeFormBloc(formBloc); - } - } + // static void updateFormBloc({ + // required Iterable fieldBlocs, + // required FormBloc? formBloc, + // bool autoValidate = false, + // }) { + // if (formBloc == null) return; + // + // for (final fieldBloc in fieldBlocs) { + // fieldBloc.updateFormBloc(formBloc, autoValidate: autoValidate); + // } + // } + // + // static void removeFormBloc({ + // required Iterable fieldBlocs, + // required FormBloc? formBloc, + // }) { + // if (formBloc == null) return; + // + // for (final fieldBloc in fieldBlocs) { + // fieldBloc.removeFormBloc(formBloc); + // } + // } /// See [FormBloc.isValuesChanged] static bool isValuesChanged( diff --git a/packages/form_bloc/lib/src/blocs/form/form_state.dart b/packages/form_bloc/lib/src/blocs/form/form_state.dart index 95b88b8e..8ccd6a0f 100644 --- a/packages/form_bloc/lib/src/blocs/form/form_state.dart +++ b/packages/form_bloc/lib/src/blocs/form/form_state.dart @@ -13,59 +13,8 @@ part of 'form_bloc.dart'; /// * [FormBlocDeleteFailed] /// * [FormBlocDeleteSuccessful] /// * [FormBlocUpdatingFields] -abstract class FormBlocState - extends Equatable { - /// Indicates if each [FieldBloc] in [FormBloc._fieldBlocs] is valid. - final Map _isValidByStep; - - bool isValid([int? step]) { - if (step == null) { - return _isValidByStep.values.every((e) => e); - } else { - return _isValidByStep[step] ?? false; - } - } - - @Deprecated( - 'In favour of [FormBloc.hasInitialValues] or [FormBloc.isValuesChanged].') - bool isInitial([int? step]) { - return FormBlocUtils.hasInitialValues(flatFieldBlocs(step) ?? const []); - } - - /// It is usually used in forms that are used as CRUD, - /// so when it is true it means that you can - /// perform the update operation. - final bool isEditing; - - final int currentStep; - - int get numberOfSteps { - if (_fieldBlocs.isNotEmpty) { - return _fieldBlocs.length; - } - return 1; - } - - bool get isLastStep => currentStep == lastStep; - - int get lastStep => numberOfSteps - 1; - - bool get isFirstStep => currentStep == 0; - - // Returns the first step that is not valid. - int? get notValidStep { - final invalidSteps = []; - if (_isValidByStep.isNotEmpty) { - for (var i = 0; i < _isValidByStep.length - 1; i++) { - if (!_isValidByStep[i]!) { - invalidSteps.add(i); - } - } - } - - return invalidSteps.isNotEmpty ? invalidSteps.first : null; - } - +abstract class FormBlocState extends Equatable + implements FieldBlocStateBase { /// Map containing all the [name]s. /// /// The `key` of each [FieldBloc] will be @@ -79,133 +28,134 @@ abstract class FormBlocState /// * [multiSelectFieldBlocOf] /// * [groupFieldBlocOf] /// * [listFieldBlocOf] - final Map> _fieldBlocs; + final Map _fieldBlocs; - Map? fieldBlocs([int? step]) { - if (step == null) { - return _allFieldBlocsMap; - } else { - return _fieldBlocs[step]; - } - } + final Map _fieldStates; - Map get _allFieldBlocsMap { - final map = {}; - _fieldBlocs.forEach((key, value) => map.addAll(value)); - return map; - } + Map get fieldBlocs => Map.unmodifiable(_fieldBlocs); - Iterable? flatFieldBlocs([int? step]) { - if (step == null) return _fieldBlocs.values.expand((e) => e.values); - return _fieldBlocs[step]?.values; - } + Map get fieldStates => + Map.unmodifiable(_fieldStates); - Map> _flatFieldBlocsStepped() { - return _fieldBlocs.map((key, fbs) => MapEntry(key, fbs.values)); + FieldBloc fieldBloc(int step) { + final fieldBloc = _fieldBlocs[step]; + if (fieldBloc == null) throw 'Not exist $step'; + return fieldBloc; } - /// States by step - late final Map> _fieldBlocsStatesByStepMap = - _fieldBlocs.map((key, value) { - return MapEntry(key, FormBlocUtils.fieldBlocsToFieldBlocsStates(value)); + @override + late final Map value = + _fieldStates.map((step, state) { + return MapEntry(step, state.value); }); - /// All states of all steps - late final Map _fieldBlocsStates = - FormBlocUtils.fieldBlocsToFieldBlocsStates({ - for (final stepFieldBlocs in _fieldBlocs.values) ...stepFieldBlocs, + @override + late final bool isValueChanged = _fieldStates.values.any((fs) { + return fs.isValueChanged; }); - /// Returns `true` if the [FormBloc] contains [fieldBloc] - bool contains(FieldBloc fieldBloc, {int? step, bool deep = true}) { - final fieldBlocs = (flatFieldBlocs(step) ?? const []); - if (deep) { - return fieldBlocs.any((fb) => fb == fieldBloc); - } - return MultiFieldBloc.deepContains(fieldBlocs, fieldBloc); - } + @override + late final bool hasInitialValue = _fieldStates.values.every((fs) { + return fs.hasInitialValue; + }); - /// Returns the value of [FieldBloc] that has this [name]. - T? valueOf(String name) { - return FormBlocUtils.getValueOfFieldBlocsStates( - path: name, - fieldBlocsStates: _fieldBlocsStates, - ) as T?; - } + @override + late final bool hasUpdatedValue = _fieldStates.values.every((fs) { + return fs.hasUpdatedValue; + }); - List? valueListOf(String name) { - return (FormBlocUtils.getValueOfFieldBlocsStates( - path: name, - fieldBlocsStates: _fieldBlocsStates, - ) as List?) - ?.cast(); - } + @override + late final bool hasError = _fieldStates.values.any((fs) => fs.hasError); - List>? valueMapListOf(String name) { - return valueListOf>(name); - } + @override + late final bool isDirty = _fieldStates.values.any((fs) => fs.isDirty); - Map? valueMapOf(String name) { - return (FormBlocUtils.getValueOfFieldBlocsStates( - path: name, - fieldBlocsStates: _fieldBlocsStates, - ) as Map?) - ?.cast(); - } + @override + late final bool isValid = _fieldStates.values.every((fs) => fs.isValid); - T? _fieldBlocOf(String name) { - final fieldBloc = FormBlocUtils.getFieldBlocFromPath( - path: name, - fieldBlocs: _allFieldBlocsMap, - ); - return fieldBloc as T?; - } + @override + late final bool isValidating = + _fieldStates.values.any((fs) => fs.isValidating) && + this is! FormBlocSubmitting; + + bool isValidStep(int step) => _fieldStates[step]?.isValid ?? false; + + /// It is usually used in forms that are used as CRUD, + /// so when it is true it means that you can + /// perform the update operation. + final bool isEditing; + + final int currentStep; - Map toJson([int? step]) { - if (step == null) { - return FormBlocUtils.fieldBlocsStatesToJson(_fieldBlocsStates); + int get numberOfSteps { + if (_fieldBlocs.isNotEmpty) { + return _fieldBlocs.length; } + return 1; + } + + bool get isLastStep => currentStep == lastStep; + + int get lastStep => numberOfSteps - 1; + + bool get isFirstStep => currentStep == 0; + + // Returns the first step that is not valid. + int? get notValidStep { + return _fieldStates.entries + .firstWhereOrNull((entry) => entry.value.isValid) + ?.key; + } - if (_fieldBlocs.containsKey(step)) { - return FormBlocUtils.fieldBlocsStatesToJson( - _fieldBlocsStatesByStepMap[step]!); + /// Returns `true` if the [FormBloc] contains [fieldBloc] + @override + bool contains(FieldBloc fieldBloc, {int? step}) { + if (step != null) { + if (_fieldBlocs[step] == fieldBloc) return true; + return _fieldStates[step]!.contains(fieldBloc); } + if (_fieldBlocs.containsValue(fieldBloc)) return true; + return _fieldStates.values.any((fs) => fs.contains(fieldBloc)); + } - return const {}; + @override + Map toJson() { + return _fieldStates.map( + (key, value) => MapEntry(key, value.toJson())); } - T? _singleFieldBlocOf(String path) { + T? _fieldBlocOf(String path) { final fieldBloc = FormBlocUtils.getFieldBlocFromPath( path: path, - fieldBlocs: _allFieldBlocsMap, + fieldBlocs: _fieldBlocs, ); return fieldBloc as T?; } InputFieldBloc? inputFieldBlocOf( - String name) => - _singleFieldBlocOf(name); + String path) => + _fieldBlocOf(path); - TextFieldBloc? textFieldBlocOf(String name) => - _singleFieldBlocOf(name); + TextFieldBloc? textFieldBlocOf(String path) => + _fieldBlocOf(path); - BooleanFieldBloc? booleanFieldBlocOf(String name) => - _singleFieldBlocOf(name); + BooleanFieldBloc? booleanFieldBlocOf(String path) => + _fieldBlocOf(path); SelectFieldBloc? selectFieldBlocOf( - String name) => - _singleFieldBlocOf(name); + String path) => + _fieldBlocOf(path); MultiSelectFieldBloc? - multiSelectFieldBlocOf(String name) => - _singleFieldBlocOf(name); + multiSelectFieldBlocOf(String path) => + _fieldBlocOf(path); GroupFieldBloc? groupFieldBlocOf(String name) => _fieldBlocOf(name); ListFieldBloc? listFieldBlocOf( - String name) => - _fieldBlocOf>(name); + String path) => + _fieldBlocOf>(path); /// Returns `true` if the state is /// [FormBlocLoaded] or [FormBlocFailure] or @@ -241,12 +191,12 @@ abstract class FormBlocState } FormBlocState({ - required Map isValidByStep, required this.isEditing, - required Map> fieldBlocs, + required Map fieldBlocs, + required Map fieldStates, required this.currentStep, - }) : _isValidByStep = isValidByStep, - _fieldBlocs = fieldBlocs; + }) : _fieldBlocs = fieldBlocs, + _fieldStates = fieldStates; /// Returns a [FormBlocLoading] /// {@template form_bloc.copy_to_form_bloc_state} @@ -260,9 +210,9 @@ abstract class FormBlocState }) { final state = this; return FormBlocLoading( - isValidByStep: _isValidByStep, isEditing: isEditing, fieldBlocs: _fieldBlocs, + fieldStates: _fieldStates, currentStep: currentStep, progress: progress ?? (state is FormBlocLoading @@ -279,13 +229,13 @@ abstract class FormBlocState {FailureResponse? failureResponse}) { final state = this; return FormBlocLoadFailed( - isValidByStep: _isValidByStep, isEditing: isEditing, failureResponse: failureResponse ?? (state is FormBlocLoadFailed ? state.failureResponse : null), fieldBlocs: _fieldBlocs, + fieldStates: _fieldStates, currentStep: currentStep, ); } @@ -295,9 +245,9 @@ abstract class FormBlocState /// /// {@macro form_bloc.form_state.FormBlocLoaded} FormBlocState toLoaded() => FormBlocLoaded( - isValidByStep: _isValidByStep, isEditing: isEditing, fieldBlocs: _fieldBlocs, + fieldStates: _fieldStates, currentStep: currentStep, ); @@ -310,20 +260,32 @@ abstract class FormBlocState /// /// * If [progress] is less than 0, it will become 0.0 /// * If [progress] is greater than 1, it will become 1.0 - FormBlocState toSubmitting( - {double? progress}) { + FormBlocState toSubmitting({ + bool? isCancelling, + double? progress, + }) { final state = this; return FormBlocSubmitting( - isValidByStep: _isValidByStep, isEditing: isEditing, progress: progress ?? (state is FormBlocSubmitting ? state.progress : 0.0), - isCanceling: state is FormBlocSubmitting - ? state.isCanceling - : false, + isCanceling: isCancelling ?? + (state is FormBlocSubmitting + ? state.isCanceling + : false), fieldBlocs: _fieldBlocs, + fieldStates: _fieldStates, + currentStep: currentStep, + ); + } + + FormBlocState toSubmissionFailed() { + return FormBlocSubmissionFailed( + isEditing: isEditing, + fieldBlocs: _fieldBlocs, + fieldStates: _fieldStates, currentStep: currentStep, ); } @@ -338,12 +300,12 @@ abstract class FormBlocState bool? isEditing, }) { return FormBlocSuccess( - isValidByStep: _isValidByStep, isEditing: isEditing ?? this.isEditing, successResponse: successResponse, canSubmitAgain: currentStep < (numberOfSteps - 1) ? true : (canSubmitAgain ?? false), fieldBlocs: _fieldBlocs, + fieldStates: _fieldStates, currentStep: currentStep < (numberOfSteps - 1) ? currentStep + 1 : currentStep, stepCompleted: currentStep, @@ -356,16 +318,18 @@ abstract class FormBlocState /// {@macro form_bloc.form_state.FormBlocFailure} FormBlocState toFailure({ FailureResponse? failureResponse, + Map? fieldBlocs, }) { final state = this; return FormBlocFailure( - isValidByStep: _isValidByStep, isEditing: isEditing, failureResponse: failureResponse ?? (state is FormBlocFailure ? state.failureResponse : null), - fieldBlocs: _fieldBlocs, + fieldBlocs: fieldBlocs ?? _fieldBlocs, + fieldStates: fieldBlocs?.map((step, fb) => MapEntry(step, fb.state)) ?? + _fieldStates, currentStep: currentStep, ); } @@ -376,12 +340,22 @@ abstract class FormBlocState /// {@macro form_bloc.form_state.FormBlocSubmissionCancelled} FormBlocState toSubmissionCancelled() => FormBlocSubmissionCancelled( - isValidByStep: _isValidByStep, isEditing: isEditing, fieldBlocs: _fieldBlocs, + fieldStates: _fieldStates, currentStep: currentStep, ); + FormBlocState toDeleting() { + return FormBlocDeleting( + isEditing: isEditing, + fieldBlocs: _fieldBlocs, + fieldStates: _fieldStates, + currentStep: currentStep, + progress: 0.0, + ); + } + /// Returns a [FormBlocDeleteFailed] /// {@macro form_bloc.copy_to_form_bloc_state} /// @@ -390,13 +364,13 @@ abstract class FormBlocState {FailureResponse? failureResponse}) { final state = this; return FormBlocDeleteFailed( - isValidByStep: _isValidByStep, isEditing: isEditing, failureResponse: failureResponse ?? (state is FormBlocDeleteFailed ? state.failureResponse : null), fieldBlocs: _fieldBlocs, + fieldStates: _fieldStates, currentStep: currentStep, ); } @@ -409,13 +383,13 @@ abstract class FormBlocState {SuccessResponse? successResponse}) { final state = this; return FormBlocDeleteSuccessful( - isValidByStep: _isValidByStep, isEditing: isEditing, successResponse: successResponse ?? (state is FormBlocDeleteSuccessful ? state.successResponse : null), fieldBlocs: _fieldBlocs, + fieldStates: _fieldStates, currentStep: currentStep, ); } @@ -428,9 +402,9 @@ abstract class FormBlocState {double? progress}) { final state = this; return FormBlocUpdatingFields( - isValidByStep: _isValidByStep, isEditing: isEditing, fieldBlocs: _fieldBlocs, + fieldStates: _fieldStates, currentStep: currentStep, progress: progress ?? (state is FormBlocUpdatingFields @@ -442,108 +416,113 @@ abstract class FormBlocState /// Returns a copy of the current state by changing [currentStep]. FormBlocState _copyWith({ int? currentStep, - Map>? fieldBlocs, - Map? isValidByStep, + Map? fieldBlocs, + Map? fieldStates, }) { final state = this; + final _fieldStates = + fieldBlocs?.map((step, fb) => MapEntry(step, fb.state)) ?? + fieldStates ?? + this._fieldStates; + if (state is FormBlocLoading) { return FormBlocLoading( - isValidByStep: isValidByStep ?? _isValidByStep, isEditing: isEditing, fieldBlocs: fieldBlocs ?? _fieldBlocs, + fieldStates: _fieldStates, currentStep: currentStep ?? this.currentStep, progress: state.progress, ); } else if (state is FormBlocLoadFailed) { return FormBlocLoadFailed( - isValidByStep: isValidByStep ?? _isValidByStep, isEditing: isEditing, failureResponse: state.failureResponse, fieldBlocs: fieldBlocs ?? _fieldBlocs, + fieldStates: _fieldStates, currentStep: currentStep ?? this.currentStep, ); } else if (state is FormBlocLoaded) { return FormBlocLoaded( - isValidByStep: isValidByStep ?? _isValidByStep, isEditing: isEditing, fieldBlocs: fieldBlocs ?? _fieldBlocs, + fieldStates: _fieldStates, currentStep: currentStep ?? this.currentStep, ); } else if (state is FormBlocSubmitting) { return FormBlocSubmitting( - isValidByStep: isValidByStep ?? _isValidByStep, isEditing: isEditing, progress: state.progress, isCanceling: state.isCanceling, fieldBlocs: fieldBlocs ?? _fieldBlocs, + fieldStates: _fieldStates, currentStep: currentStep ?? this.currentStep, ); } else if (state is FormBlocSuccess) { return FormBlocSuccess( - isValidByStep: isValidByStep ?? _isValidByStep, isEditing: isEditing, successResponse: state.successResponse, canSubmitAgain: state.canSubmitAgain, fieldBlocs: fieldBlocs ?? _fieldBlocs, + fieldStates: _fieldStates, currentStep: currentStep ?? this.currentStep, ); } else if (state is FormBlocFailure) { return FormBlocFailure( - isValidByStep: isValidByStep ?? _isValidByStep, isEditing: isEditing, failureResponse: state.failureResponse, fieldBlocs: fieldBlocs ?? _fieldBlocs, + fieldStates: _fieldStates, currentStep: currentStep ?? this.currentStep, ); } else if (state is FormBlocSubmissionFailed) { return FormBlocSubmissionFailed( - isValidByStep: isValidByStep ?? _isValidByStep, isEditing: isEditing, fieldBlocs: fieldBlocs ?? _fieldBlocs, + fieldStates: _fieldStates, currentStep: currentStep ?? this.currentStep, ); } else if (state is FormBlocSubmissionCancelled) { return FormBlocSubmissionCancelled( - isValidByStep: isValidByStep ?? _isValidByStep, isEditing: isEditing, fieldBlocs: fieldBlocs ?? _fieldBlocs, + fieldStates: _fieldStates, currentStep: currentStep ?? this.currentStep, ); } else if (state is FormBlocDeleteSuccessful) { return FormBlocDeleteSuccessful( - isValidByStep: isValidByStep ?? _isValidByStep, isEditing: isEditing, successResponse: state.successResponse, fieldBlocs: fieldBlocs ?? _fieldBlocs, + fieldStates: _fieldStates, currentStep: currentStep ?? this.currentStep, ); } else if (state is FormBlocDeleteFailed) { return FormBlocDeleteFailed( - isValidByStep: isValidByStep ?? _isValidByStep, isEditing: isEditing, failureResponse: state.failureResponse, fieldBlocs: fieldBlocs ?? _fieldBlocs, + fieldStates: _fieldStates, currentStep: currentStep ?? this.currentStep, ); } else if (state is FormBlocDeleting) { return FormBlocDeleting( - isValidByStep: isValidByStep ?? _isValidByStep, isEditing: isEditing, fieldBlocs: fieldBlocs ?? _fieldBlocs, + fieldStates: _fieldStates, currentStep: currentStep ?? this.currentStep, progress: state.progress, ); } else if (state is FormBlocUpdatingFields) { return FormBlocUpdatingFields( - isValidByStep: isValidByStep ?? _isValidByStep, isEditing: isEditing, fieldBlocs: fieldBlocs ?? _fieldBlocs, + fieldStates: _fieldStates, currentStep: currentStep ?? this.currentStep, progress: state.progress, ); @@ -552,31 +531,34 @@ abstract class FormBlocState } } + @override + List get props => [fieldBlocs, fieldStates, isEditing, currentStep]; + @override String toString() => _toStringWith(); String _toStringWith([String? extra]) { - String _allStepsToJson() { - var string = ''; - if (numberOfSteps > 1) { - _fieldBlocs.forEach((key, value) { - string += ',\n step $key - toJson($key): ${toJson(key)}'; - }); - } - - return string; - } - - String _allStepsIsValid() { - var string = ''; - if (numberOfSteps > 1) { - _isValidByStep.forEach((key, value) { - string += ',\n step $key - isValid($key): ${isValid(key)}'; - }); - } - - return string; - } + // String _allStepsToJson() { + // var string = ''; + // if (numberOfSteps > 1) { + // _fieldBlocs.forEach((key, value) { + // string += ',\n step $key - toJson($key): ${toJson(key)}'; + // }); + // } + // + // return string; + // } + + // String _allStepsIsValid() { + // var string = ''; + // if (numberOfSteps > 1) { + // _isValidByStep.forEach((key, value) { + // string += ',\n step $key - isValid($key): ${isValid(key)}'; + // }); + // } + // + // return string; + // } var _toString = '$runtimeType {'; @@ -589,9 +571,9 @@ abstract class FormBlocState _toString += ',\n currentStep: $currentStep'; _toString += ',\n numberOfSteps: $numberOfSteps'; - _toString += _allStepsIsValid(); - _toString += ',\n isValid(): ${isValid()}'; - _toString += _allStepsToJson(); + // _toString += _allStepsIsValid(); + // _toString += ',\n isValid(): ${isValid()}'; + // _toString += _allStepsToJson(); _toString += ',\n toJson(): ${toJson()}'; _toString += ',\n fieldBlocs: $_fieldBlocs'; _toString += '\n}'; @@ -615,26 +597,23 @@ class FormBlocLoading final double progress; FormBlocLoading({ - Map isValidByStep = const {}, bool isEditing = false, - Map> fieldBlocs = const {}, + Map fieldBlocs = const {}, + Map fieldStates = const {}, int currentStep = 0, double progress = 0.0, }) : assert(progress >= 0.0 && progress <= 0.0), progress = progress.clamp(0.0, 1.0), super( - isValidByStep: isValidByStep, isEditing: isEditing, fieldBlocs: fieldBlocs, + fieldStates: fieldStates, currentStep: currentStep, ); @override List get props => [ - _isValidByStep, - isEditing, - _fieldBlocs, - currentStep, + super.props, progress, ]; @@ -661,25 +640,22 @@ class FormBlocLoadFailed bool get hasFailureResponse => failureResponse != null; FormBlocLoadFailed({ - Map isValidByStep = const {}, bool isEditing = false, this.failureResponse, - Map> fieldBlocs = const {}, + Map fieldBlocs = const {}, + Map fieldStates = const {}, int currentStep = 0, }) : super( - isValidByStep: isValidByStep, isEditing: isEditing, fieldBlocs: fieldBlocs, + fieldStates: fieldStates, currentStep: currentStep, ); @override List get props => [ - _isValidByStep, + super.props, failureResponse, - isEditing, - _fieldBlocs, - currentStep, ]; @override @@ -697,24 +673,16 @@ class FormBlocLoadFailed class FormBlocLoaded extends FormBlocState { FormBlocLoaded({ - Map isValidByStep = const {}, bool isEditing = false, - Map> fieldBlocs = const {}, + Map fieldBlocs = const {}, + Map fieldStates = const {}, int currentStep = 0, }) : super( - isValidByStep: isValidByStep, isEditing: isEditing, fieldBlocs: fieldBlocs, + fieldStates: fieldStates, currentStep: currentStep, ); - - @override - List get props => [ - _isValidByStep, - isEditing, - _fieldBlocs, - currentStep, - ]; } /// {@template form_bloc.form_state.FormBlocSubmitting} @@ -737,16 +705,16 @@ class FormBlocSubmitting /// * If [progress] is less than 0, it will become 0.0 /// * If [progress] is greater than 1, it will become 1.0 FormBlocSubmitting({ - Map isValidByStep = const {}, bool isEditing = false, double progress = 0.0, this.isCanceling = false, - Map> fieldBlocs = const {}, + Map fieldBlocs = const {}, + Map fieldStates = const {}, int currentStep = 0, }) : assert(progress >= 0.0 && progress <= 1.0), progress = progress.clamp(0.0, 1.0), super( - isValidByStep: isValidByStep, + fieldStates: fieldStates, isEditing: isEditing, fieldBlocs: fieldBlocs, currentStep: currentStep, @@ -754,12 +722,9 @@ class FormBlocSubmitting @override List get props => [ - _isValidByStep, + super.props, progress, isCanceling, - isEditing, - _fieldBlocs, - currentStep, ]; @override @@ -788,15 +753,15 @@ class FormBlocSuccess bool get hasSuccessResponse => successResponse != null; FormBlocSuccess({ - Map isValidByStep = const {}, bool isEditing = false, this.successResponse, this.canSubmitAgain = false, - Map> fieldBlocs = const {}, + Map fieldBlocs = const {}, + Map fieldStates = const {}, int currentStep = 0, this.stepCompleted = 0, }) : super( - isValidByStep: isValidByStep, + fieldStates: fieldStates, isEditing: isEditing, fieldBlocs: fieldBlocs, currentStep: currentStep, @@ -804,12 +769,9 @@ class FormBlocSuccess @override List get props => [ - _isValidByStep, + super.props, successResponse, - isEditing, canSubmitAgain, - _fieldBlocs, - currentStep, stepCompleted, ]; @@ -837,25 +799,22 @@ class FormBlocFailure bool get hasFailureResponse => failureResponse != null; FormBlocFailure({ - required Map isValidByStep, bool isEditing = false, this.failureResponse, - Map> fieldBlocs = const {}, + Map fieldBlocs = const {}, + Map fieldStates = const {}, int currentStep = 0, }) : super( - isValidByStep: isValidByStep, isEditing: isEditing, fieldBlocs: fieldBlocs, + fieldStates: fieldStates, currentStep: currentStep, ); @override List get props => [ - _isValidByStep, + super.props, failureResponse, - isEditing, - _fieldBlocs, - currentStep, ]; @override @@ -875,24 +834,16 @@ class FormBlocFailure class FormBlocSubmissionCancelled extends FormBlocState { FormBlocSubmissionCancelled({ - Map isValidByStep = const {}, bool isEditing = false, - Map> fieldBlocs = const {}, + Map fieldBlocs = const {}, + Map fieldStates = const {}, int currentStep = 0, }) : super( - isValidByStep: isValidByStep, isEditing: isEditing, fieldBlocs: fieldBlocs, + fieldStates: fieldStates, currentStep: currentStep, ); - - @override - List get props => [ - _isValidByStep, - isEditing, - _fieldBlocs, - currentStep, - ]; } /// {@template form_bloc.form_state.FormBlocSubmissionFailed} @@ -902,24 +853,16 @@ class FormBlocSubmissionCancelled class FormBlocSubmissionFailed extends FormBlocState { FormBlocSubmissionFailed({ - Map isValidByStep = const {}, bool isEditing = false, - Map> fieldBlocs = const {}, + Map fieldBlocs = const {}, + Map fieldStates = const {}, int currentStep = 0, }) : super( - isValidByStep: isValidByStep, + fieldStates: fieldStates, isEditing: isEditing, fieldBlocs: fieldBlocs, currentStep: currentStep, ); - - @override - List get props => [ - _isValidByStep, - isEditing, - _fieldBlocs, - currentStep, - ]; } /// {@template form_bloc.form_state.FormBlocDeleting} @@ -930,26 +873,23 @@ class FormBlocDeleting final double progress; FormBlocDeleting({ - Map isValidByStep = const {}, bool isEditing = false, - Map> fieldBlocs = const {}, + Map fieldBlocs = const {}, + Map fieldStates = const {}, int currentStep = 0, double progress = 0.0, }) : assert(progress >= 0.0 && progress <= 1.0), progress = progress.clamp(0.0, 1.0), super( - isValidByStep: isValidByStep, isEditing: isEditing, fieldBlocs: fieldBlocs, + fieldStates: fieldStates, currentStep: currentStep, ); @override List get props => [ - _isValidByStep, - isEditing, - _fieldBlocs, - currentStep, + super.props, progress, ]; @@ -976,25 +916,22 @@ class FormBlocDeleteFailed bool get hasFailureResponse => failureResponse != null; FormBlocDeleteFailed({ - Map isValidByStep = const {}, bool isEditing = false, this.failureResponse, - Map> fieldBlocs = const {}, + Map fieldBlocs = const {}, + Map fieldStates = const {}, int currentStep = 0, }) : super( - isValidByStep: isValidByStep, isEditing: isEditing, fieldBlocs: fieldBlocs, + fieldStates: fieldStates, currentStep: currentStep, ); @override List get props => [ - _isValidByStep, + super.props, failureResponse, - isEditing, - _fieldBlocs, - currentStep, ]; @override @@ -1020,25 +957,22 @@ class FormBlocDeleteSuccessful bool get hasSuccessResponse => successResponse != null; FormBlocDeleteSuccessful({ - Map isValidByStep = const {}, bool isEditing = false, this.successResponse, - Map> fieldBlocs = const {}, + Map fieldBlocs = const {}, + Map fieldStates = const {}, int currentStep = 0, }) : super( - isValidByStep: isValidByStep, isEditing: isEditing, fieldBlocs: fieldBlocs, + fieldStates: fieldStates, currentStep: currentStep, ); @override List get props => [ - _isValidByStep, + super.props, successResponse, - isEditing, - _fieldBlocs, - currentStep, ]; @override @@ -1062,26 +996,23 @@ class FormBlocUpdatingFields final double progress; FormBlocUpdatingFields({ - required Map isValidByStep, bool isEditing = false, - Map> fieldBlocs = const {}, + Map fieldBlocs = const {}, + Map fieldStates = const {}, int currentStep = 0, required double progress, }) : assert(progress >= 0.0 && progress <= 1.0), progress = progress.clamp(0.0, 1.0), super( - isValidByStep: isValidByStep, isEditing: isEditing, fieldBlocs: fieldBlocs, + fieldStates: fieldStates, currentStep: currentStep, ); @override List get props => [ - _isValidByStep, - isEditing, - _fieldBlocs, - currentStep, + super.props, progress, ]; diff --git a/packages/form_bloc/lib/src/blocs/group_field/group_field_bloc.dart b/packages/form_bloc/lib/src/blocs/group_field/group_field_bloc.dart index e7fabc15..68e9be2a 100644 --- a/packages/form_bloc/lib/src/blocs/group_field/group_field_bloc.dart +++ b/packages/form_bloc/lib/src/blocs/group_field/group_field_bloc.dart @@ -1,42 +1,49 @@ part of '../field/field_bloc.dart'; -class GroupFieldBlocState +typedef GroupFieldBlocState + = MapFieldBlocState; + +typedef GroupFieldBloc + = MapFieldBloc; + +class MapFieldBlocState extends MultiFieldBlocState { - final Map fieldBlocs; + final Map fieldBlocs; + final Map fieldStates; + + @override + late final Map value = + fieldStates.map((name, state) { + return MapEntry(name, state.value); + }); - GroupFieldBlocState({ - required FormBloc? formBloc, - required String name, - required bool isValidating, - required bool isValid, + MapFieldBlocState({ + required this.fieldBlocs, + required this.fieldStates, required ExtraData? extraData, - required Iterable fieldBlocs, - }) : fieldBlocs = { - for (final fb in fieldBlocs) fb.state.name: fb, - }, - super( - formBloc: formBloc, - name: name, - isValidating: isValidating, - isValid: isValid, + }) : super( extraData: extraData, ); @override - GroupFieldBlocState copyWith({ - Param? formBloc, - bool? isValidating, - bool? isValid, + Map toJson() { + return fieldStates.map((key, value) { + return MapEntry(key, value.toJson()); + }); + } + + @override + MapFieldBlocState copyWith({ Param? extraData, - Iterable? fieldBlocs, + Map? fieldBlocs, + Map? fieldStates, }) { - return GroupFieldBlocState( - formBloc: formBloc == null ? this.formBloc : formBloc.value, - name: name, - isValidating: isValidating ?? this.isValidating, - isValid: isValid ?? this.isValid, + return MapFieldBlocState( extraData: extraData == null ? this.extraData : extraData.value, - fieldBlocs: fieldBlocs ?? this.fieldBlocs.values, + fieldBlocs: fieldBlocs ?? this.fieldBlocs, + fieldStates: fieldStates ?? + fieldBlocs?.map((step, fb) => MapEntry(step, fb.state)) ?? + this.fieldStates, ); } @@ -44,30 +51,75 @@ class GroupFieldBlocState Iterable get flatFieldBlocs => fieldBlocs.values; @override - List get props => [super.props, fieldBlocs]; + Iterable get flatFieldStates => fieldStates.values; + + @override + List get props => [super.props, fieldBlocs, fieldStates]; @override String toString([Object? other]) => super.toString(',\n fieldBlocs: $fieldBlocs'); } -class GroupFieldBloc - extends MultiFieldBloc> { - GroupFieldBloc({ - String? name, - List fieldBlocs = const [], +class MapFieldBloc + extends MultiFieldBloc> { + late final StreamSubscription _onValidationStatus; + + MapFieldBloc({ + Map fieldBlocs = const {}, + bool autoValidate = true, ExtraData? extraData, - }) : super(GroupFieldBlocState( - name: name ?? Uuid().v1(), - isValid: MultiFieldBloc.areFieldBlocsValid(fieldBlocs), - isValidating: MultiFieldBloc.areFieldBlocsValidating(fieldBlocs), - formBloc: null, - extraData: extraData, - fieldBlocs: fieldBlocs, - )); + }) : super( + MapFieldBlocState( + extraData: extraData, + fieldBlocs: fieldBlocs, + fieldStates: + fieldBlocs.map((name, fb) => MapEntry(name, fb.state)), + ), + autoValidate: autoValidate) { + _onValidationStatus = stream + .map((event) => event.fieldBlocs) + .distinct(const MapEquality().equals) + .switchMap((fieldBlocs) { + return Rx.combineLatestList(fieldBlocs.entries.map((entry) { + return entry.value.hotStream.map((state) => MapEntry(entry.key, state)); + })).skip(1); + }).listen((fieldStates) { + final effectiveFieldStates = Map.fromEntries(fieldStates); + + emit(state.copyWith( + fieldStates: effectiveFieldStates, + )); + }); + } + + void addAll(Map fieldBlocs) { + for (final fieldBloc in fieldBlocs.values) { + fieldBloc.updateAutoValidation(_autoValidate); + } + + emit(state.copyWith( + fieldBlocs: {...state.fieldBlocs, ...fieldBlocs}, + )); + } + + void add(TKey key, TFieldBloc fieldBloc) => addAll({key: fieldBloc}); + + void removeWhere(bool Function(TKey key, TFieldBloc fieldBloc) predicate) { + emit(state.copyWith( + fieldBlocs: state.fieldBlocs.where((k, v) => !predicate(k, v)), + )); + } + + void remove(TKey key) => removeWhere((k, fb) => k == key); @override - String toString() { - return '$runtimeType'; + Future close() async { + await _onValidationStatus.cancel(); + return super.close(); } + + @override + String toString() => '$runtimeType'; } diff --git a/packages/form_bloc/lib/src/blocs/input_field/input_field_bloc.dart b/packages/form_bloc/lib/src/blocs/input_field/input_field_bloc.dart index 5d5d24ae..784a7a84 100644 --- a/packages/form_bloc/lib/src/blocs/input_field/input_field_bloc.dart +++ b/packages/form_bloc/lib/src/blocs/input_field/input_field_bloc.dart @@ -1,8 +1,8 @@ part of '../field/field_bloc.dart'; /// A `FieldBloc` used for any type, for example `DateTime` or `File`. -class InputFieldBloc extends SingleFieldBloc, ExtraData?> { +class InputFieldBloc + extends SingleFieldBloc, ExtraData?> { /// ## InputFieldBloc /// /// ### Properties: @@ -37,7 +37,6 @@ class InputFieldBloc extends SingleFieldBloc>? validators, List>? asyncValidators, @@ -72,7 +71,6 @@ class InputFieldBloc extends SingleFieldBloc - extends FieldBlocState { +class InputFieldBlocState extends FieldBlocState { InputFieldBlocState({ required bool isValueChanged, required Value initialValue, @@ -12,8 +11,6 @@ class InputFieldBlocState required Suggestions? suggestions, required bool isValidated, required bool isValidating, - FormBloc? formBloc, - required String name, List additionalProps = const [], dynamic Function(Value value)? toJson, ExtraData? extraData, @@ -27,8 +24,6 @@ class InputFieldBlocState suggestions: suggestions, isValidated: isValidated, isValidating: isValidating, - formBloc: formBloc, - name: name, toJson: toJson, extraData: extraData, ); @@ -44,7 +39,6 @@ class InputFieldBlocState Param?>? suggestions, bool? isValidated, bool? isValidating, - Param? formBloc, Param? extraData, }) { return InputFieldBlocState( @@ -57,8 +51,6 @@ class InputFieldBlocState suggestions: suggestions == null ? this.suggestions : suggestions.value, isValidated: isValidated ?? this.isValidated, isValidating: isValidating ?? this.isValidating, - formBloc: formBloc == null ? this.formBloc : formBloc.value, - name: name, toJson: _toJson, extraData: extraData == null ? this.extraData : extraData.value, ); diff --git a/packages/form_bloc/lib/src/blocs/list_field/list_field_bloc.dart b/packages/form_bloc/lib/src/blocs/list_field/list_field_bloc.dart index 0f9bfe90..a2e8c84d 100644 --- a/packages/form_bloc/lib/src/blocs/list_field/list_field_bloc.dart +++ b/packages/form_bloc/lib/src/blocs/list_field/list_field_bloc.dart @@ -3,37 +3,37 @@ part of '../field/field_bloc.dart'; class ListFieldBlocState extends MultiFieldBlocState { final List fieldBlocs; + final List fieldStates; + + @override + late final List value = fieldStates.map((state) { + return state.value; + }).toList(); ListFieldBlocState({ - required FormBloc? formBloc, - required String name, - required bool isValidating, - required bool isValid, required ExtraData? extraData, required this.fieldBlocs, + required this.fieldStates, }) : super( - formBloc: formBloc, - name: name, - isValidating: isValidating, - isValid: isValid, extraData: extraData, ); + @override + List toJson() => + fieldStates.map((e) => e.toJson()).toList(); + @override ListFieldBlocState copyWith({ - Param? formBloc, - bool? isValidating, - bool? isValid, Param? extraData, List? fieldBlocs, + List? fieldStates, }) { return ListFieldBlocState( - formBloc: formBloc == null ? this.formBloc : formBloc.value, - name: name, - isValidating: isValidating ?? this.isValidating, - isValid: isValid ?? this.isValid, extraData: extraData == null ? this.extraData : extraData.value, fieldBlocs: fieldBlocs ?? this.fieldBlocs, + fieldStates: fieldBlocs?.map((e) => e.state).toList() ?? + fieldStates ?? + this.fieldStates, ); } @@ -41,7 +41,10 @@ class ListFieldBlocState Iterable get flatFieldBlocs => fieldBlocs; @override - List get props => [super.props, fieldBlocs]; + Iterable get flatFieldStates => fieldStates; + + @override + List get props => [super.props, fieldBlocs, fieldStates]; @override String toString([Object? other]) => @@ -50,18 +53,30 @@ class ListFieldBlocState class ListFieldBloc extends MultiFieldBloc> { + late final StreamSubscription _onValidationStatus; + ListFieldBloc({ - String? name, List fieldBlocs = const [], + bool autoValidate = true, ExtraData? extraData, - }) : super(ListFieldBlocState( - name: name ?? Uuid().v1(), - formBloc: null, - isValidating: MultiFieldBloc.areFieldBlocsValidating(fieldBlocs), - isValid: MultiFieldBloc.areFieldBlocsValid(fieldBlocs), - extraData: extraData, - fieldBlocs: fieldBlocs, - )); + }) : super( + ListFieldBlocState( + extraData: extraData, + fieldBlocs: fieldBlocs, + fieldStates: fieldBlocs.map((e) => e.state).toList(), + ), + autoValidate: autoValidate) { + _onValidationStatus = stream + .map((event) => event.fieldBlocs) + .distinct(const ListEquality().equals) + .switchMap((fieldBlocs) { + return Rx.combineLatestList(fieldBlocs.map((fb) => fb.hotStream)).skip(1); + }).listen((fieldStates) { + emit(state.copyWith( + fieldStates: fieldStates, + )); + }); + } List get value => state.fieldBlocs; @@ -73,17 +88,13 @@ class ListFieldBloc if (fieldBlocs.isNotEmpty) { final nextFieldBlocs = [...state.fieldBlocs, ...fieldBlocs]; + for (final fieldBloc in fieldBlocs) { + fieldBloc.updateAutoValidation(_autoValidate); + } + emit(state.copyWith( - isValidating: MultiFieldBloc.areFieldBlocsValidating(nextFieldBlocs), - isValid: MultiFieldBloc.areFieldBlocsValid(nextFieldBlocs), fieldBlocs: nextFieldBlocs, )); - - FormBlocUtils.updateFormBloc( - fieldBlocs: fieldBlocs, - formBloc: state.formBloc, - autoValidate: _autoValidate, - ); } } @@ -91,18 +102,11 @@ class ListFieldBloc void removeFieldBlocAt(int index) { if (state.fieldBlocs.length > index) { final nextFieldBlocs = [...state.fieldBlocs]; - final fieldBlocRemoved = nextFieldBlocs.removeAt(index); + nextFieldBlocs.removeAt(index); emit(state.copyWith( - isValidating: MultiFieldBloc.areFieldBlocsValidating(nextFieldBlocs), - isValid: MultiFieldBloc.areFieldBlocsValid(nextFieldBlocs), fieldBlocs: nextFieldBlocs, )); - - FormBlocUtils.removeFormBloc( - fieldBlocs: [fieldBlocRemoved], - formBloc: state.formBloc, - ); } } @@ -128,15 +132,8 @@ class ListFieldBloc if (fieldBlocsRemoved.isEmpty) return; emit(state.copyWith( - isValidating: MultiFieldBloc.areFieldBlocsValidating(nextFieldBlocs), - isValid: MultiFieldBloc.areFieldBlocsValid(nextFieldBlocs), fieldBlocs: nextFieldBlocs, )); - - FormBlocUtils.removeFormBloc( - fieldBlocs: fieldBlocsRemoved, - formBloc: state.formBloc, - ); } /// Insert [FieldBloc] into index. @@ -150,46 +147,34 @@ class ListFieldBloc nextFieldBlocs.insertAll(index, fieldBlocs); + for (final fieldBloc in fieldBlocs) { + fieldBloc.updateAutoValidation(_autoValidate); + } + emit(state.copyWith( - isValidating: MultiFieldBloc.areFieldBlocsValidating(nextFieldBlocs), - isValid: MultiFieldBloc.areFieldBlocsValid(nextFieldBlocs), fieldBlocs: nextFieldBlocs, )); - - FormBlocUtils.updateFormBloc( - fieldBlocs: fieldBlocs, - formBloc: state.formBloc, - autoValidate: _autoValidate, - ); } } /// Updates [FieldBloc]s. void updateFieldBlocs(List fieldBlocs) { - final previousFieldBlocs = [...state.fieldBlocs]; final nextFieldBlocs = [...fieldBlocs]; - FormBlocUtils.removeFormBloc( - fieldBlocs: previousFieldBlocs, - formBloc: state.formBloc, - ); - emit(state.copyWith( - isValidating: MultiFieldBloc.areFieldBlocsValidating(nextFieldBlocs), - isValid: MultiFieldBloc.areFieldBlocsValid(nextFieldBlocs), fieldBlocs: nextFieldBlocs, )); - - FormBlocUtils.updateFormBloc( - fieldBlocs: fieldBlocs, - formBloc: state.formBloc, - autoValidate: _autoValidate, - ); } /// Removes all [FieldBloc]s. void clearFieldBlocs() => removeFieldBlocsWhere((element) => true); + @override + Future close() async { + await _onValidationStatus.cancel(); + return super.close(); + } + @override String toString() => '$runtimeType'; } diff --git a/packages/form_bloc/lib/src/blocs/multi_select_field/multi_select_field_bloc.dart b/packages/form_bloc/lib/src/blocs/multi_select_field/multi_select_field_bloc.dart index 48286552..060464bd 100644 --- a/packages/form_bloc/lib/src/blocs/multi_select_field/multi_select_field_bloc.dart +++ b/packages/form_bloc/lib/src/blocs/multi_select_field/multi_select_field_bloc.dart @@ -2,11 +2,8 @@ part of '../field/field_bloc.dart'; /// A `FieldBloc` used to select multiple items /// from multiple items. -class MultiSelectFieldBloc extends SingleFieldBloc< - List, - Value, - MultiSelectFieldBlocState, - ExtraData?> { +class MultiSelectFieldBloc extends SingleFieldBloc, Value, + MultiSelectFieldBlocState, ExtraData?> { /// ## MultiSelectFieldBloc /// /// ### Properties: @@ -43,7 +40,6 @@ class MultiSelectFieldBloc extends SingleFieldBloc< /// This method is called when you use [FormBlocState.toJson]… /// * [extraData] : It is an object that you can use to add extra data, it will be available in the state [FieldBlocState.extraData]. MultiSelectFieldBloc({ - String? name, List initialValue = const [], List>>? validators, List>>? asyncValidators, @@ -80,7 +76,6 @@ class MultiSelectFieldBloc extends SingleFieldBloc< validators: validators, value: initialValue, ), - name: FieldBlocUtils.generateName(name), items: SingleFieldBloc._itemsWithoutDuplicates(items), toJson: toJson, extraData: extraData, @@ -158,12 +153,10 @@ class MultiSelectFieldBloc extends SingleFieldBloc< /// {@macro form_bloc.field_bloc.update_value} void select(Value valueToSelect) { var newValue = state.value; - newValue = - SingleFieldBloc._itemsWithoutDuplicates([...newValue, valueToSelect]); + newValue = SingleFieldBloc._itemsWithoutDuplicates([...newValue, valueToSelect]); if (_canUpdateValue(value: newValue, isInitialValue: false)) { final error = _getError(value: newValue); - final isValidating = - _getAsyncValidatorsError(value: newValue, error: error); + final isValidating = _getAsyncValidatorsError(value: newValue, error: error); emit(state.copyWith( isValueChanged: true, @@ -186,8 +179,7 @@ class MultiSelectFieldBloc extends SingleFieldBloc< if (_canUpdateValue(value: newValue, isInitialValue: false)) { final error = _getError(value: newValue); - final isValidating = - _getAsyncValidatorsError(value: newValue, error: error); + final isValidating = _getAsyncValidatorsError(value: newValue, error: error); emit(state.copyWith( isValueChanged: true, diff --git a/packages/form_bloc/lib/src/blocs/multi_select_field/multi_select_field_state.dart b/packages/form_bloc/lib/src/blocs/multi_select_field/multi_select_field_state.dart index ab147935..94ebc455 100644 --- a/packages/form_bloc/lib/src/blocs/multi_select_field/multi_select_field_state.dart +++ b/packages/form_bloc/lib/src/blocs/multi_select_field/multi_select_field_state.dart @@ -14,8 +14,6 @@ class MultiSelectFieldBlocState required Suggestions? suggestions, required bool isValidated, required bool isValidating, - FormBloc? formBloc, - required String name, this.items = const [], dynamic Function(List value)? toJson, ExtraData? extraData, @@ -29,8 +27,6 @@ class MultiSelectFieldBlocState suggestions: suggestions, isValidated: isValidated, isValidating: isValidating, - formBloc: formBloc, - name: name, toJson: toJson, extraData: extraData, ); @@ -46,7 +42,6 @@ class MultiSelectFieldBlocState Param?>? suggestions, bool? isValidated, bool? isValidating, - Param? formBloc, List? items, Param? extraData, }) { @@ -60,8 +55,6 @@ class MultiSelectFieldBlocState suggestions: suggestions == null ? this.suggestions : suggestions.value, isValidated: isValidated ?? this.isValidated, isValidating: isValidating ?? this.isValidating, - formBloc: formBloc == null ? this.formBloc : formBloc.value, - name: name, items: items ?? this.items, toJson: _toJson, extraData: extraData == null ? this.extraData : extraData.value, @@ -69,8 +62,7 @@ class MultiSelectFieldBlocState } @override - String toString([String extra = '']) => - super.toString(',\n items: $items$extra'); + String toString([String extra = '']) => super.toString(',\n items: $items$extra'); @override List get props => [super.props, items]; diff --git a/packages/form_bloc/lib/src/blocs/select_field/select_field_bloc.dart b/packages/form_bloc/lib/src/blocs/select_field/select_field_bloc.dart index 403250bf..cdfbb385 100644 --- a/packages/form_bloc/lib/src/blocs/select_field/select_field_bloc.dart +++ b/packages/form_bloc/lib/src/blocs/select_field/select_field_bloc.dart @@ -2,8 +2,8 @@ part of '../field/field_bloc.dart'; /// A `FieldBloc` used to select one item /// from multiple items. -class SelectFieldBloc extends SingleFieldBloc, ExtraData?> { +class SelectFieldBloc + extends SingleFieldBloc, ExtraData?> { /// ## SelectFieldBloc /// /// ### Properties: @@ -39,7 +39,6 @@ class SelectFieldBloc extends SingleFieldBloc>? validators, List>? asyncValidators, @@ -75,7 +74,6 @@ class SelectFieldBloc extends SingleFieldBloc - extends FieldBlocState { +class SelectFieldBlocState extends FieldBlocState { final List items; SelectFieldBlocState({ @@ -14,8 +13,6 @@ class SelectFieldBlocState required Suggestions? suggestions, required bool isValidated, required bool isValidating, - FormBloc? formBloc, - required String name, this.items = const [], dynamic Function(Value? value)? toJson, ExtraData? extraData, @@ -29,8 +26,6 @@ class SelectFieldBlocState suggestions: suggestions, isValidated: isValidated, isValidating: isValidating, - formBloc: formBloc, - name: name, toJson: toJson, extraData: extraData, ); @@ -46,7 +41,6 @@ class SelectFieldBlocState Param?>? suggestions, bool? isValidated, bool? isValidating, - Param? formBloc, List? items, Param? extraData, }) { @@ -60,8 +54,6 @@ class SelectFieldBlocState suggestions: suggestions == null ? this.suggestions : suggestions.value, isValidated: isValidated ?? this.isValidated, isValidating: isValidating ?? this.isValidating, - formBloc: formBloc == null ? this.formBloc : formBloc.value, - name: name, items: items ?? this.items, toJson: _toJson, extraData: extraData == null ? this.extraData : extraData.value, @@ -69,8 +61,7 @@ class SelectFieldBlocState } @override - String toString([String extra = '']) => - super.toString(',\n items: $items$extra'); + String toString([String extra = '']) => super.toString(',\n items: $items$extra'); @override List get props => [super.props, items]; diff --git a/packages/form_bloc/lib/src/blocs/text_field/text_field_bloc.dart b/packages/form_bloc/lib/src/blocs/text_field/text_field_bloc.dart index d6c2b43d..8a14a788 100644 --- a/packages/form_bloc/lib/src/blocs/text_field/text_field_bloc.dart +++ b/packages/form_bloc/lib/src/blocs/text_field/text_field_bloc.dart @@ -4,8 +4,8 @@ part of '../field/field_bloc.dart'; /// it is also used to obtain `int` and `double` values /// ​​of texts thanks to the methods /// [valueToInt] and [valueToDouble]. -class TextFieldBloc extends SingleFieldBloc, ExtraData?> { +class TextFieldBloc + extends SingleFieldBloc, ExtraData?> { /// ## TextFieldBloc /// /// ### Properties: @@ -38,7 +38,6 @@ class TextFieldBloc extends SingleFieldBloc>? validators, List>? asyncValidators, @@ -72,7 +71,6 @@ class TextFieldBloc extends SingleFieldBloc value, extraData: extraData, ), diff --git a/packages/form_bloc/lib/src/blocs/text_field/text_field_state.dart b/packages/form_bloc/lib/src/blocs/text_field/text_field_state.dart index 8c3c8d29..13d64aa5 100644 --- a/packages/form_bloc/lib/src/blocs/text_field/text_field_state.dart +++ b/packages/form_bloc/lib/src/blocs/text_field/text_field_state.dart @@ -1,7 +1,6 @@ part of '../field/field_bloc.dart'; -class TextFieldBlocState - extends FieldBlocState { +class TextFieldBlocState extends FieldBlocState { TextFieldBlocState({ required bool isValueChanged, required String initialValue, @@ -12,8 +11,6 @@ class TextFieldBlocState required Suggestions? suggestions, required bool isValidated, required bool isValidating, - FormBloc? formBloc, - required String name, dynamic Function(String value)? toJson, ExtraData? extraData, }) : super( @@ -26,8 +23,6 @@ class TextFieldBlocState suggestions: suggestions, isValidated: isValidated, isValidating: isValidating, - formBloc: formBloc, - name: name, toJson: toJson, extraData: extraData, ); @@ -53,7 +48,6 @@ class TextFieldBlocState Param?>? suggestions, bool? isValidated, bool? isValidating, - Param? formBloc, Param? extraData, }) { return TextFieldBlocState( @@ -66,8 +60,6 @@ class TextFieldBlocState suggestions: suggestions == null ? this.suggestions : suggestions.value, isValidated: isValidated ?? this.isValidated, isValidating: isValidating ?? this.isValidating, - formBloc: formBloc == null ? this.formBloc : formBloc.value, - name: name, toJson: _toJson, extraData: extraData == null ? this.extraData : extraData.value, ); diff --git a/packages/form_bloc/lib/src/extension/extension.dart b/packages/form_bloc/lib/src/extension/extension.dart index b8c56cf2..ec607cd2 100644 --- a/packages/form_bloc/lib/src/extension/extension.dart +++ b/packages/form_bloc/lib/src/extension/extension.dart @@ -1,3 +1,16 @@ +import 'package:bloc/bloc.dart'; +import 'package:rxdart/rxdart.dart'; + +extension RemoveAllListExtension on List { + void removeAll(Iterable elements) => elements.forEach(remove); +} + +extension WhereMapExtension on Map { + Map where(bool Function(K key, V value) predicate) { + return Map.fromEntries(entries.where((entry) => predicate(entry.key, entry.value))); + } +} + extension StreamExtension on Stream { Future firstWhereOrNull(bool Function(T element) test) async { try { @@ -8,6 +21,6 @@ extension StreamExtension on Stream { } } -extension RemoveAllListExtension on List { - void removeAll(Iterable elements) => elements.forEach(remove); +extension HotStreamBloc on BlocBase { + Stream get hotStream => Rx.merge([Stream.value(state), stream]); } diff --git a/packages/form_bloc/test/boolean_field/boolean_field_bloc_test.dart b/packages/form_bloc/test/boolean_field/boolean_field_bloc_test.dart index 76663e6e..7d13eaad 100644 --- a/packages/form_bloc/test/boolean_field/boolean_field_bloc_test.dart +++ b/packages/form_bloc/test/boolean_field/boolean_field_bloc_test.dart @@ -12,7 +12,6 @@ void main() { final validators = [(bool? value) => value! ? 'error' : null]; final fieldBloc = BooleanFieldBloc( - name: 'name', initialValue: false, validators: validators, suggestions: suggestions, @@ -28,7 +27,6 @@ void main() { suggestions: suggestions, isValidated: true, isValidating: false, - name: 'name', ); final state2 = state1.copyWith( updatedValue: Param(true), @@ -52,9 +50,7 @@ void main() { BooleanFieldBloc fieldBloc; BooleanFieldBlocState initialState; - fieldBloc = BooleanFieldBloc( - name: 'name', - ); + fieldBloc = BooleanFieldBloc(); initialState = createBooleanState( value: false, @@ -74,7 +70,6 @@ void main() { fieldBloc.close(); fieldBloc = BooleanFieldBloc( - name: 'name', initialValue: true, validators: [FieldBlocValidators.required, (value) => 'error'], ); @@ -99,7 +94,7 @@ void main() { BooleanFieldBloc fieldBloc; BooleanFieldBlocState initialState; - fieldBloc = BooleanFieldBloc(name: 'name'); + fieldBloc = BooleanFieldBloc(); initialState = createBooleanState( value: false, diff --git a/packages/form_bloc/test/boolean_field/boolean_field_state_test.dart b/packages/form_bloc/test/boolean_field/boolean_field_state_test.dart index 2a91fe03..8a4d4149 100644 --- a/packages/form_bloc/test/boolean_field/boolean_field_state_test.dart +++ b/packages/form_bloc/test/boolean_field/boolean_field_state_test.dart @@ -20,7 +20,6 @@ void main() { isDirty: false, isValidated: false, isValidating: false, - name: 'fieldName', ); expectState( @@ -45,7 +44,6 @@ void main() { isDirty: true, isValidated: true, isValidating: true, - name: 'fieldName', ), ); }); @@ -61,7 +59,6 @@ void main() { isDirty: true, isValidated: true, isValidating: true, - name: 'fieldName', ); expectState( @@ -86,7 +83,6 @@ void main() { isDirty: false, isValidated: false, isValidating: false, - name: 'fieldName', ), ); }); @@ -102,7 +98,6 @@ void main() { isDirty: true, isValidated: true, isValidating: true, - name: 'fieldName', ); expectState( diff --git a/packages/form_bloc/test/field_bloc/field_bloc_test.dart b/packages/form_bloc/test/field_bloc/field_bloc_test.dart index 59c8f507..1f7ed28f 100644 --- a/packages/form_bloc/test/field_bloc/field_bloc_test.dart +++ b/packages/form_bloc/test/field_bloc/field_bloc_test.dart @@ -22,7 +22,6 @@ void main() { InputFieldBlocState initialState; fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: null, ); initialState = InputFieldBlocState( @@ -35,7 +34,6 @@ void main() { suggestions: null, isValidated: true, isValidating: false, - name: 'fieldName', ); expect( @@ -45,7 +43,6 @@ void main() { fieldBloc.close(); fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: 1, ); initialState = InputFieldBlocState( @@ -58,7 +55,6 @@ void main() { suggestions: null, isValidated: true, isValidating: false, - name: 'fieldName', ); expect( @@ -70,7 +66,6 @@ void main() { test('validators verify the value and add the corresponding error.', () async { final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: null, validators: [ FieldBlocValidators.required, @@ -129,7 +124,6 @@ void main() { test('asyncValidators verify the value and add the corresponding error.', () async { final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: null, asyncValidators: [ (value) async { @@ -180,7 +174,6 @@ void main() { () async { Future> suggestions(String pattern) async => [1, 2, 3]; final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: null, suggestions: suggestions, ); @@ -207,22 +200,10 @@ void main() { [1, 2, 3], ); }); - test('_name is added to the current state', () async { - final fieldBloc = InputFieldBloc( - name: 'fieldName', - initialValue: null, - ); - - expect( - 'fieldName', - fieldBloc.state.name, - ); - }); }); test('initial state has isInitial in true.', () { final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: null, ); @@ -244,7 +225,6 @@ void main() { test('changeValue.', () async { final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: null, validators: [FieldBlocValidators.required], ); @@ -293,7 +273,6 @@ void main() { test('updateValue method.', () { final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: null, validators: [FieldBlocValidators.required], ); @@ -338,7 +317,6 @@ void main() { test('updateValue method with isRequired false', () { final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: null, ); @@ -382,7 +360,6 @@ void main() { test('updateInitialValue.', () { final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: null, validators: [FieldBlocValidators.required], ); @@ -432,7 +409,6 @@ void main() { test('clear method set value to initialValue.', () async { final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: null, validators: [FieldBlocValidators.required], ); @@ -468,7 +444,6 @@ void main() { 'selectSuggestion method SelectSuggestion event and selectedSuggestion stream.', () { final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: null, ); @@ -487,7 +462,6 @@ void main() { group('maybeValidate', () { test('auto validation', () async { final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: null, ); var state = fieldBloc.state; @@ -506,7 +480,6 @@ void main() { test('force validation', () async { final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: null, ); fieldBloc.updateFormBloc(formBloc, autoValidate: false); @@ -532,7 +505,6 @@ void main() { group('addValidators', () { test('add validators and emit error', () async { final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: 1, validators: [FieldBlocValidators.required], ); @@ -574,7 +546,6 @@ void main() { group('addAsyncValidators', () { test('Add async validators and emit error', () async { final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: null, asyncValidatorDebounceTime: const Duration(), ); @@ -603,7 +574,6 @@ void main() { test('updateValidators.', () { final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: 1, validators: [(value) => value == 1 ? '1 error' : null], ); @@ -649,7 +619,6 @@ void main() { test('updateAsyncValidators method.', () async { final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: 1, asyncValidatorDebounceTime: Duration(milliseconds: 0), validators: [FieldBlocValidators.required], @@ -733,7 +702,6 @@ void main() { group('removeValidators', () { test('remove validators and not emit error', () async { final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: null, validators: [FieldBlocValidators.required], ); @@ -762,7 +730,6 @@ void main() { value == null ? '1 error' : null; final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: null, asyncValidatorDebounceTime: const Duration(), asyncValidators: [validator], @@ -795,7 +762,6 @@ void main() { Future> suggestions2(String pattern) async => [2]; final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: null, suggestions: suggestions1, ); @@ -835,7 +801,6 @@ void main() { 'after DisableFieldBlocAutoValidate event was dispatched, updateValue method updates the value without verify the value in validators and asyncValidators.', () async { final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: null, validators: [ FieldBlocValidators.required, @@ -909,7 +874,6 @@ void main() { test('ResetFieldBlocStateIsValidated event.', () { final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: null, ); @@ -940,7 +904,6 @@ void main() { test('UpdateFieldBlocStateError event.', () { final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: null, ); @@ -973,11 +936,9 @@ void main() { test('on subscribeToFieldBlocs method and SubscribeToFieldBlocs event.', () async { final fieldBloc1 = InputFieldBloc( - name: 'fieldName', initialValue: null, ); final fieldBloc2 = InputFieldBloc( - name: 'fieldName2', initialValue: null, ); @@ -1058,7 +1019,6 @@ void main() { group('addError', () { test('addError method and with isPermanent false.', () async { final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: null, ); @@ -1102,7 +1062,6 @@ void main() { test('addError method and with isPermanent true.', () async { final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: 1, ); @@ -1146,7 +1105,6 @@ void main() { () async { final fieldBloc = InputFieldBloc( initialValue: 1, - name: 'fieldName', ); final state1 = createInputState( @@ -1195,7 +1153,6 @@ void main() { test('addError method isPermanent false after disable auto validation.', () async { final fieldBloc = InputFieldBloc( - name: 'fieldName', initialValue: null, ); diff --git a/packages/form_bloc/test/field_bloc/multi_field_bloc_test.dart b/packages/form_bloc/test/field_bloc/multi_field_bloc_test.dart index 95a020fe..3936333b 100644 --- a/packages/form_bloc/test/field_bloc/multi_field_bloc_test.dart +++ b/packages/form_bloc/test/field_bloc/multi_field_bloc_test.dart @@ -13,9 +13,7 @@ void main() { group('MultiFieldBloc:', () { group('validate', () { test('Success empty validation', () async { - final multiField = ListFieldBloc, String>( - name: 'list', - ); + final multiField = ListFieldBloc, String>(); final expected = createListState, String>( name: 'list', @@ -27,10 +25,9 @@ void main() { }); test('Success validation', () async { - final field = BooleanFieldBloc(name: 'bool'); + final field = BooleanFieldBloc(); final multiField = ListFieldBloc, String>( - name: 'list', fieldBlocs: [field], ); @@ -46,12 +43,10 @@ void main() { test('Failed validation', () async { final field = BooleanFieldBloc( - name: 'bool', validators: [FieldBlocValidators.required], ); final multiField = ListFieldBloc, String>( - name: 'list', fieldBlocs: [field], ); @@ -68,9 +63,7 @@ void main() { group('updateExtraData', () { test('update', () { - final list = ListFieldBloc, String>( - name: 'list', - ); + final list = ListFieldBloc, String>(); final expected = createListState, String>( name: 'list', @@ -90,9 +83,7 @@ void main() { final formBloc = _FakeFormBloc(); test('Success update and remove formBloc', () { - final list = ListFieldBloc, String>( - name: 'list', - ); + final list = ListFieldBloc, String>(); final expectedUpdate = createListState, String>( @@ -112,9 +103,8 @@ void main() { }); test('Success update the fieldBlocs with the new FormBloc', () { - final field = BooleanFieldBloc(name: 'bool'); + final field = BooleanFieldBloc(); final list = ListFieldBloc, String>( - name: 'list', fieldBlocs: [field], ); @@ -151,9 +141,8 @@ void main() { }); test('Failure to remove formBloc because it is not theirs', () { - final field = BooleanFieldBloc(name: 'bool'); + final field = BooleanFieldBloc(); final list = ListFieldBloc, String>( - name: 'list', fieldBlocs: [field], ); diff --git a/packages/form_bloc/test/form_bloc/form_bloc_test.dart b/packages/form_bloc/test/form_bloc/form_bloc_test.dart index 087b5a64..06a4d53e 100644 --- a/packages/form_bloc/test/form_bloc/form_bloc_test.dart +++ b/packages/form_bloc/test/form_bloc/form_bloc_test.dart @@ -66,7 +66,7 @@ void main() { test('check fieldBloc was added with valid formBloc state', () async { final formBloc = _FormBlocImpl(); - formBloc.addFieldBloc(fieldBloc: formBloc.optionalField); + formBloc.addStep(fieldBloc: formBloc.optionalField); expect( formBloc.state, @@ -82,7 +82,7 @@ void main() { test('fieldBloc was added with invalid formBloc state', () async { final formBloc = _FormBlocImpl(); - formBloc.addFieldBloc(fieldBloc: formBloc.requiredField); + formBloc.addStep(fieldBloc: formBloc.requiredField); expect( formBloc.state, @@ -112,7 +112,7 @@ void main() { ), ); - formBloc.addFieldBloc(fieldBloc: fieldBloc); + formBloc.addStep(fieldBloc: fieldBloc); verify(() => fieldBloc.updateFormBloc(formBloc, autoValidate: true)); }); @@ -122,8 +122,8 @@ void main() { test('fieldBloc and step was removed from formBloc', () async { final formBloc = _FormBlocImpl(); - formBloc.addFieldBloc(fieldBloc: formBloc.requiredField); - formBloc.removeFieldBloc(fieldBloc: formBloc.requiredField); + formBloc.addStep(fieldBloc: formBloc.requiredField); + formBloc.removeStep(fieldBloc: formBloc.requiredField); expect( formBloc.state, @@ -140,7 +140,7 @@ void main() { formBloc.addFieldBlocs( fieldBlocs: [formBloc.requiredField, formBloc.optionalField], ); - formBloc.removeFieldBloc(fieldBloc: formBloc.requiredField); + formBloc.removeStep(fieldBloc: formBloc.requiredField); expect( formBloc.state, @@ -170,8 +170,8 @@ void main() { ), ); - formBloc.addFieldBloc(fieldBloc: fieldBloc); - formBloc.removeFieldBloc(fieldBloc: fieldBloc); + formBloc.addStep(fieldBloc: fieldBloc); + formBloc.removeStep(fieldBloc: fieldBloc); verify(() => fieldBloc.removeFormBloc(formBloc)); }); @@ -213,7 +213,7 @@ void main() { expect(formBloc.stream, emitsInOrder(expectedStates)); - formBloc.addFieldBloc(fieldBloc: formBloc.requiredField); + formBloc.addStep(fieldBloc: formBloc.requiredField); formBloc.requiredField.updateValue('x'); }); @@ -254,7 +254,7 @@ void main() { await expectBloc( formBloc, act: () { - formBloc.addFieldBloc(fieldBloc: formBloc.requiredField); + formBloc.addStep(fieldBloc: formBloc.requiredField); formBloc.submit(); }, stream: expectedStates, @@ -294,7 +294,7 @@ void main() { await expectBloc( formBloc, act: () { - formBloc.addFieldBloc(fieldBloc: formBloc.optionalField); + formBloc.addStep(fieldBloc: formBloc.optionalField); formBloc.submit(); }, stream: expectedStates, @@ -357,7 +357,7 @@ void main() { expect(formBloc.stream, emitsInOrder(expectedStates)); - formBloc.addFieldBloc(fieldBloc: formBloc.optionalField); + formBloc.addStep(fieldBloc: formBloc.optionalField); formBloc.submit(); }); @@ -459,7 +459,7 @@ void main() { initialState: initialState, ); - formBloc.addFieldBloc(fieldBloc: fieldBloc); + formBloc.addStep(fieldBloc: fieldBloc); formBloc.clear(); verify(() => fieldBloc.clear()); @@ -520,7 +520,7 @@ void main() { expect(formBloc.stream, emitsInOrder(expectedStates)); - formBloc.addFieldBloc(fieldBloc: formBloc.optionalField); + formBloc.addStep(fieldBloc: formBloc.optionalField); formBloc.cancelSubmission(); formBloc.submit(); diff --git a/packages/form_bloc/test/form_bloc/form_bloc_utils_test.dart b/packages/form_bloc/test/form_bloc/form_bloc_utils_test.dart index 9a4c29ef..c0ab842d 100644 --- a/packages/form_bloc/test/form_bloc/form_bloc_utils_test.dart +++ b/packages/form_bloc/test/form_bloc/form_bloc_utils_test.dart @@ -6,14 +6,13 @@ import '../utils/states.dart'; class GroupFieldBlocImpl extends GroupFieldBloc { GroupFieldBlocImpl({ - required String name, - required List fieldBlocs, + required Map fieldBlocs, required dynamic extraData, - }) : super(fieldBlocs: fieldBlocs, name: name, extraData: extraData); + }) : super(fieldBlocs: fieldBlocs, extraData: extraData); @override String toString() { - return '$runtimeType: ${state.name}'; + return '$runtimeType'; } } @@ -26,43 +25,33 @@ void main() { group('FormBlocUtils:', () { group('getAllSingleFieldBlocs:', () { final textFieldBloc1 = TextFieldBloc( - name: 'textFieldBloc1', initialValue: 'text1', ); final textFieldBloc2 = TextFieldBloc( - name: 'textFieldBloc2', initialValue: 'text2', ); final textFieldBloc3 = TextFieldBloc( - name: 'textFieldBloc3', initialValue: 'text3', ); final textFieldBloc4 = TextFieldBloc( - name: 'textFieldBloc4', initialValue: 'text4', ); final textFieldBloc5 = TextFieldBloc( - name: 'textFieldBloc5', initialValue: 'text5', ); final textFieldBloc6 = TextFieldBloc( - name: 'textFieldBloc6', initialValue: 'text6', ); - final textFieldBloc7 = TextFieldBloc( - name: 'textFieldBloc7', initialValue: 'text7', ); final textFieldBloc8 = TextFieldBloc( - name: 'textFieldBloc8', initialValue: 'text8', ); final groupFieldWithSingleFieldBlocs1 = GroupFieldBlocImpl( - name: 'groupFieldWithSingleFieldBlocs1', fieldBlocs: [ textFieldBloc3, textFieldBloc4, @@ -71,7 +60,6 @@ void main() { ); final groupFieldWithSingleFieldBlocs2 = GroupFieldBlocImpl( - name: 'groupFieldWithSingleFieldBlocs2', fieldBlocs: [ textFieldBloc5, textFieldBloc6, @@ -80,7 +68,6 @@ void main() { ); final groupFieldBlocWithGroupAndSingleFieldBlocs1 = GroupFieldBlocImpl( - name: 'groupFieldBlocWithGroupAndSingleFieldBlocs', fieldBlocs: [ groupFieldWithSingleFieldBlocs2, textFieldBloc7, @@ -89,33 +76,25 @@ void main() { extraData: null, ); - final booleanFieldBloc1 = - BooleanFieldBloc(name: 'booleanFieldBloc1'); - final booleanFieldBloc2 = - BooleanFieldBloc(name: 'booleanFieldBloc2'); + final booleanFieldBloc1 = BooleanFieldBloc(); + final booleanFieldBloc2 = BooleanFieldBloc(); final fieldBlocListWithSingleFieldBlocs1 = ListFieldBloc( - name: 'fieldBlocListWithSingleFieldBlocs1', fieldBlocs: [ booleanFieldBloc1, booleanFieldBloc2, ], ); - final selectFieldBloc1 = - SelectFieldBloc(name: 'selectFieldBloc1'); - final multiSelectFieldBloc1 = - MultiSelectFieldBloc(name: 'multiSelectFieldBloc1'); + final selectFieldBloc1 = SelectFieldBloc(); + final multiSelectFieldBloc1 = MultiSelectFieldBloc(); - final multiSelectFieldBloc3 = - MultiSelectFieldBloc(name: 'booleanFieldBloc3'); - final textFieldBloc9 = TextFieldBloc(name: 'textFieldBloc9'); - final inputFieldBloc1 = InputFieldBloc( - name: 'inputFieldBloc1', initialValue: null); + final multiSelectFieldBloc3 = MultiSelectFieldBloc(); + final textFieldBloc9 = TextFieldBloc(); + final inputFieldBloc1 = InputFieldBloc(initialValue: null); final groupFieldBlocWithAll1 = GroupFieldBlocImpl( - name: 'groupFieldBlocWithAll1', fieldBlocs: [ selectFieldBloc1, multiSelectFieldBloc1, @@ -128,7 +107,6 @@ void main() { ); final fieldBlocListWithAll1 = ListFieldBloc( - name: 'fieldBlocListWithAll1', fieldBlocs: [ selectFieldBloc1, multiSelectFieldBloc1, @@ -140,7 +118,6 @@ void main() { ); final fieldBlocListWithAll2 = ListFieldBloc( - name: 'fieldBlocListWithAll2', fieldBlocs: [ selectFieldBloc1, multiSelectFieldBloc1, @@ -152,7 +129,6 @@ void main() { ); final fieldBlocListWithAll3 = ListFieldBloc( - name: 'fieldBlocListWithAll3', fieldBlocs: [ fieldBlocListWithAll1, fieldBlocListWithAll2, @@ -232,8 +208,7 @@ void main() { }); test('Empty FieldBlocList', () { - final fieldBlocList21 = - ListFieldBloc(name: 'list21'); + final fieldBlocList21 = ListFieldBloc(); final fieldBlocs = [ fieldBlocList21, @@ -251,7 +226,6 @@ void main() { test('FieldBlocList with SingleFieldBlocs', () { final fieldBlocList21 = ListFieldBloc( - name: 'list21', fieldBlocs: [ textFieldBloc1, booleanFieldBloc1, @@ -321,7 +295,6 @@ void main() { test('FieldBlocList with GroupFieldBlocs', () { final fieldBlocList21 = ListFieldBloc( - name: 'list21', fieldBlocs: [ groupFieldWithSingleFieldBlocs1, ], @@ -345,34 +318,32 @@ void main() { }); }); - final booleanFieldBloc1 = BooleanFieldBloc(name: 'boolean1'); - final textFieldBloc1 = - TextFieldBloc(name: 'textFieldBloc1', initialValue: 'text1'); + final booleanFieldBloc1 = BooleanFieldBloc(); + final textFieldBloc1 = TextFieldBloc(initialValue: 'text1'); - final booleanFieldBloc2 = BooleanFieldBloc(name: 'boolean2'); - final textFieldBloc2 = - TextFieldBloc(name: 'textFieldBloc2', initialValue: 'text2'); + final booleanFieldBloc2 = BooleanFieldBloc(); + final textFieldBloc2 = TextFieldBloc(initialValue: 'text2'); - final groupFieldBloc1 = GroupFieldBlocImpl( - name: 'group1', fieldBlocs: [booleanFieldBloc1], extraData: null); + final groupFieldBloc1 = + GroupFieldBlocImpl(fieldBlocs: [booleanFieldBloc1], extraData: null); final fieldBlocList1 = ListFieldBloc( - name: 'list1', fieldBlocs: [booleanFieldBloc1, textFieldBloc1]); + fieldBlocs: [booleanFieldBloc1, textFieldBloc1]); final fieldBlocList2 = ListFieldBloc( - name: 'list2', fieldBlocs: [booleanFieldBloc2, textFieldBloc2]); + fieldBlocs: [booleanFieldBloc2, textFieldBloc2]); final fieldBlocList3 = ListFieldBloc( - name: 'list3', fieldBlocs: [fieldBlocList1, fieldBlocList2]); + fieldBlocs: [fieldBlocList1, fieldBlocList2]); - final groupFieldBloc2 = GroupFieldBlocImpl( - name: 'group2', fieldBlocs: [fieldBlocList3], extraData: null); + final groupFieldBloc2 = + GroupFieldBlocImpl(fieldBlocs: [fieldBlocList3], extraData: null); - final groupFieldBloc3 = GroupFieldBlocImpl( - name: 'group3', fieldBlocs: [groupFieldBloc2], extraData: null); + final groupFieldBloc3 = + GroupFieldBlocImpl(fieldBlocs: [groupFieldBloc2], extraData: null); - final fieldBlocList4 = ListFieldBloc( - name: 'list4', fieldBlocs: [groupFieldBloc3]); + final fieldBlocList4 = + ListFieldBloc(fieldBlocs: [groupFieldBloc3]); group('getFieldBlocFromPath', () { test('First name of path is a SingleFieldBloc', () { final fieldBlocs = { @@ -799,7 +770,7 @@ void main() { test('SingleFieldBloc', () async { final formBloc = FormBlocImpl(); - final booleanFieldBloc = BooleanFieldBloc(name: ''); + final booleanFieldBloc = BooleanFieldBloc(); FormBlocUtils.updateFormBloc( fieldBlocs: [booleanFieldBloc], @@ -843,7 +814,7 @@ void main() { test('ListFieldBloc', () async { final formBloc = FormBlocImpl(); - final listFieldBloc = ListFieldBloc(name: ''); + final listFieldBloc = ListFieldBloc(); FormBlocUtils.updateFormBloc( fieldBlocs: [listFieldBloc], @@ -878,7 +849,7 @@ void main() { final formBloc = FormBlocImpl(); final listFieldBloc = - GroupFieldBlocImpl(name: '', fieldBlocs: [], extraData: null); + GroupFieldBlocImpl(fieldBlocs: [], extraData: null); FormBlocUtils.updateFormBloc( fieldBlocs: [listFieldBloc], diff --git a/packages/form_bloc/test/text_field/text_field_state_test.dart b/packages/form_bloc/test/text_field/text_field_state_test.dart index d727cff7..58c96e3e 100644 --- a/packages/form_bloc/test/text_field/text_field_state_test.dart +++ b/packages/form_bloc/test/text_field/text_field_state_test.dart @@ -20,7 +20,6 @@ void main() { isDirty: false, isValidated: false, isValidating: false, - name: 'fieldName', ); expectState( @@ -45,7 +44,6 @@ void main() { isDirty: true, isValidated: true, isValidating: true, - name: 'fieldName', ), ); }); @@ -61,7 +59,6 @@ void main() { isDirty: true, isValidated: true, isValidating: true, - name: 'fieldName', ); expectState( @@ -86,7 +83,6 @@ void main() { isDirty: false, isValidated: false, isValidating: false, - name: 'fieldName', ), ); }); @@ -102,7 +98,6 @@ void main() { isDirty: true, isValidated: true, isValidating: true, - name: 'fieldName', ); expectState(