wyatt-packages/packages/wyatt_authentication_bloc

Authentication BLoC

Style: Wyatt Analysis SDK: Flutter

Authentication Bloc for Flutter.

Features

  • 🧐 Wyatt Architecture
  • 🧱 Entities
    • Account -> Contains account information from provider.
    • Session -> Contains account and associated data retrieved from an external source.
    • AuthenticationChangeEvent -> Describes an event in authentication change (sign in, sign up, sign out, etc...)
    • SessionWrapper -> Contains latest authentication change event and session.
  • 🔑 Powerful and secured authentication repository
  • 🔥 Multiple data sources
    • Mock
    • Firebase
  • 🧊 Cubits, why make it complicated when you can make it simple?
    • Goes to the essential.
  • 📐 Consistent
    • Every class have same naming convention
  • 🧪 Tested
  • 📚 Documented: available here

Getting started

Simply add wyatt_authentication_bloc in pubspec.yaml , then

import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';

Data source

The first step is to provide a data source.

getIt.registerLazySingleton<AuthenticationRemoteDataSource<int>>(
    () => AuthenticationFirebaseDataSourceImpl<int>(
        firebaseAuth: FirebaseAuth.instance,
        googleSignIn:
            GoogleSignIn(clientId: DefaultFirebaseOptions.ios.iosClientId)),
);

Here we use GetIt (see example project)

Repository

Then you can configure your repository.

final AuthenticationRepository<int> authenticationRepository = AuthenticationRepositoryImpl(
    authenticationRemoteDataSource:
        getIt<AuthenticationRemoteDataSource<int>>(),
    customPasswordValidator: const CustomPassword.pure(),
    extraSignUpInputs: [
        FormInput(
        AuthFormField.confirmPassword,
        const ConfirmedPassword.pure(),
        metadata: const FormInputMetadata<void>(export: false),
        ),
    ],
);

Here we pass some extra inputs for the sign up, and a custom password validator.

Cubits

It is necessary to implement each cubit. Don't panic, most of the work is already done 😊 you just have to customize the logic of these.

In each of these cubits it is necessary to overload the various callbacks.

Here the associated data Data is a int

Authentication

In the authentication are managed, the refresh, the deletion of account or the disconnection.

class ExampleAuthenticationCubit extends AuthenticationCubit<int> {
  ExampleAuthenticationCubit({required super.authenticationRepository});

  @override
  FutureOrResult<int?> onReauthenticate(Result<Account, AppException> result) async {
    // TODO
  }

  @override
  FutureOrResult<int?> onRefresh(Result<Account, AppException> result) {
    // TODO
  }

  @override
  FutureOrResult<int?> onSignInFromCache(SessionWrapper<int> wrapper) {
    // TODO
  }

  @override
  FutureOrResult<void> onSignOut() {
    // TODO
  }

  @override
  FutureOrResult<void> onDelete() {
    // TODO
  }
}

Sign Up

class ExampleSignUpCubit extends SignUpCubit<int> {
  ExampleSignUpCubit({
    required super.authenticationRepository,
  });

  @override
  FutureOrResult<int?> onSignUpWithEmailAndPassword(Result<Account, AppException> result, WyattForm form) async {
    // TODO
  }
}

Sign In

class ExampleSignInCubit extends SignInCubit<int> {
  ExampleSignInCubit({
    required super.authenticationRepository,
  });

  @override
  FutureOrResult<int?> onSignInWithEmailAndPassword(Result<Account, AppException> result, WyattForm form) {
    // TODO
  }

  @override
  FutureOrResult<int?> onSignInAnonymously(Result<Account, AppException> result, WyattForm form) {
    // TODO
  }

  @override
  FutureOrResult<int?> onSignInWithGoogle(Result<Account, AppException> result, WyattForm form) {
    // TODO
  }
}

After setting up all these cubits you can provide them in the application. And that's it!

BlocProvider<SignUpCubit<int>>(
    create: (_) => ExampleSignUpCubit(
        authenticationRepository: authenticationRepository,
    ),
),
BlocProvider<SignInCubit<int>>(
    create: (_) => ExampleSignInCubit(
        authenticationRepository: authenticationRepository,
    ),
),

Widgets

Widgets are provided to make your life easier. Starting with the AuthenticationBuilder which allows you to build according to the authentication state.

AuthenticationBuilder<int>(
    authenticated: (context, sessionWrapper) => Text(
        'Logged as ${sessionWrapper.session?.account.email} | GeneratedId is ${sessionWrapper.session?.data}'),
    unauthenticated: (context) =>
        const Text('Not logged (unauthenticated)'),
    unknown: (context) => const Text('Not logged (unknown)'),
),

A BuildContext extension is also available to access certain attributes more quickly.

Text('Home | ${context.account<AuthenticationCubit<int>, int>()?.email}'),

Listeners are used to listen to the status of the sign in and sign up forms.

return SignInListener<int>(
    onError: (context, status, errorMessage) => ScaffoldMessenger.of(context)
    ..hideCurrentSnackBar()
    ..showSnackBar(
        SnackBar(content: Text(errorMessage ?? 'Sign In Failure')),
    ),
    child: ...
);