Skip to main content

User profiles

Nexconn Chat UI keeps profile display in the UI layer thin. Message bubbles, channel items, and profile pages can show names and avatars, but the app remains responsible for providing the data source that fits its account system.

Use two related APIs:

  • ChatProfileInfo and ChatProfileProvider for lightweight display data in chat UI surfaces.
  • NexconnUserProfileProvider for the full user profile page state, loading, refresh, and error handling.

Choose a profile source

SourceUse whenIntegration
App-owned account serviceYour product already owns display names, avatars, roles, or privacy rules.Provide ChannelConfig.profileProvider, ChatPageConfig.profileProvider, displayProfileResolver, or NexconnUserProfileProviderConfig resolvers.
Nexconn managed profileYou rely on SDK user profile APIs for the displayed profile data.Use NexconnUserProfilePage or NexconnUserProfileProvider without custom resolvers.
HybridYou need SDK IDs and app-owned presentation data together.Resolve app data first, then fall back to SDK profile or raw user ID.

Display profiles in chat

ChatProfileInfo is the small model used by message bubbles, references, and combined-message previews.

Dart
class ChatProfileInfo {
final String id;
final String? name;
final String? portraitUri;
final String? extra;

const ChatProfileInfo({
required this.id,
this.name,
this.portraitUri,
this.extra,
});
}

Provide it through ChatPageConfig.profileProvider.

Dart
import 'package:ai_nexconn_chatui_plugin/ai_nexconn_chatui_plugin.dart';

ChatPage(
channel: DirectChannel('target_user_id'),
config: ChatPageConfig(
profileProvider: (channel, {message}) async {
final userId = message?.senderUserId ?? channel.channelId;
final profile = await accountRepository.loadUser(userId);

return ChatProfileInfo(
id: userId,
name: profile?.displayName ?? userId,
portraitUri: profile?.avatarUrl,
);
},
),
);

The provider receives the active BaseChannel and, when available, the Message being rendered. In a group channel, use message.senderUserId to resolve the sender. In a direct channel, the channel ID is usually enough for the target user.

Share profiles between the channel list and chat page

When the channel list opens the built-in ChatPage, prefer ChannelConfig.profileProvider if the same resolver should drive channel rows, the default chat title, message senders, references, and combined-message previews.

Dart
ChannelPage(
config: ChannelConfig(
profileProvider: (channel, {message}) async {
final userId = message?.senderUserId ?? channel.channelId;
final profile = await accountRepository.loadUser(userId);

return ChatProfileInfo(
id: userId,
name: profile?.displayName ?? userId,
portraitUri: profile?.avatarUrl,
);
},
),
);

ChannelItemConfig.displayProfiles and ChannelItemConfig.displayProfileResolver still take priority for channel rows. Use them when the channel list needs a different display value from the chat page. Custom onItemTap or chatPageOpener integrations remain fully owned by your app.

Display profiles only in the channel list

Channel rows use ChannelDisplayProfile, because a row can represent a direct, group, system, open, community, or sub-channel entry.

Dart
ChannelPage(
config: ChannelConfig(
itemConfig: ChannelItemConfig(
displayProfileResolver: (context, channel) async {
if (channel.channelType == ChannelType.direct) {
final user = await accountRepository.loadUser(channel.channelId);
return ChannelDisplayProfile(
displayName: user?.displayName ?? channel.channelId,
avatarUrl: user?.avatarUrl,
);
}

return ChannelDisplayProfile(displayName: channel.channelId);
},
),
),
);

For static or preloaded data, ChannelItemConfig.displayProfiles can be used instead of an async resolver.

Full user profile page

NexconnUserProfileProvider powers NexconnUserProfilePage. If you do not provide custom resolvers, it falls back to the Nexconn SDK user APIs:

  • Current user: NCEngine.user.getMyUserProfile.
  • Other users: NCEngine.user.getUserProfiles.
Dart
NexconnUserProfilePage(userId: 'target_user_id');

To plug in an app-owned profile service, configure the page with resolvers.

Dart
NexconnUserProfilePage(
userId: 'target_user_id',
config: NexconnUserProfilePageConfig(
providerConfig: NexconnUserProfileProviderConfig(
autoLoad: true,
profileResolver: (userId) {
return accountRepository.loadNexconnProfile(userId);
},
currentUserProfileResolver: (_) {
return accountRepository.loadCurrentNexconnProfile();
},
),
),
);

You can also own the provider directly when several widgets need the same state.

Dart
final provider = NexconnUserProfileProvider(
userId: 'target_user_id',
profileResolver: accountRepository.loadNexconnProfile,
autoLoad: true,
);

NexconnUserProfilePage(provider: provider);

Loading flow

Caching guidance

Cache profile data in the app layer when profile lookups come from your own backend. A simple cache avoids repeated requests while scrolling message lists.

Dart
class ProfileCache {
final Map<String, Future<ChatProfileInfo?>> _profiles = {};

Future<ChatProfileInfo?> get(String userId) {
return _profiles.putIfAbsent(userId, () async {
final user = await accountRepository.loadUser(userId);
if (user == null) {
return null;
}
return ChatProfileInfo(
id: userId,
name: user.displayName,
portraitUri: user.avatarUrl,
);
});
}

void invalidate(String userId) {
_profiles.remove(userId);
}
}

When a user changes their name or avatar, invalidate the affected cache entry and call the owning provider's refresh() method where applicable.