Implementing BLoC
Implementing BLoC Patterns¶
The BLoC (Business Logic Component) pattern is a structured approach to managing state in Flutter apps by decoupling business logic from the UI. It uses streams to propagate state changes reactively, enabling predictable and testable code. At its core, BLoC relies on three components: events (user actions or external triggers), bloc (the logic that processes events), and states (the UI-relevant data emitted by the bloc). This pattern is particularly effective for complex apps requiring real-time updates or asynchronous operations.
Core BLoC Components¶
Events¶
Events represent user interactions or external triggers (e.g., button clicks, API calls). They are emitted as Stream<Event>, which the bloc subscribes to.
Example:
abstract class CounterEvent {}
class IncrementEvent extends CounterEvent {}
class DecrementEvent extends CounterEvent {}
Bloc¶
The bloc class contains the business logic to process events and emit states. It typically uses StreamController to manage event streams and state emissions.
Example:
class CounterBloc {
final _counterController = StreamController<int>();
Stream<int> get counterStream => _counterController.stream;
void increment() {
_counterController.add(_counterController.value + 1);
}
void decrement() {
_counterController.add(_counterController.value - 1);
}
void close() => _counterController.close();
}
States¶
States are the data emitted by the bloc to update the UI. They are typically immutable objects that represent the current application state.
Example:
Implementing a BLoC Class¶
To implement a BLoC, define a class that processes events and emits states. Use StreamController to manage the flow of data.
Example: A simple counter app
class CounterBloc {
final _eventController = StreamController<CounterEvent>();
final _stateController = StreamController<CounterState>();
Stream<CounterEvent> get eventStream => _eventController.stream;
Stream<CounterState> get stateStream => _stateController.stream;
void handleEvent(CounterEvent event) {
if (event is IncrementEvent) {
_stateController.add(CounterState(_stateController.value.count + 1));
} else if (event is DecrementEvent) {
_stateController.add(CounterState(_stateController.value.count - 1));
}
}
void close() {
_eventController.close();
_stateController.close();
}
}
Diagram:
Event-Driven State Transitions¶
BLoC relies on event streams to trigger state changes. Use StreamTransformer for complex logic, such as validating form inputs.
Example: Form validation
class LoginFormBloc {
final _eventController = StreamController<LoginEvent>();
final _stateController = StreamController<LoginState>();
Stream<LoginEvent> get eventStream => _eventController.stream;
Stream<LoginState> get stateStream => _stateController.stream;
void handleEvent(LoginEvent event) {
if (event is SubmitFormEvent) {
final isValid = validateCredentials(event.username, event.password);
_stateController.add(LoginState(isValid: isValid));
}
}
bool validateCredentials(String username, String password) {
// Business logic for validation
return username.length > 3 && password.length > 5;
}
void close() {
_eventController.close();
_stateController.close();
}
}
Best Practices for BLoC Implementation¶
- Single Responsibility: Each bloc should manage a single domain or feature.
- Avoid Tight Coupling: Use dependency injection to decouple blocs from specific UI components.
- Use
StreamTransformerfor Complex Logic: Handle data transformations or filtering in separate transformers. - Close Streams Properly: Always call
close()onStreamControllerto prevent memory leaks. - Leverage
flutter_blocPackage: Use the official package for simplified BLoC implementation (e.g.,Bloc,BlocBuilder).
Key takeaways¶
- BLoC separates business logic from UI, enabling reactive state management.
- Events drive state transitions, with blocs processing logic and emitting states.
- Use
StreamControllerandStreamTransformerfor managing data flow and transformations. - Follow best practices like single responsibility and proper stream cleanup for maintainable code.
- Leverage the
flutter_blocpackage to simplify BLoC implementation in Flutter apps.