The IAuthFacade
abstraction is in place after the previous part. Using this interface allows us to write sign-in form application logic without having a clue about how the authentication is actually implemented. Let's model how the UI will communicate with the BLoC using states and events.
Bloc overview
As shown on the diagram, a BLoC receives events from the UI which contain raw data and outputs states which contain validated data, among other things. Let's now create the classes for the SignInFormBloc
.
Let's add flutter_bloc as a dependency.
pubspec.yaml
dependencies:
...
flutter_bloc: ^3.2.0
Create new directories so that the path application/auth/sign_in_form exists. Using the extensions mentioned above, create a a Bloc with the name "sign_in_form".

Folder contents
Events
What possible events can occur in the UI of the sign-in form? Apart from the different sign-in buttons being pressed, there are also two other events which are usually handled directly in the UI unless you're following DDD - email and password input changes. Expressed as a union, the events will look like:
sign_in_form_event.dart
part of 'sign_in_form_bloc.dart';
@freezed
abstract class SignInFormEvent with _$SignInFormEvent {
// Notice that these events take in "raw" unvalidated Strings
const factory SignInFormEvent.emailChanged(String emailStr) = EmailChanged;
const factory SignInFormEvent.passwordChanged(String passwordStr) =
PasswordChanged;
const factory SignInFormEvent.registerWithEmailAndPasswordPressed() =
RegisterWithEmailAndPasswordPressed;
const factory SignInFormEvent.signInWithEmailAndPasswordPressed() =
SignInWithEmailAndPasswordPressed;
const factory SignInFormEvent.signInWithGooglePressed() =
SignInWithGooglePressed;
}
part '*.freezed.dart'
statement here because of the part of
statement.State
BLoC usually outputs multiple subclasses (or union cases) of state. This is different with BLoCs responsible for validating UI data which have only a single state class.
What do we need to communicate back to the UI of the sign-in form?
1. Validated values
We surely want to pass back the validated EmailAddress
and Password
value objects to be able to show error messages in the TextFormField
s.
2. Auth progress
Showing a progress indicator is a no-brainer, so we have to also pass back a bool isSubmitting
.
3. Success or error backend response
To show an error Snackbar
when something goes wrong in the backend, we will need to pass back the Either<AuthFailure, Unit>
returned from the IAuthFacade
. We're going to call it authFailureOrSuccess
and you can think of it as of the auth backend "response".
However, there will initially be no response until the user presses a button. We could just initially assign null
to the authFailureOrSuccess
field but you know that this sucks.
A much better option would be to use an Option
🙃. Much like Either
, it's a union of two values - Some
and None
. It's a sort of a non-nullable type where null
gets replaced by the None
union case. Only the Some
union case holds a value which will be the Either<AuthFailure, Unit>
.
4. Whether or not to show input error messages
Lastly, we want to show the input validation error messages under the TextFormField
s only after the first press of a sign-in/register button. This will be communicated back to the UI inside a bool showErrorMessages
.
sign_in_form_state.dart
part of 'sign_in_form_bloc.dart';
@freezed
abstract class SignInFormState with _$SignInFormState {
const factory SignInFormState({
@required EmailAddress emailAddress,
@required Password password,
@required bool showErrorMessages,
@required bool isSubmitting,
@required Option<Either<AuthFailure, Unit>> authFailureOrSuccessOption,
}) = _SignInFormState;
factory SignInFormState.initial() => SignInFormState(
emailAddress: EmailAddress(''),
password: Password(''),
showErrorMessages: false,
isSubmitting: false,
authFailureOrSuccessOption: none(),
);
}
Bloc
While events and the state data class can be viewed as View Models which live in the presentation layer, the SignInFormBloc
class itself performs application logic, i.e. orchestrating the other layers to work together.
This is where the raw String
s turn into validated ValueObject
s and where the IAuthFacade
's methods are called. The logic performed here is focused on transforming incoming events into states.
File & class setup
Although we'll leave writing the logic for the next part, let's at least set the file up. Because of the changes we've done to the default events and states generated by the extension, we need to add the part
statement for freezed and change the initialState
. Additionally, we'll add a field for the IAuthFacade
dependency.
sign_in_form_bloc.dart
part 'sign_in_form_event.dart';
part 'sign_in_form_state.dart';
part 'sign_in_form_bloc.freezed.dart';
class SignInFormBloc extends Bloc<SignInFormEvent, SignInFormState> {
final IAuthFacade _authFacade;
SignInFormBloc(this._authFacade);
@override
SignInFormState get initialState => SignInFormState.initial();
@override
Stream<SignInFormState> mapEventToState(
SignInFormEvent event,
) async* {
// TODO: Implement
}
}
Now it's finally the time to run our most favorite command to kick off code generation.
👨💻 terminal
flutter pub run build_runner watch --delete-conflicting-outputs
Get ready for the logic
We took important steps in this part by modeling the events that can be performed by the UI and the state which is a way for the BLoC to communicate values back to the UI. With all of this in place, we will be able to jump right into writing the application logic.
hi tnx,
why dont you put all the event propertis in the bloc itself?
sorry for my english…
i mean somethong like this: https://pastebin.com/raw/cCHB2cjJ
It’s best to separate events, states and bloc logic in different files. [Clean Code]
Why do you stop releasing Firebase & DDD tutorials ? The finished project does not work.
funion snnipet not found.
The superclass ‘Bloc’ does not have a zero argument constructor
Please add an example how to do this with provider/riverpod!
Thank you!