Events and listeners
Nexconn Chat UI exposes EngineProvider, ChannelProvider, and ChatProvider as Flutter notifiers. The default pages update their own UI state, while these listeners let your app add analytics, notifications, routing, badges, and other side effects.
Engine events
EngineProvider bridges global SDK state from NCEngine. It exposes connection, message, deletion, typing, channel sync, unread count, server time, failed-message tracking, and local notification state.
final engineProvider = context.read<EngineProvider>();
engineProvider.connectionStatusNotifier.addListener(() {
debugPrint('${engineProvider.connectionStatus}');
});
engineProvider.receivedMessageNotifier.addListener(() {
final message = engineProvider.receivedMessageNotifier.value;
debugPrint('Received: ${message?.messageId}');
});
For message-specific side effects, register an async listener and remove it from the same owning object.
late final NexconnAsyncMessageReceivedListener messageListener;
messageListener = (message) async {
await analytics.logEvent(name: 'message_received');
};
engineProvider.addAsyncMessageReceivedListener(messageListener);
engineProvider.removeAsyncMessageReceivedListener(messageListener);
In a StatefulWidget, register listeners in initState or didChangeDependencies and remove the same callback in dispose.
class BadgeSyncState extends State<BadgeSync> {
late final EngineProvider engineProvider;
late final NexconnAsyncMessageReceivedListener messageListener;
void initState() {
super.initState();
engineProvider = context.read<EngineProvider>();
messageListener = (message) async {
await syncBadgeCount();
};
engineProvider.addAsyncMessageReceivedListener(messageListener);
}
void dispose() {
engineProvider.removeAsyncMessageReceivedListener(messageListener);
super.dispose();
}
}
Channel updates
ChannelProvider owns the channel list state for ChannelPage. It listens to EngineProvider.channelRefreshNotifier and reloads when channel data should refresh.
final channelProvider = context.read<ChannelProvider>();
channelProvider.addListener(() {
debugPrint('Channels: ${channelProvider.channels.length}');
debugPrint('Unread: ${channelProvider.engineProvider.totalUnreadCount}');
});
await channelProvider.reload();
await channelProvider.loadMore();
Chat callbacks
ChatPageConfig exposes send hooks and an async received-message hook. The default page still updates the list; use these callbacks for side effects.
ChatPageConfig(
onBeforeSendMessage: (channel, params) async {
return channel.channelId.isNotEmpty;
},
onAfterSendMessage: (channel, params, message, error) async {
debugPrint('Sent: ${message?.messageId}');
},
onAsyncMessageReceived: (context, message) async {
debugPrint('Received: ${message.messageId}');
},
);
ChatProvider state
ChatProvider owns message loading, selection, search, reference state, unread tips, and send operations for a BaseChannel.
final chatProvider = context.read<ChatProvider>();
chatProvider.addListener(() {
debugPrint('Messages: ${chatProvider.messages.length}');
debugPrint('Selected: ${chatProvider.selectedMessages.length}');
});
await chatProvider.loadInitialMessages();
await chatProvider.sendText('Hello');
Page-level taps
Use ChannelPage and ChatPage callbacks when the side effect is tied to a specific user action.
ChannelPage(
onItemTap: (channel, index, context) {
openChannelChatPage(context, channel);
},
);
ChatPage(
channel: channel,
onMessageTap: (message) {
debugPrint('Tapped: ${message.messageId}');
},
);