Browse Source

优化IM,开发需求

ios
Jolie 3 months ago
parent
commit
8cc6668b13
15 changed files with 1019 additions and 371 deletions
  1. 6
      android/app/src/main/AndroidManifest.xml
  2. 28
      android/app/src/main/kotlin/com/juxinghe/touchme/MainActivity.kt
  3. BIN
      assets/images/im_coin_icon.png
  4. 6
      ios/Runner/Info.plist
  5. 67
      lib/controller/message/conversation_controller.dart
  6. 8
      lib/controller/mine/user_controller.dart
  7. 2
      lib/generated/assets.dart
  8. 365
      lib/im/im_manager.dart
  9. 184
      lib/main.dart
  10. 4
      lib/model/home/marriage_data.dart
  11. 85
      lib/widget/message/image_item.dart
  12. 105
      lib/widget/message/text_item.dart
  13. 301
      lib/widget/message/video_item.dart
  14. 161
      lib/widget/message/voice_item.dart
  15. 68
      location_plugin/example/pubspec.lock

6
android/app/src/main/AndroidManifest.xml

@ -5,6 +5,12 @@
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- 环信IM SDK 所需权限 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<!-- Android 13以下版本的文件读写权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
<application
android:label="趣恋恋"
android:name="${applicationName}"

28
android/app/src/main/kotlin/com/juxinghe/touchme/MainActivity.kt

@ -1,5 +1,31 @@
package com.juxinghe.touchme
import android.os.Bundle
import io.flutter.embedding.android.FlutterActivity
import java.lang.Thread.UncaughtExceptionHandler
class MainActivity : FlutterActivity()
class MainActivity : FlutterActivity() {
private var defaultExceptionHandler: UncaughtExceptionHandler? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 设置全局异常处理器,防止原生层崩溃导致应用闪退
defaultExceptionHandler = Thread.getDefaultUncaughtExceptionHandler()
Thread.setDefaultUncaughtExceptionHandler { thread, exception ->
// 检查是否是 IM SDK 相关的异常
val stackTrace = exception.stackTraceToString()
if (stackTrace.contains("easemob") || stackTrace.contains("im_flutter_sdk") ||
stackTrace.contains("ClientWrapper")) {
// IM SDK 异常,只记录日志,不崩溃应用
android.util.Log.e("MainActivity", "IM SDK 异常(已捕获,应用继续运行): ${exception.message}")
android.util.Log.e("MainActivity", "堆栈跟踪: $stackTrace")
// 不调用默认处理器,让应用继续运行
return@setDefaultUncaughtExceptionHandler
}
// 其他异常,使用默认处理器
defaultExceptionHandler?.uncaughtException(thread, exception)
}
}
}

BIN
assets/images/im_coin_icon.png

Before After
Width: 80  |  Height: 80  |  Size: 4.0 KiB

6
ios/Runner/Info.plist

@ -42,7 +42,11 @@
<true/>
</dict>
<key>NSPhotoLibraryUsageDescription</key>
<string>Replace with your permission description.</string>
<string>需要访问相册以选择和发送图片、视频</string>
<key>NSCameraUsageDescription</key>
<string>需要访问相机以拍摄照片和视频</string>
<key>NSMicrophoneUsageDescription</key>
<string>需要访问麦克风以录制语音和视频</string>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>

67
lib/controller/message/conversation_controller.dart

