Introduction — Why Should You Care?
When flutter apps grow, using setState() everywhere can turn your code into a tangled mess.
Bloc (Business Logic Component) fixes that by separating what happens (events) from what you show (states), keeping your code clean and testable.
How the Bloc works?
- You do something — like tap a button or scroll the page.
 - This action creates an Event — a message that says “something happened.”
 - The Bloc gets the event and does the work — maybe call an API, check data, or run some calculations.
 - When the work is done, the Bloc sends out a new State — a message about what the app should look like now.
 - The UI is always listening, so when the state changes, the screen updates automatically.
 
Code Example with Bloc
Now we show here full login with bloc. Let’s start,
1️⃣ Events:
Here, we created two events — one for login and one for logout. Remember, events represent actions taken by the user.
// auth_event.dart
abstract class AuthEvent {}
class LoginRequested extends AuthEvent {
 final String email;
 final String password;
LoginRequested(this.email, this.password);
}
class LogoutRequested extends AuthEvent {}
2️⃣ States
Next, we create the states. In this example, we have three: Loading, Loaded, and Error. You can also add your own, like LoadedDone, if your app requires it.
// auth_state.dart
abstract class AuthState {}
class AuthInitial extends AuthState {}
class AuthLoading extends AuthState {}
class Authenticated extends AuthState {
 final String userId;
 Authenticated(this.userId);
}
class AuthError extends AuthState {
 final String message;
 AuthError(this.message);
}
3️⃣ The Bloc
The Bloc class takes in a user action (event) and outputs the appropriate data (state).
// auth_bloc.dart
import 'package:flutter_bloc/flutter_bloc.dart';
import 'auth_event.dart';
import 'auth_state.dart';
import '../repositories/auth_repository.dart';
class AuthBloc extends Bloc<AuthEvent, AuthState> {
 final AuthRepository authRepository;
AuthBloc(this.authRepository) : super(AuthInitial()) {
 on<LoginRequested>(_onLoginRequested);
 on<LogoutRequested>(_onLogoutRequested);
 }
Future<void> _onLoginRequested(
 LoginRequested event,
 Emitter<AuthState> emit,
 ) async {
 emit(AuthLoading());
 try {
 final userId = await authRepository.login(event.email, event.password);
 emit(Authenticated(userId));
 } catch (e) {
 emit(AuthError(e.toString()));
 }
 }
void _onLogoutRequested(
 LogoutRequested event,
 Emitter<AuthState> emit,
 ) {
 authRepository.logout();
 emit(AuthInitial());
 }
}
4️⃣ Using It in UI
This is the UI section. Here, we take the state returned from the Bloc and show it in a ListView.builder.
BlocProvider(
 create: (context) => AuthBloc(context.read<AuthRepository>()),
 child: LoginScreen(),
);
// Inside LoginScreen:
BlocBuilder<AuthBloc, AuthState>(
 builder: (context, state) {
 if (state is AuthLoading) return CircularProgressIndicator();
 if (state is Authenticated) return Text("Welcome, ${state.userId}");
 if (state is AuthError) return Text("Error: ${state.message}");
 return LoginForm();
 },
)
Why Use Bloc?
Separation of Concerns: Keeps business logic out of the UI, making the app easier to maintain.
Testability: Each layer (Bloc, Event, State) can be tested independently.
Scalability: Well-suited for large apps where managing state becomes complex.
Consistency: Provides a predictable state flow, making debugging easier.
What challenges have you faced with Bloc? Share your experiences or questions in the comments!

    
Top comments (0)