Browse Source

feat(message): 更新消息页面并优化图片展示逻辑

- 在获取会话列表后添加发送文本消息功能
- 移除图像项中的网络缩略图显示逻辑
- 修改图像项优先显示本地路径图片- 移除图像加载进度构建器- 调整消息页面Tab内容区域布局结构
- 移除不必要的日志打印语句
- 导入retrofit/http包用于图像项组件
ios
Jolie 4 months ago
parent
commit
be2e7de461
6 changed files with 423 additions and 267 deletions
  1. 67
      lib/controller/message/chat_controller.dart
  2. 24
      lib/im/im_manager.dart
  3. 113
      lib/pages/message/chat_page.dart
  4. 55
      lib/widget/message/chat_input_bar.dart
  5. 430
      pubspec.lock
  6. 1
      pubspec.yaml

67
lib/controller/message/chat_controller.dart

@ -5,13 +5,13 @@ import 'package:im_flutter_sdk/im_flutter_sdk.dart';
class ChatController extends GetxController {
final String userId;
//
final Rx<EMUserInfo?> userInfo = Rx<EMUserInfo?>(null);
//
final RxList<EMMessage> messages = RxList<EMMessage>([]);
//
String? _cursor;
@ -64,11 +64,14 @@ class ChatController extends GetxController {
return false;
}
}
///
Future<bool> sendImageMessage(String imagePath) async {
try {
final message = await IMManager.instance.sendImageMessage(imagePath, userId);
final message = await IMManager.instance.sendImageMessage(
imagePath,
userId,
);
if (message != null) {
//
messages.insert(0, message);
@ -83,19 +86,45 @@ class ChatController extends GetxController {
return false;
}
}
///
Future<void> fetchMessages({bool loadMore = false}) async {
///
Future<bool> sendVoiceMessage(String filePath, int seconds) async {
try {
final List<EMMessage?> fetchedMessages = await IMManager.instance.getMessages(
final message = await IMManager.instance.sendVoiceMessage(
filePath,
userId,
pageSize: 20,
startMsgId: loadMore ? _cursor : null,
seconds,
);
if (message != null) {
//
messages.insert(0, message);
update();
return true;
}
return false;
} catch (e) {
if (Get.isLogEnable) {
Get.log('发送语音消息失败: $e');
}
return false;
}
}
///
Future<void> fetchMessages({bool loadMore = false}) async {
try {
final List<EMMessage?> fetchedMessages = await IMManager.instance
.getMessages(
userId,
pageSize: 20,
startMsgId: loadMore ? _cursor : null,
);
// null消息
final List<EMMessage> validMessages = fetchedMessages.whereType<EMMessage>().toList();
final List<EMMessage> validMessages = fetchedMessages
.whereType<EMMessage>()
.toList();
if (loadMore) {
//
messages.addAll(validMessages);
@ -103,15 +132,15 @@ class ChatController extends GetxController {
//
messages.assignAll(validMessages);
}
// UI更新
update();
//
if (validMessages.isNotEmpty) {
_cursor = validMessages.last.msgId;
}
if (Get.isLogEnable) {
Get.log('获取消息成功,数量: ${validMessages.length}');
}
@ -121,11 +150,11 @@ class ChatController extends GetxController {
}
}
}
///
Future<void> loadMoreMessages() async {
if (_cursor != null) {
await fetchMessages(loadMore: true);
}
}
}
}

24
lib/im/im_manager.dart

@ -165,6 +165,30 @@ class IMManager {
}
}
///
Future<EMMessage?> sendVoiceMessage(
String filePath,
String toChatUsername,
int duration
) async {
try {
//
final message = EMMessage.createVoiceSendMessage(
targetId: toChatUsername,
filePath: filePath,
duration: duration
);
//
await EMClient.getInstance.chatManager.sendMessage(message);
print('Image message sent successfully');
return message;
} catch (e) {
print('Failed to send image message: $e');
return null;
}
}
///
Future<EMMessage?> sendImageMessage(
String imagePath,

113
lib/pages/message/chat_page.dart

@ -14,57 +14,54 @@ class ChatPage extends StatelessWidget {
const ChatPage({required this.userId, super.key});
@override
Widget build(BuildContext context) {
return GetBuilder<ChatController>(
init: ChatController(userId: userId),
builder: (controller) {
return Scaffold(
backgroundColor: Color(0xffF5F5F5),
appBar: AppBar(
title: Text(controller.userInfo.value?.nickName ?? ''),
centerTitle: true,
actions: [
Container(
padding: EdgeInsets.only(right: 16.w),
child: Image.asset(Assets.imagesMore, width: 16.w,),
).onTap(() {
})
],
leading: IconButton(
icon: Icon(Icons.arrow_back_ios),
onPressed: () {
Get.back();
},
),
),
body: Column(
children: [
//
Expanded(
child: Container(
color: Color(0xffF5F5F5),
child: GestureDetector(
onTap: () {
//
FocusManager.instance.primaryFocus?.unfocus();
backgroundColor: Color(0xffF5F5F5),
appBar: AppBar(
title: Text(controller.userInfo.value?.nickName ?? ''),
centerTitle: true,
actions: [
Container(
padding: EdgeInsets.only(right: 16.w),
child: Image.asset(Assets.imagesMore, width: 16.w),
).onTap(() {}),
],
leading: IconButton(
icon: Icon(Icons.arrow_back_ios),
onPressed: () {
Get.back();
},
behavior: HitTestBehavior.opaque,
child: ListView.builder(
),
),
body: Column(
children: [
//
Expanded(
child: Container(
color: Color(0xffF5F5F5),
child: GestureDetector(
onTap: () {
//
FocusManager.instance.primaryFocus?.unfocus();
},
behavior: HitTestBehavior.opaque,
child: ListView.builder(
reverse: true,
padding: EdgeInsets.all(16.w),
itemCount: controller.messages.length,
itemBuilder: (context, index) {
final message = controller.messages[index];
final isSentByMe = message.direction == MessageDirection.SEND;
final previousMessage = index > 0 ? controller.messages[index - 1] : null;
final isSentByMe =
message.direction == MessageDirection.SEND;
final previousMessage = index > 0
? controller.messages[index - 1]
: null;
return MessageItem(
message: message,
isSentByMe: isSentByMe,
@ -72,24 +69,28 @@ class ChatPage extends StatelessWidget {
);
},
),
),
),
),
// 使
ChatInputBar(
onSendMessage: (message) async {
await controller.sendMessage(message);
},
onImageSelected: (imagePaths) async {
//
for (var imagePath in imagePaths) {
await controller.sendImageMessage(imagePath);
}
},
),
),
),
// 使
ChatInputBar(
onSendMessage: (message) async {
await controller.sendMessage(message);
},
onImageSelected: (imagePaths) async {
//
for (var imagePath in imagePaths) {
await controller.sendImageMessage(imagePath);
}
},
onVoiceRecorded: (filePath, seconds) async {
//
await controller.sendVoiceMessage(filePath, seconds);
},
),
],
),
],
),
);
);
},
);
}

55
lib/widget/message/chat_input_bar.dart

@ -1,16 +1,22 @@
import 'package:dating_touchme_app/extension/ex_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import '../../generated/assets.dart';
import 'more_options_view.dart';
import 'voice_input_view.dart';
class ChatInputBar extends StatefulWidget {
final ValueChanged<String> onSendMessage;
final ValueChanged<List<String>>? onImageSelected;
final Function(String filePath, int seconds)? onVoiceRecorded;
const ChatInputBar({required this.onSendMessage, this.onImageSelected, super.key});
const ChatInputBar({
required this.onSendMessage,
this.onImageSelected,
this.onVoiceRecorded,
super.key,
});
@override
State<ChatInputBar> createState() => _ChatInputBarState();
@ -19,6 +25,7 @@ class ChatInputBar extends StatefulWidget {
class _ChatInputBarState extends State<ChatInputBar> {
final TextEditingController _textController = TextEditingController();
bool _isMoreOptionsVisible = false;
bool _isVoiceVisible = false;
void _handleSendMessage() {
if (_textController.text.isNotEmpty) {
@ -31,6 +38,9 @@ class _ChatInputBarState extends State<ChatInputBar> {
void _toggleMoreOptions() {
setState(() {
_isMoreOptionsVisible = !_isMoreOptionsVisible;
if (_isMoreOptionsVisible) {
_isVoiceVisible = false;
}
//
FocusManager.instance.primaryFocus?.unfocus();
});
@ -50,6 +60,16 @@ class _ChatInputBarState extends State<ChatInputBar> {
}
}
void _toggleVoiceOptions() {
setState(() {
_isVoiceVisible = !_isVoiceVisible;
if (_isVoiceVisible) {
_isMoreOptionsVisible = false;
}
FocusManager.instance.primaryFocus?.unfocus();
});
}
@override
Widget build(BuildContext context) {
return Column(
@ -76,7 +96,10 @@ class _ChatInputBarState extends State<ChatInputBar> {
decoration: InputDecoration(
border: InputBorder.none,
hintText: "请输入聊天内容~",
hintStyle: TextStyle(fontSize: 14.sp, color: Colors.grey),
hintStyle: TextStyle(
fontSize: 14.sp,
color: Colors.grey,
),
),
style: TextStyle(fontSize: 14.sp, color: Colors.black),
),
@ -85,7 +108,10 @@ class _ChatInputBarState extends State<ChatInputBar> {
SizedBox(width: 12.w),
//
Container(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
padding: EdgeInsets.symmetric(
horizontal: 16.w,
vertical: 8.h,
),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(5.h),
@ -107,7 +133,11 @@ class _ChatInputBarState extends State<ChatInputBar> {
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
//
Image.asset(Assets.imagesAudio, width: 24.w, height: 24.w),
Image.asset(
Assets.imagesAudio,
width: 24.w,
height: 24.w,
).onTap(_toggleVoiceOptions),
//
Image.asset(Assets.imagesVideo, width: 24.w, height: 24.w),
//
@ -115,7 +145,11 @@ class _ChatInputBarState extends State<ChatInputBar> {
//
Image.asset(Assets.imagesEmoji, width: 24.w, height: 24.w),
//
Image.asset(Assets.imagesAdd, width: 24.w, height: 24.w).onTap(_toggleMoreOptions),
Image.asset(
Assets.imagesAdd,
width: 24.w,
height: 24.w,
).onTap(_toggleMoreOptions),
],
),
],
@ -126,10 +160,13 @@ class _ChatInputBarState extends State<ChatInputBar> {
isVisible: _isMoreOptionsVisible,
onImageSelected: _handleImageTap,
onCameraSelected: _handleCameraTap,
)
),
// MoreOptionsView
VoiceInputView(
isVisible: _isVoiceVisible,
onVoiceRecorded: widget.onVoiceRecorded,
),
],
);
}
}

430
pubspec.lock
File diff suppressed because it is too large
View File

1
pubspec.yaml

@ -57,6 +57,7 @@ dependencies:
wechat_assets_picker: ^9.8.0
wechat_camera_picker: ^4.4.0
tdesign_flutter: ^0.2.5
record: ^6.1.2
dev_dependencies:
flutter_test:

Loading…
Cancel
Save