Flutter简易聊天插件simple_chat的使用
Flutter简易聊天插件simple_chat的使用
Simple Chat
是一个简单的UI解决方案,用于快速集成IM聊天和AI机器人聊天。
支持自定义消息单元、消息分组、图像预览等功能。
支持平台
- iOS
- Android
截图
| | |
基本用法
初始化控制器与视图
ChatActionHandler
用于处理发送消息、用户头像点击、图像预览缩略图点击等事件。
ChatConfig
是可选的,你可以自定义输入框提示文本、图像最大数量等。
final controller = ChatController(
config: ChatConfig(
inputBoxHintText: 'Type a message...',
),
actionHandler: ChatActionHandler(
onSendMessage: (output) {},
),
);
ChatView(
controller: controller,
theme: ChatThemeData(
dark: coloredThemeData,
light: coloredThemeData,
),
)
配置用户
用户通过 id
、name
、avatarUrl
和 isCurrentUser
标志来配置。
id
用于标识用户,并在相应的用户头像下显示消息。name
用于在聊天界面中显示。avatarUrl
用于显示用户头像。isCurrentUser
用于标识当前用户。
await controller.store.addUsers(users: [
ModelUser(
id: '1',
name: 'Lawrence',
avatarUrl: 'https://example.com/avatar/1.png',
isCurrentUser: true,
),
ModelUser(
id: '2',
name: 'Ciel',
avatarUrl: 'https://example.com/avatar/2.png',
isCurrentUser: false,
),
]);
添加消息
isInitial
用于标识消息是否为历史消息,并确定消息是否带动画显示。userId
用于标识发送消息的用户。sequence
用于确定消息顺序。displayDatetime
用于确定消息显示时间。
await controller.store.addMessage(
isInitial: !withDelay,
message: ModelTextMessage(
id: '$i',
text: 'Hello, how are you?',
userId: '1',
sequence: i,
displayDatetime: DateTime.now(),
),
);
加载指示器
阻塞并显示加载指示器
final controller = ChatController(
config: ChatConfig(
loadingIndicatorType: LoadingIndicatorType.sendBtnLoading,
),
);
非阻塞并显示回复生成器
await controller.store.showReplyGeneratingIndicator();
await Future.delayed(const Duration(seconds: 3));
await controller.store.hideReplyGeneratingIndicator();
未读指示器
带有未读计数的指示器
final controller = ChatController(
config: ChatConfig(
showUnreadCount: true,
),
...
);
不带有未读计数的指示器
final controller = ChatController(
config: ChatConfig(
showUnreadCount: false,
),
...
);
消息状态
对于每条消息,我们还可以设置状态,如 sending
、failed to send
。
await controller.store.updateSendStatus(
messageId: messageId,
status: ModelBaseMessageStatus.sending,
);
await controller.store.updateSendStatus(
messageId: messageId,
status: ModelBaseMessageStatus.failedToSend,
);
自定义
添加自定义的消息单元UI
定义自定义的消息模型
如果一个用户连续发送多条消息且没有中断,这些消息通常会被组合在一起。如果你想将它们分成不同的消息单元,可以将 forceNewBlock
设置为 true
。
class CustomMessage extends ModelBaseMessage {
[@override](/user/override)
final String id;
[@override](/user/override)
final String userId;
[@override](/user/override)
final int sequence;
[@override](/user/override)
final DateTime displayDatetime;
[@override](/user/override)
final bool forceNewBlock;
final String data;
CustomMessage({
required this.id,
required this.userId,
required this.sequence,
required this.displayDatetime,
required this.forceNewBlock,
required this.data,
});
}
定义自定义的消息单元UI
你可以使用 MessageBubble
包裹你的自定义消息单元UI。
class CustomMessageCell extends StatelessWidget {
final ModelLoadingIndicatorMessage message;
final bool isMessageFromCurrentUser;
CustomMessageCell({
super.key,
required this.message,
required this.isMessageFromCurrentUser,
});
[@override](/user/override)
Widget build(BuildContext context) {
return MessageBubble(
isCurrentUser: isMessageFromCurrentUser,
padding: const EdgeInsets.all(12),
...
);
}
}
注册自定义的消息单元UI
注册后,每当添加一条具有自定义消息模型的消息时,注册的UI将用于显示该消息。
controller.viewFactory.register<CustomMessage>(
(BuildContext context, {
required CustomMessage message,
required bool isMessageFromCurrentUser,
}) =>
CustomMessageCell(
message: message,
isMessageFromCurrentUser: isMessageFromCurrentUser,
),
);
示例代码
以下是完整的示例代码:
import 'dart:math'; // Added import for Random
import 'package:flutter/material.dart';
import 'package:simple_chat/simple_chat.dart';
import 'package:uuid/uuid.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return const MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({
super.key,
});
[@override](/user/override)
State<HomePage> createState() {
return _HomePageState();
}
}
class _HomePageState extends State<HomePage> {
late final ChatController controller;
var sequence = 0;
final userId1 = const Uuid().v4();
final userId2 = const Uuid().v4();
[@override](/user/override)
void initState() {
super.initState();
controller = ChatController(
config: ChatConfig(
loadingIndicatorType: LoadingIndicatorType.noBlocking,
showUnreadCount: true,
imageMaxCount: 1,
),
actionHandler: ChatActionHandler(
onSendMessage: _handleSendingMessage,
// onSendMessage: _handleSendingMessageWithStatus,
),
);
setupTests();
}
[@override](/user/override)
void dispose() {
super.dispose();
}
Future<void> _handleSendingMessage(ChatMessageSendOutput output) async {
if (output.message.isNotEmpty) {
await controller.store.addMessage(
message: ModelTextMessage(
id: const Uuid().v4(),
text: output.message,
userId: userId1,
sequence: sequence++,
displayDatetime: DateTime.now(),
),
);
}
if (output.imageFiles.isNotEmpty) {
// 在实际应用中,通常最好先将图片上传到服务器
await controller.store.addMessage(
message: ModelImageMessage(
id: const Uuid().v4(),
userId: userId1,
sequence: sequence++,
displayDatetime: DateTime.now(),
imageUrls: output.imageFiles.map((e) => e.path).toList(),
),
);
}
await controller.store.showReplyGeneratingIndicator();
await Future.delayed(const Duration(seconds: 3));
await controller.store.hideReplyGeneratingIndicator();
await controller.store.addMessage(
message: ModelTextMessage(
id: const Uuid().v4(),
userId: userId2,
sequence: sequence++,
displayDatetime: DateTime.now(),
text: 'Example reply~',
),
);
}
Future<void> _handleSendingMessageWithStatus(ChatMessageSendOutput output) async {
if (output.message.isNotEmpty) {
final messageId = const Uuid().v4();
await controller.store.addMessage(
message: ModelTextMessage(
id: messageId,
text: output.message,
userId: userId1,
sequence: sequence++,
displayDatetime: DateTime.now(),
),
);
await Future.delayed(const Duration(seconds: 1));
await controller.store.updateSendStatus(
messageId: messageId,
status: ModelBaseMessageStatus.sending,
);
await Future.delayed(const Duration(seconds: 1));
await controller.store.updateSendStatus(
messageId: messageId,
status: ModelBaseMessageStatus.failedToSend,
);
}
}
Future<void> setupTests() async {
await injectUsers();
}
Future<void> injectUsers() async {
await controller.store.addUsers(users: [
ModelUser(
id: userId1,
name: 'Lawrence',
avatarUrl: 'https://lh3.googleusercontent.com/ogw/AF2bZyj1OQs6QwRQMGfY0H5g_VOdijzbC7Ea3XE3Z8eDYTrOZQ=s64-c-mo',
isCurrentUser: true,
),
ModelUser(
id: userId2,
name: 'Ciel',
avatarUrl: 'https://media.karousell.com/media/photos/profiles/2018/01/09/imwithye_1515485479.jpg',
isCurrentUser: false,
),
]);
}
Future<void> injectMessages({required bool withDelay}) async {
final random = Random();
final totalCount = withDelay ? 1 : 100;
for (var i = 0; i < totalCount; i++) {
if (withDelay) {
await Future.delayed(const Duration(milliseconds: 1000));
}
final userId = random.nextBool() ? userId1 : userId2;
final textLength = random.nextInt(50) + 10; // 随机长度在10到59之间
await controller.store.addMessage(
isInitial: !withDelay,
message: ModelTextMessage(
id: const Uuid().v4(),
text: generateRandomText(textLength),
userId: userId,
sequence: sequence++,
displayDatetime: DateTime.now(),
),
);
}
}
String generateRandomText(int length) {
const chars = 'abcdefghijklmnopqrstuvwxyz0123456789 ';
final random = Random();
return String.fromCharCodes(Iterable.generate(length, (_) => chars.codeUnitAt(random.nextInt(chars.length))));
}
[@override](/user/override)
Widget build(BuildContext context) {
final coloredThemeData = ChatColorThemeData();
return Scaffold(
appBar: AppBar(
title: const Text('Home Page'),
actions: [
PopupMenuButton<String>(
icon: const Icon(Icons.more_vert),
onSelected: (value) {
switch (value) {
case 'scroll_top':
controller.chatScrollController.scrollToTop();
break;
case 'scroll_bottom':
controller.chatScrollController.scrollToBottom();
break;
case 'inject_messages':
injectMessages(withDelay: false);
break;
case 'inject_messages_delay':
injectMessages(withDelay: true);
break;
case 'clear_all':
controller.store.clearAll();
break;
}
},
itemBuilder: (BuildContext context) => [
const PopupMenuItem<String>(
value: 'scroll_top',
child: ListTile(
leading: Icon(Icons.arrow_upward),
title: Text('Scroll to Top'),
),
),
const PopupMenuItem<String>(
value: 'scroll_bottom',
child: ListTile(
leading: Icon(Icons.arrow_downward),
title: Text('Scroll to Bottom'),
),
),
const PopupMenuItem<String>(
value: 'inject_messages',
child: ListTile(
leading: Icon(Icons.message),
title: Text('Inject Messages'),
),
),
const PopupMenuItem<String>(
value: 'inject_messages_delay',
child: ListTile(
leading: Icon(Icons.auto_awesome),
title: Text('Inject Messages with Delay'),
),
),
const PopupMenuItem<String>(
value: 'clear_all',
child: ListTile(
leading: Icon(Icons.clear),
title: Text('Clear All'),
),
),
],
),
],
),
body: ChatView(
controller: controller,
theme: ChatThemeData(
dark: coloredThemeData,
light: coloredThemeData,
),
),
);
}
}
更多关于Flutter简易聊天插件simple_chat的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter简易聊天插件simple_chat的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,我可以为你提供一个关于如何使用Flutter中的simple_chat
插件的代码示例。simple_chat
是一个用于创建简易聊天应用的Flutter插件。下面是一个简单的示例,展示如何使用这个插件来创建一个基本的聊天界面。
首先,你需要在你的pubspec.yaml
文件中添加simple_chat
依赖:
dependencies:
flutter:
sdk: flutter
simple_chat: ^x.y.z # 请替换为实际的最新版本号
然后,运行flutter pub get
来安装依赖。
接下来,你可以在你的Flutter应用中按照以下步骤使用simple_chat
插件:
- 导入必要的包:
import 'package:flutter/material.dart';
import 'package:simple_chat/simple_chat.dart';
- 创建聊天页面:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Simple Chat Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ChatScreen(),
);
}
}
class ChatScreen extends StatefulWidget {
@override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
// 假设我们有一个简单的消息列表
final List<ChatMessage> messages = [
ChatMessage(
user: 'User1',
text: 'Hello!',
timestamp: DateTime.now().subtract(Duration(minutes: 5)),
isMe: true,
),
ChatMessage(
user: 'User2',
text: 'Hi there!',
timestamp: DateTime.now().subtract(Duration(minutes: 4)),
isMe: false,
),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Simple Chat'),
),
body: Chat(
messages: messages,
onSend: (String message) {
// 这里处理发送消息的逻辑
setState(() {
messages.add(ChatMessage(
user: 'Me',
text: message,
timestamp: DateTime.now(),
isMe: true,
));
});
// 清空输入框
// 假设你有一个TextField控制器用于输入消息
// _controller.clear();
},
// 你可以自定义用户头像等
userAvatar: 'https://example.com/avatar.png',
userName: 'Me',
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// 显示输入框(这通常与TextField结合使用,这里为了简单起见省略)
// _showTextField();
},
tooltip: 'Send message',
child: Icon(Icons.send),
),
);
}
}
请注意,上述代码省略了一些实际实现细节,例如显示和隐藏文本输入字段的逻辑,以及实际的消息发送机制(通常你会将其与后端服务集成)。此外,ChatMessage
类是一个假设的类,你需要根据simple_chat
插件的实际API来实现它。
在实际应用中,你可能还需要处理更多细节,比如:
- 从服务器获取和发送消息。
- 显示消息的时间戳。
- 处理消息状态(如发送中、已发送、已读/未读)。
- 自定义用户头像和昵称显示。
这些功能通常需要根据simple_chat
插件的文档和API来实现。由于simple_chat
插件的具体API和实现细节可能会随着版本更新而变化,因此请参考该插件的最新文档和示例代码以获取最准确的信息。