Difference between future/ stream to emit the state in BLOC ?
#state#bloc#future#stream
Answer
Overview
In
text
flutter_bloctext
async*text
yieldtext
asynctext
emit()text
emit()Modern API — async + emit() (Recommended)
Since
text
flutter_bloc 7.2+text
Future<void>text
Emitter<State>dartclass LoginBloc extends Bloc<LoginEvent, LoginState> { LoginBloc() : super(LoginInitial()) { on<LoginSubmitted>(_onLoginSubmitted); } // Future-based handler 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 (e) { emit(LoginFailure(e.toString())); } } }
Stream-based emit (for streaming data sources)
When your data source IS a stream (e.g., Firestore, MQTT), use
text
emit.forEachtext
emit.onEachdart// Listening to a Stream from Firestore Future<void> _onWatchUsers( WatchUsersEvent event, Emitter<UsersState> emit, ) async { await emit.forEach<List<User>>( userRepository.watchUsers(), // Returns Stream<List<User>> onData: (users) => UsersLoaded(users), onError: (error, stackTrace) => UsersError(error.toString()), ); }
dart// Using emit.onEach for side effects without state mapping await emit.onEach<int>( timerStream, onData: (tick) => emit(TimerTick(tick)), onError: (e, _) => emit(TimerError()), );
Key Differences
| Feature | text | text |
|---|---|---|
| Use case | Single async operation (API call) | Continuous stream (Firestore, WS) |
| Cancellation | Cancelled if new event fires | Automatically managed |
| Error handling | try/catch | text |
| testability | Easy | Requires stream testing |
| BLoC version | 7.2+ | 7.2+ |
Old API (Pre-7.2) — mapEventToState with yield
dart// ❌ Old style — deprecated in newer flutter_bloc versions Stream<LoginState> mapEventToState(LoginEvent event) async* { if (event is LoginSubmitted) { yield LoginLoading(); try { final user = await authRepo.signIn(event.email, event.password); yield LoginSuccess(user); } catch (e) { yield LoginFailure(e.toString()); } } }
Real-World Pattern — Mixed
dartclass DashboardBloc extends Bloc<DashboardEvent, DashboardState> { DashboardBloc() : super(DashboardInitial()) { on<LoadDashboard>(_onLoad); on<WatchNotifications>(_onWatchNotifications); } // One-time API call → use emit() directly Future<void> _onLoad(LoadDashboard event, Emitter<DashboardState> emit) async { emit(DashboardLoading()); final data = await apiService.fetchDashboard(); emit(DashboardLoaded(data)); } // Ongoing stream → use emit.forEach Future<void> _onWatchNotifications( WatchNotifications event, Emitter<DashboardState> emit, ) async { await emit.forEach( notificationService.stream, onData: (notifications) => NotificationsUpdated(notifications), ); } }
Rule: Use
directly for one-shot operations (API calls, DB reads). Usetextemit()/textemit.forEachwhen your data source is a continuoustextemit.onEach.textStream