Broadcasting
Broadcast PostPublished through the normal event dispatcher:
await Event.dispatch(PostPublished(postId: 42, title: 'Hello Maat'));Broadcast events to public, private, or presence channels through a configured
Pusher-compatible server. Add BroadcastServiceProvider to your application’s
providers, then configure a connection and declare the channels clients may
join.
Broadcast Event Contract
class PostPublished implements ShouldBroadcast, BroadcastsAs, BroadcastsWith, BroadcastsWhen { PostPublished({required this.postId, required this.title});
final int postId; final String title;
@override List<Channel> broadcastOn() => [PrivateChannel('posts.$postId')];
@override String broadcastAs() => 'PostPublished';
@override Map<String, Object?> broadcastWith() => {'id': postId, 'title': title};
@override bool broadcastWhen() => title.isNotEmpty;}Implement ShouldBroadcast to make an event broadcastable. BroadcastsAs,
BroadcastsWith, and BroadcastsWhen are optional: without them the event’s
runtime type is its name, its payload is empty, and it is sent.
Channel Types
Use Channel for public channels, PrivateChannel for authenticated channels,
and PresenceChannel for authenticated channels that also share member data.
Prefer names without private- or presence-; the channel type adds the wire
prefix. Already-prefixed names are also accepted and keep exactly one prefix.
Authorizing private and presence channels
Register unprefixed patterns with Broadcast.channel. The built-in
POST /broadcasting/auth endpoint authenticates the configured guard, strips
the private or presence prefix before matching, and returns 403 when the
user, pattern, or authorizer denies access.
Broadcast.channel('posts.{id}', (user, params) async { return user.authIdentifier.toString() == params['id'];});
Broadcast.channel('chat.{roomId}', (user, params) async { return {'name': 'Member ${user.authIdentifier}'};});Return true for an allowed private channel. Return a Map<String, Object?>
for an allowed presence channel; it becomes that member’s data. Return false
or null to deny either kind.
maat make:channel PostsChannel creates routes/posts_channel.dart with a
deny-by-default authorizer. Replace its TODO with application-specific
ownership logic before calling registerPostsChannel() while booting your
application.
Driver Configuration
Use this exact config/broadcasting.dart shape:
Map<String, dynamic> get broadcasting => { 'default': env('BROADCAST_CONNECTION', 'log'), 'connections': { 'pusher': { 'driver': 'pusher', 'key': env('PUSHER_APP_KEY', ''), 'secret': env('PUSHER_APP_SECRET', ''), 'app_id': env('PUSHER_APP_ID', ''), 'host': env('PUSHER_HOST', '127.0.0.1'), 'port': envInt('PUSHER_PORT', 6001), 'scheme': env('PUSHER_SCHEME', 'http'), }, 'log': {'driver': 'log'}, 'null': {'driver': 'null'}, },};pusher signs HTTP publishes for Pusher Protocol v7 backends such as Thoth.
Use log during development to inspect broadcasts, or null to discard them.
Connections are created and cached when first selected, so the blank Pusher
credentials above do not break the default log connection.
Dispatching Events
Dispatch the event as usual; listeners run before the broadcast:
await Event.dispatch(PostPublished(postId: 42, title: 'Hello Maat'));You can also send an anonymous event without creating a class:
await Broadcast.on('posts') .as('PostPublished') .with_({'id': 42, 'title': 'Hello Maat'}) .send();To omit the browser that caused an update, pass its request to toOthers.
Maat reads X-Socket-ID first, then the _socket_id form field.
await broadcast( PostPublished(postId: 42, title: 'Hello Maat'),).toOthers(request).send();Inspecting Channels
List registered authorization patterns in registration order:
maat channel:listUse Thoth’s signed inspection endpoints to see live channel occupancy; see WebSockets with Thoth.
Faking Broadcasts
Replace the application’s broadcaster with an in-memory fake and assert only the broadcasts your test cares about:
final broadcasts = Broadcast.fake();
await Broadcast.on('posts').as('PostPublished').with_({'id': 42}).send();
broadcasts.assertBroadcasted( 'PostPublished', (sent) => sent.channels.contains('posts') && sent.payload['id'] == 42,);broadcasts.assertBroadcastedTimes('PostPublished', 1);broadcasts.assertNotBroadcasted('PostArchived');Use assertNothingBroadcasted() when a test expects no broadcast at all.