王子贤 3 months ago
parent
commit
081267ce3a
12 changed files with 1056 additions and 350 deletions
  1. 59
      lib/controller/home/user_information_controller.dart
  2. 435
      lib/controller/message/chat_settings_controller.dart
  3. 17
      lib/controller/message/conversation_controller.dart
  4. 16
      lib/controller/setting/spread_controller.dart
  5. 2
      lib/network/api_urls.dart
  6. 5
      lib/network/user_api.dart
  7. 37
      lib/network/user_api.g.dart
  8. 65
      lib/pages/home/user_information_page.dart
  9. 373
      lib/pages/message/chat_page.dart
  10. 387
      lib/pages/message/chat_settings_page.dart
  11. 8
      lib/pages/message/conversation_tab.dart
  12. 2
      lib/widget/live/live_room_notice_chat_panel.dart

59
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<String> tagList = [
"北京", "160cm", "想要甜甜的恋爱", "本科", "朋友圈摄影师", "英雄联盟", "流放之路",
@ -25,9 +31,30 @@ class UserInformationController extends GetxController {
@override
void onInit() {
super.onInit();
_userApi = Get.find<UserApi>();
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<UserApi>();
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退
}
}
}

435
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<EMUserInfo?> userInfo = Rx<EMUserInfo?>(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<ConversationController>()) {
try {
final conversationController = Get.find<ConversationController>();
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<void> 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<ConversationController>()) {
try {
final conversationController = Get.find<ConversationController>();
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<void> _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<ConversationController>()) {
try {
final conversationController = Get.find<ConversationController>();
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<ConversationController>()) {
try {
final conversationController = Get.find<ConversationController>();
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<void> _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<String>('remark_$userId');
if (savedRemark != null) {
remark.value = savedRemark;
}
}
///
Future<void> 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<ConversationController>()) {
try {
final conversationController = Get.find<ConversationController>();
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<ConversationController>()) {
try {
final conversationController = Get.find<ConversationController>();
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<void> 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('跳转失败,请稍后重试');
}
}
}

17
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<EMConversation> 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) {

16
lib/controller/setting/spread_controller.dart

@ -174,8 +174,18 @@ class SpreadController extends GetxController with WidgetsBindingObserver {
return;
}
try {
final response = await _userApi.submitOrder({
"productSpecId": roseList[activePay.value].productSpecId
var matchmakerOrderType = 1;
var matchmakerType = roseList[activePay.value].subCategory;
if(GlobalData().userData!.matchmakerFlag!){
if(matchmakerType != GlobalData().userData!.matchmakerType){
matchmakerOrderType = 2;
} else {
matchmakerOrderType = 3;
}
}
final response = await _userApi.submitMatchmakerOrder({
"productSpecId": roseList[activePay.value].productSpecId,
"matchmakerOrderType": matchmakerOrderType
});
if (response.data.isSuccess && response.data.data != null) {
final data = response.data.data;
@ -188,7 +198,7 @@ class SpreadController extends GetxController with WidgetsBindingObserver {
} else {
fluwx.open(target: MiniProgram(
username: 'gh_9ea8d46add6f',
path: "pages/index/index?amount=${roseList[activePay.value].unitSellingPrice}&paymentOrderId=${data!.paymentOrderId}&url=match-fee",
path: "pages/index/index?amount=${roseList[activePay.value].unitSellingPrice}&paymentOrderId=${data.paymentOrderId}&url=match-fee",
miniProgramType: WXMiniProgramType.preview
));
countdownSeconds.value = 3;

2
lib/network/api_urls.dart

@ -104,4 +104,6 @@ class ApiUrls {
static const String getChatVideoStatics =
'dating-agency-chat-audio/user/get/own-user-management';
static const String submitMatchmakerOrder = 'dating-agency-mall/user/submit/matchmaker-order';
}

5
lib/network/user_api.dart

@ -97,6 +97,11 @@ abstract class UserApi {
@Body() Map<String, dynamic> data,
);
@POST(ApiUrls.submitMatchmakerOrder)
Future<HttpResponse<BaseResponse<SubmitOrderData>>> submitMatchmakerOrder(
@Body() Map<String, dynamic> data,
);
@GET(ApiUrls.getEducationList)
Future<HttpResponse<BaseResponse<List<EducationData>>>> getEducationList(
@Body() Map<String, dynamic> data,

37
lib/network/user_api.g.dart

@ -512,6 +512,43 @@ class _UserApi implements UserApi {
return httpResponse;
}
@override
Future<HttpResponse<BaseResponse<SubmitOrderData>>> submitMatchmakerOrder(
Map<String, dynamic> data,
) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
final _data = <String, dynamic>{};
_data.addAll(data);
final _options =
_setStreamType<HttpResponse<BaseResponse<SubmitOrderData>>>(
Options(method: 'POST', headers: _headers, extra: _extra)
.compose(
_dio.options,
'dating-agency-mall/user/submit/matchmaker-order',
queryParameters: queryParameters,
data: _data,
)
.copyWith(
baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl),
),
);
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late BaseResponse<SubmitOrderData> _value;
try {
_value = BaseResponse<SubmitOrderData>.fromJson(
_result.data!,
(json) => SubmitOrderData.fromJson(json as Map<String, dynamic>),
);
} on Object catch (e, s) {
errorLogger?.logError(e, s, _options);
rethrow;
}
final httpResponse = HttpResponse(_value, _result);
return httpResponse;
}
@override
Future<HttpResponse<BaseResponse<List<EducationData>>>> getEducationList(
Map<String, dynamic> data,

65
lib/pages/home/user_information_page.dart

@ -23,7 +23,66 @@ class UserInformationPage extends StatelessWidget {
return GetX<UserInformationController>(
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();
);
},
);
}

373
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<ChatPage> {
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<ChatPage> {
}
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), //
],
],
),
),
),
],
],
),
),
),
);

387
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<ChatSettingsController>(
init: ChatSettingsController(
userId: userId,
userData: userData,
),
body: GetBuilder<ChatSettingsController>(
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<bool> 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<ChatSettingsController>(
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<ChatSettingsController>();
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<ChatSettingsController>(
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(

8
lib/pages/message/conversation_tab.dart

@ -60,8 +60,14 @@ class _ConversationTabState extends State<ConversationTab>
//
Widget _buildConversationItem(EMConversation conversation) {
//
final cachedUserInfo = controller.getCachedUserInfo(conversation.id);
return FutureBuilder<ExtendedUserInfo?>(
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<EMMessage?>(

2
lib/widget/live/live_room_notice_chat_panel.dart

@ -103,7 +103,7 @@ class _LiveRoomNoticeChatPanelState extends State<LiveRoomNoticeChatPanel> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
controller.isLive.value ? '申请连麦中' : '免费连麦',
controller.isLive.value ? '下麦结束' : '免费连麦',
style: TextStyle(
fontSize: 13.w,
color: Colors.white,

Loading…
Cancel
Save