CodeBlanks Logo Icon CodeBlanks

Summary - Bloc advanced

Combine Bloc, event and state to make a login flow.

Login flow

In a login flow, Bloc, event and state work together. When the user presses the button, LoginSubmittedEvent is triggered; the Bloc receives it, first emits LoginLoading, then on completion transitions to LoginSuccess or LoginFailure. The UI uses BlocBuilder to show different widgets for each state.

class LoginBloc extends Bloc<LoginEvent, LoginState> {
  LoginBloc() : super(LoginInitial()) {
    on<LoginSubmittedEvent>((event, emit) async {
      emit(LoginLoading());
      await Future.delayed(Duration(seconds: 1));
      emit(LoginSuccess('Ayşe'));
    });
  }
}

Practice Write a complete login Bloc

Define LoginEvent and LoginSubmittedEvent. Write the LoginState abstract class with LoginInitial, LoginLoading, and LoginSuccess (holds a username) subclasses. LoginBloc should start with LoginInitial; its LoginSubmittedEvent handler should first emit LoginLoading, then after a short delay emit LoginSuccess('Ayşe'). In main, trigger the event, wait, and print the username on success.

Starter code

main.dart
import 'package:bloc/bloc.dart';

// LoginEvent soyut sınıfı
// LoginSubmittedEvent alt sınıfı

// LoginState soyut sınıfı
// LoginInitial, LoginLoading, LoginSuccess alt sınıfları
// LoginSuccess: kullanıcı adı tutar

// LoginBloc sınıfını yaz
// başlangıç state LoginInitial
// event işleyici: önce Loading, sonra Success

void main() async {
  // oluştur, event ekle, bekle, durumu yazdır
}

Sample solution

main.dart
import 'package:bloc/bloc.dart';

abstract class LoginEvent {}
class LoginSubmittedEvent extends LoginEvent {}

abstract class LoginState {}
class LoginInitial extends LoginState {}
class LoginLoading extends LoginState {}
class LoginSuccess extends LoginState {
  final String user;
  LoginSuccess(this.user);
}

class LoginBloc extends Bloc<LoginEvent, LoginState> {
  LoginBloc() : super(LoginInitial()) {
    on<LoginSubmittedEvent>((event, emit) async {
      emit(LoginLoading());
      await Future.delayed(Duration(milliseconds: 10));
      emit(LoginSuccess('Ayşe'));
    });
  }
}

void main() async {
  final bloc = LoginBloc();
  bloc.add(LoginSubmittedEvent());
  await Future.delayed(Duration(milliseconds: 50));
  final state = bloc.state;
  if (state is LoginSuccess) {
    print(state.user);
  }
}

Expected output

Ayşe

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 What happens when the button is pressed in a login flow?

  • The state becomes LoginSuccess immediately
  • LoginSubmittedEvent is triggered and the Bloc processes it Correct answer
  • BlocBuilder does not rebuild
  • The event class is removed from memory

Explanation: When the button is pressed, a LoginSubmittedEvent is created and sent to the Bloc via bloc.add; the Bloc processes the event and produces state.

2 Emit loading state

Question
on<LoginSubmittedEvent>((event, emit) async {
  ____(LoginLoading());
  await Future.delayed(Duration(milliseconds: 10));
  emit(LoginSuccess('Ayşe'));
});
Answer
on<LoginSubmittedEvent>((event, emit) async {
  emit(LoginLoading());
  await Future.delayed(Duration(milliseconds: 10));
  emit(LoginSuccess('Ayşe'));
});

Explanation: Inside a handler you call emit to produce a new state; add is for adding events and is meaningless inside a handler.

3 Login flow parts

Term Meaning
LoginSubmittedEvent User intent
LoginLoading Waiting state
LoginSuccess Success state
on<LoginSubmittedEvent> Event handler

All lessons in Advanced