@ -2,6 +2,7 @@ import 'package:get/get.dart';
import 'package:im_flutter_sdk/im_flutter_sdk.dart';
import '../../im/im_manager.dart';
import '../../model/mine/user_base_data.dart';
import '../mine/user_controller.dart';
import 'chat_controller.dart';
//
@ -291,7 +292,71 @@ class ConversationController extends GetxController {
///
Future<void> refreshConversations() async {
await loadConversations();
// IM未登录
if (!IMManager.instance.isLoggedIn) {
if (Get.isLogEnable) {
Get.log('🔄 [ConversationController] IM未登录,尝试重新登录...');
}
try {
// token并登录
if (Get.isRegistered<UserController>()) {
final userController = Get.find<UserController>();
final token = await userController.getHxUserToken();
if (token != null) {
// 5
int waitCount = 0;
const maxWait = 10; // 10500ms5
while (waitCount < maxWait && !IMManager.instance.isLoggedIn) {
await Future.delayed(Duration(milliseconds: 500));
waitCount++;
}
if (IMManager.instance.isLoggedIn) {
if (Get.isLogEnable) {
Get.log('✅ [ConversationController] IM登录成功,开始加载会话列表');
}
//
await loadConversations();
return;
} else {
if (Get.isLogEnable) {
Get.log('⚠️ [ConversationController] IM登录超时');
}
errorMessage.value = 'IM登录超时,请稍后重试';
isLoading.value = false;
return;
}
} else {
if (Get.isLogEnable) {
Get.log('❌ [ConversationController] 获取IM token失败');
}
errorMessage.value = '获取IM token失败,请稍后重试';
isLoading.value = false;
return;
}
} else {
if (Get.isLogEnable) {
Get.log('❌ [ConversationController] UserController未注册');
}
errorMessage.value = 'IM未登录,请稍后重试';
isLoading.value = false;
return;
}
} catch (e) {
if (Get.isLogEnable) {
Get.log('❌ [ConversationController] 重试登录失败: $e');
}
errorMessage.value = '重试登录失败,请稍后重试';
isLoading.value = false;
return;
}
} else {
//
await loadConversations();
}
}
/// 退

8
lib/controller/mine/user_controller.dart

@ -37,7 +37,13 @@ class UserController extends GetxController {
if (token != null) {
// token
print('获取环信用户token成功: $token');
IMManager.instance.login(token);
//
final loginSuccess = await IMManager.instance.login(token);
if (loginSuccess) {
print('✅ IM登录成功');
} else {
print('⚠️ IM登录失败');
}
return token;
} else {
SmartDialog.showToast('获取的环信用户token为空');

2
lib/generated/assets.dart

@ -221,5 +221,5 @@ class Assets {
static const String imagesWallet = 'assets/images/wallet.png';
static const String imagesWechatPay = 'assets/images/wechat_pay.png';
static const String imagesWomenIcon = 'assets/images/women_icon.png';
static const String imagesImCoinIcon = 'assets/images/im_coin_icon.png';
}

365
lib/im/im_manager.dart

@ -1,4 +1,7 @@
import 'dart:io';
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:im_flutter_sdk/im_flutter_sdk.dart';
@ -9,6 +12,7 @@ import '../controller/message/conversation_controller.dart';
import '../controller/message/chat_controller.dart';
import '../controller/global.dart';
import '../pages/mine/login_page.dart';
import '../network/user_api.dart';
// IM管理器实现使SDK类型和方法
class IMManager {
@ -19,9 +23,11 @@ class IMManager {
// getter用于instance访问
static IMManager get instance => _instance;
bool _isInitialized = false;
bool _initialized = false;
bool _listenersRegistered = false;
bool _isLoggedIn = false;
bool _loggedIn = false;
bool _isReconnecting = false; //
Completer<void>? _initCompleter; //
//
static const String _connectionHandlerKey = 'im_manager_connection_handler';
@ -38,31 +44,89 @@ class IMManager {
print('IMManager instance created');
}
/// IM SDK
Future<bool> initialize(String appKey) async {
bool get isInitialized => _initialized;
bool get isLoggedIn => _loggedIn;
/// IM
Future<void> ensureInitialized({required String appKey}) async {
//
if (_initialized) {
debugPrint('✅ IM 已初始化,无需重复初始化');
return;
}
//
if (_initCompleter != null) {
debugPrint('🟡 IM 初始化中,等待完成...');
return _initCompleter!.future;
}
_initCompleter = Completer<void>();
debugPrint('🟡 IM 开始初始化');
debugPrint('📋 AppKey: $appKey');
try {
if (_isInitialized) {
print('IM SDK already initialized');
return true;
// Flutter
WidgetsFlutterBinding.ensureInitialized();
debugPrint('✅ WidgetsFlutterBinding 已初始化');
// appKey
if (appKey.isEmpty) {
throw Exception('AppKey 不能为空');
}
if (!appKey.contains('#')) {
debugPrint('⚠️ AppKey 格式可能不正确,应为 "orgname#appname" 格式');
}
// EMOptions实例
final options = EMOptions(
appKey: appKey,
// EMOptions
final options = EMOptions.withAppKey(
appKey,
autoLogin: false,
debugMode: true,
usingHttpsOnly: true,
acceptInvitationAlways: false,
debugMode: true
);
debugPrint('✅ EMOptions 创建成功');
// SDK
await EMClient.getInstance.init(options);
// SDK
debugPrint('🟡 调用 EMClient.getInstance.init()...');
try {
await EMClient.getInstance.init(options);
debugPrint('🟢 EMClient.getInstance.init() 调用完成');
} on PlatformException catch (e) {
debugPrint('❌ PlatformException 捕获:');
debugPrint(' code: ${e.code}');
debugPrint(' message: ${e.message}');
debugPrint(' details: ${e.details}');
debugPrint(' stacktrace: ${e.stacktrace}');
rethrow;
} catch (e, s) {
debugPrint('❌ 其他异常: $e');
debugPrint('堆栈: $s');
rethrow;
}
_isInitialized = true;
print('IM SDK initialized successfully');
return true;
} catch (e) {
print('Failed to initialize IM SDK: $e');
return false;
//
_registerListeners();
_initialized = true;
debugPrint('✅ IM 初始化成功');
_initCompleter!.complete();
} catch (e, s) {
debugPrint('❌ IM 初始化失败');
debugPrint('错误类型: ${e.runtimeType}');
debugPrint('错误信息: $e');
debugPrint('堆栈跟踪:');
debugPrint('$s');
_initialized = false;
_initCompleter!.completeError(e, s);
_initCompleter = null; //
//
rethrow;
}
}
@ -82,17 +146,17 @@ class IMManager {
_connectionHandlerKey,
EMConnectionEventHandler(
onConnected: () {
if (Get.isLogEnable) {
Get.log('✅ [IMManager] 已连接到IM服务器');
}
//
_isReconnecting = false;
debugPrint('🔌 IM 已连接');
// ConversationController
_refreshConversationList();
},
onDisconnected: () {
if (Get.isLogEnable) {
Get.log('❌ [IMManager] 与IM服务器断开连接');
}
// TODO:
debugPrint('🔌 IM 已断开');
_loggedIn = false;
//
_handleDisconnected();
},
onTokenDidExpire: () {
if (Get.isLogEnable) {
@ -117,9 +181,7 @@ class IMManager {
_chatHandlerKey,
EMChatEventHandler(
onMessagesReceived: (messages) {
if (Get.isLogEnable) {
Get.log('📨 [IMManager] 收到 ${messages.length} 条新消息');
}
debugPrint('📩 收到消息数: ${messages.length}');
//
for (var message in messages) {
if (message.direction == MessageDirection.RECEIVE) {
@ -132,9 +194,64 @@ class IMManager {
_notifyChatControllers(messages);
},
onMessageContentChanged: (EMMessage message, String operatorId, int operationTime){
Get.log('-------------📨 [IMManager] -------------: ${operatorId}');
Get.log('-------------📨 [IMManager] -------------: ${message}');
Get.log('-------------📨 [IMManager] -------------: ${operationTime}');
Get.log('-------------📨 [IMManager] -------------: ${message.localTime}');
if (Get.isLogEnable) {
Get.log('📨 [IMManager] 消息内容已修改: ${message.localTime}');
}
//
// API operatorId attributes
//
if (message.direction == MessageDirection.RECEIVE) {
try {
double? coinValue;
// 1 attributes
if (message.attributes != null) {
final coinValueStr = message.attributes!['coin_value'] as String?;
if (coinValueStr != null && coinValueStr.isNotEmpty) {
coinValue = double.tryParse(coinValueStr);
}
}
// 2 attributes operatorId
if (coinValue == null && operatorId.isNotEmpty) {
coinValue = double.tryParse(operatorId);
}
// attributes
if (coinValue != null && coinValue > 0) {
if (message.attributes == null) {
message.attributes = {};
}
message.attributes!['coin_value'] = coinValue.toString();
// ChatController
final fromId = message.from;
if (fromId != null && fromId.isNotEmpty) {
final controller = _activeChatControllers[fromId];
if (controller != null) {
//
final index = controller.messages.indexWhere((msg) => msg.msgId == message.msgId);
if (index != -1) {
controller.messages[index] = message;
controller.update();
if (Get.isLogEnable) {
Get.log('✅ [IMManager] 已更新接收消息的金币数值: msgId=${message.msgId}, coinValue=$coinValue');
}
}
}
}
}
} catch (e) {
if (Get.isLogEnable) {
Get.log('⚠️ [IMManager] 处理金币数值失败: $e');
}
}
}
}
),
);
@ -249,7 +366,8 @@ class IMManager {
final userId = presence.publisher;
if (userId != null && userId.isNotEmpty) {
// 使 statusDescription 线
final statusDesc = presence.statusDescription?.toLowerCase() ?? '';
final statusDescStr = presence.statusDescription ?? '';
final statusDesc = statusDescStr.toLowerCase();
// 线onlineavailable 线
final isOnline = statusDesc == 'online' || statusDesc == 'available';
@ -292,54 +410,157 @@ class IMManager {
}
}
/// IM服务
Future<bool> login(String token) async {
/// IM
Future<bool> loginWithToken({
required String appKey,
required String userId,
required String token,
}) async {
try {
if (!_isInitialized) {
print('IM SDK not initialized');
return false;
}
var userId = storage.read('userId');
if (Get.isLogEnable) {
Get.log('🔐 [IMManager] 准备登录IM,userId: $userId');
} else {
print('🔐 [IMManager] 准备登录IM,userId: $userId');
}
await EMClient.getInstance.logout();
await ensureInitialized(appKey: appKey);
//
await EMClient.getInstance.logout().catchError((_) {});
await EMClient.getInstance.loginWithToken(userId, token);
//
_registerListeners();
//
_isLoggedIn = true;
_loggedIn = true;
//
_isReconnecting = false;
// ConversationController
_refreshConversationList();
if (Get.isLogEnable) {
Get.log('✅ [IMManager] IM登录成功,userId: $userId');
} else {
print('✅ [IMManager] IM登录成功,userId: $userId');
}
debugPrint('✅ IM 登录成功: $userId');
return true;
} catch (e) {
if (Get.isLogEnable) {
Get.log('❌ [IMManager] IM登录失败: $e');
} else {
print('❌ [IMManager] IM登录失败: $e');
}
} catch (e, s) {
_loggedIn = false;
debugPrint('❌ IM 登录失败: $e');
debugPrint('$s');
return false;
}
}
/// IM服务
Future<bool> login(String token) async {
final userId = storage.read('userId');
if (userId == null) {
debugPrint('❌ IM 登录失败: userId 为空');
return false;
}
return await loginWithToken(
appKey: '1165251016193374#demo',
userId: userId,
token: token,
);
}
/// IM服务
Future<bool> logout() async {
Future<void> logout() async {
try {
await EMClient.getInstance.logout();
//
_isLoggedIn = false;
print('IM logout successful');
return true;
} catch (e) {
print('IM logout failed: $e');
return false;
} finally {
_loggedIn = false;
}
}
///
void _handleDisconnected() async {
//
if (_isReconnecting) {
if (Get.isLogEnable) {
Get.log('⚠️ [IMManager] 正在重连中,跳过重复重连');
}
return;
}
//
if (!_initialized || !_loggedIn) {
if (Get.isLogEnable) {
Get.log('⚠️ [IMManager] SDK未初始化或用户未登录,跳过重连');
}
return;
}
_isReconnecting = true;
if (Get.isLogEnable) {
Get.log('🔄 [IMManager] 开始自动重连...');
} else {
print('🔄 [IMManager] 开始自动重连...');
}
try {
// 2
await Future.delayed(const Duration(seconds: 2));
//
if (!_loggedIn) {
if (Get.isLogEnable) {
Get.log('⚠️ [IMManager] 用户已登出,取消重连');
}
_isReconnecting = false;
return;
}
// UserApi token
if (!Get.isRegistered<UserApi>()) {
if (Get.isLogEnable) {
Get.log('❌ [IMManager] UserApi 未注册,无法获取token重连');
}
_isReconnecting = false;
return;
}
final userApi = Get.find<UserApi>();
final response = await userApi.getHxUserToken();
// code == 0
if (Get.isLogEnable) {
Get.log('📡 [IMManager] 获取token响应: code=${response.data.code}, message=${response.data.message}');
} else {
print('📡 [IMManager] 获取token响应: code=${response.data.code}, message=${response.data.message}');
}
if (response.data.isSuccess && response.data.data != null) {
final token = response.data.data!;
if (Get.isLogEnable) {
Get.log('✅ [IMManager] 获取到新的token (长度: ${token.length}),开始重新登录');
} else {
print('✅ [IMManager] 获取到新的token (长度: ${token.length}),开始重新登录');
}
//
final loginSuccess = await login(token);
if (loginSuccess) {
if (Get.isLogEnable) {
Get.log('✅ [IMManager] 自动重连成功');
} else {
print('✅ [IMManager] 自动重连成功');
}
} else {
if (Get.isLogEnable) {
Get.log('❌ [IMManager] 自动重连失败:登录失败');
} else {
print('❌ [IMManager] 自动重连失败:登录失败');
}
}
} else {
if (Get.isLogEnable) {
Get.log('❌ [IMManager] 获取token失败:code=${response.data.code}, message=${response.data.message}, data=${response.data.data}');
} else {
print('❌ [IMManager] 获取token失败:code=${response.data.code}, message=${response.data.message}, data=${response.data.data}');
}
}
} catch (e, stackTrace) {
if (Get.isLogEnable) {
Get.log('❌ [IMManager] 自动重连异常: $e');
Get.log('堆栈跟踪: $stackTrace');
} else {
print('❌ [IMManager] 自动重连异常: $e');
print('堆栈跟踪: $stackTrace');
}
} finally {
_isReconnecting = false;
}
}
@ -382,9 +603,6 @@ class IMManager {
}
}
///
bool get isLoggedIn => _isLoggedIn;
/// IM系统中
Future<bool> checkUserExists(String userId) async {
try {
@ -676,7 +894,7 @@ class IMManager {
/// [callback]
Future<bool> subscribeUserPresence(String userId, Function(bool isOnline) callback) async {
try {
if (!_isLoggedIn) {
if (!_loggedIn) {
if (Get.isLogEnable) {
Get.log('⚠️ [IMManager] IM未登录,无法订阅在线状态');
}
@ -730,7 +948,7 @@ class IMManager {
/// [userId] ID
Future<bool> unsubscribeUserPresence(String userId) async {
try {
if (!_isLoggedIn) {
if (!_loggedIn) {
return false;
}
@ -758,7 +976,7 @@ class IMManager {
/// true 线false 线null
Future<bool?> getUserPresenceStatus(String userId) async {
try {
if (!_isLoggedIn) {
if (!_loggedIn) {
if (Get.isLogEnable) {
Get.log('⚠️ [IMManager] IM未登录,无法获取在线状态');
}
@ -781,7 +999,8 @@ class IMManager {
final presence = presences.first;
// 使 statusDescription 线
final statusDesc = presence.statusDescription?.toLowerCase() ?? '';
final statusDescStr = presence.statusDescription ?? '';
final statusDesc = statusDescStr.toLowerCase();
// 线onlineavailable 线
final isOnline = statusDesc == 'online' || statusDesc == 'available';

184
lib/main.dart

@ -11,6 +11,7 @@ import 'package:dating_touchme_app/rtc/rtc_manager.dart';
import 'package:dating_touchme_app/widget/live/draggable_overlay_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
@ -19,39 +20,72 @@ import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// GetStorage
await GetStorage.init();
// - release模式
EnvConfig.setEnvironment(Environment.dev);
await RTCManager.instance.initialize(appId: '4c2ea9dcb4c5440593a418df0fdd512d');
await IMManager.instance.initialize('1165251016193374#demo');
//
final networkService = NetworkService();
Get.put(networkService);
Get.put(networkService.userApi);
Get.put(networkService.homeApi);
// Overlay
Get.put(OverlayController());
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark,
statusBarBrightness: Brightness.light,
systemNavigationBarColor: Colors.white,
systemNavigationBarIconBrightness: Brightness.dark,
),
);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
if (Platform.isIOS) {
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: [SystemUiOverlay.top],
//
FlutterError.onError = (FlutterErrorDetails details) {
FlutterError.presentError(details);
if (kReleaseMode) {
//
print('Flutter Error: ${details.exception}');
}
};
//
PlatformDispatcher.instance.onError = (error, stack) {
print('Platform Error: $error');
print('Stack: $stack');
return true;
};
try {
WidgetsFlutterBinding.ensureInitialized();
// GetStorage
await GetStorage.init();
// - release模式
EnvConfig.setEnvironment(Environment.dev);
// RTC
try {
await RTCManager.instance.initialize(appId: '4c2ea9dcb4c5440593a418df0fdd512d');
} catch (e) {
print('RTC初始化失败: $e');
}
// IM初始化改为异步
// IM初始化将在 MyApp initState
//
final networkService = NetworkService();
Get.put(networkService);
Get.put(networkService.userApi);
Get.put(networkService.homeApi);
// Overlay
Get.put(OverlayController());
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark,
statusBarBrightness: Brightness.light,
systemNavigationBarColor: Colors.white,
systemNavigationBarIconBrightness: Brightness.dark,
),
);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
if (Platform.isIOS) {
SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: [SystemUiOverlay.top],
);
}
} catch (e, stackTrace) {
print('应用初始化失败: $e');
print('堆栈跟踪: $stackTrace');
// 使
}
runApp(
GetMaterialApp(
locale: Get.locale, // 使GetX的locale
@ -82,16 +116,23 @@ void main() async {
child ?? const SizedBox(),
// overlay
Obx(() {
final overlayController = Get.find<OverlayController>();
return overlayController.showOverlay.value
? DraggableOverlayWidget(
size: 60,
backgroundColor: const Color.fromRGBO(0, 0, 0, 0.6),
onClose: () {
overlayController.hide();
},
)
: const SizedBox.shrink();
try {
if (Get.isRegistered<OverlayController>()) {
final overlayController = Get.find<OverlayController>();
return overlayController.showOverlay.value
? DraggableOverlayWidget(
size: 60,
backgroundColor: const Color.fromRGBO(0, 0, 0, 0.6),
onClose: () {
overlayController.hide();
},
)
: const SizedBox.shrink();
}
} catch (e) {
print('获取OverlayController失败: $e');
}
return const SizedBox.shrink();
}),
],
),
@ -110,29 +151,72 @@ class MyApp extends StatefulWidget {
}
class _MyAppState extends State<MyApp> {
Fluwx fluwx = Fluwx();
bool _screenUtilInitialized = false;
@override
void initState() {
super.initState();
_initFluwx();
// IM
_initIMAsync();
}
/// IM SDK
void _initIMAsync() {
// 使 Future.delayed
Future.delayed(const Duration(milliseconds: 500), () async {
try {
print('🚀 开始异步初始化IM SDK(懒加载模式)...');
print('📋 AppKey: 1165251016193374#demo');
// 使 ensureInitialized init
await IMManager.instance.ensureInitialized(appKey: '1165251016193374#demo');
print('✅ IM SDK异步初始化完成');
} catch (e, stackTrace) {
print('❌ IM SDK异步初始化失败');
print('错误类型: ${e.runtimeType}');
print('错误信息: $e');
print('堆栈跟踪:');
print('$stackTrace');
//
if (e.toString().contains('PlatformException')) {
print('⚠️ 可能是原生层配置问题,请检查:');
print(' 1. Android: 检查 AndroidManifest.xml 中的权限配置');
print(' 2. iOS: 检查 Info.plist 中的权限配置');
print(' 3. 检查网络连接');
}
// IM初始化失败不应该阻止应用运行
}
});
}
_initFluwx() async {
await fluwx.registerApi(
appId: 'wx57624b8918fdd95c',
doOnAndroid: true,
doOnIOS: true,
universalLink: 'https://your.univerallink.com/link/',
);
await fluwx.isWeChatInstalled;
try {
await fluwx.registerApi(
appId: 'wx57624b8918fdd95c',
doOnAndroid: true,
doOnIOS: true,
universalLink: 'https://your.univerallink.com/link/',
);
await fluwx.isWeChatInstalled;
} catch (e) {
print('微信SDK初始化失败: $e');
// SDK初始化失败不应该阻止应用运行
}
}
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
ScreenUtil.init(context, designSize: const Size(375, 812));
// ScreenUtil.init build时调用
if (!_screenUtilInitialized) {
ScreenUtil.init(context, designSize: const Size(375, 812));
_screenUtilInitialized = true;
}
// token是否为空
final storage = GetStorage();

4
lib/model/home/marriage_data.dart

@ -49,8 +49,8 @@ class MarriageData {
factory MarriageData.fromJson(Map<String, dynamic> json) {
return MarriageData(
miId: '1180578682436194304',//json['miId'] ?? '',
userId: '1114267797208305664',//json['userId'] ?? '',
miId: json['miId'] ?? '',
userId: json['userId'] ?? '',
profilePhoto: json['profilePhoto'] ?? '',
nickName: json['nickName'] ?? '',
isRealNameCertified: json['isRealNameCertified'] ?? false,

85
lib/widget/message/image_item.dart

@ -232,6 +232,9 @@ class _ImageItemState extends State<ImageItem> {
@override
Widget build(BuildContext context) {
//
final coinValue = _getCoinValue();
return Column(
children: [
if (widget.showTime) _buildTimeLabel(),
@ -264,20 +267,31 @@ class _ImageItemState extends State<ImageItem> {
),
if (widget.isSentByMe) SizedBox(width: 10.w),
//
GestureDetector(
onTap: _onImageTap,
child: Container(
margin: EdgeInsets.only(top: 10.h),
decoration: BoxDecoration(
color: widget.isSentByMe ? Color(0xff8E7BF6) : Colors.white,
borderRadius: BorderRadius.circular(18.w),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(18.w),
child: _buildImageContent(width, height),
Column(
crossAxisAlignment: widget.isSentByMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: [
//
GestureDetector(
onTap: _onImageTap,
child: Container(
margin: EdgeInsets.only(top: 10.h),
decoration: BoxDecoration(
color: widget.isSentByMe ? Color(0xff8E7BF6) : Colors.white,
borderRadius: BorderRadius.circular(18.w),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(18.w),
child: _buildImageContent(width, height),
),
),
),
),
//
if (!widget.isSentByMe && coinValue != null)
Padding(
padding: EdgeInsets.only(top: 10.h),
child: _buildCoinLabel(coinValue),
),
],
),
if (widget.isSentByMe) SizedBox(width: 8.w),
@ -637,6 +651,51 @@ class _ImageItemState extends State<ImageItem> {
}
}
//
double? _getCoinValue() {
try {
final attributes = widget.message.attributes;
if (attributes != null && attributes.containsKey('coin_value')) {
final coinValueStr = attributes['coin_value'] as String?;
if (coinValueStr != null && coinValueStr.isNotEmpty) {
return double.tryParse(coinValueStr);
}
}
} catch (e) {
//
}
return null;
}
//
Widget _buildCoinLabel(double coinValue) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
decoration: BoxDecoration(
color: Color.fromRGBO(0, 0, 0, 0.05),
borderRadius: BorderRadius.circular(20.w),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
Assets.imagesImCoinIcon,
width: 16.w,
height: 16.w,
),
SizedBox(width: 4.w),
Text(
'+${coinValue.toStringAsFixed(2)}',
style: TextStyle(
fontSize: 12.sp,
color: Color.fromRGBO(255, 132, 0, 1),
),
),
],
),
);
}
//
void _onImageTap() {
//

105
lib/widget/message/text_item.dart

@ -25,6 +25,9 @@ class TextItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
//
final coinValue = _getCoinValue();
return Column(
children: [
//
@ -50,30 +53,41 @@ class TextItem extends StatelessWidget {
),
),
if (isSentByMe) SizedBox(width: 10.w),
Container(
constraints: BoxConstraints(maxWidth: 240.w),
margin: EdgeInsets.only(top: 10.h),
padding: EdgeInsets.symmetric(
horizontal: 12.w,
vertical: 8.h,
),
decoration: BoxDecoration(
color: isSentByMe ? Color(0xff8E7BF6) : Colors.white, //
borderRadius: BorderRadius.only(
topLeft: isSentByMe ? Radius.circular(12.w) : Radius.circular(0),
topRight: isSentByMe ? Radius.circular(0) : Radius.circular(12.w),
bottomLeft: Radius.circular(12.w),
bottomRight: Radius.circular(12.w),
),
),
child: EmojiTextWidget(
text: textBody.content,
textStyle: TextStyle(
fontSize: 14.sp,
color: isSentByMe ? Colors.white : Colors.black, //
Column(
crossAxisAlignment: isSentByMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: [
Container(
constraints: BoxConstraints(maxWidth: 240.w),
margin: EdgeInsets.only(top: 10.h),
padding: EdgeInsets.symmetric(
horizontal: 12.w,
vertical: 8.h,
),
decoration: BoxDecoration(
color: isSentByMe ? Color(0xff8E7BF6) : Colors.white, //
borderRadius: BorderRadius.only(
topLeft: isSentByMe ? Radius.circular(12.w) : Radius.circular(0),
topRight: isSentByMe ? Radius.circular(0) : Radius.circular(12.w),
bottomLeft: Radius.circular(12.w),
bottomRight: Radius.circular(12.w),
),
),
child: EmojiTextWidget(
text: textBody.content,
textStyle: TextStyle(
fontSize: 14.sp,
color: isSentByMe ? Colors.white : Colors.black, //
),
emojiSize: 24.w,
),
),
emojiSize: 24.w,
),
//
if (!isSentByMe && coinValue != null)
Padding(
padding: EdgeInsets.only(top: 10.h),
child: _buildCoinLabel(coinValue),
),
],
),
if (isSentByMe) SizedBox(width: 8.w),
if (isSentByMe) _buildAvatar(),
@ -121,6 +135,51 @@ class TextItem extends StatelessWidget {
);
}
//
double? _getCoinValue() {
try {
final attributes = message.attributes;
if (attributes != null && attributes.containsKey('coin_value')) {
final coinValueStr = attributes['coin_value'] as String?;
if (coinValueStr != null && coinValueStr.isNotEmpty) {
return double.tryParse(coinValueStr);
}
}
} catch (e) {
//
}
return null;
}
//
Widget _buildCoinLabel(double coinValue) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
decoration: BoxDecoration(
color: Color.fromRGBO(0, 0, 0, 0.05),
borderRadius: BorderRadius.circular(20.w),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
Assets.imagesImCoinIcon,
width: 16.w,
height: 16.w,
),
SizedBox(width: 4.w),
Text(
'+${coinValue.toStringAsFixed(2)}',
style: TextStyle(
fontSize: 12.sp,
color: Color.fromRGBO(255, 132, 0, 1),
),
),
],
),
);
}
//
Widget _buildMessageStatus() {
//

301
lib/widget/message/video_item.dart

@ -203,6 +203,9 @@ class _VideoItemState extends State<VideoItem> {
@override
Widget build(BuildContext context) {
//
final coinValue = _getCoinValue();
return Column(
children: [
if (widget.showTime) _buildTimeLabel(),
@ -233,142 +236,153 @@ class _VideoItemState extends State<VideoItem> {
),
),
if (widget.isSentByMe) SizedBox(width: 10.w),
// 🚀
Stack(
Column(
crossAxisAlignment: widget.isSentByMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: [
GestureDetector(
onTap: _playVideo,
child: Container(
margin: EdgeInsets.only(top: 10.h),
width: 180.w,
height: 180.w * (304 / 289), // 289:304
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(18.w),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 8,
offset: Offset(0, 2),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(18.w),
child: Stack(
fit: StackFit.expand,
children: [
// 🚀
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Colors.grey[300]!,
Colors.grey[400]!,
],
),
),
child: Icon(
Icons.videocam,
size: 48.w,
color: Colors.grey[600],
),
),
// 🚀
if (_thumbnailPath != null && _thumbnailPath!.isNotEmpty)
_buildThumbnail(),
//
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.black.withOpacity(0.1),
Colors.black.withOpacity(0.5),
],
),
// 🚀
Stack(
children: [
GestureDetector(
onTap: _playVideo,
child: Container(
margin: EdgeInsets.only(top: 10.h),
width: 180.w,
height: 180.w * (304 / 289), // 289:304
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(18.w),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 8,
offset: Offset(0, 2),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
//
Container(
padding: EdgeInsets.all(8.w),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
shape: BoxShape.circle,
),
child: Icon(
Icons.play_arrow_rounded,
size: 48.w,
color: Colors.white,
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(18.w),
child: Stack(
fit: StackFit.expand,
children: [
// 🚀
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Colors.grey[300]!,
Colors.grey[400]!,
],
),
),
],
),
),
//
Positioned(
right: 8.w,
bottom: 8.h,
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 8.w,
vertical: 4.h,
),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.7),
borderRadius: BorderRadius.circular(4.w),
child: Icon(
Icons.videocam,
size: 48.w,
color: Colors.grey[600],
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.videocam_rounded,
size: 12.w,
color: Colors.white,
// 🚀
if (_thumbnailPath != null && _thumbnailPath!.isNotEmpty)
_buildThumbnail(),
//
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.black.withOpacity(0.1),
Colors.black.withOpacity(0.5),
],
),
SizedBox(width: 4.w),
Text(
_formatDuration(widget.videoBody.duration ?? 0),
style: TextStyle(
fontSize: 11.sp,
color: Colors.white,
fontWeight: FontWeight.w600,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
//
Container(
padding: EdgeInsets.all(8.w),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
shape: BoxShape.circle,
),
child: Icon(
Icons.play_arrow_rounded,
size: 48.w,
color: Colors.white,
),
),
],
),
),
//
Positioned(
right: 8.w,
bottom: 8.h,
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 8.w,
vertical: 4.h,
),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.7),
borderRadius: BorderRadius.circular(4.w),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.videocam_rounded,
size: 12.w,
color: Colors.white,
),
SizedBox(width: 4.w),
Text(
_formatDuration(widget.videoBody.duration ?? 0),
style: TextStyle(
fontSize: 11.sp,
color: Colors.white,
fontWeight: FontWeight.w600,
),
),
],
),
],
),
),
),
],
),
],
),
),
),
),
),
// 🚀
if (_isLoadingVideo)
Positioned.fill(
child: Container(
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.5),
borderRadius: BorderRadius.circular(18.w),
),
child: Center(
child: CircularProgressIndicator(
strokeWidth: 3,
valueColor: AlwaysStoppedAnimation<Color>(
Colors.white,
// 🚀
if (_isLoadingVideo)
Positioned.fill(
child: Container(
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.5),
borderRadius: BorderRadius.circular(18.w),
),
child: Center(
child: CircularProgressIndicator(
strokeWidth: 3,
valueColor: AlwaysStoppedAnimation<Color>(
Colors.white,
),
),
),
),
),
),
),
],
),
],
),
//
if (!widget.isSentByMe && coinValue != null)
Padding(
padding: EdgeInsets.only(top: 10.h),
child: _buildCoinLabel(coinValue),
),
],
),
if (widget.isSentByMe) SizedBox(width: 8.w),
if (widget.isSentByMe) _buildAvatar(),
],
@ -445,6 +459,51 @@ class _VideoItemState extends State<VideoItem> {
}
}
//
double? _getCoinValue() {
try {
final attributes = widget.message.attributes;
if (attributes != null && attributes.containsKey('coin_value')) {
final coinValueStr = attributes['coin_value'] as String?;
if (coinValueStr != null && coinValueStr.isNotEmpty) {
return double.tryParse(coinValueStr);
}
}
} catch (e) {
//
}
return null;
}
//
Widget _buildCoinLabel(double coinValue) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
decoration: BoxDecoration(
color: Color.fromRGBO(0, 0, 0, 0.05),
borderRadius: BorderRadius.circular(20.w),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
Assets.imagesImCoinIcon,
width: 16.w,
height: 16.w,
),
SizedBox(width: 4.w),
Text(
'+${coinValue.toStringAsFixed(2)}',
style: TextStyle(
fontSize: 12.sp,
color: Color.fromRGBO(255, 132, 0, 1),
),
),
],
),
);
}
// 🚀
Widget _buildThumbnail() {
if (_thumbnailPath == null || _thumbnailPath!.isEmpty) {

161
lib/widget/message/voice_item.dart

@ -234,6 +234,9 @@ class _VoiceItemState extends State<VoiceItem> with TickerProviderStateMixin {
//
final isPlaying = _playerManager.isPlaying(widget.messageId);
//
final coinValue = _getCoinValue();
return Column(
children: [
@ -249,59 +252,70 @@ class _VoiceItemState extends State<VoiceItem> with TickerProviderStateMixin {
children: [
if (!widget.isSentByMe) _buildAvatar(),
if (!widget.isSentByMe) SizedBox(width: 8.w),
Container(
margin: EdgeInsets.only(top: 10.h),
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 10.h),
decoration: BoxDecoration(
color: widget.isSentByMe ? Color(0xff8E7BF6) : Colors.white,
borderRadius: BorderRadius.only(
topLeft: widget.isSentByMe
? Radius.circular(12.w)
: Radius.circular(0),
topRight: widget.isSentByMe
? Radius.circular(0)
: Radius.circular(12.w),
bottomLeft: Radius.circular(12.w),
bottomRight: Radius.circular(12.w),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
//
Container(
width: 20.w,
height: 20.w,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: widget.isSentByMe ? Colors.white : Colors.black,
),
child: Icon(
isPlaying ? Icons.pause : Icons.play_arrow,
color: widget.isSentByMe
? Color(0xff8E7BF6)
: Colors.white,
size: 16.w,
Column(
crossAxisAlignment: widget.isSentByMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.only(top: 10.h),
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 10.h),
decoration: BoxDecoration(
color: widget.isSentByMe ? Color(0xff8E7BF6) : Colors.white,
borderRadius: BorderRadius.only(
topLeft: widget.isSentByMe
? Radius.circular(12.w)
: Radius.circular(0),
topRight: widget.isSentByMe
? Radius.circular(0)
: Radius.circular(12.w),
bottomLeft: Radius.circular(12.w),
bottomRight: Radius.circular(12.w),
),
),
SizedBox(width: 8.w),
//
Text(
durationText,
style: TextStyle(
fontSize: 14.sp,
color: widget.isSentByMe ? Colors.white : Colors.black,
fontWeight: FontWeight.w500,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
//
Container(
width: 20.w,
height: 20.w,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: widget.isSentByMe ? Colors.white : Colors.black,
),
child: Icon(
isPlaying ? Icons.pause : Icons.play_arrow,
color: widget.isSentByMe
? Color(0xff8E7BF6)
: Colors.white,
size: 16.w,
),
),
SizedBox(width: 8.w),
//
Text(
durationText,
style: TextStyle(
fontSize: 14.sp,
color: widget.isSentByMe ? Colors.white : Colors.black,
fontWeight: FontWeight.w500,
),
),
SizedBox(width: 8.w),
//
_buildWaveform(),
],
),
).onTap(() {
_handlePlayPause();
}),
//
if (!widget.isSentByMe && coinValue != null)
Padding(
padding: EdgeInsets.only(top: 10.h),
child: _buildCoinLabel(coinValue),
),
SizedBox(width: 8.w),
//
_buildWaveform(),
],
),
).onTap(() {
_handlePlayPause();
}),
],
),
if (widget.isSentByMe) SizedBox(width: 8.w),
if (widget.isSentByMe) _buildAvatar(),
],
@ -341,6 +355,53 @@ class _VoiceItemState extends State<VoiceItem> with TickerProviderStateMixin {
);
}
//
double? _getCoinValue() {
try {
if (widget.message != null) {
final attributes = widget.message!.attributes;
if (attributes != null && attributes.containsKey('coin_value')) {
final coinValueStr = attributes['coin_value'] as String?;
if (coinValueStr != null && coinValueStr.isNotEmpty) {
return double.tryParse(coinValueStr);
}
}
}
} catch (e) {
//
}
return null;
}
//
Widget _buildCoinLabel(double coinValue) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
decoration: BoxDecoration(
color: Color.fromRGBO(0, 0, 0, 0.05),
borderRadius: BorderRadius.circular(20.w),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
Assets.imagesImCoinIcon,
width: 16.w,
height: 16.w,
),
SizedBox(width: 4.w),
Text(
'+${coinValue.toStringAsFixed(2)}',
style: TextStyle(
fontSize: 12.sp,
color: Color.fromRGBO(255, 132, 0, 1),
),
),
],
),
);
}
//
Widget _buildWaveform() {
// 20

68
location_plugin/example/pubspec.lock

@ -6,7 +6,7 @@ packages:
description:
name: async
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.13.0"
boolean_selector:
@ -14,7 +14,7 @@ packages:
description:
name: boolean_selector
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.2"
characters:
@ -22,7 +22,7 @@ packages:
description:
name: characters
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.4.0"
clock:
@ -30,7 +30,7 @@ packages:
description:
name: clock
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.2"
collection:
@ -38,7 +38,7 @@ packages:
description:
name: collection
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.19.1"
cupertino_icons:
@ -46,7 +46,7 @@ packages:
description:
name: cupertino_icons
sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.8"
fake_async:
@ -54,7 +54,7 @@ packages:
description:
name: fake_async
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.3"
file:
@ -62,7 +62,7 @@ packages:
description:
name: file
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "7.0.1"
flutter:
@ -80,7 +80,7 @@ packages:
description:
name: flutter_lints
sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.0.0"
flutter_test:
@ -98,7 +98,7 @@ packages:
description:
name: http
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.6.0"
http_parser:
@ -106,7 +106,7 @@ packages:
description:
name: http_parser
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.1.2"
integration_test:
@ -119,7 +119,7 @@ packages:
description:
name: leak_tracker
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "11.0.2"
leak_tracker_flutter_testing:
@ -127,7 +127,7 @@ packages:
description:
name: leak_tracker_flutter_testing
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.10"
leak_tracker_testing:
@ -135,7 +135,7 @@ packages:
description:
name: leak_tracker_testing
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.2"
lints:
@ -143,7 +143,7 @@ packages:
description:
name: lints
sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.1.1"
location_plugin:
@ -158,7 +158,7 @@ packages:
description:
name: matcher
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.12.17"
material_color_utilities:
@ -166,7 +166,7 @@ packages:
description:
name: material_color_utilities
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.11.1"
meta:
@ -174,7 +174,7 @@ packages:
description:
name: meta
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.16.0"
path:
@ -182,7 +182,7 @@ packages:
description:
name: path
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.9.1"
platform:
@ -190,7 +190,7 @@ packages:
description:
name: platform
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.6"
plugin_platform_interface:
@ -198,7 +198,7 @@ packages:
description:
name: plugin_platform_interface
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.8"
process:
@ -206,7 +206,7 @@ packages:
description:
name: process
sha256: c6248e4526673988586e8c00bb22a49210c258dc91df5227d5da9748ecf79744
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.0.5"
sky_engine:
@ -219,7 +219,7 @@ packages:
description:
name: source_span
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.10.1"
stack_trace:
@ -227,7 +227,7 @@ packages:
description:
name: stack_trace
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.12.1"
stream_channel:
@ -235,7 +235,7 @@ packages:
description:
name: stream_channel
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.4"
string_scanner:
@ -243,7 +243,7 @@ packages:
description:
name: string_scanner
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.4.1"
sync_http:
@ -251,7 +251,7 @@ packages:
description:
name: sync_http
sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.3.1"
term_glyph:
@ -259,7 +259,7 @@ packages:
description:
name: term_glyph
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.2"
test_api:
@ -267,7 +267,7 @@ packages:
description:
name: test_api
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.7.6"
typed_data:
@ -275,7 +275,7 @@ packages:
description:
name: typed_data
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.4.0"
vector_math:
@ -283,7 +283,7 @@ packages:
description:
name: vector_math
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.0"
vm_service:
@ -291,7 +291,7 @@ packages:
description:
name: vm_service
sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "15.0.2"
web:
@ -299,7 +299,7 @@ packages:
description:
name: web
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.1"
webdriver:
@ -307,7 +307,7 @@ packages:
description:
name: webdriver
sha256: "2f3a14ca026957870cfd9c635b83507e0e51d8091568e90129fbf805aba7cade"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.0"
sdks:

Loading…
Cancel
Save