elysia/lib/page/child/ChatPage.dart
2025-11-04 09:53:47 +08:00

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),
],
),
);
}
}