doc(auth): update pubspec and readme
This commit is contained in:
parent
762b9bcd11
commit
33ac5c6280
@ -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.
|
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user