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 'package:im_flutter_sdk/im_flutter_sdk.dart';
import 'conversation_controller.dart';
import '../mine/rose_controller.dart';
class ChatController extends GetxController {
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 {
final currentIndex = messages.indexWhere((msg) => msg.msgId == messageId);
@ -1801,6 +1818,9 @@ class ChatController extends GetxController {
update();
//
_refreshConversationList();
//
_refreshRoseBalance();
if (Get.isLogEnable) {
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/chat_controller.dart';
import '../../controller/message/voice_player_manager.dart';
import '../../controller/mine/rose_controller.dart';
import '../../generated/assets.dart';
import '../../model/home/marriage_data.dart';
import '../../model/rtc/chat_audio_product_model.dart';
@ -131,6 +132,9 @@ class _ChatPageState extends State<ChatPage> {
return;
}
// LiveRoomPage
_refreshRoseBalance();
SmartDialog.show(
builder: (context) {
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(
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_screenutil/flutter_screenutil.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 {
const ChatGiftPopup({
@ -32,6 +35,52 @@ class _ChatGiftPopupState extends State<ChatGiftPopup> {
if (widget.giftList.isNotEmpty && widget.activeGift.value == null) {
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(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// 1
SizedBox(width: 1.w),
//
_buildRoseBalance(),
ValueListenableBuilder<int>(
valueListenable: widget.giftNum,
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),
),
padding: EdgeInsets.symmetric(horizontal: 16.w),
alignment: Alignment.center,
child: ExtendedTextField(
controller: _textController,
focusNode: _focusNode,
@ -297,6 +298,7 @@ class _ChatInputBarState extends State<ChatInputBar> {
inputFormatters: [
EmojiTextInputFormatter(),
],
textAlignVertical: TextAlignVertical.center,
decoration: InputDecoration(
border: InputBorder.none,
hintText: "请输入聊天内容~",
@ -304,6 +306,8 @@ class _ChatInputBarState extends State<ChatInputBar> {
fontSize: 14.sp,
color: Colors.grey,
),
isDense: true,
contentPadding: EdgeInsets.symmetric(vertical: 0),
),
style: TextStyle(
fontSize: 14.sp,

148
lib/widget/message/image_item.dart

@ -63,6 +63,8 @@ class _ImageItemState extends State<ImageItem> {
Future.delayed(Duration(seconds: 1), () {
if (mounted) {
_checkRemotePathAndUpdateStatus();
_initImagePaths();
setState(() {});
}
});
@ -70,6 +72,23 @@ class _ImageItemState extends State<ImageItem> {
Future.delayed(Duration(seconds: 2), () {
if (mounted) {
_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;
//
_initImagePaths();
//
if (_currentLocalPath != null && _currentLocalPath!.isNotEmpty) {
final file = File(_currentLocalPath!);
@ -138,6 +160,11 @@ class _ImageItemState extends State<ImageItem> {
final size = await file.length();
if (size > 1024) { // 1KB才认为有效
print('✅ 本地图片已存在且有效: $_currentLocalPath, 大小: ${size/1024}KB');
if (mounted) {
setState(() {
_hasError = false;
});
}
return;
} else {
print('⚠️ 本地图片文件太小或无效: $_currentLocalPath, 大小: $size bytes');
@ -153,10 +180,21 @@ class _ImageItemState extends State<ImageItem> {
await _downloadImage();
}
} 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() {
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);
}
//
if (_currentLocalPath != null && _currentLocalPath!.isNotEmpty) {
return _buildLocalImage(width, height);
//
if (_hasError && !_isDownloading) {
return _buildErrorState(width, height);
}
//
//
if (_currentRemotePath != null && _currentRemotePath!.isNotEmpty) {
return _buildNetworkImage(width, height);
}
@ -384,33 +439,34 @@ class _ImageItemState extends State<ImageItem> {
}
}
// 3. loading
// 3. 使 CachedNetworkImage
if (_currentRemotePath != null && _currentRemotePath!.isNotEmpty) {
print('🌐 发送方使用远程路径: $_currentRemotePath');
// loading状态loading
return Image.network(
_currentRemotePath!,
// 使 CachedNetworkImage Image.network
return CachedNetworkImage(
imageUrl: _currentRemotePath!,
width: width,
height: height,
fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) {
print('✅ 发送方网络图片加载完成');
return child;
}
placeholder: (context, url) {
// loading状态loading
if (_shouldHideProgressStatus) {
print('⏭️ 发送方:已超时,跳过网络图片loading状态');
return _buildSenderPlaceholder(width, height);
}
// loading
// loading
return _buildLoadingState(width, height);
},
errorBuilder: (context, error, stackTrace) {
print('❌ 发送方加载网络图片失败: $error');
errorWidget: (context, url, error) {
print('❌ 发送方加载网络图片失败: $error, url: $url');
//
return _buildSenderPlaceholder(width, height);
},
//
maxWidthDiskCache: 800,
maxHeightDiskCache: 800,
memCacheWidth: 800,
memCacheHeight: 800,
);
}
@ -450,34 +506,62 @@ class _ImageItemState extends State<ImageItem> {
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
print('❌ 加载本地图片失败: $error');
// 使
if (_currentRemotePath != null && _currentRemotePath!.isNotEmpty) {
print('🔄 本地图片加载失败,尝试使用远程图片');
return _buildNetworkImage(width, height);
}
return _buildErrorState(width, height);
},
);
} else {
print('❌ 本地图片文件不存在: $_currentLocalPath');
// 使
if (_currentRemotePath != null && _currentRemotePath!.isNotEmpty) {
print('🔄 本地文件不存在,尝试使用远程图片');
return _buildNetworkImage(width, height);
}
return _buildErrorState(width, height);
}
} catch (e) {
print('❌ 加载本地图片异常: $e');
// 使
if (_currentRemotePath != null && _currentRemotePath!.isNotEmpty) {
print('🔄 本地图片加载异常,尝试使用远程图片');
return _buildNetworkImage(width, height);
}
return _buildErrorState(width, height);
}
}
//
// 使 CachedNetworkImage
Widget _buildNetworkImage(double width, double height) {
return Image.network(
_currentRemotePath!,
// 使 CachedNetworkImage Image.network
return CachedNetworkImage(
imageUrl: _currentRemotePath!,
width: width,
height: height,
fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
placeholder: (context, url) {
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);
},
//
maxWidthDiskCache: 800,
maxHeightDiskCache: 800,
memCacheWidth: 800,
memCacheHeight: 800,
);
}

Loading…
Cancel
Save