diff --git a/packages/wyatt_authentication_bloc/README.md b/packages/wyatt_authentication_bloc/README.md index 4d1a1e6d..755c77e0 100644 --- a/packages/wyatt_authentication_bloc/README.md +++ b/packages/wyatt_authentication_bloc/README.md @@ -29,43 +29,28 @@ Authentication Bloc for Flutter. ## Features -- UserInterface - * UserFirebase : FirebaseAuth user implementation -- AuthenticationRepositoryInterface - * AuthenticationRepositoryFirebase : FirebaseAuth implementation -- ExceptionsInterface - * ExceptionsFirebase : FirebaseAuth Exception parsing implementation -- AuthenticationBloc - * Tracks every user changes - - Right after the listener has been registered. - - When a user is signed in. - - When the current user is signed out. - - When there is a change in the current user's token. - - On `refresh()` - * Start/Stop listening on demand - - `start()` to listen to user changes - - `stop()` to cancel listener -- SignUpCubit - * Handles email/password validation and password confirmation - * Handles register with email/password - * Handles custom form fields thanks `wyatt_form_bloc` - - Use `entries` to pass a `FormData` object - - You can use several pre configured `FormInput` for validation - - You can use `updateFormData()` to change FormData and validators during runtime (intersection, union, difference or replace) -- SignInCubit - * Handles email/password validation - * Handles login with email/password -- EmailVerificationCubit - * Handles send email verification process - * Handles email verification check -- PasswordResetCubit - * Handles send password reset email process -- Builders - * AuthenticationBuilder to build widgets on user state changes +- Wyatt Architecture +- Entities: + - Account : AccountModel -> Contains account information from provider + - AccountWrapper : AccountWrapperModel -> Contains account and extra data. +- Data Sources: + - Local: + - Cached Authentication Data : AuthenticationCacheDataSourceImpl -> Provides a cache implementation + - Remote: + - Remote Authentication Data : AuthenticationFirebaseDataSourceImpl -> Provides a proxy to FirebaseAuth +- Repositories: + - AuthenticationRepository : AuthenticationRepositoryImpl -> Provides all authentication methods +- Features: + - Authentication: + - AuthenticationBuilder : widget to build reactive view from authentication state + - AuthenticationCubit : tracks every auth changes, have sign out capability. + - SignUp: + - SignUpCubit: implementation of a FormDataCubit from `wyatt_form_bloc` for the sign up + - SignIn: + - SignUpCubit: implementation of a FormDataCubit from `wyatt_form_bloc` for the sign in - Consistent * Every class have same naming convention - Tested - * Partially tested with *bloc_test* ## Getting started @@ -77,227 +62,4 @@ import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart'; ## Usage -Create an authentication repository: - -```dart -final AuthenticationRepositoryInterface _authenticationRepository = AuthenticationRepositoryFirebase(); -``` - -Create an authentication cubit: - -```dart -final AuthenticationCubit _authenticationCubit = AuthenticationCubit( - authenticationRepository: _authenticationRepository, -); -``` - -Create a sign up cubit: - -```dart -final SignUpCubit _signUpCubit = SignUpCubit( - authenticationRepository: _authenticationRepository, - authenticationCubit: _authenticationCubit, -); -``` - -You can use `AuthenticationBloc` to route your app. - -```dart -return MultiRepositoryProvider( - providers: [ - RepositoryProvider( - create: (context) => _authenticationRepository, - ), - ], - child: MultiBlocProvider( - providers: [ - BlocProvider( - create: (context) => _authenticationCubit..init(), - ), - BlocProvider( - create: (context) => _signUpCubit, - ), - ], - child: const AppView(), - ), -); -``` -> Don't forget to call `init()` on authentication cubit. - -And in `AppView` use an `AuthenticationBuilder`: - -```dart -AuthenticationBuilder( - unknown: (context) => const LoadingPage(), - unauthenticated: (context) => const LoginPage(), - authenticated: (context, user, userData) => const HomePage(), -) -``` - -To create a `SignInCubit` you'll need the same `AuthenticationRepository`, you can use the `context`: - -```dart -BlocProvider( - create: (_) => SignInCubit(context.read()), - child: const LoginForm(), -), -``` - -> In practice it's better to create it in the main `MultiBlocProvider` because the LoginPage can be destroyed, and cubit closed, before login flow ends - -## Recipes - -### Password confirmation - -In this recipe we'll se how to create a custom `FormEntry` to confirm password. - -First, create an entry at the SignUpCubit creation: - -```dart -SignUpCubit _signUpCubit = SignUpCubit( - authenticationRepository: _authenticationRepository, - authenticationCubit: _authenticationCubit, - entries: const FormData([ - FormEntry('form_field_confirmPassword', ConfirmedPassword.pure()), - ]), -); -``` - -Then, in the sign up form, create an input for password confirmation: - -- `ConfirmedPassword` validator need password value and confirm password value to compare. - -```dart -return BlocBuilder( - builder: (context, state) { - return TextField( - onChanged: (confirmPassword) => context - .read() - .dataChanged( - 'form_field_confirmPassword', - ConfirmedPassword.dirty( - password: context.read().state.password.value, - value: confirmPassword, - ), - ), - obscureText: true, - decoration: InputDecoration( - labelText: 'confirm password', - errorText: state.data!.input('form_field_confirmPassword').invalid - ? 'passwords do not match' - : null, - ), - ); - }, -); -``` - -> `form_field_confirmPassword` is the field identifier used in all application to retrieve data. You can use a constant to avoid typos. - -You'll need to update password input to update confirm state on password update ! - -```dart -return BlocBuilder( - builder: (context, state) { - return TextField( - onChanged: (password) { - context.read().passwordChanged(password); - context.read().dataChanged( - 'form_field_confirmPassword', - ConfirmedPassword.dirty( - password: password, - value: context - .read() - .state - .data! - .input('form_field_confirmPassword') - .value, - ), - ); - }, - obscureText: true, - decoration: InputDecoration( - labelText: 'password', - errorText: state.password.invalid ? 'invalid password' : null, - ), - ); - }, -); -``` - -> Here you call standard `passwordChanged()` AND `dataChanged()`. - -And voilĂ  ! - -### Create Firestore Document on Sign Up - -In this recipe we'll se how to create a Firestore Document on sign up success. - -First create a callback function: - -```dart -Future onSignUpSuccess(SignUpState state, String? uid) async { - if (uid != null) { - final user = { - 'uid': uid, - 'email': state.email.value, - ...state.data.toMap(), - }; - await FirebaseFirestore.instance.collection('users').doc(uid).set(user); - } -} -``` - -Then create SignUpCubit with custom entries and register callback: - -```dart -SignUpCubit _signUpCubit = SignUpCubit( - authenticationRepository: _authenticationRepository, - authenticationCubit: _authenticationCubit, - entries: const FormData([ - FormEntry('form_field_name', Name.pure(), fieldName: 'name'), - FormEntry('form_field_phone', Phone.pure(), fieldName: 'phone'), - FormEntry('form_field_confirmPassword', ConfirmedPassword.pure(), export: false), - ]), - onSignUpSuccess: onSignUpSuccess, -); -``` - -> Use `fieldName` and `export` to control `.toMap()` result on FormData ! Useful to disable exportation of sensible data like passwords. - -Create widgets for each inputs: - -```dart -class _PhoneInput extends StatelessWidget { - @override - Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - return TextField( - onChanged: (phone) => context - .read() - .dataChanged('form_field_phone', Phone.dirty(phone)), - keyboardType: TextInputType.phone, - decoration: InputDecoration( - labelText: 'phone', - helperText: '', - errorText: state.data!.input('form_field_phone').invalid - ? 'invalid phone' - : null, - ), - ); - }, - ); - } -} -``` - -> Create widgets for Name and ConfirmedPassword too. - -Then add a sign up button with: - -```dart -context.read().signUpFormSubmitted() -``` - -And voilĂ , a document with `uid` as id, and fields `email`, `name`, `phone`, `uid` will be create in `users` collection. \ No newline at end of file +// TODO \ No newline at end of file diff --git a/packages/wyatt_authentication_bloc/pubspec.yaml b/packages/wyatt_authentication_bloc/pubspec.yaml index faa7f1e0..d541d3ad 100644 --- a/packages/wyatt_authentication_bloc/pubspec.yaml +++ b/packages/wyatt_authentication_bloc/pubspec.yaml @@ -12,7 +12,7 @@ environment: dependencies: flutter: sdk: flutter - + crypto: ^3.0.2 flutter_bloc: ^8.1.1 equatable: ^2.0.5 @@ -23,28 +23,23 @@ dependencies: twitter_login: ^4.2.3 wyatt_form_bloc: - git: - url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages - ref: wyatt_form_bloc-v0.1.0+1 - path: packages/wyatt_form_bloc - + hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/ + version: 0.1.0+1 + wyatt_architecture: - git: - url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages - ref: wyatt_architecture-v0.0.2-dev.0 - path: packages/wyatt_architecture - + hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/ + version: 0.0.2 + wyatt_type_utils: hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/ version: 0.0.3+1 - dev_dependencies: flutter_test: sdk: flutter bloc_test: ^9.1.0 mocktail: ^0.3.0 - + wyatt_analysis: hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/ version: 2.2.2