Question #237MediumState Management

Explain about the BLOC data flow ?

#bloc

Answer

Overview

BLoC (Business Logic Component) is a design pattern where UI events flow into the BLoC, business logic runs, and new states flow back to the UI — in a strictly unidirectional data flow.


BLoC Data Flow

text
UI Widget
   │ dispatch Event (e.g., LoginButtonPressed)
Bloc.add(event)
   │ mapEventToState / on<Event>() handler
Business Logic (API calls, validation, transformations)
   │ emit(newState)
State (e.g., LoginLoading, LoginSuccess, LoginFailure)
   │ BlocBuilder / BlocListener listens
UI Widget rebuilds

Complete BLoC Example — Login

1. Events (User Actions)

dart
abstract class LoginEvent {}

class LoginSubmitted extends LoginEvent {
  final String email;
  final String password;
  LoginSubmitted({required this.email, required this.password});
}

class LogoutRequested extends LoginEvent {}

2. States (UI States)

dart
abstract class LoginState {}

class LoginInitial extends LoginState {}
class LoginLoading extends LoginState {}

class LoginSuccess extends LoginState {
  final User user;
  LoginSuccess(this.user);
}

class LoginFailure extends LoginState {
  final String error;
  LoginFailure(this.error);
}

3. BLoC (Business Logic)

dart
import 'package:flutter_bloc/flutter_bloc.dart';

class LoginBloc extends Bloc<LoginEvent, LoginState> {
  final AuthRepository _authRepo;

  LoginBloc(this._authRepo) : super(LoginInitial()) {
    on<LoginSubmitted>(_onLoginSubmitted);
    on<LogoutRequested>(_onLogoutRequested);
  }

  Future<void> _onLoginSubmitted(
    LoginSubmitted event,
    Emitter<LoginState> emit,
  ) async {
    emit(LoginLoading());
    try {
      final user = await _authRepo.signIn(event.email, event.password);
      emit(LoginSuccess(user));
    } catch (error) {
      emit(LoginFailure(error.toString()));
    }
  }

  void _onLogoutRequested(LogoutRequested event, Emitter<LoginState> emit) {
    _authRepo.signOut();
    emit(LoginInitial());
  }
}

4. Provide BLoC

dart
BlocProvider(
  create: (context) => LoginBloc(AuthRepository()),
  child: LoginScreen(),
)

5. UI — Consume BLoC

dart
class LoginScreen extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return BlocListener<LoginBloc, LoginState>(
      listener: (context, state) {
        // Side effects (navigation, snackbar)
        if (state is LoginSuccess) {
          Navigator.pushReplacementNamed(context, '/home');
        }
        if (state is LoginFailure) {
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(content: Text(state.error)),
          );
        }
      },
      child: BlocBuilder<LoginBloc, LoginState>(
        builder: (context, state) {
          if (state is LoginLoading) return CircularProgressIndicator();

          return Column(
            children: [
              EmailField(),
              PasswordField(),
              ElevatedButton(
                onPressed: () {
                  context.read<LoginBloc>().add(
                    LoginSubmitted(email: _email, password: _password),
                  );
                },
                child: Text('Login'),
              ),
            ],
          );
        },
      ),
    );
  }
}

BLoC Widgets Summary

WidgetPurpose
text
BlocProvider
Create and provide BLoC
text
BlocBuilder
Rebuild UI on state change
text
BlocListener
Side effects (navigation, snackbar)
text
BlocConsumer
Both Builder + Listener in one
text
MultiBlocProvider
Provide multiple BLoCs
text
context.read<B>()
Get BLoC without listening
text
context.watch<B>()
Get BLoC and rebuild on change

Key Principle: Events go IN → Business Logic runs → States come OUT. The UI never modifies state directly — it only dispatches events.