diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index cf1689d..a980108 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -30,6 +30,11 @@ android { targetSdk = flutter.targetSdkVersion versionCode = flutter.versionCode versionName = flutter.versionName + + ndk { + // 添加所有常见的 ABI,按优先级排序 + abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64") + } } signingConfigs { @@ -56,7 +61,7 @@ android { signingConfig = signingConfigs.getByName("release") ndk { abiFilters.clear() - abiFilters += "arm64-v8a" + abiFilters += listOf("armeabi-v7a", "arm64-v8a") // 或者:abiFilters.add("arm64-v8a") } @@ -74,7 +79,7 @@ android { signingConfig = signingConfigs.getByName("release") ndk { abiFilters.clear() - abiFilters += "arm64-v8a" + abiFilters += listOf("x86", "x86_64", "armeabi-v7a", "arm64-v8a") // 或者:abiFilters.add("arm64-v8a") } diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 79e108e..e668ae5 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -101,7 +101,7 @@ PODS: - FlutterMacOS - permission_handler_apple (9.3.0): - Flutter - - photo_manager (3.8.2): + - photo_manager (3.8.3): - Flutter - FlutterMacOS - record_ios (1.1.0): @@ -236,7 +236,7 @@ SPEC CHECKSUMS: package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 path_provider_foundation: 0b743cbb62d8e47eab856f09262bb8c1ddcfe6ba permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 - photo_manager: 8f40300912c856ef39d0d533a205afab5fbee62f + photo_manager: 647b6f5c5057632e2d00e2921c6ce3437ded57a9 record_ios: 840d21cce013c5a3b2168b74a54ebdb4136359e2 sensors_plus: 7229095999f30740798f0eeef5cd120357a8f4f2 shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 53e8c42..eeaedd4 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -8,14 +8,14 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 2D2952405A246D6FED52F01C /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AFB57A27E7776B3F359A1C5C /* Pods_RunnerTests.framework */; }; 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 629D13DAEFC0F1B763022B0C /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F143A14201D60D1AB5F91F17 /* Pods_RunnerTests.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 74C4FE64A41BA4C47423BB91 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E4B909E9A193D37A26B4444E /* Pods_Runner.framework */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - CA4B8AEFC4889FC29913AC62 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4160400E07E3D928E2B9607F /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -48,7 +48,6 @@ 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 4160400E07E3D928E2B9607F /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 56C88C7D01770BE2DBFDD697 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 682C5B3EB10C94F9A20C1D3D /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; 6A05741EC62B5EEBE22A6CF2 /* Runner.entitlements */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; @@ -65,7 +64,8 @@ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9B57E2640B7D789CFC5E81D9 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; A68585D9660E736DD281FC18 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; - F143A14201D60D1AB5F91F17 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + AFB57A27E7776B3F359A1C5C /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + E4B909E9A193D37A26B4444E /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -73,7 +73,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 629D13DAEFC0F1B763022B0C /* Pods_RunnerTests.framework in Frameworks */, + 2D2952405A246D6FED52F01C /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -81,7 +81,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - CA4B8AEFC4889FC29913AC62 /* Pods_Runner.framework in Frameworks */, + 74C4FE64A41BA4C47423BB91 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -91,8 +91,8 @@ 18EC60AE150D603E57C4D66F /* Frameworks */ = { isa = PBXGroup; children = ( - 4160400E07E3D928E2B9607F /* Pods_Runner.framework */, - F143A14201D60D1AB5F91F17 /* Pods_RunnerTests.framework */, + E4B909E9A193D37A26B4444E /* Pods_Runner.framework */, + AFB57A27E7776B3F359A1C5C /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -504,8 +504,13 @@ PRODUCT_BUNDLE_IDENTIFIER = com.juxinghe.touchme; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; }; name = Profile; @@ -692,9 +697,14 @@ PRODUCT_BUNDLE_IDENTIFIER = com.juxinghe.touchme; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; @@ -720,8 +730,13 @@ PRODUCT_BUNDLE_IDENTIFIER = com.juxinghe.touchme; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 6de6103..d224937 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -1,72 +1,70 @@ + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + 趣恋恋 + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + dating_touchme_app + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleURLTypes + + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSApplicationQueriesSchemes + + weixin + weixinULAPI + weixinURLParamsAPI + + LSRequiresIPhoneOS + + NSAppTransportSecurity - CADisableMinimumFrameDurationOnPhone + NSAllowsArbitraryLoads - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - 趣恋恋 - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - dating_touchme_app - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleURLTypes - - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSApplicationQueriesSchemes - - weixin - weixinULAPI - weixinURLParamsAPI - - LSRequiresIPhoneOS + NSAllowsArbitraryLoadsInWebContent - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - NSAllowsArbitraryLoadsInWebContent - - - NSPhotoLibraryUsageDescription - 需要访问相册以选择和发送图片、视频 - NSCameraUsageDescription - 需要访问相机以拍摄照片和视频 - NSMicrophoneUsageDescription - 需要访问麦克风以录制语音和视频 - UIApplicationSupportsIndirectInputEvents - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIStatusBarHidden - + NSCameraUsageDescription + 需要访问相机以拍摄照片和视频 + NSMicrophoneUsageDescription + 需要访问麦克风以录制语音和视频 + NSPhotoLibraryUsageDescription + 需要访问相册以选择和发送图片、视频 + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIStatusBarHidden + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + diff --git a/lib/pages/home/content_card.dart b/lib/pages/home/content_card.dart index e842417..cf7067c 100644 --- a/lib/pages/home/content_card.dart +++ b/lib/pages/home/content_card.dart @@ -48,7 +48,7 @@ class _CardHeader extends StatelessWidget { ), ), errorWidget: (context, url, error) => Image.asset( - Assets.imagesAvatarsExample, + Assets.imagesUserAvatar, width: 60, height: 60, fit: BoxFit.cover, @@ -397,7 +397,7 @@ class _NetworkImageWidget extends StatelessWidget { ), ), errorWidget: (context, url, error) => Image.asset( - Assets.imagesAvatarsExample, + Assets.imagesUserAvatar, width: 60, height: 60, fit: BoxFit.cover, diff --git a/lib/pages/message/chat_settings_page.dart b/lib/pages/message/chat_settings_page.dart index 408ff42..373fca2 100644 --- a/lib/pages/message/chat_settings_page.dart +++ b/lib/pages/message/chat_settings_page.dart @@ -133,7 +133,7 @@ class ChatSettingsPage extends StatelessWidget { fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return Image.asset( - Assets.imagesAvatarsExample, + Assets.imagesUserAvatar, width: 50.w, height: 50.w, fit: BoxFit.cover, @@ -141,7 +141,7 @@ class ChatSettingsPage extends StatelessWidget { }, ) : Image.asset( - Assets.imagesAvatarsExample, + Assets.imagesUserAvatar, width: 50.w, height: 50.w, fit: BoxFit.cover, @@ -187,7 +187,7 @@ class ChatSettingsPage extends StatelessWidget { children: [ ClipOval( child: Image.asset( - Assets.imagesAvatarsExample, + Assets.imagesUserAvatar, width: 50.w, height: 50.w, fit: BoxFit.cover, diff --git a/lib/pages/message/conversation_tab.dart b/lib/pages/message/conversation_tab.dart index f5424d9..930c780 100644 --- a/lib/pages/message/conversation_tab.dart +++ b/lib/pages/message/conversation_tab.dart @@ -4,7 +4,10 @@ import 'package:dating_touchme_app/generated/assets.dart'; import 'package:get/get.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart'; import 'package:im_flutter_sdk/im_flutter_sdk.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; import '../../controller/message/conversation_controller.dart'; +import '../../widget/message/emoji_text_widget.dart'; +import '../../config/emoji_config.dart'; class ConversationTab extends StatefulWidget { const ConversationTab({super.key}); @@ -151,7 +154,7 @@ class _ConversationTabState extends State ? ClipRRect( borderRadius: BorderRadius.circular(28), child: Image.asset( - Assets.imagesAvatarsExample, + Assets.imagesUserAvatar, width: 56, height: 56, fit: BoxFit.cover, @@ -195,17 +198,9 @@ class _ConversationTabState extends State crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( - child: Text( + child: _buildLastMessageContent( controller.getLastMessageContent(message), - style: TextStyle( - fontSize: 14, - color: (message != null && - message.direction == MessageDirection.SEND && - message.status == MessageStatus.FAIL) - ? Color.fromRGBO(248, 85, 66, 1) // 发送失败时显示红色 - : Colors.grey, - ), - overflow: TextOverflow.ellipsis, + message, ), ), if (unreadCount > 0) @@ -268,6 +263,94 @@ class _ConversationTabState extends State ); } + // 构建最后一条消息内容(支持表情显示) + Widget _buildLastMessageContent(String content, EMMessage? message) { + // 检查是否是发送失败的消息 + final isFailed = message != null && + message.direction == MessageDirection.SEND && + message.status == MessageStatus.FAIL; + + // 检查是否包含表情 + final containsEmoji = EmojiTextHelper.containsEmoji(content); + + if (containsEmoji && !isFailed) { + // 如果包含表情且不是失败消息,使用自定义的单行表情文本 widget + return _buildSingleLineEmojiText( + content, + TextStyle( + fontSize: 14.sp, + color: Colors.grey, + ), + ); + } else { + // 如果不包含表情或是失败消息,使用普通 Text 显示 + return Text( + content, + style: TextStyle( + fontSize: 14.sp, + color: isFailed + ? Color.fromRGBO(248, 85, 66, 1) // 发送失败时显示红色 + : Colors.grey, + ), + overflow: TextOverflow.ellipsis, + maxLines: 1, + ); + } + } + + // 构建单行表情文本(支持省略) + Widget _buildSingleLineEmojiText(String text, TextStyle textStyle) { + final RegExp emojiRegex = RegExp(r'\[emoji:(\d+)\]'); + final List spans = []; + int lastMatchEnd = 0; + final matches = emojiRegex.allMatches(text); + + for (final match in matches) { + // 添加表情之前的文本 + if (match.start > lastMatchEnd) { + final textPart = text.substring(lastMatchEnd, match.start); + spans.add(TextSpan(text: textPart, style: textStyle)); + } + + // 添加表情图片(使用 WidgetSpan) + final emojiId = match.group(1); + if (emojiId != null) { + final emoji = EmojiConfig.getEmojiById(emojiId); + if (emoji != null) { + spans.add(WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 2.w), + child: Image.asset( + emoji.path, + width: 16.w, + height: 16.w, + fit: BoxFit.contain, + ), + ), + )); + } else { + // 如果表情不存在,显示原始文本 + spans.add(TextSpan(text: match.group(0)!, style: textStyle)); + } + } + + lastMatchEnd = match.end; + } + + // 添加剩余的文本 + if (lastMatchEnd < text.length) { + final textPart = text.substring(lastMatchEnd); + spans.add(TextSpan(text: textPart, style: textStyle)); + } + + return RichText( + text: TextSpan(children: spans), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ); + } + @override bool get wantKeepAlive => true; } diff --git a/lib/pages/message/friend_tab.dart b/lib/pages/message/friend_tab.dart index 39a7259..df75994 100644 --- a/lib/pages/message/friend_tab.dart +++ b/lib/pages/message/friend_tab.dart @@ -22,7 +22,7 @@ class _FriendTabState extends State with TickerProviderStateMixin { { "id": 1, "name": "叫我大王", - "avatar": Assets.imagesAvatarsExample, + "avatar": Assets.imagesUserAvatar, "age": 30, "location": "广州", "gender": "female", @@ -31,7 +31,7 @@ class _FriendTabState extends State with TickerProviderStateMixin { { "id": 2, "name": "林园园", - "avatar": Assets.imagesAvatarsExample, + "avatar": Assets.imagesUserAvatar, "age": 28, "location": "深圳", "gender": "female", @@ -40,7 +40,7 @@ class _FriendTabState extends State with TickerProviderStateMixin { { "id": 3, "name": "张雪", - "avatar": Assets.imagesAvatarsExample, + "avatar": Assets.imagesUserAvatar, "age": 25, "location": "北京", "gender": "female", @@ -53,37 +53,37 @@ class _FriendTabState extends State with TickerProviderStateMixin { // { // "id": 1, // "name": "林园园", - // "avatar": Assets.imagesAvatarsExample, + // "avatar": Assets.imagesUserAvatar, // "isOnline": true, // }, // { // "id": 2, // "name": "李晖", - // "avatar": Assets.imagesAvatarsExample, + // "avatar": Assets.imagesUserAvatar, // "isOnline": false, // }, // { // "id": 3, // "name": "李哲", - // "avatar": Assets.imagesAvatarsExample, + // "avatar": Assets.imagesUserAvatar, // "isOnline": false, // }, // { // "id": 4, // "name": "李夏", - // "avatar": Assets.imagesAvatarsExample, + // "avatar": Assets.imagesUserAvatar, // "isOnline": true, // }, // { // "id": 5, // "name": "张雪", - // "avatar": Assets.imagesAvatarsExample, + // "avatar": Assets.imagesUserAvatar, // "isOnline": false, // }, // { // "id": 6, // "name": "王强", - // "avatar": Assets.imagesAvatarsExample, + // "avatar": Assets.imagesUserAvatar, // "isOnline": true, // }, // ]; @@ -93,7 +93,7 @@ class _FriendTabState extends State with TickerProviderStateMixin { { "id": 1, "name": "李晖", - "avatar": Assets.imagesAvatarsExample, + "avatar": Assets.imagesUserAvatar, "age": 32, "location": "上海", "gender": "male", @@ -101,7 +101,7 @@ class _FriendTabState extends State with TickerProviderStateMixin { { "id": 2, "name": "王强", - "avatar": Assets.imagesAvatarsExample, + "avatar": Assets.imagesUserAvatar, "age": 29, "location": "杭州", "gender": "male", @@ -109,7 +109,7 @@ class _FriendTabState extends State with TickerProviderStateMixin { { "id": 3, "name": "李哲", - "avatar": Assets.imagesAvatarsExample, + "avatar": Assets.imagesUserAvatar, "age": 27, "location": "成都", "gender": "male", diff --git a/lib/pages/mine/user_info_page.dart b/lib/pages/mine/user_info_page.dart index afca27a..2fe671e 100644 --- a/lib/pages/mine/user_info_page.dart +++ b/lib/pages/mine/user_info_page.dart @@ -320,7 +320,7 @@ class _UserInfoState extends State { fit: BoxFit.cover, ) : Image.asset( - Assets.imagesAvatarsExample, + Assets.imagesUserAvatar, fit: BoxFit.cover, )), ), diff --git a/lib/widget/message/call_item.dart b/lib/widget/message/call_item.dart index 60d667b..5d9edea 100644 --- a/lib/widget/message/call_item.dart +++ b/lib/widget/message/call_item.dart @@ -2,8 +2,11 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:im_flutter_sdk/im_flutter_sdk.dart'; +import 'package:get/get.dart'; import '../../generated/assets.dart'; +import '../../controller/global.dart'; +import '../../controller/message/chat_controller.dart'; /// 通话类型 enum CallType { @@ -43,7 +46,7 @@ class CallItem extends StatelessWidget { final content = textBody.content; // 检查是否是通话消息(以 [CALL:] 开头) - if (content != null && content.startsWith('[CALL:]')) { + if (content.startsWith('[CALL:]')) { final jsonStr = content.substring(7); // 移除 '[CALL:]' 前缀 return jsonDecode(jsonStr) as Map; } @@ -256,16 +259,76 @@ class CallItem extends StatelessWidget { // 构建头像 Widget _buildAvatar() { - return Container( - width: 40.w, - height: 40.w, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20.w), - image: DecorationImage( - image: AssetImage(Assets.imagesAvatarsExample), - fit: BoxFit.cover, - ), - ), + String? avatarUrl; + + if (isSentByMe) { + // 发送的消息:使用当前登录用户的头像 + // 优先从消息 attributes 中获取 + try { + final attributes = message.attributes; + if (attributes != null) { + avatarUrl = attributes['sender_avatarUrl'] as String? ?? + attributes['avatarUrl'] as String?; + } + } catch (e) { + // 忽略错误 + } + + // 如果消息中没有,使用当前登录用户的头像 + if (avatarUrl == null || avatarUrl.isEmpty) { + avatarUrl = GlobalData().userData?.profilePhoto; + } + } else { + // 接收的消息:使用发送者的头像 + try { + final attributes = message.attributes; + if (attributes != null) { + avatarUrl = attributes['sender_avatarUrl'] as String? ?? + attributes['avatarUrl'] as String?; + } + } catch (e) { + // 忽略错误 + } + + // 如果消息中没有,尝试从 ChatController 获取对方用户头像 + if ((avatarUrl == null || avatarUrl.isEmpty)) { + try { + // 尝试从 Get 获取 ChatController + final chatController = Get.find(); + avatarUrl = chatController.userAvatarUrl; + } catch (e) { + // ChatController 可能不存在,忽略错误 + } + } + } + + // 清理头像URL(移除反引号) + if (avatarUrl != null && avatarUrl.isNotEmpty) { + avatarUrl = avatarUrl.trim().replaceAll('`', ''); + } + + return ClipOval( + child: avatarUrl != null && avatarUrl.isNotEmpty + ? Image.network( + avatarUrl, + width: 40.w, + height: 40.w, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Image.asset( + Assets.imagesUserAvatar, + width: 40.w, + height: 40.w, + fit: BoxFit.cover, + ); + }, + ) + : Image.asset( + Assets.imagesUserAvatar, + width: 40.w, + height: 40.w, + fit: BoxFit.cover, + ), ); } diff --git a/lib/widget/message/gift_item.dart b/lib/widget/message/gift_item.dart index 5ffebe9..ece218e 100644 --- a/lib/widget/message/gift_item.dart +++ b/lib/widget/message/gift_item.dart @@ -3,8 +3,11 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:im_flutter_sdk/im_flutter_sdk.dart'; +import 'package:get/get.dart'; import '../../generated/assets.dart'; +import '../../controller/global.dart'; +import '../../controller/message/chat_controller.dart'; class GiftItem extends StatelessWidget { final EMMessage message; @@ -235,16 +238,76 @@ class GiftItem extends StatelessWidget { // 构建头像 Widget _buildAvatar() { - return Container( - width: 40.w, - height: 40.w, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20.w), - image: DecorationImage( - image: AssetImage(Assets.imagesAvatarsExample), - fit: BoxFit.cover, - ), - ), + String? avatarUrl; + + if (isSentByMe) { + // 发送的消息:使用当前登录用户的头像 + // 优先从消息 attributes 中获取 + try { + final attributes = message.attributes; + if (attributes != null) { + avatarUrl = attributes['sender_avatarUrl'] as String? ?? + attributes['avatarUrl'] as String?; + } + } catch (e) { + // 忽略错误 + } + + // 如果消息中没有,使用当前登录用户的头像 + if (avatarUrl == null || avatarUrl.isEmpty) { + avatarUrl = GlobalData().userData?.profilePhoto; + } + } else { + // 接收的消息:使用发送者的头像 + try { + final attributes = message.attributes; + if (attributes != null) { + avatarUrl = attributes['sender_avatarUrl'] as String? ?? + attributes['avatarUrl'] as String?; + } + } catch (e) { + // 忽略错误 + } + + // 如果消息中没有,尝试从 ChatController 获取对方用户头像 + if ((avatarUrl == null || avatarUrl.isEmpty)) { + try { + // 尝试从 Get 获取 ChatController + final chatController = Get.find(); + avatarUrl = chatController.userAvatarUrl; + } catch (e) { + // ChatController 可能不存在,忽略错误 + } + } + } + + // 清理头像URL(移除反引号) + if (avatarUrl != null && avatarUrl.isNotEmpty) { + avatarUrl = avatarUrl.trim().replaceAll('`', ''); + } + + return ClipOval( + child: avatarUrl != null && avatarUrl.isNotEmpty + ? Image.network( + avatarUrl, + width: 40.w, + height: 40.w, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Image.asset( + Assets.imagesUserAvatar, + width: 40.w, + height: 40.w, + fit: BoxFit.cover, + ); + }, + ) + : Image.asset( + Assets.imagesUserAvatar, + width: 40.w, + height: 40.w, + fit: BoxFit.cover, + ), ); } diff --git a/lib/widget/message/image_item.dart b/lib/widget/message/image_item.dart index 5e4c400..711187e 100644 --- a/lib/widget/message/image_item.dart +++ b/lib/widget/message/image_item.dart @@ -6,6 +6,8 @@ import 'package:im_flutter_sdk/im_flutter_sdk.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:dating_touchme_app/generated/assets.dart'; import 'package:dating_touchme_app/pages/message/image_viewer_page.dart'; +import 'package:dating_touchme_app/controller/global.dart'; +import 'package:dating_touchme_app/controller/message/chat_controller.dart'; class ImageItem extends StatefulWidget { final EMImageMessageBody imageBody; @@ -593,16 +595,76 @@ class _ImageItemState extends State { // 构建头像 Widget _buildAvatar() { - return Container( - width: 40.w, - height: 40.w, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20.w), - image: DecorationImage( - image: AssetImage(Assets.imagesAvatarsExample), - fit: BoxFit.cover, - ), - ), + String? avatarUrl; + + if (widget.isSentByMe) { + // 发送的消息:使用当前登录用户的头像 + // 优先从消息 attributes 中获取 + try { + final attributes = widget.message.attributes; + if (attributes != null) { + avatarUrl = attributes['sender_avatarUrl'] as String? ?? + attributes['avatarUrl'] as String?; + } + } catch (e) { + // 忽略错误 + } + + // 如果消息中没有,使用当前登录用户的头像 + if (avatarUrl == null || avatarUrl.isEmpty) { + avatarUrl = GlobalData().userData?.profilePhoto; + } + } else { + // 接收的消息:使用发送者的头像 + try { + final attributes = widget.message.attributes; + if (attributes != null) { + avatarUrl = attributes['sender_avatarUrl'] as String? ?? + attributes['avatarUrl'] as String?; + } + } catch (e) { + // 忽略错误 + } + + // 如果消息中没有,尝试从 ChatController 获取对方用户头像 + if ((avatarUrl == null || avatarUrl.isEmpty)) { + try { + // 尝试从 Get 获取 ChatController + final chatController = Get.find(); + avatarUrl = chatController.userAvatarUrl; + } catch (e) { + // ChatController 可能不存在,忽略错误 + } + } + } + + // 清理头像URL(移除反引号) + if (avatarUrl != null && avatarUrl.isNotEmpty) { + avatarUrl = avatarUrl.trim().replaceAll('`', ''); + } + + return ClipOval( + child: avatarUrl != null && avatarUrl.isNotEmpty + ? Image.network( + avatarUrl, + width: 40.w, + height: 40.w, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Image.asset( + Assets.imagesUserAvatar, + width: 40.w, + height: 40.w, + fit: BoxFit.cover, + ); + }, + ) + : Image.asset( + Assets.imagesUserAvatar, + width: 40.w, + height: 40.w, + fit: BoxFit.cover, + ), ); } diff --git a/lib/widget/message/room_item.dart b/lib/widget/message/room_item.dart index 05902ca..5cdd576 100644 --- a/lib/widget/message/room_item.dart +++ b/lib/widget/message/room_item.dart @@ -5,8 +5,9 @@ import 'package:im_flutter_sdk/im_flutter_sdk.dart'; import 'package:get/get.dart'; import '../../generated/assets.dart'; -import '../../pages/discover/live_room_page.dart'; import '../../controller/discover/room_controller.dart'; +import '../../controller/global.dart'; +import '../../controller/message/chat_controller.dart'; class RoomItem extends StatelessWidget { final EMMessage message; @@ -298,16 +299,76 @@ class RoomItem extends StatelessWidget { // 构建头像 Widget _buildAvatar() { - return Container( - width: 40.w, - height: 40.w, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20.w), - image: DecorationImage( - image: AssetImage(Assets.imagesAvatarsExample), - fit: BoxFit.cover, - ), - ), + String? avatarUrl; + + if (isSentByMe) { + // 发送的消息:使用当前登录用户的头像 + // 优先从消息 attributes 中获取 + try { + final attributes = message.attributes; + if (attributes != null) { + avatarUrl = attributes['sender_avatarUrl'] as String? ?? + attributes['avatarUrl'] as String?; + } + } catch (e) { + // 忽略错误 + } + + // 如果消息中没有,使用当前登录用户的头像 + if (avatarUrl == null || avatarUrl.isEmpty) { + avatarUrl = GlobalData().userData?.profilePhoto; + } + } else { + // 接收的消息:使用发送者的头像 + try { + final attributes = message.attributes; + if (attributes != null) { + avatarUrl = attributes['sender_avatarUrl'] as String? ?? + attributes['avatarUrl'] as String?; + } + } catch (e) { + // 忽略错误 + } + + // 如果消息中没有,尝试从 ChatController 获取对方用户头像 + if ((avatarUrl == null || avatarUrl.isEmpty)) { + try { + // 尝试从 Get 获取 ChatController + final chatController = Get.find(); + avatarUrl = chatController.userAvatarUrl; + } catch (e) { + // ChatController 可能不存在,忽略错误 + } + } + } + + // 清理头像URL(移除反引号) + if (avatarUrl != null && avatarUrl.isNotEmpty) { + avatarUrl = avatarUrl.trim().replaceAll('`', ''); + } + + return ClipOval( + child: avatarUrl != null && avatarUrl.isNotEmpty + ? Image.network( + avatarUrl, + width: 40.w, + height: 40.w, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Image.asset( + Assets.imagesUserAvatar, + width: 40.w, + height: 40.w, + fit: BoxFit.cover, + ); + }, + ) + : Image.asset( + Assets.imagesUserAvatar, + width: 40.w, + height: 40.w, + fit: BoxFit.cover, + ), ); } diff --git a/lib/widget/message/text_item.dart b/lib/widget/message/text_item.dart index 0e9f702..20058a8 100644 --- a/lib/widget/message/text_item.dart +++ b/lib/widget/message/text_item.dart @@ -1,8 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:im_flutter_sdk/im_flutter_sdk.dart'; +import 'package:get/get.dart'; import '../../../generated/assets.dart'; +import '../../../controller/global.dart'; +import '../../../controller/message/chat_controller.dart'; import 'emoji_text_widget.dart'; class TextItem extends StatelessWidget { @@ -122,16 +125,76 @@ class TextItem extends StatelessWidget { // 构建头像 Widget _buildAvatar() { - return Container( - width: 40.w, - height: 40.w, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20.w), - image: DecorationImage( - image: AssetImage(Assets.imagesAvatarsExample), - fit: BoxFit.cover, - ), - ), + String? avatarUrl; + + if (isSentByMe) { + // 发送的消息:使用当前登录用户的头像 + // 优先从消息 attributes 中获取 + try { + final attributes = message.attributes; + if (attributes != null) { + avatarUrl = attributes['sender_avatarUrl'] as String? ?? + attributes['avatarUrl'] as String?; + } + } catch (e) { + // 忽略错误 + } + + // 如果消息中没有,使用当前登录用户的头像 + if (avatarUrl == null || avatarUrl.isEmpty) { + avatarUrl = GlobalData().userData?.profilePhoto; + } + } else { + // 接收的消息:使用发送者的头像 + try { + final attributes = message.attributes; + if (attributes != null) { + avatarUrl = attributes['sender_avatarUrl'] as String? ?? + attributes['avatarUrl'] as String?; + } + } catch (e) { + // 忽略错误 + } + + // 如果消息中没有,尝试从 ChatController 获取对方用户头像 + if ((avatarUrl == null || avatarUrl.isEmpty)) { + try { + // 尝试从 Get 获取 ChatController + final chatController = Get.find(); + avatarUrl = chatController.userAvatarUrl; + } catch (e) { + // ChatController 可能不存在,忽略错误 + } + } + } + + // 清理头像URL(移除反引号) + if (avatarUrl != null && avatarUrl.isNotEmpty) { + avatarUrl = avatarUrl.trim().replaceAll('`', ''); + } + + return ClipOval( + child: avatarUrl != null && avatarUrl.isNotEmpty + ? Image.network( + avatarUrl, + width: 40.w, + height: 40.w, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Image.asset( + Assets.imagesUserAvatar, + width: 40.w, + height: 40.w, + fit: BoxFit.cover, + ); + }, + ) + : Image.asset( + Assets.imagesUserAvatar, + width: 40.w, + height: 40.w, + fit: BoxFit.cover, + ), ); } diff --git a/lib/widget/message/video_item.dart b/lib/widget/message/video_item.dart index 26dc917..3c975ad 100644 --- a/lib/widget/message/video_item.dart +++ b/lib/widget/message/video_item.dart @@ -8,6 +8,8 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:video_player/video_player.dart'; import 'package:dating_touchme_app/pages/message/video_player_page.dart'; import 'package:dating_touchme_app/generated/assets.dart'; +import 'package:dating_touchme_app/controller/global.dart'; +import 'package:dating_touchme_app/controller/message/chat_controller.dart'; class VideoItem extends StatefulWidget { final EMVideoMessageBody videoBody; @@ -396,16 +398,76 @@ class _VideoItemState extends State { // 构建头像 Widget _buildAvatar() { - return Container( - width: 40.w, - height: 40.w, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20.w), - image: DecorationImage( - image: AssetImage(Assets.imagesAvatarsExample), - fit: BoxFit.cover, - ), - ), + String? avatarUrl; + + if (widget.isSentByMe) { + // 发送的消息:使用当前登录用户的头像 + // 优先从消息 attributes 中获取 + try { + final attributes = widget.message.attributes; + if (attributes != null) { + avatarUrl = attributes['sender_avatarUrl'] as String? ?? + attributes['avatarUrl'] as String?; + } + } catch (e) { + // 忽略错误 + } + + // 如果消息中没有,使用当前登录用户的头像 + if (avatarUrl == null || avatarUrl.isEmpty) { + avatarUrl = GlobalData().userData?.profilePhoto; + } + } else { + // 接收的消息:使用发送者的头像 + try { + final attributes = widget.message.attributes; + if (attributes != null) { + avatarUrl = attributes['sender_avatarUrl'] as String? ?? + attributes['avatarUrl'] as String?; + } + } catch (e) { + // 忽略错误 + } + + // 如果消息中没有,尝试从 ChatController 获取对方用户头像 + if ((avatarUrl == null || avatarUrl.isEmpty)) { + try { + // 尝试从 Get 获取 ChatController + final chatController = Get.find(); + avatarUrl = chatController.userAvatarUrl; + } catch (e) { + // ChatController 可能不存在,忽略错误 + } + } + } + + // 清理头像URL(移除反引号) + if (avatarUrl != null && avatarUrl.isNotEmpty) { + avatarUrl = avatarUrl.trim().replaceAll('`', ''); + } + + return ClipOval( + child: avatarUrl != null && avatarUrl.isNotEmpty + ? Image.network( + avatarUrl, + width: 40.w, + height: 40.w, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Image.asset( + Assets.imagesUserAvatar, + width: 40.w, + height: 40.w, + fit: BoxFit.cover, + ); + }, + ) + : Image.asset( + Assets.imagesUserAvatar, + width: 40.w, + height: 40.w, + fit: BoxFit.cover, + ), ); } diff --git a/lib/widget/message/voice_item.dart b/lib/widget/message/voice_item.dart index d60da43..20530c1 100644 --- a/lib/widget/message/voice_item.dart +++ b/lib/widget/message/voice_item.dart @@ -10,6 +10,8 @@ import 'package:im_flutter_sdk/im_flutter_sdk.dart'; import '../../../generated/assets.dart'; import '../../../controller/message/voice_player_manager.dart'; +import '../../../controller/global.dart'; +import '../../../controller/message/chat_controller.dart'; class VoiceItem extends StatefulWidget { final EMVoiceMessageBody voiceBody; @@ -342,16 +344,80 @@ class _VoiceItemState extends State with TickerProviderStateMixin { // 构建头像 Widget _buildAvatar() { - return Container( - width: 40.w, - height: 40.w, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20.w), - image: DecorationImage( - image: AssetImage(Assets.imagesAvatarsExample), - fit: BoxFit.cover, - ), - ), + String? avatarUrl; + + if (widget.isSentByMe) { + // 发送的消息:使用当前登录用户的头像 + // 优先从消息 attributes 中获取 + try { + if (widget.message != null) { + final attributes = widget.message!.attributes; + if (attributes != null) { + avatarUrl = attributes['sender_avatarUrl'] as String? ?? + attributes['avatarUrl'] as String?; + } + } + } catch (e) { + // 忽略错误 + } + + // 如果消息中没有,使用当前登录用户的头像 + if (avatarUrl == null || avatarUrl.isEmpty) { + avatarUrl = GlobalData().userData?.profilePhoto; + } + } else { + // 接收的消息:使用发送者的头像 + try { + if (widget.message != null) { + final attributes = widget.message!.attributes; + if (attributes != null) { + avatarUrl = attributes['sender_avatarUrl'] as String? ?? + attributes['avatarUrl'] as String?; + } + } + } catch (e) { + // 忽略错误 + } + + // 如果消息中没有,尝试从 ChatController 获取对方用户头像 + if ((avatarUrl == null || avatarUrl.isEmpty)) { + try { + // 尝试从 Get 获取 ChatController + final chatController = Get.find(); + avatarUrl = chatController.userAvatarUrl; + } catch (e) { + // ChatController 可能不存在,忽略错误 + } + } + } + + // 清理头像URL(移除反引号) + if (avatarUrl != null && avatarUrl.isNotEmpty) { + avatarUrl = avatarUrl.trim().replaceAll('`', ''); + } + + return ClipOval( + child: avatarUrl != null && avatarUrl.isNotEmpty + ? Image.network( + avatarUrl, + width: 40.w, + height: 40.w, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Image.asset( + Assets.imagesUserAvatar, + width: 40.w, + height: 40.w, + fit: BoxFit.cover, + ); + }, + ) + : Image.asset( + Assets.imagesUserAvatar, + width: 40.w, + height: 40.w, + fit: BoxFit.cover, + ), ); } diff --git a/location_plugin/example/pubspec.lock b/location_plugin/example/pubspec.lock index 36a3835..9ece7b5 100644 --- a/location_plugin/example/pubspec.lock +++ b/location_plugin/example/pubspec.lock @@ -173,10 +173,10 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.flutter-io.cn" source: hosted - version: "1.16.0" + version: "1.17.0" path: dependency: transitive description: @@ -266,10 +266,10 @@ packages: dependency: transitive description: name: test_api - sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 url: "https://pub.flutter-io.cn" source: hosted - version: "0.7.6" + version: "0.7.7" typed_data: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 324e520..1d1e4e8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -47,7 +47,6 @@ dependencies: dio: ^5.9.0 retrofit: 4.9.0 flutter_native_splash: ^2.4.7 - im_flutter_sdk: ^4.15.2 easy_localization: ^3.0.8 flutter_oss_aliyun: ^6.4.2 permission_handler: ^12.0.1 @@ -77,7 +76,8 @@ dependencies: url_launcher: ^6.3.2 wakelock_plus: ^1.4.0 pinput: ^6.0.1 - + im_flutter_sdk: 4.15.2 + dev_dependencies: flutter_test: sdk: flutter