Event Handling
Overview
Stream Feeds provides real-time event handling, allowing you to react to various changes in the feed system.
Available Events
| Event Name | Description |
|---|---|
| Activity Events | |
feeds.activity.added |
Fired when a new activity is added to a feed |
feeds.activity.updated |
Fired when an activity is modified |
feeds.activity.deleted |
Fired when an activity is removed |
feeds.activity.restored |
Fired when an activity is restored |
feeds.activity.removed_from_feed |
Fired when an activity is removed from a specific feed |
feeds.activity.pinned |
Fired when an activity is pinned to the top |
feeds.activity.unpinned |
Fired when an activity is unpinned |
feeds.activity.feedback |
Fired when activity feedback is provided |
feeds.activity.marked |
Fired when an activity is marked |
| Notification Events | |
feeds.notification_feed.updated |
Fired when the notification status, or notification groups (aggregated activities) are updated |
| Comment Events | |
feeds.comment.added |
Fired when a new comment is added to an activity |
feeds.comment.updated |
Fired when a comment is modified |
feeds.comment.deleted |
Fired when a comment is removed |
feeds.comment.restored |
Fired when a comment is restored |
| Reaction Events | |
feeds.activity.reaction.added |
Fired when a reaction is added to an activity |
feeds.activity.reaction.deleted |
Fired when a reaction is removed from an activity |
feeds.activity.reaction.updated |
Fired when a reaction on an activity is updated |
feeds.comment.reaction.added |
Fired when a reaction is added to a comment |
feeds.comment.reaction.deleted |
Fired when a reaction is removed from a comment |
feeds.comment.reaction.updated |
Fired when a reaction on a comment is updated |
| Poll Events | |
feeds.poll.closed |
Fired when a poll is closed |
feeds.poll.deleted |
Fired when a poll is deleted |
feeds.poll.updated |
Fired when a poll is modified |
feeds.poll.vote_casted |
Fired when a vote is cast |
feeds.poll.vote_changed |
Fired when a vote is changed |
feeds.poll.vote_removed |
Fired when a vote is removed |
| Feed Events | |
feeds.feed.created |
Fired when a new feed is created |
feeds.feed.updated |
Fired when a feed is modified |
feeds.feed.deleted |
Fired when a feed is deleted |
| Feed Group Events | |
feeds.feed_group.changed |
Fired when a feed group is changed |
feeds.feed_group.deleted |
Fired when a feed group is deleted |
feeds.feed_group.restored |
Fired when a feed group is restored |
| Member Events | |
feeds.feed_member.added |
Fired when a member is added to a feed |
feeds.feed_member.removed |
Fired when a member is removed from a feed |
feeds.feed_member.updated |
Fired when a member's role/permissions change |
| Follow Events | |
feeds.follow.created |
Fired when a follow relationship is created |
feeds.follow.deleted |
Fired when a follow relationship is removed |
feeds.follow.updated |
Fired when follow settings are modified |
| Bookmark Events | |
feeds.bookmark.added |
Fired when an activity is bookmarked |
feeds.bookmark.deleted |
Fired when a bookmark is removed |
feeds.bookmark.updated |
Fired when bookmark metadata is modified |
feeds.bookmark_folder.deleted |
Fired when bookmark folder is deleted |
feeds.bookmark_folder.updated |
Fired when bookmark folder is updated |
| Stories Events | |
feeds.stories_feed.updated |
Fired when a stories feed is updated |
| Moderation Events | |
moderation.custom_action |
Fired when a custom moderation action is performed |
moderation.flagged |
Fired when content is flagged for moderation |
moderation.mark_reviewed |
Fired when content is marked as reviewed |
| App Events | |
app.updated |
Fired when the app configuration changes |
| Connection Events | |
health.check |
The periodic keep-alive on the WebSocket connection |
connection.ok |
Fired when the WebSocket connection is established |
connection.error |
Fired when the WebSocket connection fails |
| User Events | |
user.banned |
Fired when a user is banned |
user.deactivated |
Fired when a user is deactivated |
user.reactivated |
Fired when a user is reactivated |
user.unbanned |
Fired when a user is unbanned |
user.updated |
Fired when a user is updated |
Listening to events
client.events emits every WebSocket event the client receives.
// Listen to all WebSocket events from the client
client.events.listen((event) {
print('Received event: ${event.runtimeType}');
});You rarely need to pattern-match on the event classes: the SDK applies each event to the state of the feeds, activities and lists you hold, so following that state is the idiomatic way to react to a change.
Following a feed's state
Real-time updates require a watched feed. FeedQuery.watch defaults to true, so a feed you create the plain way is already watched.
// Real-time updates require a watched feed. `watch` defaults to true, so a
// plain `client.feed(...)` is already watched; pass false to opt out.
final feed = client.feedFromQuery(
const FeedQuery(fid: FeedId.user('john'), watch: true),
);
await feed.getOrCreate();
// The SDK applies each event to the feed's state, so following the state is
// usually all an app needs.
feed.stream.listen((state) {
print('${state.activities.length} activities');
});Feed.stream emits the whole FeedState after each change. See State Layer for what it holds.
A watched query is rejected when the client connected with connectWebSocket: false. See Connecting without a WebSocket.
Notification feed state
For a notification feed, the state carries the unread and unseen counts and the per-group read state, all of which stay current as events arrive.
// The feed state stream emits whenever the feed state changes (activities,
// aggregated groups, notification status, etc.)
notificationFeed.stream.listen((state) {
final unread = state.notificationStatus?.unread ?? 0;
final unseen = state.notificationStatus?.unseen ?? 0;
print('Unread: $unread, Unseen: $unseen');
// Per-activity isRead/isSeen are updated automatically when the
// activity.marked WebSocket event arrives after markActivity() calls.
for (final group in state.aggregatedActivities) {
print('Group ${group.group}: read=${group.isRead}, seen=${group.isSeen}');
}
});markActivity produces a feeds.activity.marked event, and the SDK uses it to update the isRead and isSeen flags on the feed's state. That is also how a mark performed on another device shows up here.
// The activity.marked WS event fires when activities are marked read/seen.
// The SDK automatically updates the feed state's isRead/isSeen flags.
// Observe changes via the feed state stream:
notificationFeed.stream.listen((state) {
final unreadCount = state.notificationStatus?.unread ?? 0;
print('Unread count updated: $unreadCount');
// All aggregated groups with their current read state
final unreadGroups = state.aggregatedActivities.where((g) => g.isRead != true);
print('Unread groups: ${unreadGroups.length}');
});Connection events
// The connection's own comings and goings
client.connectionState.listen((state) {
print('Connection state: $state');
});A disconnection carries the source that closed it and whether it is worth reopening. See Error Handling.
Server-side webhooks
Webhooks are configured with your API secret, so they belong on your backend rather than in the app you ship. Setting up an endpoint, verifying signatures, retries and event failover are covered under Event handling in the server-side docs, with the full transport reference in the platform webhooks guide.