From 403ef7062dc3f451b36b33c4978b6657c5e49621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=AD=90=E8=B4=A4?= Date: Wed, 3 Dec 2025 11:27:36 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E8=BF=87=E6=9C=9F=E6=A3=80=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/controller/mine/my_friend_controller.dart | 4 ++ lib/network/api_urls.dart | 4 +- lib/network/network_config.dart | 20 ++++++++- lib/network/user_api.dart | 8 ++++ lib/network/user_api.g.dart | 42 ++++++++++++++++++- 5 files changed, 74 insertions(+), 4 deletions(-) diff --git a/lib/controller/mine/my_friend_controller.dart b/lib/controller/mine/my_friend_controller.dart index de8e840..339f0af 100644 --- a/lib/controller/mine/my_friend_controller.dart +++ b/lib/controller/mine/my_friend_controller.dart @@ -14,4 +14,8 @@ class MyFriendController extends GetxController with GetSingleTickerProviderStat tabController = TabController(length: 4, vsync: this); } + getFriendList() async { + + } + } \ No newline at end of file diff --git a/lib/network/api_urls.dart b/lib/network/api_urls.dart index 3da7c62..249062c 100644 --- a/lib/network/api_urls.dart +++ b/lib/network/api_urls.dart @@ -87,7 +87,9 @@ class ApiUrls { static const String getDongwoMarriageInformationDetail = 'dating-agency-service/user/get/dongwo/marriage-information-detail'; static const String getPaymentOrderDetail = - '/dating-agency-mall/user/get/payment-order/detail'; + 'dating-agency-mall/user/get/payment-order/detail'; + static const String userPageFriendRelation = + 'dating-agency-chat-audio/user/page/friend-relation'; //首页相关接口 static const String getMarriageList = diff --git a/lib/network/network_config.dart b/lib/network/network_config.dart index c892bf5..39ff207 100644 --- a/lib/network/network_config.dart +++ b/lib/network/network_config.dart @@ -1,3 +1,6 @@ +import 'package:dating_touchme_app/controller/global.dart'; +import 'package:dating_touchme_app/controller/message/conversation_controller.dart'; +import 'package:dating_touchme_app/im/im_manager.dart'; import 'package:dio/dio.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart' hide Response; @@ -91,6 +94,9 @@ class ResponseInterceptor extends Interceptor { if (code == 0) { // 请求成功 handler.next(response); + } else if(code == 401) { + _showError("登录已过期,请重新登录"); + _handleTokenExpired(); } else { // 业务错误 final message = data['message'] ?? '请求失败'; @@ -150,10 +156,20 @@ class ResponseInterceptor extends Interceptor { SmartDialog.showToast(message, displayTime: const Duration(seconds: 2)); } - void _handleTokenExpired() { + void _handleTokenExpired() async { // 处理token过期逻辑,如清除本地数据、跳转登录页等 // 这里可以使用Get.offAllNamed('/login')等方式跳转 - GetStorage().remove('token'); + // 先退出 IM 登录 + await IMManager.instance.logout(); + // 清除会话列表和用户信息缓存 + if (Get.isRegistered()) { + final conversationController = Get.find(); + conversationController.clearConversations(); + } + // 清除本地存储 + GetStorage().erase(); + // 清除全局数据 + GlobalData().logout(); Get.offAll(() => LoginPage()); } } \ No newline at end of file diff --git a/lib/network/user_api.dart b/lib/network/user_api.dart index e3b7ca0..9c61a19 100644 --- a/lib/network/user_api.dart +++ b/lib/network/user_api.dart @@ -204,4 +204,12 @@ abstract class UserApi { @GET(ApiUrls.getHxUserToken) Future>> getChatStaticsInfo(); + + @GET(ApiUrls.userPageFriendRelation) + Future>> userPageFriendRelation( + { + @Query('pageNum') required int pageNum, + @Query('pageSize') required int pageSize, + } + ); } diff --git a/lib/network/user_api.g.dart b/lib/network/user_api.g.dart index c3de8e0..08a145f 100644 --- a/lib/network/user_api.g.dart +++ b/lib/network/user_api.g.dart @@ -1211,7 +1211,7 @@ class _UserApi implements UserApi { Options(method: 'GET', headers: _headers, extra: _extra) .compose( _dio.options, - '/dating-agency-mall/user/get/payment-order/detail', + 'dating-agency-mall/user/get/payment-order/detail', queryParameters: queryParameters, data: _data, ) @@ -1266,6 +1266,46 @@ class _UserApi implements UserApi { return httpResponse; } + @override + Future>> userPageFriendRelation({ + required int pageNum, + required int pageSize, + }) async { + final _extra = {}; + final queryParameters = { + r'pageNum': pageNum, + r'pageSize': pageSize, + }; + final _headers = {}; + const Map? _data = null; + final _options = + _setStreamType>>( + Options(method: 'GET', headers: _headers, extra: _extra) + .compose( + _dio.options, + 'dating-agency-chat-audio/user/page/friend-relation', + queryParameters: queryParameters, + data: _data, + ) + .copyWith( + baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl), + ), + ); + final _result = await _dio.fetch>(_options); + late BaseResponse _value; + try { + _value = BaseResponse.fromJson( + _result.data!, + (json) => WithdrawAuditData.fromJson(json as Map), + ); + } on Object catch (e, s) { + errorLogger?.logError(e, s, _options); + rethrow; + } + final httpResponse = HttpResponse(_value, _result); + return httpResponse; + } + RequestOptions _setStreamType(RequestOptions requestOptions) { if (T != dynamic && !(requestOptions.responseType == ResponseType.bytes || From 32e2fd2ca1a16b3481cca656ade3cdc91acf01df Mon Sep 17 00:00:00 2001 From: Jolie <> Date: Wed, 3 Dec 2025 12:15:08 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E8=81=8A=E5=A4=A9=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E3=80=81=E8=81=8A=E5=A4=A9=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../home/user_information_controller.dart | 59 ++- .../message/chat_settings_controller.dart | 435 +++++++++++++++++- .../message/conversation_controller.dart | 17 +- lib/pages/home/user_information_page.dart | 65 ++- lib/pages/message/chat_page.dart | 373 +++++++-------- lib/pages/message/chat_settings_page.dart | 387 ++++++++++------ lib/pages/message/conversation_tab.dart | 8 +- 7 files changed, 998 insertions(+), 346 deletions(-) diff --git a/lib/controller/home/user_information_controller.dart b/lib/controller/home/user_information_controller.dart index 534e44b..72a00fa 100644 --- a/lib/controller/home/user_information_controller.dart +++ b/lib/controller/home/user_information_controller.dart @@ -13,6 +13,12 @@ class UserInformationController extends GetxController { final nowSelect = 0.obs; final userData = UserInfoData().obs; + + // 加载状态 + final isLoading = true.obs; + + // 错误信息 + final errorMessage = RxString(''); List tagList = [ "北京", "160cm", "想要甜甜的恋爱", "本科", "朋友圈摄影师", "英雄联盟", "流放之路", @@ -25,9 +31,30 @@ class UserInformationController extends GetxController { @override void onInit() { super.onInit(); - _userApi = Get.find(); - - getUserData(); + + // 验证参数 + if (miId.isEmpty) { + errorMessage.value = '用户ID无效'; + isLoading.value = false; + SmartDialog.showToast('用户ID无效'); + return; + } + + if (userId.isEmpty) { + errorMessage.value = '用户ID无效'; + isLoading.value = false; + SmartDialog.showToast('用户ID无效'); + return; + } + + try { + _userApi = Get.find(); + getUserData(); + } catch (e) { + errorMessage.value = '初始化失败'; + isLoading.value = false; + SmartDialog.showToast('初始化失败,请稍后重试'); + } } int calculateAge(String birthdayStr) { @@ -49,19 +76,27 @@ class UserInformationController extends GetxController { getUserData() async { try { + isLoading.value = true; + errorMessage.value = ''; + final response = await _userApi.getDongwoMarriageInformationDetail(miId: miId); if (response.data.isSuccess && response.data.data != null) { - userData.value = response.data.data ?? UserInfoData(); + userData.value = response.data.data!; + isLoading.value = false; } else { - - // 响应失败,抛出异常 - throw Exception(response.data.message ?? '获取数据失败'); + // 响应失败,设置错误信息 + final errorMsg = response.data.message ?? '获取数据失败'; + errorMessage.value = errorMsg; + isLoading.value = false; + SmartDialog.showToast(errorMsg); } - } catch(e){ - print('钱包数据获取失败: $e'); - SmartDialog.showToast('钱包数据获取失败'); - rethrow; - + } catch(e, stackTrace){ + print('获取用户信息失败: $e'); + print('堆栈跟踪: $stackTrace'); + errorMessage.value = '获取用户信息失败,请稍后重试'; + isLoading.value = false; + SmartDialog.showToast('获取用户信息失败,请稍后重试'); + // 不再 rethrow,避免导致闪退 } } } \ No newline at end of file diff --git a/lib/controller/message/chat_settings_controller.dart b/lib/controller/message/chat_settings_controller.dart index 27e026c..edfddc1 100644 --- a/lib/controller/message/chat_settings_controller.dart +++ b/lib/controller/message/chat_settings_controller.dart @@ -1,15 +1,24 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:get_storage/get_storage.dart'; import 'package:im_flutter_sdk/im_flutter_sdk.dart'; import '../../im/im_manager.dart'; +import '../../model/home/marriage_data.dart'; import '../../pages/home/report_page.dart'; +import '../../pages/home/user_information_page.dart'; +import '../message/conversation_controller.dart'; class ChatSettingsController extends GetxController { final String userId; + final MarriageData? userData; + final _storage = GetStorage(); // 用户信息 final Rx userInfo = Rx(null); + + // 用户miId(用于跳转到用户主页) + String? miId; // 是否拉黑 final RxBool isBlacklisted = RxBool(false); @@ -20,36 +29,297 @@ class ChatSettingsController extends GetxController { // 备注名 final RxString remark = RxString(''); - ChatSettingsController({required this.userId}); + ChatSettingsController({ + required this.userId, + this.userData, + }); @override void onInit() { super.onInit(); - // 初始化时获取用户信息 - fetchUserInfo(); - // 检查黑名单状态 - checkBlacklistStatus(); - // 检查关注状态 - checkFollowStatus(); + + try { + // 验证 userId + if (userId.isEmpty) { + print('❌ [ChatSettings] userId 为空'); + return; + } + + // 如果传入了用户信息,优先使用(同步操作,安全) + if (userData != null) { + try { + miId = userData!.miId; + } catch (e) { + print('❌ [ChatSettings] 设置 miId 失败: $e'); + } + } + + // 加载备注名(同步,立即执行,安全) + try { + loadRemark(); + } catch (e) { + print('❌ [ChatSettings] 加载备注名失败: $e'); + } + + // 延迟执行异步操作,避免阻塞 UI + Future.microtask(() { + try { + // 如果传入了用户信息,同步到缓存 + if (userData != null) { + _syncUserDataToCache(); + } + // 初始化时获取用户信息(异步,不阻塞) + fetchUserInfo(); + // 检查黑名单状态(异步,不阻塞) + checkBlacklistStatus(); + // 检查关注状态(异步,不阻塞) + checkFollowStatus(); + } catch (e, stackTrace) { + print('❌ [ChatSettings] 异步初始化失败: $e'); + print('堆栈跟踪: $stackTrace'); + } + }); + } catch (e, stackTrace) { + print('❌ [ChatSettings] onInit 失败: $e'); + print('堆栈跟踪: $stackTrace'); + } + } + + /// 将传入的用户信息同步到 ConversationController 缓存 + void _syncUserDataToCache() { + try { + if (userData == null) return; + + if (Get.isRegistered()) { + try { + final conversationController = Get.find(); + final extendedInfo = ExtendedUserInfo( + userId: userId, + nickName: userData!.nickName, + avatarUrl: userData!.profilePhoto.trim().replaceAll('`', ''), + ); + conversationController.cacheUserInfo(userId, extendedInfo); + if (Get.isLogEnable) { + Get.log('✅ [ChatSettings] 已同步用户信息到缓存: nickName=${userData!.nickName}'); + } + } catch (e) { + if (Get.isLogEnable) { + Get.log('⚠️ [ChatSettings] 获取 ConversationController 失败: $e'); + } + } + } + } catch (e) { + if (Get.isLogEnable) { + Get.log('⚠️ [ChatSettings] 同步用户信息到缓存失败: $e'); + } + } } /// 获取用户信息 Future fetchUserInfo() async { try { - final EMUserInfo? info = await IMManager.instance.getUserInfo(userId); - if (info != null) { - userInfo.value = info; + // 0. 如果传入了用户信息,优先使用(已在 onInit 中处理) + if (userData != null) { if (Get.isLogEnable) { - Get.log('获取用户信息成功: ${info.nickName}'); + Get.log('✅ [ChatSettings] 使用传入的用户信息: nickName=${userData!.nickName}, miId=${userData!.miId}'); } - } else { + // 仍然尝试从IM获取,以便更新 userInfo.value + } + + // 1. 优先从ConversationController的缓存中获取用户信息 + if (Get.isRegistered()) { + try { + final conversationController = Get.find(); + final cachedUserInfo = conversationController.getCachedUserInfo(userId); + if (cachedUserInfo != null && (cachedUserInfo.nickName != null || cachedUserInfo.avatarUrl != null)) { + // 使用缓存中的用户信息创建 EMUserInfo + // 注意:EMUserInfo 可能没有公开构造函数,所以我们先尝试从IM获取 + // 如果IM没有,我们使用缓存的信息来显示 + if (Get.isLogEnable) { + Get.log('✅ [ChatSettings] 从缓存获取到用户信息: userId=$userId, nickName=${cachedUserInfo.nickName}'); + } + } + } catch (e) { + if (Get.isLogEnable) { + Get.log('⚠️ [ChatSettings] 获取 ConversationController 失败: $e'); + } + } + } + + // 2. 尝试从IM获取用户信息 + EMUserInfo? info; + try { + info = await IMManager.instance.getUserInfo(userId); + if (info != null && (info.nickName?.isNotEmpty ?? false)) { + userInfo.value = info; + if (Get.isLogEnable) { + Get.log('✅ [ChatSettings] 从IM获取到用户信息: ${info.nickName}'); + } + } + } catch (e) { if (Get.isLogEnable) { - Get.log('未找到用户信息: $userId'); + Get.log('⚠️ [ChatSettings] 从IM获取用户信息失败: $e'); + } + } + + // 3. 如果IM没有用户信息,尝试从会话历史消息中提取 + if (userInfo.value == null || (userInfo.value?.nickName?.isEmpty ?? true)) { + await _fetchUserInfoFromConversation(); + } + + // 4. 尝试从会话历史消息中获取miId + await _tryGetMiIdFromConversation(); + } catch (e) { + if (Get.isLogEnable) { + Get.log('❌ [ChatSettings] 获取用户信息失败: $e'); + } + } + } + + /// 从会话历史消息中提取用户信息 + Future _fetchUserInfoFromConversation() async { + try { + final conversation = await EMClient.getInstance.chatManager.getConversation( + userId, + type: EMConversationType.Chat, + createIfNeed: false, + ); + + if (conversation == null) { + if (Get.isLogEnable) { + Get.log('⚠️ [ChatSettings] 会话不存在: $userId'); + } + return; + } + + final messages = await conversation.loadMessages(loadCount: 20); + + for (var message in messages) { + try { + final attributes = message.attributes; + if (attributes == null || attributes.isEmpty) { + continue; + } + + String? nickName; + String? avatarUrl; + + if (message.direction == MessageDirection.RECEIVE) { + // 接收到的消息:从扩展字段中提取发送者信息(sender_ 前缀) + final fromUserId = message.from; + if (fromUserId != null && fromUserId == userId) { + nickName = attributes['sender_nickName'] as String? ?? attributes['nickName'] as String?; + avatarUrl = attributes['sender_avatarUrl'] as String? ?? attributes['avatarUrl'] as String?; + } + } else if (message.direction == MessageDirection.SEND) { + // 发送的消息:从扩展字段中提取接收者信息(receiver_ 前缀) + final toUserId = message.to; + if (toUserId != null && toUserId == userId) { + nickName = attributes['receiver_nickName'] as String?; + avatarUrl = attributes['receiver_avatarUrl'] as String?; + } + } + + if (nickName != null || avatarUrl != null) { + if (Get.isLogEnable) { + Get.log('✅ [ChatSettings] 从消息扩展字段提取到用户信息: nickName=$nickName, avatarUrl=$avatarUrl'); + } + + // 将提取到的用户信息保存到 ConversationController 的缓存中 + if (Get.isRegistered()) { + try { + final conversationController = Get.find(); + final extendedInfo = ExtendedUserInfo( + userId: userId, + nickName: nickName, + avatarUrl: avatarUrl, + ); + conversationController.cacheUserInfo(userId, extendedInfo); + } catch (e) { + if (Get.isLogEnable) { + Get.log('⚠️ [ChatSettings] 获取 ConversationController 失败: $e'); + } + } + } + + // 如果IM没有返回用户信息,尝试再次从IM获取(可能信息已更新) + if (userInfo.value == null || (userInfo.value?.nickName?.isEmpty ?? true)) { + try { + final imInfo = await IMManager.instance.getUserInfo(userId); + if (imInfo != null) { + userInfo.value = imInfo; + } + } catch (e) { + // 忽略错误,使用缓存的信息 + } + } + + break; + } + } catch (e) { + continue; + } + } + + // 再次尝试从缓存获取 + if (Get.isRegistered()) { + try { + final conversationController = Get.find(); + final cachedUserInfo = conversationController.getCachedUserInfo(userId); + if (cachedUserInfo != null && userInfo.value == null) { + // 如果IM没有返回用户信息,但缓存中有,尝试再次从IM获取 + // 或者直接使用缓存的信息(通过 getDisplayName 方法) + if (Get.isLogEnable) { + Get.log('✅ [ChatSettings] 使用缓存中的用户信息: nickName=${cachedUserInfo.nickName}'); + } + } + } catch (e) { + if (Get.isLogEnable) { + Get.log('⚠️ [ChatSettings] 获取 ConversationController 失败: $e'); + } + } + } + } catch (e) { + if (Get.isLogEnable) { + Get.log('❌ [ChatSettings] 从会话提取用户信息失败: $e'); + } + } + } + + /// 尝试从会话历史消息中获取miId + Future _tryGetMiIdFromConversation() async { + try { + final conversation = await EMClient.getInstance.chatManager.getConversation( + userId, + type: EMConversationType.Chat, + createIfNeed: false, + ); + if (conversation != null) { + final messages = await conversation.loadMessages(loadCount: 20); + for (var message in messages) { + try { + final attributes = message.attributes; + if (attributes != null && attributes.isNotEmpty) { + // 尝试从消息扩展字段中获取miId + final msgMiId = attributes['sender_miId'] as String? ?? + attributes['receiver_miId'] as String? ?? + attributes['miId'] as String?; + if (msgMiId != null && msgMiId.isNotEmpty) { + miId = msgMiId; + if (Get.isLogEnable) { + Get.log('从消息扩展字段获取到miId: $miId'); + } + break; + } + } + } catch (e) { + continue; + } } } } catch (e) { if (Get.isLogEnable) { - Get.log('获取用户信息失败: $e'); + Get.log('从会话获取miId失败: $e'); } } } @@ -127,10 +397,19 @@ class ChatSettingsController extends GetxController { } } + /// 加载备注名 + void loadRemark() { + final savedRemark = _storage.read('remark_$userId'); + if (savedRemark != null) { + remark.value = savedRemark; + } + } + /// 设置备注名 Future setRemark(String newRemark) async { try { - // TODO: 调用实际的设置备注名API + // 保存到本地存储 + await _storage.write('remark_$userId', newRemark); remark.value = newRemark; SmartDialog.showToast('备注名设置成功'); update(); @@ -141,11 +420,137 @@ class ChatSettingsController extends GetxController { SmartDialog.showToast('设置失败,请重试'); } } + + /// 获取显示名称(优先显示备注名,其次显示昵称) + String getDisplayName() { + try { + if (remark.value.isNotEmpty) { + return remark.value; + } + + // 优先使用传入的用户信息 + if (userData != null && userData!.nickName.isNotEmpty) { + return userData!.nickName; + } + + // 其次使用 IM 返回的用户信息 + if (userInfo.value != null && userInfo.value!.nickName != null && userInfo.value!.nickName!.isNotEmpty) { + return userInfo.value!.nickName!; + } + + // 如果 IM 没有,尝试从 ConversationController 缓存中获取 + if (Get.isRegistered()) { + try { + final conversationController = Get.find(); + final cachedUserInfo = conversationController.getCachedUserInfo(userId); + if (cachedUserInfo != null && cachedUserInfo.nickName != null && cachedUserInfo.nickName!.isNotEmpty) { + return cachedUserInfo.nickName!; + } + } catch (e) { + // 忽略错误,使用默认值 + } + } + + return userId.isNotEmpty ? userId : '未知用户'; + } catch (e) { + print('❌ [ChatSettings] getDisplayName 失败: $e'); + return userId.isNotEmpty ? userId : '未知用户'; + } + } + + /// 获取用户头像URL + String? getAvatarUrl() { + try { + // 优先使用传入的用户信息 + if (userData != null && userData!.profilePhoto.isNotEmpty) { + return userData!.profilePhoto.trim().replaceAll('`', ''); + } + + // 其次使用 IM 返回的用户信息 + if (userInfo.value != null && userInfo.value!.avatarUrl != null && userInfo.value!.avatarUrl!.isNotEmpty) { + return userInfo.value!.avatarUrl!; + } + + // 如果 IM 没有,尝试从 ConversationController 缓存中获取 + if (Get.isRegistered()) { + try { + final conversationController = Get.find(); + final cachedUserInfo = conversationController.getCachedUserInfo(userId); + if (cachedUserInfo != null && cachedUserInfo.avatarUrl != null && cachedUserInfo.avatarUrl!.isNotEmpty) { + return cachedUserInfo.avatarUrl!; + } + } catch (e) { + // 忽略错误,返回 null + } + } + + return null; + } catch (e) { + print('❌ [ChatSettings] getAvatarUrl 失败: $e'); + return null; + } + } /// 举报用户 void reportUser() { // 跳转到举报页面 Get.to(() => ReportPage()); } + + /// 跳转到用户主页 + Future navigateToUserProfile() async { + try { + String? targetMiId; + String targetUserId = userId; + + // 优先使用传入的用户信息中的 miId + if (userData != null && userData!.miId.isNotEmpty) { + targetMiId = userData!.miId; + targetUserId = userData!.userId.isNotEmpty ? userData!.userId : userId; + } else if (miId != null && miId!.isNotEmpty) { + // 如果已经有miId,直接使用 + targetMiId = miId; + } else { + // 如果没有miId,尝试从会话中获取 + await _tryGetMiIdFromConversation(); + if (miId != null && miId!.isNotEmpty) { + targetMiId = miId; + } + } + + // 验证参数 + if (targetMiId == null || targetMiId.isEmpty) { + SmartDialog.showToast('无法获取用户信息,请稍后重试'); + if (Get.isLogEnable) { + Get.log('❌ [ChatSettings] 无法跳转到用户主页:miId为空'); + } + return; + } + + if (targetUserId.isEmpty) { + SmartDialog.showToast('用户ID无效'); + if (Get.isLogEnable) { + Get.log('❌ [ChatSettings] 无法跳转到用户主页:userId为空'); + } + return; + } + + if (Get.isLogEnable) { + Get.log('✅ [ChatSettings] 跳转到用户主页:miId=$targetMiId, userId=$targetUserId'); + } + + // 跳转到用户主页 + await Get.to(() => UserInformationPage( + miId: targetMiId!, + userId: targetUserId, + )); + } catch (e, stackTrace) { + if (Get.isLogEnable) { + Get.log('❌ [ChatSettings] 跳转到用户主页失败: $e'); + Get.log('堆栈跟踪: $stackTrace'); + } + SmartDialog.showToast('跳转失败,请稍后重试'); + } + } } diff --git a/lib/controller/message/conversation_controller.dart b/lib/controller/message/conversation_controller.dart index 48d6a51..25c2890 100644 --- a/lib/controller/message/conversation_controller.dart +++ b/lib/controller/message/conversation_controller.dart @@ -52,6 +52,11 @@ class ConversationController extends GetxController { Get.log('✅ [ConversationController] 已缓存用户信息: userId=$userId, nickName=${userInfo.nickName}'); } } + + /// 从缓存获取用户信息(公开方法,供 UI 调用) + ExtendedUserInfo? getCachedUserInfo(String userId) { + return _userInfoCache[userId]; + } @override void onInit() { @@ -116,12 +121,14 @@ class ConversationController extends GetxController { // 从IMManager获取会话列表 final List convList = await IMManager.instance .getConversations(); - // 更新会话列表 + + // 先提取用户信息并缓存,然后再更新会话列表 + // 这样可以确保在列表渲染时,用户信息已经在缓存中了 + await _extractUserInfoFromConversations(convList); + + // 更新会话列表(在用户信息提取完成后) conversations.value = convList; - // 从所有会话的历史消息中提取用户信息并缓存(应用重启后恢复用户信息) - _extractUserInfoFromConversations(convList); - // 使用GetX日志系统 if (Get.isLogEnable) { Get.log('Loaded ${convList.length} conversations'); @@ -216,7 +223,7 @@ class ConversationController extends GetxController { break; } } - } + } } } catch (e) { if (Get.isLogEnable) { diff --git a/lib/pages/home/user_information_page.dart b/lib/pages/home/user_information_page.dart index c345b3e..f940a59 100644 --- a/lib/pages/home/user_information_page.dart +++ b/lib/pages/home/user_information_page.dart @@ -23,7 +23,66 @@ class UserInformationPage extends StatelessWidget { return GetX( init: UserInformationController(miId: miId, userId: userId), builder: (controller){ - return controller.userData.value.id != null ? Scaffold( + // 如果正在加载,显示加载中 + if (controller.isLoading.value) { + return Scaffold( + appBar: AppBar( + title: const Text('用户主页'), + ), + body: const Center( + child: CircularProgressIndicator(), + ), + ); + } + + // 如果有错误,显示错误信息 + if (controller.errorMessage.value.isNotEmpty) { + return Scaffold( + appBar: AppBar( + title: const Text('用户主页'), + leading: IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () => Get.back(), + ), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + controller.errorMessage.value, + style: const TextStyle(color: Colors.grey), + ), + const SizedBox(height: 16), + ElevatedButton( + onPressed: () { + controller.getUserData(); + }, + child: const Text('重试'), + ), + ], + ), + ), + ); + } + + // 如果用户数据为空,显示提示 + if (controller.userData.value.id == null || controller.userData.value.id!.isEmpty) { + return Scaffold( + appBar: AppBar( + title: const Text('用户主页'), + leading: IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () => Get.back(), + ), + ), + body: const Center( + child: Text('用户信息不存在'), + ), + ); + } + + return Scaffold( body: Stack( children: [ controller.userData.value.photoList!.isNotEmpty ? CachedNetworkImage( @@ -166,7 +225,7 @@ class UserInformationPage extends StatelessWidget { ), SizedBox(width: 2.w,), Text( - "${controller.calculateAge(controller.userData.value.birthDate ?? "${controller.userData.value.birthYear}-01-01" ?? "")}", + "${controller.calculateAge(controller.userData.value.birthDate ?? (controller.userData.value.birthYear != null && controller.userData.value.birthYear!.isNotEmpty ? "${controller.userData.value.birthYear}-01-01" : ""))}", style: TextStyle( fontSize: 11.w, color: const Color.fromRGBO(120, 140, 255, 1) @@ -452,7 +511,7 @@ class UserInformationPage extends StatelessWidget { ), ) ) : null, - ) : SizedBox(); + ); }, ); } diff --git a/lib/pages/message/chat_page.dart b/lib/pages/message/chat_page.dart index d84e441..39395b1 100644 --- a/lib/pages/message/chat_page.dart +++ b/lib/pages/message/chat_page.dart @@ -14,6 +14,7 @@ import '../../../widget/message/message_item.dart'; import '../../../widget/message/chat_gift_popup.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'chat_settings_page.dart'; +import '../home/user_information_page.dart'; class ChatPage extends StatefulWidget { final String userId; // 对应 MarriageData.userId @@ -143,8 +144,11 @@ class _ChatPageState extends State { padding: EdgeInsets.only(right: 16.w), child: Image.asset(Assets.imagesMore, width: 16.w), ).onTap(() { - // 跳转到聊天设置页面 - Get.to(() => ChatSettingsPage(userId: widget.userId)); + // 跳转到聊天设置页面,传递用户信息 + Get.to(() => ChatSettingsPage( + userId: widget.userId, + userData: widget.userData ?? _controller.userData, + )); }), ], leading: IconButton( @@ -276,218 +280,227 @@ class _ChatPageState extends State { } return IntrinsicHeight( - child: Container( - margin: EdgeInsets.only(bottom: 16.w), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(16.w), - image: DecorationImage( - image: AssetImage(Assets.imagesChatUserBg), - fit: BoxFit.cover, + child: GestureDetector( + onTap: () { + // 点击卡片跳转到用户主页 + Get.to(() => UserInformationPage( + miId: marriageData.miId, + userId: marriageData.userId, + )); + }, + child: Container( + margin: EdgeInsets.only(bottom: 16.w), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16.w), + image: DecorationImage( + image: AssetImage(Assets.imagesChatUserBg), + fit: BoxFit.cover, + ), ), - ), - child: Stack( - children: [ - // 顶层背景图(chat_user_bg_bottom,覆盖整个卡片) - Positioned.fill( - child: ClipRRect( - borderRadius: BorderRadius.circular(16.w), - child: Image.asset( - Assets.imagesChatUserBgBottom, - fit: BoxFit.cover, // 覆盖整个区域,保持宽高比 + child: Stack( + children: [ + // 顶层背景图(chat_user_bg_bottom,覆盖整个卡片) + Positioned.fill( + child: ClipRRect( + borderRadius: BorderRadius.circular(16.w), + child: Image.asset( + Assets.imagesChatUserBgBottom, + fit: BoxFit.cover, // 覆盖整个区域,保持宽高比 + ), ), ), - ), - // 内容(自适应高度) - Padding( - padding: EdgeInsets.all(16.w), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // 头部:名称、标签、箭头 - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // 名称和标签 - Wrap( - spacing: 8.w, - runSpacing: 4.w, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Text( - marriageData.nickName, - style: TextStyle( - fontSize: 16.sp, - fontWeight: FontWeight.w600, - color: Colors.black87, + // 内容(自适应高度) + Padding( + padding: EdgeInsets.all(16.w), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 头部:名称、标签、箭头 + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 名称和标签 + Wrap( + spacing: 8.w, + runSpacing: 4.w, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + Text( + marriageData.nickName, + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w600, + color: Colors.black87, + ), ), - ), - // 实名认证标签(和主页一样) - if (marriageData.isRealNameCertified) + // 实名认证标签(和主页一样) + if (marriageData.isRealNameCertified) + Container( + padding: EdgeInsets.symmetric( + horizontal: 8.w, + vertical: 4.h, + ), + decoration: BoxDecoration( + color: Color(0xFFF3E9FF), + borderRadius: BorderRadius.circular(12.w), + border: Border.all( + width: 1, + color: Color.fromRGBO(117, 98, 249, 0.32), + ), + ), + constraints: BoxConstraints( + minWidth: 40.w, + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Image.asset( + Assets.imagesVerifiedIcon, + width: 14.w, + height: 12.w, + ), + SizedBox(width: 4.w), + Text( + '实名', + style: TextStyle( + fontSize: 9.sp, + color: Color.fromRGBO(160, 92, 255, 1), + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ), + // 在线状态标签(和主页一样) Container( padding: EdgeInsets.symmetric( horizontal: 8.w, vertical: 4.h, ), decoration: BoxDecoration( - color: Color(0xFFF3E9FF), + color: Color.fromRGBO(234, 255, 219, 1), borderRadius: BorderRadius.circular(12.w), border: Border.all( width: 1, color: Color.fromRGBO(117, 98, 249, 0.32), ), ), - constraints: BoxConstraints( - minWidth: 40.w, - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Image.asset( - Assets.imagesVerifiedIcon, - width: 14.w, - height: 12.w, - ), - SizedBox(width: 4.w), - Text( - '实名', - style: TextStyle( - fontSize: 9.sp, - color: Color.fromRGBO(160, 92, 255, 1), - fontWeight: FontWeight.w500, - ), - ), - ], - ), - ), - // 在线状态标签(和主页一样) - Container( - padding: EdgeInsets.symmetric( - horizontal: 8.w, - vertical: 4.h, - ), - decoration: BoxDecoration( - color: Color.fromRGBO(234, 255, 219, 1), - borderRadius: BorderRadius.circular(12.w), - border: Border.all( - width: 1, - color: Color.fromRGBO(117, 98, 249, 0.32), - ), - ), - child: Text( - '在线', - style: TextStyle( - fontSize: 9.sp, - color: Color.fromRGBO(38, 199, 124, 1), - fontWeight: FontWeight.w500, + child: Text( + '在线', + style: TextStyle( + fontSize: 9.sp, + color: Color.fromRGBO(38, 199, 124, 1), + fontWeight: FontWeight.w500, + ), ), ), - ), - ], - ), - SizedBox(height: 8.h), - // 用户信息:性别图标、年龄、位置(根据 MarriageData 显示) - Row( - children: [ - // 性别图标(根据实际数据调整,这里暂时默认显示女性图标) - Image.asset( - Assets.imagesFemale, - width: 14.w, - height: 14.w, - ), - SizedBox(width: 4.w), - Text( - '${marriageData.age}', - style: TextStyle( - fontSize: 12.sp, - color: Colors.grey[700], + ], + ), + SizedBox(height: 8.h), + // 用户信息:性别图标、年龄、位置(根据 MarriageData 显示) + Row( + children: [ + // 性别图标(根据实际数据调整,这里暂时默认显示女性图标) + Image.asset( + Assets.imagesFemale, + width: 14.w, + height: 14.w, ), - ), - if (marriageData.cityName.isNotEmpty) ...[ - SizedBox(width: 12.w), + SizedBox(width: 4.w), Text( - marriageData.cityName, + '${marriageData.age}', style: TextStyle( fontSize: 12.sp, color: Colors.grey[700], ), ), + if (marriageData.cityName.isNotEmpty) ...[ + SizedBox(width: 12.w), + Text( + marriageData.cityName, + style: TextStyle( + fontSize: 12.sp, + color: Colors.grey[700], + ), + ), + ], ], - ], - ), - ], + ), + ], + ), ), - ), - // 右侧箭头 - Container( - width: 32.w, - height: 32.w, - decoration: BoxDecoration( - color: Colors.grey[200], - shape: BoxShape.circle, + // 右侧箭头 + Container( + width: 32.w, + height: 32.w, + decoration: BoxDecoration( + color: Colors.grey[200], + shape: BoxShape.circle, + ), + child: Icon( + Icons.arrow_forward_ios, + size: 16.w, + color: Colors.grey[700], + ), ), - child: Icon( - Icons.arrow_forward_ios, - size: 16.w, - color: Colors.grey[700], + ], + ), + SizedBox(height: 8.h), // 减小间距 + // 个人简介(单行) + if (marriageData.describeInfo.isNotEmpty) + Text( + marriageData.describeInfo, + style: TextStyle( + fontSize: 13.sp, + color: Colors.grey[800], + height: 1.4, ), + maxLines: 1, + overflow: TextOverflow.ellipsis, ), - ], - ), - SizedBox(height: 8.h), // 减小间距 - // 个人简介(单行) - if (marriageData.describeInfo.isNotEmpty) - Text( - marriageData.describeInfo, - style: TextStyle( - fontSize: 13.sp, - color: Colors.grey[800], - height: 1.4, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - if (marriageData.photoList.isNotEmpty) ...[ - SizedBox(height: 8.h), // 图片上方间距 - // 图片画廊(正方形) - Row( - children: marriageData.photoList.take(4).toList().asMap().entries.map((entry) { - final index = entry.key; - final photo = entry.value; - final photoUrl = photo.photoUrl.trim().replaceAll('`', ''); // 移除URL中的反引号 - return Expanded( - child: AspectRatio( - aspectRatio: 1, // 设置为正方形 - child: Container( - margin: EdgeInsets.only( - right: index < 3 ? 8.w : 0, - ), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8.w), - image: DecorationImage( - image: NetworkImage(photoUrl), - fit: BoxFit.cover, - onError: (exception, stackTrace) { - // 加载失败时显示占位图 - return; - }, + if (marriageData.photoList.isNotEmpty) ...[ + SizedBox(height: 8.h), // 图片上方间距 + // 图片画廊(正方形) + Row( + children: marriageData.photoList.take(4).toList().asMap().entries.map((entry) { + final index = entry.key; + final photo = entry.value; + final photoUrl = photo.photoUrl.trim().replaceAll('`', ''); // 移除URL中的反引号 + return Expanded( + child: AspectRatio( + aspectRatio: 1, // 设置为正方形 + child: Container( + margin: EdgeInsets.only( + right: index < 3 ? 8.w : 0, + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.w), + image: DecorationImage( + image: NetworkImage(photoUrl), + fit: BoxFit.cover, + onError: (exception, stackTrace) { + // 加载失败时显示占位图 + return; + }, + ), ), ), ), - ), - ); - }).toList(), - ), - SizedBox(height: 4.h), // 图片下方间距 - ], - ], + ); + }).toList(), + ), + SizedBox(height: 4.h), // 图片下方间距 + ], + ], + ), ), - ), - ], + ], + ), ), ), ); diff --git a/lib/pages/message/chat_settings_page.dart b/lib/pages/message/chat_settings_page.dart index 0c79f27..408ff42 100644 --- a/lib/pages/message/chat_settings_page.dart +++ b/lib/pages/message/chat_settings_page.dart @@ -6,79 +6,128 @@ import 'package:get/get.dart'; import '../../controller/message/chat_settings_controller.dart'; import '../../generated/assets.dart'; +import '../../model/home/marriage_data.dart'; class ChatSettingsPage extends StatelessWidget { final String userId; + final MarriageData? userData; - const ChatSettingsPage({required this.userId, super.key}); + const ChatSettingsPage({ + required this.userId, + this.userData, + super.key, + }); @override Widget build(BuildContext context) { - // 初始化控制器 - Get.put(ChatSettingsController(userId: userId)); - - return Scaffold( - backgroundColor: Colors.white, - appBar: AppBar( - title: Text( - '聊天设置', - style: TextStyle( - fontSize: 18.sp, - fontWeight: FontWeight.w500, - color: Colors.black, + // 验证参数 + if (userId.isEmpty) { + return Scaffold( + appBar: AppBar( + title: const Text('聊天设置'), + leading: IconButton( + icon: const Icon(Icons.arrow_back_ios), + onPressed: () => Get.back(), ), ), - centerTitle: true, - backgroundColor: Colors.white, - elevation: 0, - leading: IconButton( - icon: Icon(Icons.arrow_back_ios, color: Colors.black, size: 20.w), - onPressed: () => Get.back(), + body: const Center( + child: Text('用户ID无效'), ), + ); + } + + // 初始化控制器,传递用户信息 + // 使用 init 参数确保控制器在 GetBuilder 之前初始化 + return GetBuilder( + init: ChatSettingsController( + userId: userId, + userData: userData, ), - body: GetBuilder( - builder: (controller) { - return Column( - children: [ - // 用户信息区域 - _buildUserInfoSection(controller), - - // 分隔线 - Container( - height: 8.h, - color: Color(0xFFF5F5F5), + builder: (controller) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBar( + title: Text( + '聊天设置', + style: TextStyle( + fontSize: 18.sp, + fontWeight: FontWeight.w500, + color: Colors.black, ), - - // 设置选项 - _buildSettingsList(controller), - - // 间隔 - SizedBox(height: 17.h), - - // 关注按钮 - _buildFollowButton(controller), - - SizedBox(height: MediaQuery.of(context).padding.bottom + 20.h), - ], - ); - }, - ), + ), + centerTitle: true, + backgroundColor: Colors.white, + elevation: 0, + leading: IconButton( + icon: Icon(Icons.arrow_back_ios, color: Colors.black, size: 20.w), + onPressed: () => Get.back(), + ), + ), + body: _buildBody(context, controller), + ); + }, ); } + + Widget _buildBody(BuildContext context, ChatSettingsController controller) { + try { + return Column( + children: [ + // 用户信息区域 + _buildUserInfoSection(controller), + + // 分隔线 + Container( + height: 8.h, + color: Color(0xFFF5F5F5), + ), + + // 设置选项 + _buildSettingsList(controller), + + // 间隔 + SizedBox(height: 17.h), + + // 关注按钮 + _buildFollowButton(controller), + + SizedBox(height: MediaQuery.of(context).padding.bottom + 20.h), + ], + ); + } catch (e, stackTrace) { + print('❌ [ChatSettingsPage] _buildBody 失败: $e'); + print('堆栈跟踪: $stackTrace'); + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('页面加载失败: $e'), + SizedBox(height: 16), + ElevatedButton( + onPressed: () => Get.back(), + child: Text('返回'), + ), + ], + ), + ); + } + } // 用户信息区域 Widget _buildUserInfoSection(ChatSettingsController controller) { - return Container( - padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), - child: Row( - children: [ - // 头像 - Obx(() { - final userInfo = controller.userInfo.value; - return ClipOval( - child: userInfo?.avatarUrl != null && userInfo!.avatarUrl!.isNotEmpty + try { + final avatarUrl = controller.getAvatarUrl(); + final displayName = controller.getDisplayName(); + + return Container( + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), + child: Row( + children: [ + // 头像 + ClipOval( + child: avatarUrl != null && avatarUrl.isNotEmpty ? Image.network( - userInfo.avatarUrl!, + avatarUrl, width: 50.w, height: 50.w, fit: BoxFit.cover, @@ -97,38 +146,73 @@ class ChatSettingsPage extends StatelessWidget { height: 50.w, fit: BoxFit.cover, ), - ); - }), - - SizedBox(width: 12.w), - - // 昵称 - Expanded( - child: Obx(() { - final userInfo = controller.userInfo.value; - return Text( - userInfo?.nickName ?? '加载中...', + ), + + SizedBox(width: 12.w), + + // 昵称(优先显示备注名) + Expanded( + child: Text( + displayName, style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w500, color: Colors.black, ), - ); - }), - ), - - // 右箭头 - Icon( - Icons.arrow_forward_ios, - size: 16.w, - color: Color(0xFF999999), - ), - ], - ), - ).onTap(() { - // 点击跳转到用户详情页 - // TODO: 导航到用户详情页 - }); + ), + ), + + // 右箭头 + Icon( + Icons.arrow_forward_ios, + size: 16.w, + color: Color(0xFF999999), + ), + ], + ), + ).onTap(() { + // 点击跳转到用户详情页 + try { + controller.navigateToUserProfile(); + } catch (e) { + print('❌ [ChatSettingsPage] 跳转失败: $e'); + } + }); + } catch (e, stackTrace) { + print('❌ [ChatSettingsPage] _buildUserInfoSection 失败: $e'); + print('堆栈跟踪: $stackTrace'); + return Container( + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), + child: Row( + children: [ + ClipOval( + child: Image.asset( + Assets.imagesAvatarsExample, + width: 50.w, + height: 50.w, + fit: BoxFit.cover, + ), + ), + SizedBox(width: 12.w), + Expanded( + child: Text( + controller.userId.isNotEmpty ? controller.userId : '加载中...', + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w500, + color: Colors.black, + ), + ), + ), + Icon( + Icons.arrow_forward_ios, + size: 16.w, + color: Color(0xFF999999), + ), + ], + ), + ); + } } // 设置选项列表 @@ -154,6 +238,7 @@ class ChatSettingsPage extends StatelessWidget { onChanged: (value) { controller.toggleBlacklist(value); }, + controller: controller, ), _buildDivider(), @@ -209,30 +294,32 @@ class ChatSettingsPage extends StatelessWidget { required String title, required bool value, required ValueChanged onChanged, + required ChatSettingsController controller, }) { - return Container( - padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 12.h), - child: Row( - children: [ - Expanded( - child: Text( - title, - style: TextStyle( - fontSize: 16.sp, - color: Colors.black, + return GetBuilder( + builder: (ctrl) { + return Container( + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 12.h), + child: Row( + children: [ + Expanded( + child: Text( + title, + style: TextStyle( + fontSize: 16.sp, + color: Colors.black, + ), + ), ), - ), + CupertinoSwitch( + value: ctrl.isBlacklisted.value, + onChanged: onChanged, + activeColor: Color.fromRGBO(117, 98, 249, 1), + ), + ], ), - Obx(() { - final controller = Get.find(); - return CupertinoSwitch( - value: controller.isBlacklisted.value, - onChanged: onChanged, - activeColor: Color.fromRGBO(117, 98, 249, 1), - ); - }), - ], - ), + ); + }, ); } @@ -247,39 +334,74 @@ class ChatSettingsPage extends StatelessWidget { // 关注按钮 Widget _buildFollowButton(ChatSettingsController controller) { - return Obx(() { - final isFollowing = controller.isFollowing.value; - return Container( - margin: EdgeInsets.symmetric(horizontal: 25.w), - width: double.infinity, - height: 50.h, - child: ElevatedButton( - onPressed: () { - controller.toggleFollow(); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Color.fromRGBO(117, 98, 249, 1), - foregroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(24.r), + return GetBuilder( + builder: (ctrl) { + try { + final isFollowing = ctrl.isFollowing.value; + return Container( + margin: EdgeInsets.symmetric(horizontal: 25.w), + width: double.infinity, + height: 50.h, + child: ElevatedButton( + onPressed: () { + try { + ctrl.toggleFollow(); + } catch (e) { + print('❌ [ChatSettingsPage] 切换关注状态失败: $e'); + } + }, + style: ElevatedButton.styleFrom( + backgroundColor: Color.fromRGBO(117, 98, 249, 1), + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(24.r), + ), + elevation: 0, + ), + child: Text( + isFollowing ? '已关注' : '关注', + style: TextStyle( + fontSize: 18.sp, + fontWeight: FontWeight.w500, + ), + ), ), - elevation: 0, - ), - child: Text( - isFollowing ? '已关注' : '关注', - style: TextStyle( - fontSize: 18.sp, - fontWeight: FontWeight.w500, + ); + } catch (e) { + print('❌ [ChatSettingsPage] _buildFollowButton 失败: $e'); + return Container( + margin: EdgeInsets.symmetric(horizontal: 25.w), + width: double.infinity, + height: 50.h, + child: ElevatedButton( + onPressed: () {}, + style: ElevatedButton.styleFrom( + backgroundColor: Color.fromRGBO(117, 98, 249, 1), + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(24.r), + ), + elevation: 0, + ), + child: Text( + '关注', + style: TextStyle( + fontSize: 18.sp, + fontWeight: FontWeight.w500, + ), + ), ), - ), - ), - ); - }); + ); + } + }, + ); } // 设置备注名对话框 void _showSetRemarkDialog(ChatSettingsController controller) { - final TextEditingController textController = TextEditingController(); + final TextEditingController textController = TextEditingController( + text: controller.remark.value, // 预填充当前备注名 + ); Get.dialog( AlertDialog( @@ -293,6 +415,7 @@ class ChatSettingsPage extends StatelessWidget { ), ), autofocus: true, + maxLength: 20, // 限制最大长度 ), actions: [ TextButton( @@ -308,6 +431,10 @@ class ChatSettingsPage extends StatelessWidget { if (remark.isNotEmpty) { controller.setRemark(remark); Get.back(); + } else { + // 如果输入为空,清除备注名 + controller.setRemark(''); + Get.back(); } }, child: Text( diff --git a/lib/pages/message/conversation_tab.dart b/lib/pages/message/conversation_tab.dart index e7c5c70..e8fcda5 100644 --- a/lib/pages/message/conversation_tab.dart +++ b/lib/pages/message/conversation_tab.dart @@ -60,8 +60,14 @@ class _ConversationTabState extends State // 构建会话项 Widget _buildConversationItem(EMConversation conversation) { + // 优先从缓存获取用户信息,避免闪烁 + final cachedUserInfo = controller.getCachedUserInfo(conversation.id); + return FutureBuilder( - future: controller.loadContact(conversation.id), + future: cachedUserInfo != null + ? Future.value(cachedUserInfo) + : controller.loadContact(conversation.id), + initialData: cachedUserInfo, builder: (context, userSnapshot) { final ExtendedUserInfo? userInfo = userSnapshot.data; return FutureBuilder(