Browse Source

feat(chat): 添加玫瑰余额显示和图片消息优化

- 在聊天控制器中集成玫瑰控制器并添加余额刷新功能
- 在礼物弹窗中显示玫瑰余额并实现自动刷新机制
- 优化图片消息的下载和显示逻辑,增加本地缓存检查
- 使用CachedNetworkImage替代Image.network提升图片加载体验
- 修复图片加载失败时的错误处理和重试机制
- 调整输入框样式使其居中对齐并优化内边距
- 在聊天页面中添加玫瑰余额刷新功能
master
Jolie 2 months ago
parent
commit
89fde1698c
5 changed files with 211 additions and 34 deletions
  1. 20
      lib/controller/message/chat_controller.dart
  2. 20
      lib/pages/message/chat_page.dart
  3. 53
      lib/widget/message/chat_gift_popup.dart
  4. 4
      lib/widget/message/chat_input_bar.dart
  5. 148
      lib/widget/message/image_item.dart

20
lib/controller/message/chat_controller.dart

@ -11,6 +11,7 @@ import '../../network/network_service.dart';
import '../../widget/live/live_recharge_popup.dart'; import '../../widget/live/live_recharge_popup.dart';
import 'package:im_flutter_sdk/im_flutter_sdk.dart'; import 'package:im_flutter_sdk/im_flutter_sdk.dart';
import 'conversation_controller.dart'; import 'conversation_controller.dart';
import '../mine/rose_controller.dart';
class ChatController extends GetxController { class ChatController extends GetxController {
final String userId; final String userId;
@ -1238,6 +1239,22 @@ class ChatController extends GetxController {
} }
} }
///
void _refreshRoseBalance() {
try {
// RoseController
if (Get.isRegistered<RoseController>()) {
final roseController = Get.find<RoseController>();
roseController.getRoseNum();
}
} catch (e) {
// RoseController
if (Get.isLogEnable) {
Get.log('刷新玫瑰余额失败: $e');
}
}
}
/// ///
void _checkMessageStatusAndUpdate(String messageId) async { void _checkMessageStatusAndUpdate(String messageId) async {
final currentIndex = messages.indexWhere((msg) => msg.msgId == messageId); final currentIndex = messages.indexWhere((msg) => msg.msgId == messageId);
@ -1801,6 +1818,9 @@ class ChatController extends GetxController {
update(); update();
// //
_refreshConversationList(); _refreshConversationList();
//
_refreshRoseBalance();
if (Get.isLogEnable) { if (Get.isLogEnable) {
Get.log('✅ 礼物消息发送成功: ${gift.productTitle}'); Get.log('✅ 礼物消息发送成功: ${gift.productTitle}');

20
lib/pages/message/chat_page.dart

@ -7,6 +7,7 @@ import 'package:im_flutter_sdk/im_flutter_sdk.dart';
import '../../controller/message/call_controller.dart'; import '../../controller/message/call_controller.dart';
import '../../controller/message/chat_controller.dart'; import '../../controller/message/chat_controller.dart';
import '../../controller/message/voice_player_manager.dart'; import '../../controller/message/voice_player_manager.dart';
import '../../controller/mine/rose_controller.dart';
import '../../generated/assets.dart'; import '../../generated/assets.dart';
import '../../model/home/marriage_data.dart'; import '../../model/home/marriage_data.dart';
import '../../model/rtc/chat_audio_product_model.dart'; import '../../model/rtc/chat_audio_product_model.dart';
@ -131,6 +132,9 @@ class _ChatPageState extends State<ChatPage> {
return; return;
} }
// LiveRoomPage
_refreshRoseBalance();
SmartDialog.show( SmartDialog.show(
builder: (context) { builder: (context) {
return ChatGiftPopup( return ChatGiftPopup(
@ -155,6 +159,22 @@ class _ChatPageState extends State<ChatPage> {
); );
} }
// LiveRoomPage
Future<void> _refreshRoseBalance() async {
try {
// RoseController
final roseController = Get.isRegistered<RoseController>()
? Get.find<RoseController>()
: Get.put(RoseController());
//
await roseController.getRoseNum();
print('✅ 刷新玫瑰余额成功: ${roseController.roseNum.value}');
} catch (e) {
print('❌ 刷新玫瑰余额失败: $e');
}
}
// //
void _showCallTypeSelectionDialog( void _showCallTypeSelectionDialog(
ChatController controller, { ChatController controller, {

53
lib/widget/message/chat_gift_popup.dart

@ -3,6 +3,9 @@ import 'package:dating_touchme_app/widget/message/chat_gift_item.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:dating_touchme_app/controller/mine/rose_controller.dart';
import 'package:dating_touchme_app/generated/assets.dart';
class ChatGiftPopup extends StatefulWidget { class ChatGiftPopup extends StatefulWidget {
const ChatGiftPopup({ const ChatGiftPopup({
@ -32,6 +35,52 @@ class _ChatGiftPopupState extends State<ChatGiftPopup> {
if (widget.giftList.isNotEmpty && widget.activeGift.value == null) { if (widget.giftList.isNotEmpty && widget.activeGift.value == null) {
widget.activeGift.value = 0; widget.activeGift.value = 0;
} }
//
_refreshRoseBalance();
}
//
void _refreshRoseBalance() {
try {
// RoseController
final roseController = Get.isRegistered<RoseController>()
? Get.find<RoseController>()
: Get.put(RoseController());
//
roseController.getRoseNum();
print('✅ ChatGiftPopup 刷新玫瑰余额: ${roseController.roseNum.value}');
} catch (e) {
print('❌ ChatGiftPopup 刷新玫瑰余额失败: $e');
}
}
// LiveGiftPopup
Widget _buildRoseBalance() {
// RoseController
final roseController = Get.isRegistered<RoseController>()
? Get.find<RoseController>()
: Get.put(RoseController());
return Row(
children: [
Image.asset(Assets.imagesRoseGift, width: 21.w, height: 21.w),
SizedBox(width: 8.w),
Obx(() {
// 访 GetX
final roseCount = roseController.roseNum.value;
print('🔄 Obx 更新玫瑰余额: $roseCount');
return Text(
roseCount.toString(),
style: TextStyle(
fontSize: 13.w,
color: Colors.black87, // ChatGiftPopup 使
),
);
}),
SizedBox(width: 12.w),
],
);
} }
// //
@ -149,8 +198,8 @@ class _ChatGiftPopupState extends State<ChatGiftPopup> {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
// 1
SizedBox(width: 1.w),
//
_buildRoseBalance(),
ValueListenableBuilder<int>( ValueListenableBuilder<int>(
valueListenable: widget.giftNum, valueListenable: widget.giftNum,
builder: (context, num, _) { builder: (context, num, _) {

4
lib/widget/message/chat_input_bar.dart

@ -290,6 +290,7 @@ class _ChatInputBarState extends State<ChatInputBar> {
borderRadius: BorderRadius.circular(5.h), borderRadius: BorderRadius.circular(5.h),
), ),
padding: EdgeInsets.symmetric(horizontal: 16.w), padding: EdgeInsets.symmetric(horizontal: 16.w),
alignment: Alignment.center,
child: ExtendedTextField( child: ExtendedTextField(
controller: _textController, controller: _textController,
focusNode: _focusNode, focusNode: _focusNode,
@ -297,6 +298,7 @@ class _ChatInputBarState extends State<ChatInputBar> {
inputFormatters: [ inputFormatters: [
EmojiTextInputFormatter(), EmojiTextInputFormatter(),
], ],
textAlignVertical: TextAlignVertical.center,
decoration: InputDecoration( decoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
hintText: "请输入聊天内容~", hintText: "请输入聊天内容~",
@ -304,6 +306,8 @@ class _ChatInputBarState extends State<ChatInputBar> {
fontSize: 14.sp, fontSize: 14.sp,
color: Colors.grey, color: Colors.grey,
), ),
isDense: true,
contentPadding: EdgeInsets.symmetric(vertical: 0),
), ),
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,

148
lib/widget/message/image_item.dart

@ -63,6 +63,8 @@ class _ImageItemState extends State<ImageItem> {
Future.delayed(Duration(seconds: 1), () { Future.delayed(Duration(seconds: 1), () {
if (mounted) { if (mounted) {
_checkRemotePathAndUpdateStatus(); _checkRemotePathAndUpdateStatus();
_initImagePaths();
setState(() {});
} }
}); });
@ -70,6 +72,23 @@ class _ImageItemState extends State<ImageItem> {
Future.delayed(Duration(seconds: 2), () { Future.delayed(Duration(seconds: 2), () {
if (mounted) { if (mounted) {
_checkRemotePathAndUpdateStatus(); _checkRemotePathAndUpdateStatus();
_initImagePaths();
setState(() {});
}
});
} else {
//
Future.delayed(Duration(milliseconds: 500), () {
if (mounted) {
_initImagePaths();
_checkAndDownloadImage();
}
});
Future.delayed(Duration(seconds: 1), () {
if (mounted) {
_initImagePaths();
_checkAndDownloadImage();
} }
}); });
} }
@ -131,6 +150,9 @@ class _ImageItemState extends State<ImageItem> {
// //
if (widget.isSentByMe) return; if (widget.isSentByMe) return;
//
_initImagePaths();
// //
if (_currentLocalPath != null && _currentLocalPath!.isNotEmpty) { if (_currentLocalPath != null && _currentLocalPath!.isNotEmpty) {
final file = File(_currentLocalPath!); final file = File(_currentLocalPath!);
@ -138,6 +160,11 @@ class _ImageItemState extends State<ImageItem> {
final size = await file.length(); final size = await file.length();
if (size > 1024) { // 1KB才认为有效 if (size > 1024) { // 1KB才认为有效
print('✅ 本地图片已存在且有效: $_currentLocalPath, 大小: ${size/1024}KB'); print('✅ 本地图片已存在且有效: $_currentLocalPath, 大小: ${size/1024}KB');
if (mounted) {
setState(() {
_hasError = false;
});
}
return; return;
} else { } else {
print('⚠️ 本地图片文件太小或无效: $_currentLocalPath, 大小: $size bytes'); print('⚠️ 本地图片文件太小或无效: $_currentLocalPath, 大小: $size bytes');
@ -153,10 +180,21 @@ class _ImageItemState extends State<ImageItem> {
await _downloadImage(); await _downloadImage();
} }
} else { } else {
print('❌ 没有可用的远程图片路径');
setState(() {
_hasError = true;
});
print('❌ 没有可用的远程图片路径,尝试重新获取');
//
_initImagePaths();
if (_currentRemotePath != null && _currentRemotePath!.isNotEmpty) {
if (!_isDownloading) {
await _downloadImage();
}
} else {
print('❌ 仍然没有可用的远程图片路径');
if (mounted) {
setState(() {
_hasError = true;
});
}
}
} }
} }
@ -228,8 +266,15 @@ class _ImageItemState extends State<ImageItem> {
} }
void _retryDownload() { void _retryDownload() {
if (!_isDownloading) {
_downloadImage();
if (!_isDownloading && mounted) {
//
_initImagePaths();
//
setState(() {
_hasError = false;
});
//
_checkAndDownloadImage();
} }
} }
@ -317,9 +362,19 @@ class _ImageItemState extends State<ImageItem> {
} }
// //
//
if (_hasError && !_isDownloading) {
return _buildErrorState(width, height);
//
if (_currentLocalPath != null && _currentLocalPath!.isNotEmpty) {
final file = File(_currentLocalPath!);
if (file.existsSync()) {
try {
final size = file.lengthSync();
if (size > 1024) { // 1KB才认为有效
return _buildLocalImage(width, height);
}
} catch (e) {
print('⚠️ 检查本地文件大小失败: $e');
}
}
} }
// //
@ -327,12 +382,12 @@ class _ImageItemState extends State<ImageItem> {
return _buildDownloadingState(width, height); return _buildDownloadingState(width, height);
} }
//
if (_currentLocalPath != null && _currentLocalPath!.isNotEmpty) {
return _buildLocalImage(width, height);
//
if (_hasError && !_isDownloading) {
return _buildErrorState(width, height);
} }
//
//
if (_currentRemotePath != null && _currentRemotePath!.isNotEmpty) { if (_currentRemotePath != null && _currentRemotePath!.isNotEmpty) {
return _buildNetworkImage(width, height); return _buildNetworkImage(width, height);
} }
@ -384,33 +439,34 @@ class _ImageItemState extends State<ImageItem> {
} }
} }
// 3. loading
// 3. 使 CachedNetworkImage
if (_currentRemotePath != null && _currentRemotePath!.isNotEmpty) { if (_currentRemotePath != null && _currentRemotePath!.isNotEmpty) {
print('🌐 发送方使用远程路径: $_currentRemotePath'); print('🌐 发送方使用远程路径: $_currentRemotePath');
// loading状态loading
return Image.network(
_currentRemotePath!,
// 使 CachedNetworkImage Image.network
return CachedNetworkImage(
imageUrl: _currentRemotePath!,
width: width, width: width,
height: height, height: height,
fit: BoxFit.cover, fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) {
print('✅ 发送方网络图片加载完成');
return child;
}
placeholder: (context, url) {
// loading状态loading // loading状态loading
if (_shouldHideProgressStatus) { if (_shouldHideProgressStatus) {
print('⏭️ 发送方:已超时,跳过网络图片loading状态'); print('⏭️ 发送方:已超时,跳过网络图片loading状态');
return _buildSenderPlaceholder(width, height); return _buildSenderPlaceholder(width, height);
} }
// loading
// loading
return _buildLoadingState(width, height); return _buildLoadingState(width, height);
}, },
errorBuilder: (context, error, stackTrace) {
print('❌ 发送方加载网络图片失败: $error');
errorWidget: (context, url, error) {
print('❌ 发送方加载网络图片失败: $error, url: $url');
// //
return _buildSenderPlaceholder(width, height); return _buildSenderPlaceholder(width, height);
}, },
//
maxWidthDiskCache: 800,
maxHeightDiskCache: 800,
memCacheWidth: 800,
memCacheHeight: 800,
); );
} }
@ -450,34 +506,62 @@ class _ImageItemState extends State<ImageItem> {
fit: BoxFit.cover, fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) { errorBuilder: (context, error, stackTrace) {
print('❌ 加载本地图片失败: $error'); print('❌ 加载本地图片失败: $error');
// 使
if (_currentRemotePath != null && _currentRemotePath!.isNotEmpty) {
print('🔄 本地图片加载失败,尝试使用远程图片');
return _buildNetworkImage(width, height);
}
return _buildErrorState(width, height); return _buildErrorState(width, height);
}, },
); );
} else { } else {
print('❌ 本地图片文件不存在: $_currentLocalPath'); print('❌ 本地图片文件不存在: $_currentLocalPath');
// 使
if (_currentRemotePath != null && _currentRemotePath!.isNotEmpty) {
print('🔄 本地文件不存在,尝试使用远程图片');
return _buildNetworkImage(width, height);
}
return _buildErrorState(width, height); return _buildErrorState(width, height);
} }
} catch (e) { } catch (e) {
print('❌ 加载本地图片异常: $e'); print('❌ 加载本地图片异常: $e');
// 使
if (_currentRemotePath != null && _currentRemotePath!.isNotEmpty) {
print('🔄 本地图片加载异常,尝试使用远程图片');
return _buildNetworkImage(width, height);
}
return _buildErrorState(width, height); return _buildErrorState(width, height);
} }
} }
//
// 使 CachedNetworkImage
Widget _buildNetworkImage(double width, double height) { Widget _buildNetworkImage(double width, double height) {
return Image.network(
_currentRemotePath!,
// 使 CachedNetworkImage Image.network
return CachedNetworkImage(
imageUrl: _currentRemotePath!,
width: width, width: width,
height: height, height: height,
fit: BoxFit.cover, fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
placeholder: (context, url) {
return _buildLoadingState(width, height); return _buildLoadingState(width, height);
}, },
errorBuilder: (context, error, stackTrace) {
print('❌ 加载网络图片失败: $error');
errorWidget: (context, url, error) {
print('❌ 接收方加载网络图片失败: $error, url: $url');
//
if (!_isDownloading) {
Future.microtask(() {
if (mounted && !_isDownloading) {
_checkAndDownloadImage();
}
});
}
return _buildErrorState(width, height); return _buildErrorState(width, height);
}, },
//
maxWidthDiskCache: 800,
maxHeightDiskCache: 800,
memCacheWidth: 800,
memCacheHeight: 800,
); );
} }

Loading…
Cancel
Save