Data Source
Need to implement channel information data source Channel Information Data Source
Channel Information
Get Channel Information
Copy
// Get channel information
WKIM.shared.channelManager.getChannel(String channelID, int channelType);
Force Refresh Channel Information
Copy
// Force refresh channel information
WKIM.shared.channelManager.fetchChannelInfo(String channelID, int channelType)
Events
Listen for Channel Update Events
Copy
// Listen for channel refresh events
WKIM.shared.channelManager.addOnRefreshListener('key', (wkChannel) {
// Refresh corresponding channel information
});
// Remove listener
WKIM.shared.channelManager.removeOnRefreshListener('key');
The key is a unique identifier for the listener, can be any string. The same key must be used when adding and removing listeners.
Common Methods
Copy
// Batch save channel information
WKIM.shared.channelManager.addOrUpdateChannels(List<WKChannel> channelList);
// Search
WKIM.shared.channelManager.search(String keyword);
Complete Channel Management Example
Copy
class ChannelManager {
static final ChannelManager _instance = ChannelManager._internal();
factory ChannelManager() => _instance;
ChannelManager._internal();
final Map<String, WKChannel> _channelCache = {};
final StreamController<WKChannel> _channelUpdateController = StreamController.broadcast();
// Stream for UI to listen
Stream<WKChannel> get channelUpdateStream => _channelUpdateController.stream;
void initialize() {
_setupChannelListener();
}
void _setupChannelListener() {
// Listen for channel refresh
WKIM.shared.channelManager.addOnRefreshListener('global', (wkChannel) {
_handleChannelUpdate(wkChannel);
});
}
void _handleChannelUpdate(WKChannel channel) {
final channelKey = '${channel.channelID}_${channel.channelType}';
_channelCache[channelKey] = channel;
// Notify listeners
_channelUpdateController.add(channel);
}
// Get channel with caching
WKChannel? getChannelWithCache(String channelID, int channelType) {
final channelKey = '${channelID}_$channelType';
// First check cache
if (_channelCache.containsKey(channelKey)) {
return _channelCache[channelKey];
}
// Get from SDK
final channel = WKIM.shared.channelManager.getChannel(channelID, channelType);
if (channel != null) {
_channelCache[channelKey] = channel;
return channel;
}
// Trigger network request
fetchChannelInfo(channelID, channelType);
return null;
}
// Force refresh channel information
void fetchChannelInfo(String channelID, int channelType) {
WKIM.shared.channelManager.fetchChannelInfo(channelID, channelType);
}
// Batch save channels
void saveChannels(List<WKChannel> channels) {
WKIM.shared.channelManager.addOrUpdateChannels(channels);
// Update cache
for (var channel in channels) {
final channelKey = '${channel.channelID}_${channel.channelType}';
_channelCache[channelKey] = channel;
}
}
// Search channels
List<WKChannel> searchChannels(String keyword) {
return WKIM.shared.channelManager.search(keyword);
}
// Get channel display name
String getChannelDisplayName(WKChannel channel) {
return channel.channelRemark.isNotEmpty
? channel.channelRemark
: (channel.channelName.isNotEmpty ? channel.channelName : channel.channelID);
}
// Check if channel is online
bool isChannelOnline(WKChannel channel) {
return channel.online == 1;
}
// Check if channel is muted
bool isChannelMuted(WKChannel channel) {
return channel.mute == 1;
}
// Check if channel is pinned
bool isChannelPinned(WKChannel channel) {
return channel.top == 1;
}
// Get channel type text
String getChannelTypeText(int channelType) {
switch (channelType) {
case WKChannelType.personal:
return 'Personal';
case WKChannelType.group:
return 'Group';
default:
return 'Unknown';
}
}
// Get channel avatar URL
String getChannelAvatarUrl(WKChannel channel) {
if (channel.avatar.isNotEmpty) {
final cacheKey = channel.avatarCacheKey.isNotEmpty ? '?v=${channel.avatarCacheKey}' : '';
return '${channel.avatar}$cacheKey';
}
return _getDefaultAvatar(channel.channelID);
}
String _getDefaultAvatar(String channelID) {
return 'https://ui-avatars.com/api/?name=$channelID&background=random';
}
// Format last offline time
String formatLastOfflineTime(int lastOffline) {
if (lastOffline == 0) return 'Never';
final date = DateTime.fromMillisecondsSinceEpoch(lastOffline * 1000);
final now = DateTime.now();
final difference = now.difference(date);
if (difference.inMinutes < 1) return 'Just now';
if (difference.inMinutes < 60) return '${difference.inMinutes} minutes ago';
if (difference.inHours < 24) return '${difference.inHours} hours ago';
if (difference.inDays < 7) return '${difference.inDays} days ago';
return '${date.month}/${date.day}/${date.year}';
}
// Get device flag text
String getDeviceFlagText(int deviceFlag) {
switch (deviceFlag) {
case 0: return 'APP';
case 1: return 'WEB';
case 2: return 'PC';
default: return 'Unknown';
}
}
// Check if channel allows invites
bool canInviteMembers(WKChannel channel) {
return channel.invite == 1;
}
// Check if channel has receipt enabled
bool hasReceiptEnabled(WKChannel channel) {
return channel.receipt == 1;
}
// Check if channel is a robot
bool isRobotChannel(WKChannel channel) {
return channel.robot == 1;
}
// Clear channel cache
void clearChannelCache() {
_channelCache.clear();
}
void dispose() {
_channelUpdateController.close();
WKIM.shared.channelManager.removeOnRefreshListener('global');
}
}
Data Structure Description
Copy
class WKChannel {
String channelID = "";
int channelType = WKChannelType.personal;
String channelName = "";
// Channel remark (channel's remark name, personal remark for individuals, group alias for groups)
String channelRemark = "";
int showNick = 0;
// Whether pinned
int top = 0;
// Whether saved in contacts
int save = 0;
// Do not disturb
int mute = 0;
// Forbidden
int forbidden = 0;
// Invite confirmation
int invite = 0;
// Channel status [1: normal 2: blacklist]
int status = 1;
// Whether followed 0: not followed (stranger) 1: followed (friend)
int follow = 0;
// Whether deleted
int isDeleted = 0;
// Creation time
String createdAt = "";
// Update time
String updatedAt = "";
// Channel avatar
String avatar = "";
// Version
int version = 0;
// Extension fields
dynamic localExtra;
// Whether online
int online = 0;
// Last offline time
int lastOffline = 0;
// Last offline device flag
int deviceFlag = 0;
// Whether receipt message
int receipt = 0;
// Robot
int robot = 0;
// Category [service: customer service]
String category = "";
String username = "";
String avatarCacheKey = "";
dynamic remoteExtraMap;
String parentChannelID = "";
int parentChannelType = 0;
}
UI Integration Example
Copy
class ChannelInfoWidget extends StatefulWidget {
final String channelID;
final int channelType;
const ChannelInfoWidget({
Key? key,
required this.channelID,
required this.channelType,
}) : super(key: key);
@override
_ChannelInfoWidgetState createState() => _ChannelInfoWidgetState();
}
class _ChannelInfoWidgetState extends State<ChannelInfoWidget> {
WKChannel? _channel;
late StreamSubscription<WKChannel> _channelSubscription;
@override
void initState() {
super.initState();
_loadChannelInfo();
_setupChannelListener();
}
void _loadChannelInfo() {
_channel = ChannelManager().getChannelWithCache(widget.channelID, widget.channelType);
if (mounted) setState(() {});
}
void _setupChannelListener() {
_channelSubscription = ChannelManager().channelUpdateStream.listen((channel) {
if (channel.channelID == widget.channelID && channel.channelType == widget.channelType) {
setState(() {
_channel = channel;
});
}
});
}
@override
void dispose() {
_channelSubscription.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (_channel == null) {
return const Center(child: CircularProgressIndicator());
}
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
CircleAvatar(
radius: 30,
backgroundImage: NetworkImage(
ChannelManager().getChannelAvatarUrl(_channel!),
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
ChannelManager().getChannelDisplayName(_channel!),
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
ChannelManager().getChannelTypeText(_channel!.channelType),
style: TextStyle(
color: Colors.grey[600],
),
),
],
),
),
if (ChannelManager().isChannelOnline(_channel!))
Container(
width: 12,
height: 12,
decoration: const BoxDecoration(
color: Colors.green,
shape: BoxShape.circle,
),
),
],
),
const SizedBox(height: 16),
_buildInfoRow('Status', ChannelManager().isChannelOnline(_channel!) ? 'Online' : 'Offline'),
if (!ChannelManager().isChannelOnline(_channel!))
_buildInfoRow('Last Seen', ChannelManager().formatLastOfflineTime(_channel!.lastOffline)),
_buildInfoRow('Muted', ChannelManager().isChannelMuted(_channel!) ? 'Yes' : 'No'),
_buildInfoRow('Pinned', ChannelManager().isChannelPinned(_channel!) ? 'Yes' : 'No'),
if (ChannelManager().isRobotChannel(_channel!))
_buildInfoRow('Type', 'Robot'),
],
),
),
);
}
Widget _buildInfoRow(String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
label,
style: TextStyle(
color: Colors.grey[600],
),
),
Text(
value,
style: const TextStyle(
fontWeight: FontWeight.w500,
),
),
],
),
);
}
}
Next Steps
Channel Member Management
Learn how to manage channel members
Data Source Configuration
Configure channel data sources
Message Management
Return to message handling functionality
Advanced Features
Explore advanced features and custom messages

