249 lines
8.4 KiB
Dart
249 lines
8.4 KiB
Dart
import 'package:elysia/page/ChatRoomPage.dart';
|
|
import 'package:elysia/plugin/Notify.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_chat_list_builder/flutter_chat_list_builder.dart';
|
|
import '../../plugin/C.dart';
|
|
import '../../plugin/CacheAavatar.dart';
|
|
import '../../plugin/ChatInput.dart';
|
|
import '../../plugin/HTTP.dart';
|
|
import '../../plugin/MarkdownSelectableText.dart';
|
|
import '../../plugin/UID.dart';
|
|
|
|
class Message {
|
|
final String messageId;
|
|
final String msg;
|
|
final bool isMeSend;
|
|
bool isSending; // 新增字段:是否正在发送
|
|
Message(this.messageId, this.msg, this.isMeSend, {this.isSending = false});
|
|
}
|
|
|
|
class ChatPage extends StatefulWidget {
|
|
final String roomId;
|
|
final String robotAvatar;
|
|
final String robotName;
|
|
|
|
const ChatPage({
|
|
Key? key,
|
|
required this.roomId,
|
|
required this.robotAvatar,
|
|
required this.robotName,
|
|
}) : super(key: key);
|
|
|
|
@override
|
|
_ChatPageState createState() => _ChatPageState();
|
|
}
|
|
|
|
class _ChatPageState extends State<ChatPage> {
|
|
late ChatListController<Message> controller;
|
|
int size = 50;
|
|
int current = 0;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
controller = ChatListController<Message>();
|
|
C.socket.addMessageListener(onMessage);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
try {
|
|
controller.dispose();
|
|
} catch (e) {}
|
|
C.socket.removeMessageListener(onMessage);
|
|
C.PUSH_STATUS = true;
|
|
super.dispose();
|
|
}
|
|
|
|
void onMessage(dynamic message) {
|
|
if (message["roomId"] == widget.roomId) {
|
|
controller.addNewMessage(Message(message["messageId"],message["content"], false));
|
|
ChatRoomPage.flushData!({
|
|
"roomId": message["roomId"],
|
|
"message": message["content"],
|
|
});
|
|
} else {
|
|
Notify.showNotification(
|
|
channel: NotifyChannel.message,
|
|
title: message["name"],
|
|
body: message["content"],
|
|
smallIcon: message["avatar"],
|
|
payload: {
|
|
"type": C.NOTIFY_MESSAGE,
|
|
"roomId": message["roomId"],
|
|
"avatar": message["avatar"],
|
|
"name": message["name"],
|
|
},
|
|
);
|
|
ChatRoomPage.flushData!({
|
|
"roomId": message["roomId"],
|
|
"message": message["content"],
|
|
});
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final theme = Theme.of(context);
|
|
final activeColor = theme.colorScheme.primary;
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text(
|
|
widget.robotName,
|
|
style: const TextStyle(color: Colors.black),
|
|
),
|
|
centerTitle: true,
|
|
backgroundColor: Colors.grey[100],
|
|
leading: IconButton(
|
|
icon: const Icon(Icons.arrow_back, color: Colors.black),
|
|
onPressed: () => Navigator.pop(context),
|
|
),
|
|
elevation: 0,
|
|
),
|
|
body: Column(
|
|
children: [
|
|
Expanded(
|
|
child: Padding(
|
|
padding: EdgeInsets.symmetric(horizontal: 10),
|
|
child: ChatListBuilder<Message>(
|
|
controller: controller,
|
|
loadHistory: () async {
|
|
current++;
|
|
dynamic result = await HTTP
|
|
.create('${C.BASE_URL}/chat/history/${widget.roomId}')
|
|
.setHeader(C.TOKEN)
|
|
.setParam({"size": size, "current": current})
|
|
.setRequestType(RequestType.GET)
|
|
.execute();
|
|
|
|
if (result['code'] != 200) {
|
|
return LoadHistoryResponse(isHasMore: false, data: []);
|
|
}
|
|
|
|
List data = result['data']['result'];
|
|
int rows = result['data']['rows'];
|
|
print("加载历史第 $current 页");
|
|
|
|
return LoadHistoryResponse(
|
|
isHasMore: current < rows,
|
|
data: List.generate(
|
|
data.length,
|
|
(index) => Message(
|
|
data[index]['messageId'],
|
|
data[index]['message'] ?? "",
|
|
data[index]['from'] == 1,
|
|
),
|
|
),
|
|
);
|
|
},
|
|
loadingBackgroundColor: Colors.white,
|
|
itemBuilder: (_, element) {
|
|
if (element.isMeSend) {
|
|
return Row(
|
|
textDirection: TextDirection.rtl,
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
children: [
|
|
CacheAvatar(
|
|
url: C.PROFILE["avatar"],
|
|
size: Size(40, 40),
|
|
),
|
|
Container(
|
|
margin: const EdgeInsets.all(10),
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 10,
|
|
vertical: 2,
|
|
),
|
|
constraints: BoxConstraints(
|
|
maxWidth: MediaQuery.of(context).size.width * 0.7,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: activeColor,
|
|
borderRadius: BorderRadius.circular(15),
|
|
),
|
|
child: MarkdownSelectableText(
|
|
element.msg,
|
|
style: TextStyle(fontSize: 16),
|
|
),
|
|
),
|
|
if (element.isSending)
|
|
const Padding(
|
|
padding: EdgeInsets.only(right: 8.0),
|
|
child: SizedBox(
|
|
width: 16,
|
|
height: 16,
|
|
child: CircularProgressIndicator(strokeWidth: 2),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
} else {
|
|
return Row(
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
children: [
|
|
CacheAvatar(
|
|
url: widget.robotAvatar,
|
|
size: Size(40, 40),
|
|
),
|
|
Container(
|
|
margin: const EdgeInsets.all(10),
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 10,
|
|
vertical: 2,
|
|
),
|
|
constraints: BoxConstraints(
|
|
maxWidth: MediaQuery.of(context).size.width * 0.7,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: Colors.grey[200],
|
|
borderRadius: BorderRadius.circular(15),
|
|
),
|
|
child: MarkdownSelectableText(
|
|
element.msg,
|
|
style: TextStyle(fontSize: 16),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
},
|
|
),
|
|
),
|
|
),
|
|
ChatInput(
|
|
onSend: (str) async {
|
|
String uid =UID.getShort();
|
|
// 先添加一个正在发送的消息
|
|
final message = Message(uid, str, true, isSending: true);
|
|
ChatRoomPage.flushData!({
|
|
"roomId": widget.roomId,
|
|
"message": str,
|
|
});
|
|
controller.addNewMessage(message);
|
|
Future.delayed(const Duration(milliseconds: 300), () {
|
|
controller.scrollController.animateTo(
|
|
controller.scrollController.position.maxScrollExtent,
|
|
duration: const Duration(milliseconds: 300),
|
|
curve: Curves.easeOut,
|
|
);
|
|
});
|
|
// 发送消息
|
|
await HTTP
|
|
.create("${C.BASE_URL}/chat/send/${widget.roomId}")
|
|
.setHeader(C.TOKEN)
|
|
.setParam({"message": str,"messageId":uid})
|
|
.setRequestType(RequestType.POST)
|
|
.execute();
|
|
|
|
// 请求完成后,取消 loading
|
|
setState(() {
|
|
message.isSending = false;
|
|
});
|
|
},
|
|
),
|
|
const SizedBox(height: 10),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|