diff --git a/lib/controller/message/chat_controller.dart b/lib/controller/message/chat_controller.dart index 601cbee..122616e 100644 --- a/lib/controller/message/chat_controller.dart +++ b/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()) { + final roseController = Get.find(); + 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}'); diff --git a/lib/pages/message/chat_page.dart b/lib/pages/message/chat_page.dart index fd0906f..90bece9 100644 --- a/lib/pages/message/chat_page.dart +++ b/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 { return; } + // 刷新玫瑰余额(参考 LiveRoomPage 的实现) + _refreshRoseBalance(); + SmartDialog.show( builder: (context) { return ChatGiftPopup( @@ -155,6 +159,22 @@ class _ChatPageState extends State { ); } + // 刷新玫瑰余额(参考 LiveRoomPage 的实现) + Future _refreshRoseBalance() async { + try { + // 确保 RoseController 已注册,如果未注册则注册它 + final roseController = Get.isRegistered() + ? Get.find() + : Get.put(RoseController()); + + // 刷新玫瑰余额 + await roseController.getRoseNum(); + print('✅ 刷新玫瑰余额成功: ${roseController.roseNum.value}'); + } catch (e) { + print('❌ 刷新玫瑰余额失败: $e'); + } + } + // 显示通话类型选择弹框 void _showCallTypeSelectionDialog( ChatController controller, { diff --git a/lib/widget/message/chat_gift_popup.dart b/lib/widget/message/chat_gift_popup.dart index a99bec8..e346877 100644 --- a/lib/widget/message/chat_gift_popup.dart +++ b/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 { if (widget.giftList.isNotEmpty && widget.activeGift.value == null) { widget.activeGift.value = 0; } + // 刷新玫瑰余额 + _refreshRoseBalance(); + } + + // 刷新玫瑰余额 + void _refreshRoseBalance() { + try { + // 确保 RoseController 已注册,如果未注册则注册它 + final roseController = Get.isRegistered() + ? Get.find() + : Get.put(RoseController()); + + // 刷新玫瑰余额 + roseController.getRoseNum(); + print('✅ ChatGiftPopup 刷新玫瑰余额: ${roseController.roseNum.value}'); + } catch (e) { + print('❌ ChatGiftPopup 刷新玫瑰余额失败: $e'); + } + } + + // 构建玫瑰余额显示(参考 LiveGiftPopup 的实现) + Widget _buildRoseBalance() { + // 确保 RoseController 已注册,如果未注册则注册它 + final roseController = Get.isRegistered() + ? Get.find() + : 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 { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - // 数量选择(暂时不实现,固定为1) - SizedBox(width: 1.w), + // 左下角:显示玫瑰余额 + _buildRoseBalance(), ValueListenableBuilder( valueListenable: widget.giftNum, builder: (context, num, _) { diff --git a/lib/widget/message/chat_input_bar.dart b/lib/widget/message/chat_input_bar.dart index d26ce19..db31b12 100644 --- a/lib/widget/message/chat_input_bar.dart +++ b/lib/widget/message/chat_input_bar.dart @@ -290,6 +290,7 @@ class _ChatInputBarState extends State { 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 { inputFormatters: [ EmojiTextInputFormatter(), ], + textAlignVertical: TextAlignVertical.center, decoration: InputDecoration( border: InputBorder.none, hintText: "请输入聊天内容~", @@ -304,6 +306,8 @@ class _ChatInputBarState extends State { fontSize: 14.sp, color: Colors.grey, ), + isDense: true, + contentPadding: EdgeInsets.symmetric(vertical: 0), ), style: TextStyle( fontSize: 14.sp, diff --git a/lib/widget/message/image_item.dart b/lib/widget/message/image_item.dart index 146882a..8cbf86b 100644 --- a/lib/widget/message/image_item.dart +++ b/lib/widget/message/image_item.dart @@ -63,6 +63,8 @@ class _ImageItemState extends State { Future.delayed(Duration(seconds: 1), () { if (mounted) { _checkRemotePathAndUpdateStatus(); + _initImagePaths(); + setState(() {}); } }); @@ -70,6 +72,23 @@ class _ImageItemState extends State { 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 { // 发送方不需要下载 if (widget.isSentByMe) return; + // 先重新初始化路径(可能消息状态已更新) + _initImagePaths(); + // 如果已经有本地文件,检查是否有效 if (_currentLocalPath != null && _currentLocalPath!.isNotEmpty) { final file = File(_currentLocalPath!); @@ -138,6 +160,11 @@ class _ImageItemState extends State { 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 { 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 { } void _retryDownload() { - if (!_isDownloading) { - _downloadImage(); + if (!_isDownloading && mounted) { + // 重新初始化路径 + _initImagePaths(); + // 重置错误状态 + setState(() { + _hasError = false; + }); + // 重新检查和下载 + _checkAndDownloadImage(); } } @@ -317,9 +362,19 @@ class _ImageItemState extends State { } // 接收方逻辑 - // 如果有错误且正在下载 - 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 { 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 { } } - // 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 { 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, ); }