doc(auth): update pubspec and readme

This commit is contained in:
Hugo Pointcheval 2022-11-11 16:54:58 -05:00
parent 762b9bcd11
commit 33ac5c6280
Signed by: hugo
GPG Key ID: A9E8E9615379254F
2 changed files with 28 additions and 271 deletions

View File

@ -29,43 +29,28 @@ Authentication Bloc for Flutter.
## Features ## Features
- UserInterface - Wyatt Architecture
* UserFirebase : FirebaseAuth user implementation - Entities:
- AuthenticationRepositoryInterface - Account : AccountModel -> Contains account information from provider
* AuthenticationRepositoryFirebase : FirebaseAuth implementation - AccountWrapper : AccountWrapperModel -> Contains account and extra data.
- ExceptionsInterface - Data Sources:
* ExceptionsFirebase : FirebaseAuth Exception parsing implementation - Local:
- AuthenticationBloc - Cached Authentication Data : AuthenticationCacheDataSourceImpl -> Provides a cache implementation
* Tracks every user changes - Remote:
- Right after the listener has been registered. - Remote Authentication Data : AuthenticationFirebaseDataSourceImpl -> Provides a proxy to FirebaseAuth
- When a user is signed in. - Repositories:
- When the current user is signed out. - AuthenticationRepository : AuthenticationRepositoryImpl -> Provides all authentication methods
- When there is a change in the current user's token. - Features:
- On `refresh()` - Authentication:
* Start/Stop listening on demand - AuthenticationBuilder : widget to build reactive view from authentication state
- `start()` to listen to user changes - AuthenticationCubit : tracks every auth changes, have sign out capability.
- `stop()` to cancel listener - SignUp:
- SignUpCubit - SignUpCubit: implementation of a FormDataCubit from `wyatt_form_bloc` for the sign up
* Handles email/password validation and password confirmation - SignIn:
* Handles register with email/password - SignUpCubit: implementation of a FormDataCubit from `wyatt_form_bloc` for the sign in
* 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
- Consistent - Consistent
* Every class have same naming convention * Every class have same naming convention
- Tested - Tested
* Partially tested with *bloc_test*
## Getting started ## Getting started
@ -77,227 +62,4 @@ import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
## Usage ## Usage
Create an authentication repository: // TODO
```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<AuthenticationRepositoryInterface>(
create: (context) => _authenticationRepository,
),
],
child: MultiBlocProvider(
providers: [
BlocProvider<AuthenticationCubit>(
create: (context) => _authenticationCubit..init(),
),
BlocProvider<SignUpCubit>(
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<AuthenticationRepositoryInterface>()),
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<SignUpCubit, SignUpState>(
builder: (context, state) {
return TextField(
onChanged: (confirmPassword) => context
.read<SignUpCubit>()
.dataChanged(
'form_field_confirmPassword',
ConfirmedPassword.dirty(
password: context.read<SignUpCubit>().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<SignUpCubit, SignUpState>(
builder: (context, state) {
return TextField(
onChanged: (password) {
context.read<SignUpCubit>().passwordChanged(password);
context.read<SignUpCubit>().dataChanged(
'form_field_confirmPassword',
ConfirmedPassword.dirty(
password: password,
value: context
.read<SignUpCubit>()
.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<void> 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<SignUpCubit, SignUpState>(
builder: (context, state) {
return TextField(
onChanged: (phone) => context
.read<SignUpCubit>()
.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<SignUpCubit>().signUpFormSubmitted()
```
And voilà, a document with `uid` as id, and fields `email`, `name`, `phone`, `uid` will be create in `users` collection.

View File

@ -12,7 +12,7 @@ environment:
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
crypto: ^3.0.2 crypto: ^3.0.2
flutter_bloc: ^8.1.1 flutter_bloc: ^8.1.1
equatable: ^2.0.5 equatable: ^2.0.5
@ -23,28 +23,23 @@ dependencies:
twitter_login: ^4.2.3 twitter_login: ^4.2.3
wyatt_form_bloc: wyatt_form_bloc:
git: hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages version: 0.1.0+1
ref: wyatt_form_bloc-v0.1.0+1
path: packages/wyatt_form_bloc
wyatt_architecture: wyatt_architecture:
git: hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages version: 0.0.2
ref: wyatt_architecture-v0.0.2-dev.0
path: packages/wyatt_architecture
wyatt_type_utils: wyatt_type_utils:
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/ hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
version: 0.0.3+1 version: 0.0.3+1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
bloc_test: ^9.1.0 bloc_test: ^9.1.0
mocktail: ^0.3.0 mocktail: ^0.3.0
wyatt_analysis: wyatt_analysis:
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/ hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
version: 2.2.2 version: 2.2.2