← → or space · progress saves for Continue on the roadmap
Goal
Model messages as a stream with join/leave events from a controller-backed API.
Step 1 - Event types
sealed class ChatEvent {}
class MessageEvent extends ChatEvent {
final String user;
final String text;
MessageEvent(this.user, this.text);
}
class JoinEvent extends ChatEvent {
final String user;
JoinEvent(this.user);
}
class PartEvent extends ChatEvent {
final String user;
PartEvent(this.user);
}Step 2 - Room facade
import 'dart:async';
class ChatRoom {
final _ctrl = StreamController<ChatEvent>.broadcast();
Stream<ChatEvent> get events => _ctrl.stream;
void join(String user) => _ctrl.add(JoinEvent(user));
void part(String user) => _ctrl.add(PartEvent(user));
void say(String user, String text) => _ctrl.add(MessageEvent(user, text));
Future<void> close() async {
await _ctrl.close();
}
}
Future<void> main() async {
final room = ChatRoom();
final sub = room.events.listen((e) {
if (e is JoinEvent) print('${e.user} joined');
if (e is PartEvent) print('${e.user} left');
if (e is MessageEvent) print('${e.user}: ${e.text}');
});
room.join('Asha');
room.say('Asha', 'hi');
room.part('Asha');
await Future.delayed(const Duration(milliseconds: 20));
await sub.cancel();
await room.close();
}Practice tasks
- Replace
sealed classwith a simpleenum ChatKind+ record payload if you target an older Dart SDK. - Add
SystemEventfor moderation messages and show howswitchstays exhaustive withsealed. - Buffer the last 20
MessageEventvalues in aListinside one listener for a fake transcript.