From 689a42ebd6acc33dcf607794c80dd9f50cecdb70 Mon Sep 17 00:00:00 2001 From: ZHR007 Date: Thu, 13 Nov 2025 08:55:44 +0800 Subject: [PATCH 01/10] no message --- lib/controller/mine/auth_controller.dart | 62 +++++++ lib/pages/mine/auth_center_page.dart | 111 ++++++++++++ lib/pages/mine/login_page.dart | 1 - lib/pages/mine/real_name_page.dart | 218 +++++++++++++++++++++++ 4 files changed, 391 insertions(+), 1 deletion(-) create mode 100644 lib/controller/mine/auth_controller.dart create mode 100644 lib/pages/mine/auth_center_page.dart create mode 100644 lib/pages/mine/real_name_page.dart diff --git a/lib/controller/mine/auth_controller.dart b/lib/controller/mine/auth_controller.dart new file mode 100644 index 0000000..7183607 --- /dev/null +++ b/lib/controller/mine/auth_controller.dart @@ -0,0 +1,62 @@ +import 'dart:async'; +import 'package:get/get.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import '../../network/user_api.dart'; + +class AuthController extends GetxController { + final isLoading = false.obs; + final List dataList = []; +// 是否正在登录中 + final isLoggingIn = false.obs; + // 从GetX依赖注入中获取UserApi实例 + late UserApi _userApi; + + @override + void onInit() { + super.onInit(); + // 从全局依赖中获取UserApi + _userApi = Get.find(); + _loadInitialData(); + } + void _loadInitialData() { + isLoading.value = true; + Future.delayed(Duration(seconds: 1), () { + dataList.assignAll([ + AuthCard( title: '手机绑定', desc: '防止账号丢失', index: 1), + AuthCard( title: '真实头像', desc: '提高交友成功率', index: 2), + AuthCard( title: '实名认证', desc: '提高交友成功率', index: 3), + ]); + isLoading.value = false; + }); + } + + // 登录方法 + Future login() async { + try { + // 调用登录接口 + final response = await _userApi.login({}); + // 处理响应 + if (response.data.isSuccess) { + + } else { + SmartDialog.showToast(response.data.message); + } + } catch (e) { + SmartDialog.showToast('网络请求失败,请检查网络连接'); + } finally { + + } + } +} + +class AuthCard { + final String title; + final String desc; + final int index; + + AuthCard({ + required this.desc, + required this.title, + required this.index, + }); +} diff --git a/lib/pages/mine/auth_center_page.dart b/lib/pages/mine/auth_center_page.dart new file mode 100644 index 0000000..8df46e4 --- /dev/null +++ b/lib/pages/mine/auth_center_page.dart @@ -0,0 +1,111 @@ +import 'package:dating_touchme_app/extension/ex_widget.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../controller/mine/auth_controller.dart'; + +class AuthCenterPage extends StatelessWidget { + AuthCenterPage({super.key}); + final AuthController controller = Get.put(AuthController()); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Color(0xffF5F5F5), + appBar: AppBar( + title: Text('认证中心', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), + centerTitle: true, + leading: IconButton( + icon: Icon(Icons.arrow_back_ios, size: 24, color: Colors.grey,), + onPressed: () { + Get.back(); + }, + ), + ), + body: Obx(() { + if (controller.isLoading.value) { + return const Center(child: CupertinoActivityIndicator(radius: 12,)); + } + return ListView.builder( + padding: const EdgeInsets.only(top: 16, right: 16, left: 16), + itemCount: controller.dataList.length, + itemBuilder: (context, index) { + final record = controller.dataList[index]; + return _buildListItem(record); + }, + ); + }) + ); + } + + // 构建列表项 + Widget _buildListItem(AuthCard item) { + return Container( + margin: EdgeInsets.only(bottom: 12), + padding: EdgeInsets.all(24), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // 左侧图片 + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + color: Colors.blue[100], + image: DecorationImage( + image: NetworkImage('https://picsum.photos/40/40?random=$item.index'), + fit: BoxFit.cover, + ), + ), + ), + SizedBox(width: 12), + // 右侧内容 + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item.title, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: Colors.grey[800], + ), + ), + SizedBox(height: 2), + Text( + item.desc, + style: TextStyle( + fontSize: 12, + color: Colors.grey[600], + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], + ), + Spacer(), + Row( + children: [ + Text('去认证', style: TextStyle(fontSize: 12, color: Colors.grey[500])), + SizedBox(width: 4), + Icon( + Icons.navigate_next, // Material Icons + // size: 128.0, // 设置图标大小 + color: Colors.grey, // 设置图标颜色 + ) + ], + ) + ], + ), + ).onTap((){ + print(item.index); + }); + } + +} diff --git a/lib/pages/mine/login_page.dart b/lib/pages/mine/login_page.dart index 126daaf..6facb6c 100644 --- a/lib/pages/mine/login_page.dart +++ b/lib/pages/mine/login_page.dart @@ -30,7 +30,6 @@ class LoginPage extends StatelessWidget { child: Column( children: [ const SizedBox(height: 150), - // Logo和标题区域 Center( child: Column( diff --git a/lib/pages/mine/real_name_page.dart b/lib/pages/mine/real_name_page.dart new file mode 100644 index 0000000..a95463b --- /dev/null +++ b/lib/pages/mine/real_name_page.dart @@ -0,0 +1,218 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; + +import '../../controller/mine/auth_controller.dart'; + +class RealNamePage extends StatelessWidget { + RealNamePage({super.key}); + final AuthController controller = Get.put(AuthController()); +// 是否同意协议 + final agreeTerms = Rx(false); + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Color(0xffFFFFFF), + appBar: AppBar( + title: Text('实名认证', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), + centerTitle: true, + leading: IconButton( + icon: Icon(Icons.arrow_back_ios, size: 24, color: Colors.grey,), + onPressed: () { + Get.back(); + }, + ), + ), + body: Column( + children: [ + Container( + height: 48, + decoration: BoxDecoration(color: Color(0xffE7E7E7)), + padding: const EdgeInsets.only(left: 16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, // 垂直居中 + children: [ + Text( + '*请填写本人实名信息', + style: TextStyle( + fontSize: 14, + color: Colors.black87, + ), + ), + ], + ), + ), + Container( + height: 56, // 固定高度确保垂直居中 + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Colors.grey[400]!, + width: 0.5, + ), + ), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, // 垂直居中 + children: [ + // 左侧标签 - 固定宽度 + 垂直居中 + Container( + width: 100, + alignment: Alignment.centerLeft, + padding: const EdgeInsets.only(left: 16), + child: Text( + '姓名:', + style: TextStyle( + fontSize: 15, + color: Colors.black87, + ), + ), + ), + SizedBox(width: 12), + + // 输入框区域 - 使用Expanded填充剩余空间 + Expanded( + child: Container( + alignment: Alignment.centerLeft, // 输入框内容垂直居中 + child: TextField( + decoration: InputDecoration( + hintText: '请输入姓名', + hintStyle: TextStyle(color: Colors.grey[500]), + border: InputBorder.none, // 隐藏默认边框 + contentPadding: EdgeInsets.zero, // 去除默认padding + isDense: true, // 紧凑模式 + ), + style: TextStyle( + fontSize: 15, + height: 1.2, // 控制文字垂直位置 + ), + ), + ), + ), + ], + ), + ), + // SizedBox(height: 30), + Container( + height: 56, // 固定高度确保垂直居中 + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Colors.grey[400]!, + width: 0.5, + ), + ), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, // 垂直居中 + children: [ + // 左侧标签 - 固定宽度 + 垂直居中 + Container( + width: 100, + alignment: Alignment.centerLeft, + padding: const EdgeInsets.only(left: 16), + child: Text( + '身份证号:', + style: TextStyle( + fontSize: 15, + color: Colors.black87, + ), + ), + ), + SizedBox(width: 12), + + // 输入框区域 - 使用Expanded填充剩余空间 + Expanded( + child: Container( + alignment: Alignment.centerLeft, // 输入框内容垂直居中 + child: TextField( + decoration: InputDecoration( + hintText: '请输入身份证号', + hintStyle: TextStyle(color: Colors.grey[500]), + border: InputBorder.none, // 隐藏默认边框 + contentPadding: EdgeInsets.zero, // 去除默认padding + isDense: true, // 紧凑模式 + ), + style: TextStyle( + fontSize: 15, + height: 1.2, // 控制文字垂直位置 + ), + ), + ), + ), + ], + ), + ), + SizedBox(height: 24), + // 协议同意复选框 + Row( + crossAxisAlignment: CrossAxisAlignment.start, // 垂直居中 + children: [ + SizedBox(width: 8), + Obx(() => Checkbox( + value: agreeTerms.value, + onChanged: (value) { + agreeTerms.value = value ?? false; + }, + activeColor: Color(0xff7562F9), + side: const BorderSide(color: Colors.grey), + shape: const CircleBorder(), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + )), + ConstrainedBox( + constraints: BoxConstraints(maxWidth: 300), // 限制最大宽度 + child: Text( + '前排吃瓜,这里的瓜包新鲜,这里的人士有料有梗有灼见~,这里的瓜包新鲜,这里的人士有料有梗有灼见~', + style: TextStyle( fontSize: 13, color: Colors.grey ), + ), + ) + ], + ), + SizedBox(height: 48), + Container( + padding: const EdgeInsets.only(left: 24, right: 24), + child: ElevatedButton( + onPressed: controller.isLoggingIn.value + ? null + : () { + // 登录逻辑 + if (!agreeTerms.value) { + SmartDialog.showToast('请同意用户协议和隐私政策'); + return; + } + // 调用控制器的登录方法 + controller.login(); + }, + style: ElevatedButton.styleFrom( + minimumSize: const Size(double.infinity, 50), + backgroundColor: agreeTerms.value ? const Color(0xff7562F9) : Colors.grey.shade300, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(25), + ), + elevation: 0, + ), + child: controller.isLoggingIn.value ? const SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator( + color: Colors.white, + strokeWidth: 1, + ), + ) + : const Text( + '立即认证', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ), + ) + ], + ), + ); + } + +} From ececa536a4c4ed8ac6613190e68914ce37b555b1 Mon Sep 17 00:00:00 2001 From: Jolie <412895109@qq.com> Date: Thu, 13 Nov 2025 10:26:35 +0800 Subject: [PATCH 02/10] =?UTF-8?q?feat(im):=20=E5=AE=9E=E7=8E=B0=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E5=8F=91=E9=80=81=E5=90=8E=E8=87=AA=E5=8A=A8=E5=88=B7?= =?UTF-8?q?=E6=96=B0=E4=BC=9A=E8=AF=9D=E5=88=97=E8=A1=A8=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 ChatController 中添加 _refreshConversationList 方法 - 在 IMManager 中添加 _refreshConversationList 方法 - 消息发送成功后调用会话列表刷新逻辑 - 收到新消息时自动刷新会话列表 - 使用 Get.find 获取 ConversationController 实例 - 添加异常处理避免控制器未注册时的错误 --- lib/controller/message/chat_controller.dart | 27 +- lib/im/im_manager.dart | 35 +- pubspec.lock | 396 ++++++++++---------- 3 files changed, 252 insertions(+), 206 deletions(-) diff --git a/lib/controller/message/chat_controller.dart b/lib/controller/message/chat_controller.dart index 811fc91..76698f1 100644 --- a/lib/controller/message/chat_controller.dart +++ b/lib/controller/message/chat_controller.dart @@ -2,6 +2,7 @@ import 'package:get/get.dart'; import '../../im/im_manager.dart'; import 'package:im_flutter_sdk/im_flutter_sdk.dart'; +import 'conversation_controller.dart'; class ChatController extends GetxController { final String userId; @@ -54,6 +55,8 @@ class ChatController extends GetxController { // 发送成功后将消息添加到列表开头 messages.insert(0, message); update(); + // 更新会话列表 + _refreshConversationList(); return true; } return false; @@ -76,6 +79,8 @@ class ChatController extends GetxController { // 发送成功后将消息添加到列表开头 messages.insert(0, message); update(); + // 更新会话列表 + _refreshConversationList(); return true; } return false; @@ -99,6 +104,8 @@ class ChatController extends GetxController { // 发送成功后将消息添加到列表开头 messages.insert(0, message); update(); + // 更新会话列表 + _refreshConversationList(); return true; } return false; @@ -116,7 +123,7 @@ class ChatController extends GetxController { print('🎬 [ChatController] 准备发送视频消息'); print('视频路径: $filePath'); print('视频时长: $duration 秒'); - + final message = await IMManager.instance.sendVideoMessage( filePath, userId, @@ -128,6 +135,8 @@ class ChatController extends GetxController { // 发送成功后将消息添加到列表开头 messages.insert(0, message); update(); + // 更新会话列表 + _refreshConversationList(); return true; } print('❌ [ChatController] 视频消息创建失败'); @@ -188,4 +197,20 @@ class ChatController extends GetxController { await fetchMessages(loadMore: true); } } + + /// 刷新会话列表 + void _refreshConversationList() { + try { + // 尝试获取 ConversationController 并刷新会话列表 + if (Get.isRegistered()) { + final conversationController = Get.find(); + conversationController.refreshConversations(); + } + } catch (e) { + // ConversationController 可能未注册,忽略错误 + if (Get.isLogEnable) { + Get.log('刷新会话列表失败: $e'); + } + } + } } diff --git a/lib/im/im_manager.dart b/lib/im/im_manager.dart index 456e5d5..17edd6b 100644 --- a/lib/im/im_manager.dart +++ b/lib/im/im_manager.dart @@ -1,5 +1,7 @@ +import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; import 'package:im_flutter_sdk/im_flutter_sdk.dart'; +import '../controller/message/conversation_controller.dart'; // 完整的IM管理器实现,使用实际的SDK类型和方法 class IMManager { @@ -71,6 +73,9 @@ class IMManager { "", EMChatEventHandler( onMessagesReceived: (messages) { + // 收到新消息时,更新会话列表 + _refreshConversationList(); + for (var msg in messages) { switch (msg.body.type) { case MessageType.TXT: @@ -167,16 +172,16 @@ class IMManager { /// 发送语音消息 Future sendVoiceMessage( - String filePath, - String toChatUsername, - int duration - ) async { + String filePath, + String toChatUsername, + int duration, + ) async { try { // 创建图片消息 final message = EMMessage.createVoiceSendMessage( targetId: toChatUsername, filePath: filePath, - duration: duration + duration: duration, ); // 发送消息 @@ -223,14 +228,14 @@ class IMManager { print('视频路径: $videoPath'); print('接收用户: $toChatUsername'); print('视频时长: $duration 秒'); - + // 创建视频消息 final message = EMMessage.createVideoSendMessage( targetId: toChatUsername, filePath: videoPath, duration: duration, ); - + print('消息创建成功,消息类型: ${message.body.type}'); print('消息体是否为视频: ${message.body is EMVideoMessageBody}'); @@ -280,6 +285,22 @@ class IMManager { return data[userId]; } + /// 刷新会话列表 + void _refreshConversationList() { + try { + // 尝试获取 ConversationController 并刷新会话列表 + if (Get.isRegistered()) { + final conversationController = Get.find(); + conversationController.refreshConversations(); + } + } catch (e) { + // ConversationController 可能未注册,忽略错误 + if (Get.isLogEnable) { + Get.log('刷新会话列表失败: $e'); + } + } + } + /// 清理资源 void dispose() { try { diff --git a/pubspec.lock b/pubspec.lock index 6032ac6..2fd77cb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -6,7 +6,7 @@ packages: description: name: _fe_analyzer_shared sha256: f0bb5d1648339c8308cc0b9838d8456b3cfe5c91f9dc1a735b4d003269e5da9a - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "88.0.0" analyzer: @@ -14,7 +14,7 @@ packages: description: name: analyzer sha256: "0b7b9c329d2879f8f05d6c05b32ee9ec025f39b077864bdb5ac9a7b63418a98f" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "8.1.1" ansicolor: @@ -22,7 +22,7 @@ packages: description: name: ansicolor sha256: "50e982d500bc863e1d703448afdbf9e5a72eb48840a4f766fa361ffd6877055f" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.3" archive: @@ -30,7 +30,7 @@ packages: description: name: archive sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.0.7" args: @@ -38,7 +38,7 @@ packages: description: name: args sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.7.0" async: @@ -46,7 +46,7 @@ packages: description: name: async sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.13.0" audioplayers: @@ -110,7 +110,7 @@ packages: description: name: boolean_selector sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.2" build: @@ -118,7 +118,7 @@ packages: description: name: build sha256: dfb67ccc9a78c642193e0c2d94cb9e48c2c818b3178a86097d644acdcde6a8d9 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.0.2" build_config: @@ -126,7 +126,7 @@ packages: description: name: build_config sha256: "4f64382b97504dc2fcdf487d5aae33418e08b4703fc21249e4db6d804a4d0187" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.2.0" build_daemon: @@ -134,7 +134,7 @@ packages: description: name: build_daemon sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.1.1" build_runner: @@ -142,7 +142,7 @@ packages: description: name: build_runner sha256: "04f69b1502f66e22ae7990bbd01eb552b7f12793c4d3ea6e715d0ac5e98bcdac" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.10.2" built_collection: @@ -150,7 +150,7 @@ packages: description: name: built_collection sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "5.1.1" built_value: @@ -158,7 +158,7 @@ packages: description: name: built_value sha256: a30f0a0e38671e89a492c44d005b5545b830a961575bbd8336d42869ff71066d - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "8.12.0" cached_network_image: @@ -166,7 +166,7 @@ packages: description: name: cached_network_image sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.4.1" cached_network_image_platform_interface: @@ -174,7 +174,7 @@ packages: description: name: cached_network_image_platform_interface sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.1.1" cached_network_image_web: @@ -182,7 +182,7 @@ packages: description: name: cached_network_image_web sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.3.1" camera: @@ -190,7 +190,7 @@ packages: description: name: camera sha256: dfa8fc5a1adaeb95e7a54d86a5bd56f4bb0e035515354c8ac6d262e35cec2ec8 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.10.6" camera_android: @@ -198,7 +198,7 @@ packages: description: name: camera_android sha256: "8397c4fcec4f4dbafdeff994adc6ed16dcaa4a25f02e70e3b8fbe59c9a88807f" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.10.10+11" camera_avfoundation: @@ -206,7 +206,7 @@ packages: description: name: camera_avfoundation sha256: "34bcd5db30e52414f1f0783c5e3f566909fab14141a21b3b576c78bd35382bf6" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.9.22+4" camera_platform_interface: @@ -214,7 +214,7 @@ packages: description: name: camera_platform_interface sha256: "98cfc9357e04bad617671b4c1f78a597f25f08003089dd94050709ae54effc63" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.12.0" camera_web: @@ -222,7 +222,7 @@ packages: description: name: camera_web sha256: "595f28c89d1fb62d77c73c633193755b781c6d2e0ebcd8dc25b763b514e6ba8f" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.3.5" characters: @@ -230,7 +230,7 @@ packages: description: name: characters sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.4.0" checked_yaml: @@ -238,7 +238,7 @@ packages: description: name: checked_yaml sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.4" chewie: @@ -246,7 +246,7 @@ packages: description: name: chewie sha256: "44bcfc5f0dfd1de290c87c9d86a61308b3282a70b63435d5557cfd60f54a69ca" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.13.0" clock: @@ -254,7 +254,7 @@ packages: description: name: clock sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.2" code_builder: @@ -262,7 +262,7 @@ packages: description: name: code_builder sha256: "11654819532ba94c34de52ff5feb52bd81cba1de00ef2ed622fd50295f9d4243" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.11.0" collection: @@ -270,7 +270,7 @@ packages: description: name: collection sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.19.1" common_utils: @@ -278,7 +278,7 @@ packages: description: name: common_utils sha256: c26884339b13ff99b0739e56f4b02090c84054ed9dd3a045435cd24e7b99c2c1 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.0" convert: @@ -286,7 +286,7 @@ packages: description: name: convert sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.1.2" cross_file: @@ -294,7 +294,7 @@ packages: description: name: cross_file sha256: "942a4791cd385a68ccb3b32c71c427aba508a1bb949b86dff2adbe4049f16239" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.3.5" crypto: @@ -302,7 +302,7 @@ packages: description: name: crypto sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.7" csslib: @@ -310,7 +310,7 @@ packages: description: name: csslib sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.2" cupertino_icons: @@ -318,7 +318,7 @@ packages: description: name: cupertino_icons sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.8" dart_style: @@ -326,7 +326,7 @@ packages: description: name: dart_style sha256: c87dfe3d56f183ffe9106a18aebc6db431fc7c98c31a54b952a77f3d54a85697 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.1.2" dbus: @@ -334,7 +334,7 @@ packages: description: name: dbus sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.7.11" decimal: @@ -342,7 +342,7 @@ packages: description: name: decimal sha256: fc706a5618b81e5b367b01dd62621def37abc096f2b46a9bd9068b64c1fa36d0 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.2.4" dio: @@ -350,7 +350,7 @@ packages: description: name: dio sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "5.9.0" dio_web_adapter: @@ -358,7 +358,7 @@ packages: description: name: dio_web_adapter sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.1" easy_localization: @@ -366,7 +366,7 @@ packages: description: name: easy_localization sha256: "2ccdf9db8fe4d9c5a75c122e6275674508fd0f0d49c827354967b8afcc56bbed" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.8" easy_logger: @@ -374,7 +374,7 @@ packages: description: name: easy_logger sha256: c764a6e024846f33405a2342caf91c62e357c24b02c04dbc712ef232bf30ffb7 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.0.2" easy_refresh: @@ -382,7 +382,7 @@ packages: description: name: easy_refresh sha256: "486e30abfcaae66c0f2c2798a10de2298eb9dc5e0bb7e1dba9328308968cae0c" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.4.0" event_bus: @@ -390,7 +390,7 @@ packages: description: name: event_bus sha256: "1a55e97923769c286d295240048fc180e7b0768902c3c2e869fe059aafa15304" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.1" extended_image: @@ -398,7 +398,7 @@ packages: description: name: extended_image sha256: "85199f9233e03abc2ce2e68cbb2991648666af4a527ae4e6250935be8edfddae" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "9.1.0" extended_image_library: @@ -406,7 +406,7 @@ packages: description: name: extended_image_library sha256: e61dafd94400fff6ef7ed1523d445ff3af137f198f3228e4a3107bc5b4bec5d1 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.0.6" fake_async: @@ -414,7 +414,7 @@ packages: description: name: fake_async sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.3.3" ffi: @@ -422,7 +422,7 @@ packages: description: name: ffi sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.4" file: @@ -430,7 +430,7 @@ packages: description: name: file sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "7.0.1" file_selector_linux: @@ -438,7 +438,7 @@ packages: description: name: file_selector_linux sha256: "54cbbd957e1156d29548c7d9b9ec0c0ebb6de0a90452198683a7d23aed617a33" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.9.3+2" file_selector_macos: @@ -446,7 +446,7 @@ packages: description: name: file_selector_macos sha256: "88707a3bec4b988aaed3b4df5d7441ee4e987f20b286cddca5d6a8270cab23f2" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.9.4+5" file_selector_platform_interface: @@ -454,7 +454,7 @@ packages: description: name: file_selector_platform_interface sha256: "35e0bd61ebcdb91a3505813b055b09b79dfdc7d0aee9c09a7ba59ae4bb13dc85" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.7.0" file_selector_windows: @@ -462,7 +462,7 @@ packages: description: name: file_selector_windows sha256: "320fcfb6f33caa90f0b58380489fc5ac05d99ee94b61aa96ec2bff0ba81d3c2b" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.9.3+4" fixnum: @@ -470,7 +470,7 @@ packages: description: name: fixnum sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.1" flustars: @@ -478,7 +478,7 @@ packages: description: name: flustars sha256: "7019ab8d68c0d4759ee122644d91a165d450b0492717f9e7e9d0ce277dcf664b" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.1" flutter: @@ -491,7 +491,7 @@ packages: description: name: flutter_cache_manager sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.4.1" flutter_lints: @@ -499,7 +499,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_localizations: @@ -512,7 +512,7 @@ packages: description: name: flutter_native_splash sha256: "4fb9f4113350d3a80841ce05ebf1976a36de622af7d19aca0ca9a9911c7ff002" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.4.7" flutter_oss_aliyun: @@ -520,7 +520,7 @@ packages: description: name: flutter_oss_aliyun sha256: "8280c1e8dfb792dec6449d1e6052e1d3492b3b3a1dfee456cc41d3f31b4cbc26" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.4.2" flutter_plugin_android_lifecycle: @@ -528,7 +528,7 @@ packages: description: name: flutter_plugin_android_lifecycle sha256: "306f0596590e077338312f38837f595c04f28d6cdeeac392d3d74df2f0003687" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.32" flutter_screenutil: @@ -536,7 +536,7 @@ packages: description: name: flutter_screenutil sha256: "8239210dd68bee6b0577aa4a090890342d04a136ce1c81f98ee513fc0ce891de" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "5.9.3" flutter_slidable: @@ -544,7 +544,7 @@ packages: description: name: flutter_slidable sha256: a857de7ea701f276fd6a6c4c67ae885b60729a3449e42766bb0e655171042801 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.1.2" flutter_smart_dialog: @@ -552,7 +552,7 @@ packages: description: name: flutter_smart_dialog sha256: "0852df132cb03fd8fc5144eb404c31eb7eb50c22aecb1cc2504f2f598090d756" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.9.8+9" flutter_swiper_null_safety: @@ -560,7 +560,7 @@ packages: description: name: flutter_swiper_null_safety sha256: "5a855e0080d035c08e82f8b7fd2f106344943a30c9ab483b2584860a2f22eaaf" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.2" flutter_test: @@ -578,7 +578,7 @@ packages: description: name: frontend_server_client sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.0.0" get: @@ -586,7 +586,7 @@ packages: description: name: get sha256: c79eeb4339f1f3deffd9ec912f8a923834bec55f7b49c9e882b8fef2c139d425 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.7.2" get_storage: @@ -594,7 +594,7 @@ packages: description: name: get_storage sha256: "39db1fffe779d0c22b3a744376e86febe4ade43bf65e06eab5af707dc84185a2" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.1" glob: @@ -602,7 +602,7 @@ packages: description: name: glob sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.3" graphs: @@ -610,7 +610,7 @@ packages: description: name: graphs sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.3.2" hotreloader: @@ -618,7 +618,7 @@ packages: description: name: hotreloader sha256: bc167a1163807b03bada490bfe2df25b0d744df359227880220a5cbd04e5734b - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.3.0" html: @@ -626,7 +626,7 @@ packages: description: name: html sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.15.6" http: @@ -634,7 +634,7 @@ packages: description: name: http sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.6.0" http_client_helper: @@ -642,7 +642,7 @@ packages: description: name: http_client_helper sha256: "8a9127650734da86b5c73760de2b404494c968a3fd55602045ffec789dac3cb1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.0" http_multi_server: @@ -650,7 +650,7 @@ packages: description: name: http_multi_server sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.2.2" http_parser: @@ -658,7 +658,7 @@ packages: description: name: http_parser sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.1.2" im_flutter_sdk: @@ -666,7 +666,7 @@ packages: description: name: im_flutter_sdk sha256: "5f81988c5edf14a4e3868b9c47f01de2ee355e2203ae5bd95d7cacfe30e15d97" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.15.2" im_flutter_sdk_android: @@ -674,7 +674,7 @@ packages: description: name: im_flutter_sdk_android sha256: ce4e01f1374a6cf20bb46f6774e1effbde5c014fd1cd3003b6604813e4fe9e71 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.15.2" im_flutter_sdk_interface: @@ -682,7 +682,7 @@ packages: description: name: im_flutter_sdk_interface sha256: "08ad3ce9fc935e24272bce34aebfd9b3c3c30aab2b9e7d4b2e759acf4a4874f5" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.15.2" im_flutter_sdk_ios: @@ -690,7 +690,7 @@ packages: description: name: im_flutter_sdk_ios sha256: "11300c086f5821730224932c28860198ef5d879f7941f9158dddee499f7bb60e" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.15.2" image: @@ -698,7 +698,7 @@ packages: description: name: image sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.5.4" image_picker: @@ -706,7 +706,7 @@ packages: description: name: image_picker sha256: "021834d9c0c3de46bf0fe40341fa07168407f694d9b2bb18d532dc1261867f7a" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.2" image_picker_android: @@ -714,7 +714,7 @@ packages: description: name: image_picker_android sha256: "317a5d961cec5b34e777b9252393f2afbd23084aa6e60fcf601dcf6341b9ebeb" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.8.12+23" image_picker_for_web: @@ -722,7 +722,7 @@ packages: description: name: image_picker_for_web sha256: "40c2a6a0da15556dc0f8e38a3246064a971a9f512386c3339b89f76db87269b6" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.1.0" image_picker_ios: @@ -730,7 +730,7 @@ packages: description: name: image_picker_ios sha256: e675c22790bcc24e9abd455deead2b7a88de4b79f7327a281812f14de1a56f58 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.8.13+1" image_picker_linux: @@ -738,7 +738,7 @@ packages: description: name: image_picker_linux sha256: "1f81c5f2046b9ab724f85523e4af65be1d47b038160a8c8deed909762c308ed4" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.2.2" image_picker_macos: @@ -746,7 +746,7 @@ packages: description: name: image_picker_macos sha256: "86f0f15a309de7e1a552c12df9ce5b59fe927e71385329355aec4776c6a8ec91" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.2.2+1" image_picker_platform_interface: @@ -754,7 +754,7 @@ packages: description: name: image_picker_platform_interface sha256: "567e056716333a1647c64bb6bd873cff7622233a5c3f694be28a583d4715690c" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.11.1" image_picker_windows: @@ -762,7 +762,7 @@ packages: description: name: image_picker_windows sha256: d248c86554a72b5495a31c56f060cf73a41c7ff541689327b1a7dbccc33adfae - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.2.2" intl: @@ -770,7 +770,7 @@ packages: description: name: intl sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.20.2" io: @@ -778,7 +778,7 @@ packages: description: name: io sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.5" js: @@ -786,7 +786,7 @@ packages: description: name: js sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.7.2" json_annotation: @@ -794,7 +794,7 @@ packages: description: name: json_annotation sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.9.0" json_serializable: @@ -802,7 +802,7 @@ packages: description: name: json_serializable sha256: "33a040668b31b320aafa4822b7b1e177e163fc3c1e835c6750319d4ab23aa6fe" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.11.1" leak_tracker: @@ -810,7 +810,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: @@ -818,7 +818,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: @@ -826,7 +826,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" lean_builder: @@ -834,7 +834,7 @@ packages: description: name: lean_builder sha256: ef5cd5f907157eb7aa87d1704504b5a6386d2cbff88a3c2b3344477bab323ee9 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.1.2" lints: @@ -842,7 +842,7 @@ packages: description: name: lints sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "5.1.1" logging: @@ -850,7 +850,7 @@ packages: description: name: logging sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.3.0" matcher: @@ -858,7 +858,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: @@ -866,7 +866,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: @@ -874,7 +874,7 @@ packages: description: name: meta sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.16.0" mime: @@ -882,7 +882,7 @@ packages: description: name: mime sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.6" nested: @@ -890,7 +890,7 @@ packages: description: name: nested sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.0" octo_image: @@ -898,7 +898,7 @@ packages: description: name: octo_image sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.0" package_config: @@ -906,7 +906,7 @@ packages: description: name: package_config sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.0" package_info_plus: @@ -914,7 +914,7 @@ packages: description: name: package_info_plus sha256: f69da0d3189a4b4ceaeb1a3defb0f329b3b352517f52bed4290f83d4f06bc08d - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "9.0.0" package_info_plus_platform_interface: @@ -922,7 +922,7 @@ packages: description: name: package_info_plus_platform_interface sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.2.1" path: @@ -930,7 +930,7 @@ packages: description: name: path sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.9.1" path_drawing: @@ -938,7 +938,7 @@ packages: description: name: path_drawing sha256: bbb1934c0cbb03091af082a6389ca2080345291ef07a5fa6d6e078ba8682f977 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.1" path_parsing: @@ -946,7 +946,7 @@ packages: description: name: path_parsing sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.0" path_provider: @@ -954,7 +954,7 @@ packages: description: name: path_provider sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.5" path_provider_android: @@ -962,7 +962,7 @@ packages: description: name: path_provider_android sha256: e122c5ea805bb6773bb12ce667611265980940145be920cd09a4b0ec0285cb16 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.20" path_provider_foundation: @@ -970,7 +970,7 @@ packages: description: name: path_provider_foundation sha256: efaec349ddfc181528345c56f8eda9d6cccd71c177511b132c6a0ddaefaa2738 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.4.3" path_provider_linux: @@ -978,7 +978,7 @@ packages: description: name: path_provider_linux sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.1" path_provider_platform_interface: @@ -986,7 +986,7 @@ packages: description: name: path_provider_platform_interface sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.2" path_provider_windows: @@ -994,7 +994,7 @@ packages: description: name: path_provider_windows sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.3.0" permission_handler: @@ -1002,7 +1002,7 @@ packages: description: name: permission_handler sha256: bc917da36261b00137bbc8896bf1482169cd76f866282368948f032c8c1caae1 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "12.0.1" permission_handler_android: @@ -1010,7 +1010,7 @@ packages: description: name: permission_handler_android sha256: "1e3bc410ca1bf84662104b100eb126e066cb55791b7451307f9708d4007350e6" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "13.0.1" permission_handler_apple: @@ -1018,7 +1018,7 @@ packages: description: name: permission_handler_apple sha256: f000131e755c54cf4d84a5d8bd6e4149e262cc31c5a8b1d698de1ac85fa41023 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "9.4.7" permission_handler_html: @@ -1026,7 +1026,7 @@ packages: description: name: permission_handler_html sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.1.3+5" permission_handler_platform_interface: @@ -1034,7 +1034,7 @@ packages: description: name: permission_handler_platform_interface sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.3.0" permission_handler_windows: @@ -1042,7 +1042,7 @@ packages: description: name: permission_handler_windows sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.2.1" petitparser: @@ -1050,7 +1050,7 @@ packages: description: name: petitparser sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "7.0.1" photo_manager: @@ -1058,7 +1058,7 @@ packages: description: name: photo_manager sha256: a0d9a7a9bc35eda02d33766412bde6d883a8b0acb86bbe37dac5f691a0894e8a - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.7.1" photo_manager_image_provider: @@ -1066,7 +1066,7 @@ packages: description: name: photo_manager_image_provider sha256: b6015b67b32f345f57cf32c126f871bced2501236c405aafaefa885f7c821e4f - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.0" platform: @@ -1074,7 +1074,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: @@ -1082,7 +1082,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" pool: @@ -1090,7 +1090,7 @@ packages: description: name: pool sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.5.2" posix: @@ -1098,7 +1098,7 @@ packages: description: name: posix sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.0.3" protobuf: @@ -1106,7 +1106,7 @@ packages: description: name: protobuf sha256: "2fcc8a202ca7ec17dab7c97d6b6d91cf03aa07fe6f65f8afbb6dfa52cc5bd902" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "5.1.0" provider: @@ -1114,7 +1114,7 @@ packages: description: name: provider sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.1.5+1" pub_semver: @@ -1122,7 +1122,7 @@ packages: description: name: pub_semver sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.0" pubspec_parse: @@ -1130,7 +1130,7 @@ packages: description: name: pubspec_parse sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.5.0" rational: @@ -1138,7 +1138,7 @@ packages: description: name: rational sha256: cb808fb6f1a839e6fc5f7d8cb3b0a10e1db48b3be102de73938c627f0b636336 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.3" record: @@ -1146,7 +1146,7 @@ packages: description: name: record sha256: "6bad72fb3ea6708d724cf8b6c97c4e236cf9f43a52259b654efeb6fd9b737f1f" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.1.2" record_android: @@ -1154,7 +1154,7 @@ packages: description: name: record_android sha256: fb54ee4e28f6829b8c580252a9ef49d9c549cfd263b0660ad7eeac0908658e9f - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.4.4" record_ios: @@ -1162,7 +1162,7 @@ packages: description: name: record_ios sha256: "765b42ac1be019b1674ddd809b811fc721fe5a93f7bb1da7803f0d16772fd6d7" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.4" record_linux: @@ -1170,7 +1170,7 @@ packages: description: name: record_linux sha256: "235b1f1fb84e810f8149cc0c2c731d7d697f8d1c333b32cb820c449bf7bb72d8" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.2.1" record_macos: @@ -1178,7 +1178,7 @@ packages: description: name: record_macos sha256: "842ea4b7e95f4dd237aacffc686d1b0ff4277e3e5357865f8d28cd28bc18ed95" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.2" record_platform_interface: @@ -1186,7 +1186,7 @@ packages: description: name: record_platform_interface sha256: b0065fdf1ec28f5a634d676724d388a77e43ce7646fb049949f58c69f3fcb4ed - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.4.0" record_web: @@ -1194,7 +1194,7 @@ packages: description: name: record_web sha256: "20ac10d56514cb9f8cecc8f3579383084fdfb43b0d04e05a95244d0d76091d90" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.2.1" record_windows: @@ -1202,7 +1202,7 @@ packages: description: name: record_windows sha256: "223258060a1d25c62bae18282c16783f28581ec19401d17e56b5205b9f039d78" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.7" retrofit: @@ -1210,7 +1210,7 @@ packages: description: name: retrofit sha256: "7d78824afa6eeeaf6ac58220910ee7a97597b39e93360d4bda230b7c6df45089" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.9.0" retrofit_generator: @@ -1218,7 +1218,7 @@ packages: description: name: retrofit_generator sha256: "56df50afab95199dada9e1afbfe5ec228612d03859b250e5e4846c3ebfe50bf7" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "10.1.4" rxdart: @@ -1226,7 +1226,7 @@ packages: description: name: rxdart sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.28.0" sensors_plus: @@ -1234,7 +1234,7 @@ packages: description: name: sensors_plus sha256: "89e2bfc3d883743539ce5774a2b93df61effde40ff958ecad78cd66b1a8b8d52" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.1.2" sensors_plus_platform_interface: @@ -1242,7 +1242,7 @@ packages: description: name: sensors_plus_platform_interface sha256: "58815d2f5e46c0c41c40fb39375d3f127306f7742efe3b891c0b1c87e2b5cd5d" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.1" shared_preferences: @@ -1250,7 +1250,7 @@ packages: description: name: shared_preferences sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.5.3" shared_preferences_android: @@ -1258,7 +1258,7 @@ packages: description: name: shared_preferences_android sha256: "34266009473bf71d748912da4bf62d439185226c03e01e2d9687bc65bbfcb713" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.4.15" shared_preferences_foundation: @@ -1266,7 +1266,7 @@ packages: description: name: shared_preferences_foundation sha256: "1c33a907142607c40a7542768ec9badfd16293bac51da3a4482623d15845f88b" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.5.5" shared_preferences_linux: @@ -1274,7 +1274,7 @@ packages: description: name: shared_preferences_linux sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.4.1" shared_preferences_platform_interface: @@ -1282,7 +1282,7 @@ packages: description: name: shared_preferences_platform_interface sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.4.1" shared_preferences_web: @@ -1290,7 +1290,7 @@ packages: description: name: shared_preferences_web sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.4.3" shared_preferences_windows: @@ -1298,7 +1298,7 @@ packages: description: name: shared_preferences_windows sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.4.1" shelf: @@ -1306,7 +1306,7 @@ packages: description: name: shelf sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.4.2" shelf_web_socket: @@ -1314,7 +1314,7 @@ packages: description: name: shelf_web_socket sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.0" sky_engine: @@ -1327,7 +1327,7 @@ packages: description: name: source_gen sha256: "9098ab86015c4f1d8af6486b547b11100e73b193e1899015033cb3e14ad20243" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.0.2" source_helper: @@ -1335,7 +1335,7 @@ packages: description: name: source_helper sha256: "6a3c6cc82073a8797f8c4dc4572146114a39652851c157db37e964d9c7038723" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.3.8" source_span: @@ -1343,7 +1343,7 @@ packages: description: name: source_span sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.10.1" sp_util: @@ -1351,7 +1351,7 @@ packages: description: name: sp_util sha256: "9da43dce5de79c17a787d0626bf01538d63090ca32521200d22a232171c495dc" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.3" sqflite: @@ -1359,7 +1359,7 @@ packages: description: name: sqflite sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.4.2" sqflite_android: @@ -1367,7 +1367,7 @@ packages: description: name: sqflite_android sha256: ecd684501ebc2ae9a83536e8b15731642b9570dc8623e0073d227d0ee2bfea88 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.4.2+2" sqflite_common: @@ -1375,7 +1375,7 @@ packages: description: name: sqflite_common sha256: "6ef422a4525ecc601db6c0a2233ff448c731307906e92cabc9ba292afaae16a6" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.5.6" sqflite_darwin: @@ -1383,7 +1383,7 @@ packages: description: name: sqflite_darwin sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.4.2" sqflite_platform_interface: @@ -1391,7 +1391,7 @@ packages: description: name: sqflite_platform_interface sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.4.0" stack_trace: @@ -1399,7 +1399,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: @@ -1407,7 +1407,7 @@ packages: description: name: stream_channel sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.4" stream_transform: @@ -1415,7 +1415,7 @@ packages: description: name: stream_transform sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.1" string_scanner: @@ -1423,7 +1423,7 @@ packages: description: name: string_scanner sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.4.1" synchronized: @@ -1431,7 +1431,7 @@ packages: description: name: synchronized sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.4.0" tdesign_flutter: @@ -1439,7 +1439,7 @@ packages: description: name: tdesign_flutter sha256: "6c95cd746d7ff3ee5e3c9c4dd0e202f69c8c6e04cf53f29ace4da16f5cfbc7e4" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.2.5" tdesign_flutter_adaptation: @@ -1447,7 +1447,7 @@ packages: description: name: tdesign_flutter_adaptation sha256: "707bbc52ec8b5872ea808500200f402007acf24c03a1cc823a3be2113c1d813a" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.32.0" term_glyph: @@ -1455,7 +1455,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: @@ -1463,7 +1463,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: @@ -1471,7 +1471,7 @@ packages: description: name: typed_data sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.4.0" universal_io: @@ -1479,7 +1479,7 @@ packages: description: name: universal_io sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.2" uuid: @@ -1487,7 +1487,7 @@ packages: description: name: uuid sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.5.2" vector_math: @@ -1495,7 +1495,7 @@ packages: description: name: vector_math sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.0" video_player: @@ -1503,7 +1503,7 @@ packages: description: name: video_player sha256: "096bc28ce10d131be80dfb00c223024eb0fba301315a406728ab43dd99c45bdf" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.10.1" video_player_android: @@ -1511,7 +1511,7 @@ packages: description: name: video_player_android sha256: cf768d02924b91e333e2bc1ff928528f57d686445874f383bafab12d0bdfc340 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.8.17" video_player_avfoundation: @@ -1519,7 +1519,7 @@ packages: description: name: video_player_avfoundation sha256: "03fc6d07dba2499588d30887329b399c1fe2d68ce4b7fcff0db79f44a2603f69" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.8.6" video_player_platform_interface: @@ -1527,7 +1527,7 @@ packages: description: name: video_player_platform_interface sha256: "57c5d73173f76d801129d0531c2774052c5a7c11ccb962f1830630decd9f24ec" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.6.0" video_player_web: @@ -1535,7 +1535,7 @@ packages: description: name: video_player_web sha256: "9f3c00be2ef9b76a95d94ac5119fb843dca6f2c69e6c9968f6f2b6c9e7afbdeb" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.4.0" visibility_detector: @@ -1543,7 +1543,7 @@ packages: description: name: visibility_detector sha256: dd5cc11e13494f432d15939c3aa8ae76844c42b723398643ce9addb88a5ed420 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.4.0+2" vm_service: @@ -1551,7 +1551,7 @@ packages: description: name: vm_service sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "15.0.2" wakelock_plus: @@ -1559,7 +1559,7 @@ packages: description: name: wakelock_plus sha256: "9296d40c9adbedaba95d1e704f4e0b434be446e2792948d0e4aa977048104228" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.4.0" wakelock_plus_platform_interface: @@ -1567,7 +1567,7 @@ packages: description: name: wakelock_plus_platform_interface sha256: "036deb14cd62f558ca3b73006d52ce049fabcdcb2eddfe0bf0fe4e8a943b5cf2" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.3.0" watcher: @@ -1575,7 +1575,7 @@ packages: description: name: watcher sha256: "592ab6e2892f67760543fb712ff0177f4ec76c031f02f5b4ff8d3fc5eb9fb61a" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.4" web: @@ -1583,7 +1583,7 @@ packages: description: name: web sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.1" web_socket: @@ -1591,7 +1591,7 @@ packages: description: name: web_socket sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.1" web_socket_channel: @@ -1599,7 +1599,7 @@ packages: description: name: web_socket_channel sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.3" wechat_assets_picker: @@ -1607,7 +1607,7 @@ packages: description: name: wechat_assets_picker sha256: c307e50394c1e6dfcd5c4701e84efb549fce71444fedcf2e671c50d809b3e2a1 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "9.8.0" wechat_camera_picker: @@ -1615,7 +1615,7 @@ packages: description: name: wechat_camera_picker sha256: "776ce32feda72d84b63425533a27d3b822bfb93cc063d2aef3cc6d788769f36b" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.4.0" wechat_picker_library: @@ -1623,7 +1623,7 @@ packages: description: name: wechat_picker_library sha256: "5cb61b9aa935b60da5b043f8446fbb9c5077419f20ccc4856bf444aec4f44bc1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.7" win32: @@ -1631,7 +1631,7 @@ packages: description: name: win32 sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "5.15.0" xdg_directories: @@ -1639,7 +1639,7 @@ packages: description: name: xdg_directories sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.0" xml: @@ -1647,7 +1647,7 @@ packages: description: name: xml sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.6.1" xxh3: @@ -1655,7 +1655,7 @@ packages: description: name: xxh3 sha256: "399a0438f5d426785723c99da6b16e136f4953fb1e9db0bf270bd41dd4619916" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.2.0" yaml: @@ -1663,7 +1663,7 @@ packages: description: name: yaml sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.1.3" sdks: From 388f2aebee809db5b7145c0e806c344fd2fc7271 Mon Sep 17 00:00:00 2001 From: Jolie <412895109@qq.com> Date: Thu, 13 Nov 2025 10:48:23 +0800 Subject: [PATCH 03/10] =?UTF-8?q?feat(message):=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E8=AF=AD=E9=9F=B3=E6=B6=88=E6=81=AF=E6=B3=A2=E5=BD=A2=E5=8A=A8?= =?UTF-8?q?=E7=94=BB=E5=92=8C=E6=92=AD=E6=94=BE=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加波形动画控制器,实现播放时的动态波浪效果-优化音频文件路径处理逻辑,支持网络URL播放 - 改进播放状态监听,确保动画与播放状态同步- 添加文件存在性检查,提升播放稳定性 - 使用AnimatedContainer优化波形条动画过渡效果 - 完善错误提示,增强用户体验 --- .../message/voice_player_manager.dart | 6 +- lib/widget/message/voice_item.dart | 123 +++++++++++++++--- 2 files changed, 110 insertions(+), 19 deletions(-) diff --git a/lib/controller/message/voice_player_manager.dart b/lib/controller/message/voice_player_manager.dart index 0f498c6..af94c32 100644 --- a/lib/controller/message/voice_player_manager.dart +++ b/lib/controller/message/voice_player_manager.dart @@ -49,7 +49,11 @@ class VoicePlayerManager extends GetxController { // 播放新音频 _currentPlayingId = audioId; currentPlayingId.value = audioId; - await _audioPlayer.play(DeviceFileSource(filePath)); + if(filePath.startsWith('https://')){ + await _audioPlayer.play(UrlSource(filePath)); + }else{ + await _audioPlayer.play(DeviceFileSource(filePath)); + } } catch (e) { print('播放音频失败: $e'); _currentPlayingId = null; diff --git a/lib/widget/message/voice_item.dart b/lib/widget/message/voice_item.dart index 3e91478..6fc1656 100644 --- a/lib/widget/message/voice_item.dart +++ b/lib/widget/message/voice_item.dart @@ -1,3 +1,6 @@ +import 'dart:io'; +import 'dart:math' as math; +import 'package:audioplayers/audioplayers.dart'; import 'package:dating_touchme_app/extension/ex_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; @@ -28,25 +31,67 @@ class VoiceItem extends StatefulWidget { State createState() => _VoiceItemState(); } -class _VoiceItemState extends State { +class _VoiceItemState extends State with TickerProviderStateMixin { final VoicePlayerManager _playerManager = VoicePlayerManager.instance; + late AnimationController _waveformAnimationController; + int _animationFrame = 0; @override void initState() { super.initState(); + // 创建波形动画控制器 + _waveformAnimationController = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 2000), // 2秒完成一个波浪周期 + ); + + // 监听动画帧,更新波形 + _waveformAnimationController.addListener(() { + if (mounted && _playerManager.isPlaying(widget.messageId)) { + setState(() { + // 使用动画值来计算波浪位置,让波浪从左到右传播 + _animationFrame++; + }); + } + }); + // 监听播放状态变化 ever(_playerManager.currentPlayingId, (audioId) { if (mounted) { setState(() {}); + // 根据播放状态控制动画 + if (_playerManager.isPlaying(widget.messageId)) { + if (!_waveformAnimationController.isAnimating) { + _waveformAnimationController.repeat(); + } + } else { + _waveformAnimationController.stop(); + _waveformAnimationController.reset(); + _animationFrame = 0; + } } }); ever(_playerManager.playerState, (state) { if (mounted) { setState(() {}); + // 根据播放状态控制动画 + if (state == PlayerState.playing && + _playerManager.currentPlayingId.value == widget.messageId) { + _waveformAnimationController.repeat(); + } else { + _waveformAnimationController.stop(); + _animationFrame = 0; + } } }); } + @override + void dispose() { + _waveformAnimationController.dispose(); + super.dispose(); + } + // 处理播放/暂停 Future _handlePlayPause() async { try { @@ -55,19 +100,37 @@ class _VoiceItemState extends State { final localPath = widget.voiceBody.localPath; final remotePath = widget.voiceBody.remotePath; - if (localPath.isNotEmpty) { - filePath = localPath; - } else if (remotePath != null && remotePath.isNotEmpty) { - // 如果是远程路径,需要先下载(这里简化处理,实际应该先下载到本地) - filePath = remotePath; + // 优先使用本地路径 + if (remotePath != null && remotePath.isNotEmpty) { + // 只有远程路径,尝试使用远程路径(audioplayers 可能支持网络URL) + // 注意:如果远程路径是文件系统路径而不是URL,需要先下载 + if (remotePath.startsWith('http://') || + remotePath.startsWith('https://')) { + filePath = remotePath; + } else { + SmartDialog.showToast('音频文件不存在,请等待下载完成'); + print('远程音频文件路径: $remotePath'); + return; + } + } else if (localPath.isNotEmpty) { + final localFile = File(localPath); + if (await localFile.exists()) { + filePath = localPath; + } else { + SmartDialog.showToast('音频文件不存在'); + print('本地音频文件不存在: $localPath'); + return; + } } - SmartDialog.showToast('来了$remotePath'); + if (filePath != null && filePath.isNotEmpty) { await _playerManager.play(widget.messageId, filePath); } else { + SmartDialog.showToast('无法获取音频文件'); print('音频文件路径为空'); } } catch (e) { + SmartDialog.showToast('播放失败: $e'); print('播放音频失败: $e'); } } @@ -120,9 +183,7 @@ class _VoiceItemState extends State { height: 20.w, decoration: BoxDecoration( shape: BoxShape.circle, - color: widget.isSentByMe - ? Colors.white - : Colors.black, + color: widget.isSentByMe ? Colors.white : Colors.black, ), child: Icon( isPlaying ? Icons.pause : Icons.play_arrow, @@ -193,6 +254,7 @@ class _VoiceItemState extends State { Widget _buildWaveform() { // 根据时长生成波形条数量(最多20个) final barCount = (widget.voiceBody.duration / 2).ceil().clamp(5, 20); + final isPlaying = _playerManager.isPlaying(widget.messageId); return SizedBox( height: 16.h, @@ -200,19 +262,44 @@ class _VoiceItemState extends State { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: List.generate(barCount, (index) { - // 模拟波形高度变化 - final random = (index * 7) % 5; - final baseHeight = 6 + random * 2; - final height = (baseHeight.clamp(4, 16)).h; + double height; + Color color; + + if (isPlaying) { + // 播放时:波浪形动画效果 + // 使用正弦波函数创建波浪效果,从左到右传播 + // wavePhase: 时间因子(_animationFrame)让波浪移动,空间因子(index)让每个条的位置不同 + // 移除模运算,让波浪连续传播 + final wavePhase = _animationFrame * 0.15 + index * 0.6; + // 使用正弦波计算高度,范围在 4-16 之间 + final sinValue = math.sin(wavePhase); + final normalizedValue = (sinValue + 1) / 2; // 归一化到 0-1 + final baseHeight = 4 + normalizedValue * 12; + height = (baseHeight.clamp(4, 16)).h; + + // 根据高度设置颜色透明度,创造渐变效果 + final opacity = 0.5 + normalizedValue * 0.5; + color = widget.isSentByMe + ? Colors.white.withOpacity(opacity.clamp(0.5, 1.0)) + : Colors.grey.withOpacity(opacity.clamp(0.5, 0.9)); + } else { + // 未播放时:静态波形 + final random = (index * 7) % 5; + final baseHeight = 6 + random * 2; + height = (baseHeight.clamp(4, 16)).h; + color = widget.isSentByMe + ? Colors.white.withOpacity(0.8) + : Colors.grey.withOpacity(0.6); + } - return Container( + return AnimatedContainer( + duration: const Duration(milliseconds: 100), + curve: Curves.easeOut, width: 2.w, height: height, margin: EdgeInsets.symmetric(horizontal: 1.w), decoration: BoxDecoration( - color: widget.isSentByMe - ? Colors.white.withOpacity(0.8) - : Colors.grey.withOpacity(0.6), + color: color, borderRadius: BorderRadius.circular(1.w), ), ); From a13b441a2cd8a95d1f317dd879bd3b7ae2ffd97b Mon Sep 17 00:00:00 2001 From: Jolie <412895109@qq.com> Date: Thu, 13 Nov 2025 10:58:40 +0800 Subject: [PATCH 04/10] =?UTF-8?q?feat(message):=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E8=81=8A=E5=A4=A9=E9=A1=B5=E9=9D=A2=E6=B6=88=E6=81=AF=E5=88=86?= =?UTF-8?q?=E9=A1=B5=E5=8A=A0=E8=BD=BD=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 ChatPage 从 StatelessWidget 改为 StatefulWidget - 添加 ScrollController 监听列表滚动 - 实现滚动到底部时自动加载更多消息- 新增 _loadMoreMessages 方法处理分页逻辑 - 在控制器中调用 fetchMessages 方法加载历史消息- 添加加载状态防止重复请求 - 优化 dispose 方法释放滚动控制器资源 --- lib/pages/message/chat_page.dart | 109 +++++++++++++++++++++++-------- 1 file changed, 81 insertions(+), 28 deletions(-) diff --git a/lib/pages/message/chat_page.dart b/lib/pages/message/chat_page.dart index 3c9f60a..a7bbeed 100644 --- a/lib/pages/message/chat_page.dart +++ b/lib/pages/message/chat_page.dart @@ -10,15 +10,48 @@ import '../../generated/assets.dart'; import '../../../widget/message/chat_input_bar.dart'; import '../../../widget/message/message_item.dart'; -class ChatPage extends StatelessWidget { +class ChatPage extends StatefulWidget { final String userId; const ChatPage({required this.userId, super.key}); + @override + State createState() => _ChatPageState(); +} + +class _ChatPageState extends State { + final ScrollController _scrollController = ScrollController(); + bool _isLoadingMore = false; + late ChatController _controller; + + @override + void initState() { + super.initState(); + // 初始化 controller + _controller = Get.put(ChatController(userId: widget.userId)); + + // 监听滚动,当滚动到顶部时加载更多消息 + _scrollController.addListener(() { + if (_scrollController.hasClients && + _scrollController.position.pixels >= + _scrollController.position.maxScrollExtent - 100 && + !_isLoadingMore && + _controller.messages.isNotEmpty) { + _loadMoreMessages(); + } + }); + } + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return GetBuilder( - init: ChatController(userId: userId), + init: _controller, builder: (controller) { return WillPopScope( onWillPop: () async { @@ -57,6 +90,7 @@ class ChatPage extends StatelessWidget { }, behavior: HitTestBehavior.opaque, child: ListView.builder( + controller: _scrollController, reverse: true, padding: EdgeInsets.all(16.w), itemCount: controller.messages.length, @@ -79,34 +113,53 @@ class ChatPage extends StatelessWidget { ), ), ), - // 使用抽离的聊天输入栏组件 - ChatInputBar( - onSendMessage: (message) async { - await controller.sendMessage(message); - }, - onImageSelected: (imagePaths) async { - // 为每个图片路径调用控制器的方法发送图片消息 - for (var imagePath in imagePaths) { - await controller.sendImageMessage(imagePath); - } - }, - onVoiceRecorded: (filePath, seconds) async { - // 处理语音录音完成,回传文件路径和秒数 - await controller.sendVoiceMessage(filePath, seconds); - }, - onVideoRecorded: (filePath, duration) async { - print('🎬 [ChatPage] 收到视频录制/选择回调'); - print('文件路径: $filePath'); - print('时长: $duration 秒'); - // 处理视频录制/选择完成,回传文件路径和时长 - await controller.sendVideoMessage(filePath, duration); - }, - ), - ], + // 使用抽离的聊天输入栏组件 + ChatInputBar( + onSendMessage: (message) async { + await controller.sendMessage(message); + }, + onImageSelected: (imagePaths) async { + // 为每个图片路径调用控制器的方法发送图片消息 + for (var imagePath in imagePaths) { + await controller.sendImageMessage(imagePath); + } + }, + onVoiceRecorded: (filePath, seconds) async { + // 处理语音录音完成,回传文件路径和秒数 + await controller.sendVoiceMessage(filePath, seconds); + }, + onVideoRecorded: (filePath, duration) async { + print('🎬 [ChatPage] 收到视频录制/选择回调'); + print('文件路径: $filePath'); + print('时长: $duration 秒'); + // 处理视频录制/选择完成,回传文件路径和时长 + await controller.sendVideoMessage(filePath, duration); + }, + ), + ], + ), ), - ), - ); + ); }, ); } + + // 加载更多消息 + Future _loadMoreMessages() async { + if (_isLoadingMore) return; + + setState(() { + _isLoadingMore = true; + }); + + try { + await _controller.fetchMessages(loadMore: true); + } finally { + if (mounted) { + setState(() { + _isLoadingMore = false; + }); + } + } + } } From a18ae63024eb95b46f8b6164b66e637f35db276d Mon Sep 17 00:00:00 2001 From: ZHR007 Date: Thu, 13 Nov 2025 11:05:07 +0800 Subject: [PATCH 05/10] no message --- .gitignore | 2 +- lib/controller/mine/auth_controller.dart | 16 +++++++-------- lib/controller/mine/mine_controller.dart | 3 ++- lib/controller/mine/user_controller.dart | 1 + lib/pages/mine/auth_center_page.dart | 26 ++++++++++++++++++------ 5 files changed, 32 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index e469c2b..2ea8576 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,7 @@ .packages build/ # If you're building an application, you may want to check-in your pubspec.lock -pubspec.lock +/pubspec.lock # Directory created by dartdoc # If you don't generate documentation locally you can remove this line. diff --git a/lib/controller/mine/auth_controller.dart b/lib/controller/mine/auth_controller.dart index 7183607..343b58e 100644 --- a/lib/controller/mine/auth_controller.dart +++ b/lib/controller/mine/auth_controller.dart @@ -20,14 +20,12 @@ class AuthController extends GetxController { } void _loadInitialData() { isLoading.value = true; - Future.delayed(Duration(seconds: 1), () { - dataList.assignAll([ - AuthCard( title: '手机绑定', desc: '防止账号丢失', index: 1), - AuthCard( title: '真实头像', desc: '提高交友成功率', index: 2), - AuthCard( title: '实名认证', desc: '提高交友成功率', index: 3), - ]); - isLoading.value = false; - }); + dataList.assignAll([ + AuthCard( title: '手机绑定', desc: '防止账号丢失', index: 1, authed: true), + AuthCard( title: '真实头像', desc: '提高交友成功率', index: 2, authed: false), + AuthCard( title: '实名认证', desc: '提高交友成功率', index: 3, authed: false), + ]); + isLoading.value = false; } // 登录方法 @@ -53,10 +51,12 @@ class AuthCard { final String title; final String desc; final int index; + final bool authed; AuthCard({ required this.desc, required this.title, required this.index, + required this.authed, }); } diff --git a/lib/controller/mine/mine_controller.dart b/lib/controller/mine/mine_controller.dart index b32c408..29e7bf8 100644 --- a/lib/controller/mine/mine_controller.dart +++ b/lib/controller/mine/mine_controller.dart @@ -1,4 +1,5 @@ import 'package:dating_touchme_app/generated/assets.dart'; +import 'package:dating_touchme_app/pages/mine/auth_center_page.dart'; import 'package:dating_touchme_app/pages/mine/my_wallet_page.dart'; import 'package:dating_touchme_app/pages/mine/rose_page.dart'; import 'package:get/get.dart'; @@ -18,7 +19,7 @@ class MineController extends GetxController { {"icon": Assets.imagesRose, "title": "我的玫瑰", "subTitle": "新人限时福利", "path": () => RosePage()}, {"icon": Assets.imagesWallet, "title": "我的钱包", "subTitle": "提现无门槛", "path": () => MyWalletPage()}, {"icon": Assets.imagesShop, "title": "商城中心", "subTitle": "不定期更新商品", "path": () => Null}, - {"icon": Assets.imagesCert, "title": "认证中心", "subTitle": "未认证", "path": () => Null}, + {"icon": Assets.imagesCert, "title": "认证中心", "subTitle": "未认证", "path": () => AuthCenterPage()}, ].obs; List settingList = [ diff --git a/lib/controller/mine/user_controller.dart b/lib/controller/mine/user_controller.dart index 02929d1..0b23f83 100644 --- a/lib/controller/mine/user_controller.dart +++ b/lib/controller/mine/user_controller.dart @@ -73,6 +73,7 @@ class UserController extends GetxController { if (response.data.isSuccess) { // 检查data是否为null或者是空对象 final information = response.data.data; + if (information == null || information.id.isEmpty || information.genderCode.isNaN || information.birthYear == null) { //跳转到完善信息 SmartDialog.showToast('转到完善信息'); diff --git a/lib/pages/mine/auth_center_page.dart b/lib/pages/mine/auth_center_page.dart index 8df46e4..cf66016 100644 --- a/lib/pages/mine/auth_center_page.dart +++ b/lib/pages/mine/auth_center_page.dart @@ -1,9 +1,11 @@ import 'package:dating_touchme_app/extension/ex_widget.dart'; +import 'package:dating_touchme_app/pages/mine/real_name_page.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../../controller/mine/auth_controller.dart'; +import 'edit_info_page.dart'; class AuthCenterPage extends StatelessWidget { AuthCenterPage({super.key}); @@ -92,19 +94,31 @@ class AuthCenterPage extends StatelessWidget { Spacer(), Row( children: [ - Text('去认证', style: TextStyle(fontSize: 12, color: Colors.grey[500])), + Text( + item.authed ? '已认证' : '去认证', + style: TextStyle( + fontSize: 12, + color: item.authed ? Color(0xff26C77C) : Colors.grey[500] + ) + ), SizedBox(width: 4), - Icon( + item.authed ? SizedBox(width: 24) : Icon( Icons.navigate_next, // Material Icons - // size: 128.0, // 设置图标大小 - color: Colors.grey, // 设置图标颜色 - ) + // size: 128.0, // 设置图标大小#26C77C + color: Colors.grey[500] + ), ], ) ], ), ).onTap((){ - print(item.index); + if(!item.authed){ + if(item.index == 2){ + Get.to(() => EditInfoPage()); + } else if(item.index == 3){ + Get.to(() => RealNamePage()); + } + } }); } From 977a4f8526cfdbf26cd27415acc6ed121712f2bc Mon Sep 17 00:00:00 2001 From: ZHR007 Date: Thu, 13 Nov 2025 13:59:43 +0800 Subject: [PATCH 06/10] no message --- lib/controller/mine/auth_controller.dart | 90 ++++++++++++++++++++---- lib/pages/mine/real_name_page.dart | 66 ++++++----------- 2 files changed, 97 insertions(+), 59 deletions(-) diff --git a/lib/controller/mine/auth_controller.dart b/lib/controller/mine/auth_controller.dart index 343b58e..deb28d1 100644 --- a/lib/controller/mine/auth_controller.dart +++ b/lib/controller/mine/auth_controller.dart @@ -6,8 +6,11 @@ import '../../network/user_api.dart'; class AuthController extends GetxController { final isLoading = false.obs; final List dataList = []; -// 是否正在登录中 + // 是否正在登录中 final isLoggingIn = false.obs; + final name = ''.obs; + final idcard = ''.obs; + final agree = false.obs; // 从GetX依赖注入中获取UserApi实例 late UserApi _userApi; @@ -18,33 +21,90 @@ class AuthController extends GetxController { _userApi = Get.find(); _loadInitialData(); } - void _loadInitialData() { - isLoading.value = true; - dataList.assignAll([ - AuthCard( title: '手机绑定', desc: '防止账号丢失', index: 1, authed: true), - AuthCard( title: '真实头像', desc: '提高交友成功率', index: 2, authed: false), - AuthCard( title: '实名认证', desc: '提高交友成功率', index: 3, authed: false), - ]); - isLoading.value = false; - } // 登录方法 - Future login() async { + Future _loadInitialData() async { + try { + isLoading.value = true; + final one = AuthCard( title: '手机绑定', desc: '防止账号丢失', index: 1, authed: true); + dataList.assignAll([ + one, + AuthCard( title: '真实头像', desc: '提高交友成功率', index: 2, authed: false), + AuthCard( title: '实名认证', desc: '提高交友成功率', index: 3, authed: false), + ]); + // 调用登录接口 + // final response = await _userApi.login({}); + // 处理响应 + // if (response.data.isSuccess) { + // + // } + } catch (e) { + SmartDialog.showToast('网络请求失败,请检查网络连接'); + } finally { + isLoading.value = false; + } + } + + bool validateChineseID(String id) { + if (id.length != 18) return false; + + // 系数表 + final coefficients = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]; + + // 校验码对应表 + final checkCodeMap = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']; + + int sum = 0; + + try { + for (int i = 0; i < 17; i++) { + int digit = int.parse(id[i]); + sum += digit * coefficients[i]; + } + } catch (e) { + return false; // 包含非数字字符 + } + + int remainder = sum % 11; + String checkCode = checkCodeMap[remainder]; + + return id[17].toUpperCase() == checkCode; + } + + Future startAuthing() async { + if (name.value.isEmpty) { + SmartDialog.showToast('请输入姓名'); + return; + } + if (idcard.value.isEmpty) { + SmartDialog.showToast('请输入身份证号'); + return; + } + if (!validateChineseID(idcard.value)) { + SmartDialog.showToast('请输入正确的身份证号'); + return; + } + if (!agree.value) { + SmartDialog.showToast('请同意用户认证协议'); + return; + } try { // 调用登录接口 final response = await _userApi.login({}); // 处理响应 if (response.data.isSuccess) { - - } else { - SmartDialog.showToast(response.data.message); - } + SmartDialog.showToast('认证成功'); + Get.back(); + } else { + SmartDialog.showToast(response.data.message); + } } catch (e) { SmartDialog.showToast('网络请求失败,请检查网络连接'); } finally { } } + } class AuthCard { diff --git a/lib/pages/mine/real_name_page.dart b/lib/pages/mine/real_name_page.dart index a95463b..aaadc8c 100644 --- a/lib/pages/mine/real_name_page.dart +++ b/lib/pages/mine/real_name_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; import '../../controller/mine/auth_controller.dart'; @@ -9,7 +10,6 @@ class RealNamePage extends StatelessWidget { RealNamePage({super.key}); final AuthController controller = Get.put(AuthController()); // 是否同意协议 - final agreeTerms = Rx(false); @override Widget build(BuildContext context) { return Scaffold( @@ -87,6 +87,9 @@ class RealNamePage extends StatelessWidget { fontSize: 15, height: 1.2, // 控制文字垂直位置 ), + onChanged: (value) { + controller.name.value = value; + }, ), ), ), @@ -138,6 +141,9 @@ class RealNamePage extends StatelessWidget { fontSize: 15, height: 1.2, // 控制文字垂直位置 ), + onChanged: (value) { + controller.idcard.value = value; + }, ), ), ), @@ -151,9 +157,9 @@ class RealNamePage extends StatelessWidget { children: [ SizedBox(width: 8), Obx(() => Checkbox( - value: agreeTerms.value, + value: controller.agree.value, onChanged: (value) { - agreeTerms.value = value ?? false; + controller.agree.value = value ?? false; }, activeColor: Color(0xff7562F9), side: const BorderSide(color: Colors.grey), @@ -163,53 +169,25 @@ class RealNamePage extends StatelessWidget { ConstrainedBox( constraints: BoxConstraints(maxWidth: 300), // 限制最大宽度 child: Text( - '前排吃瓜,这里的瓜包新鲜,这里的人士有料有梗有灼见~,这里的瓜包新鲜,这里的人士有料有梗有灼见~', + '依据法律法规的要求,我们将收集您的真实姓名,身份证号用于实名认证,认证信息将用于直播连麦、收益提现、依据证件信息更正性别等,与账号唯一绑定,我们会使用加密方式对您的认证信息进行严格保密。', style: TextStyle( fontSize: 13, color: Colors.grey ), ), ) ], ), SizedBox(height: 48), - Container( - padding: const EdgeInsets.only(left: 24, right: 24), - child: ElevatedButton( - onPressed: controller.isLoggingIn.value - ? null - : () { - // 登录逻辑 - if (!agreeTerms.value) { - SmartDialog.showToast('请同意用户协议和隐私政策'); - return; - } - // 调用控制器的登录方法 - controller.login(); - }, - style: ElevatedButton.styleFrom( - minimumSize: const Size(double.infinity, 50), - backgroundColor: agreeTerms.value ? const Color(0xff7562F9) : Colors.grey.shade300, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(25), - ), - elevation: 0, - ), - child: controller.isLoggingIn.value ? const SizedBox( - width: 20, - height: 20, - child: CircularProgressIndicator( - color: Colors.white, - strokeWidth: 1, - ), - ) - : const Text( - '立即认证', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - ), - ) + + TDButton( + text: '立即认证', + width: MediaQuery.of(context).size.width - 40, + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.round, + theme: TDButtonTheme.primary, + onTap: (){ + controller.startAuthing(); + }, + ), ], ), ); From 3a7a7808466c9d29f222e253dcfe9eac6bc7d6a4 Mon Sep 17 00:00:00 2001 From: Jolie <412895109@qq.com> Date: Thu, 13 Nov 2025 15:11:08 +0800 Subject: [PATCH 07/10] =?UTF-8?q?feat(im):=20=E5=AE=9E=E7=8E=B0=E8=81=8A?= =?UTF-8?q?=E5=A4=A9=E6=8E=A7=E5=88=B6=E5=99=A8=E6=B3=A8=E5=86=8C=E4=B8=8E?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E9=80=9A=E7=9F=A5=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 IMManager 中添加 ChatController 的注册与注销功能- 实现消息接收时自动通知对应 ChatController 更新消息列表 - 为 ChatController 添加接收消息处理逻辑,避免重复消息插入 - 完善消息处理流程,确保新消息能及时刷新会话列表 - 添加日志记录以方便调试和追踪控制器状态变化- 优化消息通知逻辑,只处理接收到的消息并按发送者分发 --- lib/controller/message/chat_controller.dart | 25 ++++++++++ lib/im/im_manager.dart | 52 +++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/lib/controller/message/chat_controller.dart b/lib/controller/message/chat_controller.dart index 76698f1..c98df36 100644 --- a/lib/controller/message/chat_controller.dart +++ b/lib/controller/message/chat_controller.dart @@ -21,11 +21,20 @@ class ChatController extends GetxController { @override void onInit() { super.onInit(); + // 注册到 IMManager,以便接收消息时能通知到此 Controller + IMManager.instance.registerChatController(this); // 初始化时获取用户信息和消息列表 fetchUserInfo(); fetchMessages(); } + @override + void onClose() { + // 注销 ChatController + IMManager.instance.unregisterChatController(userId); + super.onClose(); + } + /// 获取用户信息 Future fetchUserInfo() async { try { @@ -198,6 +207,22 @@ class ChatController extends GetxController { } } + /// 添加接收到的消息 + void addReceivedMessage(EMMessage message) { + // 检查消息是否已存在(避免重复添加) + if (!messages.any((msg) => msg.msgId == message.msgId)) { + // 将新消息添加到列表开头 + messages.insert(0, message); + update(); + // 更新会话列表 + _refreshConversationList(); + + if (Get.isLogEnable) { + Get.log('收到新消息并添加到列表: ${message.msgId}'); + } + } + } + /// 刷新会话列表 void _refreshConversationList() { try { diff --git a/lib/im/im_manager.dart b/lib/im/im_manager.dart index 17edd6b..78ff82c 100644 --- a/lib/im/im_manager.dart +++ b/lib/im/im_manager.dart @@ -2,6 +2,7 @@ import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; import 'package:im_flutter_sdk/im_flutter_sdk.dart'; import '../controller/message/conversation_controller.dart'; +import '../controller/message/chat_controller.dart'; // 完整的IM管理器实现,使用实际的SDK类型和方法 class IMManager { @@ -14,6 +15,9 @@ class IMManager { bool _isInitialized = false; + // 存储活跃的 ChatController 实例,key 为 userId + final Map _activeChatControllers = {}; + IMManager._internal() { print('IMManager instance created'); } @@ -76,6 +80,9 @@ class IMManager { // 收到新消息时,更新会话列表 _refreshConversationList(); + // 通知对应的 ChatController 更新消息列表 + _notifyChatControllers(messages); + for (var msg in messages) { switch (msg.body.type) { case MessageType.TXT: @@ -285,6 +292,51 @@ class IMManager { return data[userId]; } + /// 注册 ChatController + void registerChatController(ChatController controller) { + _activeChatControllers[controller.userId] = controller; + if (Get.isLogEnable) { + Get.log('注册 ChatController: ${controller.userId}'); + } + } + + /// 注销 ChatController + void unregisterChatController(String userId) { + _activeChatControllers.remove(userId); + if (Get.isLogEnable) { + Get.log('注销 ChatController: $userId'); + } + } + + /// 通知 ChatController 更新消息列表 + void _notifyChatControllers(List messages) { + try { + // 遍历所有收到的消息 + for (var message in messages) { + // 只处理接收到的消息(direction == RECEIVE) + if (message.direction == MessageDirection.RECEIVE) { + // 获取消息的发送者ID(from 属性) + final fromId = message.from; + + if (fromId != null && fromId.isNotEmpty) { + // 查找对应的 ChatController + final controller = _activeChatControllers[fromId]; + if (controller != null) { + controller.addReceivedMessage(message); + if (Get.isLogEnable) { + Get.log('通知 ChatController 更新消息: $fromId'); + } + } + } + } + } + } catch (e) { + if (Get.isLogEnable) { + Get.log('通知 ChatController 更新消息列表失败: $e'); + } + } + } + /// 刷新会话列表 void _refreshConversationList() { try { From 47e676d168e1c1064323ec0fa8ed743812aa2103 Mon Sep 17 00:00:00 2001 From: ZHR007 Date: Thu, 13 Nov 2025 15:22:11 +0800 Subject: [PATCH 08/10] no message --- lib/controller/global.dart | 33 +++++++++++++++++++ lib/controller/mine/auth_controller.dart | 19 ++++++++--- lib/controller/mine/user_controller.dart | 15 +++++++-- lib/model/mine/authentication_data.dart | 31 +++++++++++++++++ lib/model/mine/user_data.dart | 4 +++ lib/network/api_urls.dart | 2 +- lib/network/user_api.dart | 7 ++++ lib/network/user_api.g.dart | 42 ++++++++++++++++++++++++ lib/pages/mine/auth_center_page.dart | 5 +-- 9 files changed, 147 insertions(+), 11 deletions(-) create mode 100644 lib/controller/global.dart create mode 100644 lib/model/mine/authentication_data.dart diff --git a/lib/controller/global.dart b/lib/controller/global.dart new file mode 100644 index 0000000..84c3dbb --- /dev/null +++ b/lib/controller/global.dart @@ -0,0 +1,33 @@ +// ignore_for_file: constant_identifier_names, non_constant_identifier_names +import 'dart:io'; + +import '../model/mine/user_data.dart'; + +class GlobalData { + String? qnToken;//uec接口的Token + String? userId;//用户id + UserData? userData;// 用户的基础信息 + + bool isLogout = false;//是否已经退出登录 + + void logout() { + isLogout = true; + userId = null; + qnToken = null; + userData = null; + } + + static GlobalData getInstance() { + _instance ??= GlobalData._init(); + return _instance!; + } + + GlobalData._init() { + if(Platform.isIOS){ + // xAppId = "503258978847966412"; + } + } + factory GlobalData() => getInstance(); + static GlobalData get instance => getInstance(); + static GlobalData? _instance; +} \ No newline at end of file diff --git a/lib/controller/mine/auth_controller.dart b/lib/controller/mine/auth_controller.dart index deb28d1..9c7dfa8 100644 --- a/lib/controller/mine/auth_controller.dart +++ b/lib/controller/mine/auth_controller.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:get/get.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import '../../network/user_api.dart'; +import '../global.dart'; class AuthController extends GetxController { final isLoading = false.obs; @@ -13,7 +14,6 @@ class AuthController extends GetxController { final agree = false.obs; // 从GetX依赖注入中获取UserApi实例 late UserApi _userApi; - @override void onInit() { super.onInit(); @@ -26,9 +26,12 @@ class AuthController extends GetxController { Future _loadInitialData() async { try { isLoading.value = true; - final one = AuthCard( title: '手机绑定', desc: '防止账号丢失', index: 1, authed: true); + late bool realAuth = false; + if(GlobalData().userData?.realAuth != null){ + realAuth = GlobalData().userData!.realAuth!; + } dataList.assignAll([ - one, + AuthCard( title: '手机绑定', desc: '防止账号丢失', index: 1, authed: true), AuthCard( title: '真实头像', desc: '提高交友成功率', index: 2, authed: false), AuthCard( title: '实名认证', desc: '提高交友成功率', index: 3, authed: false), ]); @@ -90,11 +93,17 @@ class AuthController extends GetxController { } try { // 调用登录接口 - final response = await _userApi.login({}); + final param = { + 'miId': GlobalData().userData?.id, + 'authenticationCode': 0, + 'value': '${name.value},${idcard.value}', + }; + final response = await _userApi.saveCertificationAudit(param); // 处理响应 if (response.data.isSuccess) { + GlobalData().userData?.realAuth = true; SmartDialog.showToast('认证成功'); - Get.back(); + Get.back(result: {'index': 3}); } else { SmartDialog.showToast(response.data.message); } diff --git a/lib/controller/mine/user_controller.dart b/lib/controller/mine/user_controller.dart index 0b23f83..045fe50 100644 --- a/lib/controller/mine/user_controller.dart +++ b/lib/controller/mine/user_controller.dart @@ -2,9 +2,12 @@ import 'package:dating_touchme_app/im/im_manager.dart'; import 'package:dating_touchme_app/oss/oss_manager.dart'; import 'package:get/get.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import '../../model/mine/authentication_data.dart'; +import '../../model/mine/user_data.dart'; import '../../network/user_api.dart'; import '../../pages/mine/user_info_page.dart'; import '../../pages/main/main_page.dart'; +import '../global.dart'; class UserController extends GetxController { @@ -72,9 +75,15 @@ class UserController extends GetxController { final response = await _userApi.getMarriageInformationDetail(); if (response.data.isSuccess) { // 检查data是否为null或者是空对象 - final information = response.data.data; - - if (information == null || information.id.isEmpty || information.genderCode.isNaN || information.birthYear == null) { + final information = response.data.data!; + if (information.id.isNotEmpty) { + final result = await _userApi.getCertificationList(information.id); + List list = result.data.data!; + final record = list.firstWhere((item) => item.authenticationCode == 0); + information.realAuth = record.status == 1; + } + GlobalData().userData = information; + if (information.id.isEmpty || information.genderCode.isNaN || information.birthYear == null) { //跳转到完善信息 SmartDialog.showToast('转到完善信息'); // 导航到完善信息页面 diff --git a/lib/model/mine/authentication_data.dart b/lib/model/mine/authentication_data.dart new file mode 100644 index 0000000..952a35e --- /dev/null +++ b/lib/model/mine/authentication_data.dart @@ -0,0 +1,31 @@ +class AuthenticationData { + final int? authenticationCode; + final String? authenticationName; + final String? miId; + final int? status; + + AuthenticationData({ + this.authenticationCode, + this.authenticationName, + this.miId, + this.status, + }); + + factory AuthenticationData.fromJson(Map json) { + return AuthenticationData( + authenticationCode: json['authenticationCode'] as int?, + authenticationName: json['authenticationName'] as String?, + miId: json['miId'] as String?, + status: json['status'] as int?, + ); + } + + Map toJson() { + return { + 'authenticationCode': authenticationCode, + 'authenticationName': authenticationName, + 'miId': miId, + 'status': status, + }; + } +} \ No newline at end of file diff --git a/lib/model/mine/user_data.dart b/lib/model/mine/user_data.dart index fc2fa39..0b9696b 100644 --- a/lib/model/mine/user_data.dart +++ b/lib/model/mine/user_data.dart @@ -59,6 +59,7 @@ class UserData { final String? hometownProvinceName; final String? hometownCityCode; final String? hometownCityName; + bool? realAuth; UserData({ required this.id, @@ -120,6 +121,7 @@ class UserData { this.hometownProvinceName, this.hometownCityCode, this.hometownCityName, + this.realAuth, }); // 从JSON映射创建实例 @@ -184,6 +186,7 @@ class UserData { hometownProvinceName: json['hometownProvinceName'], hometownCityCode: json['hometownCityCode'], hometownCityName: json['hometownCityName'], + realAuth: json['realAuth'], ); } @@ -249,6 +252,7 @@ class UserData { 'hometownProvinceName': hometownProvinceName, 'hometownCityCode': hometownCityCode, 'hometownCityName': hometownCityName, + 'realAuth': realAuth, }; } diff --git a/lib/network/api_urls.dart b/lib/network/api_urls.dart index c939f15..39cb2f5 100644 --- a/lib/network/api_urls.dart +++ b/lib/network/api_urls.dart @@ -12,7 +12,7 @@ class ApiUrls { static const String getHxUserToken = 'dating-agency-chat-audio/user/get/hx/user/token'; static const String getApplyTempAuth = 'dating-agency-uec/get/apply-temp-auth'; static const String saveCertificationAudit = 'dating-agency-service/user/save/certification/audit'; - + static const String getCertificationList = '/dating-agency-service/user/get/certification/item/all/list'; //首页相关接口 static const String getMarriageList = 'dating-agency-service/user/page/dongwo/marriage-information'; diff --git a/lib/network/user_api.dart b/lib/network/user_api.dart index 5cd5f5d..299ba83 100644 --- a/lib/network/user_api.dart +++ b/lib/network/user_api.dart @@ -7,6 +7,8 @@ import 'package:dating_touchme_app/network/api_urls.dart'; import 'package:retrofit/retrofit.dart'; import 'package:dio/dio.dart'; +import '../model/mine/authentication_data.dart'; + part 'user_api.g.dart'; @RestApi(baseUrl: '') @@ -41,6 +43,11 @@ abstract class UserApi { @Body() Map data, ); + @GET(ApiUrls.getCertificationList) + Future>>> getCertificationList( + @Query('miId') String miId, + ); + @GET(ApiUrls.getHxUserToken) Future>> getHxUserToken(); diff --git a/lib/network/user_api.g.dart b/lib/network/user_api.g.dart index 18b97e6..5835815 100644 --- a/lib/network/user_api.g.dart +++ b/lib/network/user_api.g.dart @@ -220,6 +220,48 @@ class _UserApi implements UserApi { return httpResponse; } + @override + Future>>> + getCertificationList(String miId) async { + final _extra = {}; + final queryParameters = {r'miId': miId}; + final _headers = {}; + const Map? _data = null; + final _options = + _setStreamType>>>( + Options(method: 'GET', headers: _headers, extra: _extra) + .compose( + _dio.options, + '/dating-agency-service/user/get/certification/item/all/list', + queryParameters: queryParameters, + data: _data, + ) + .copyWith( + baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl), + ), + ); + final _result = await _dio.fetch>(_options); + late BaseResponse> _value; + try { + _value = BaseResponse>.fromJson( + _result.data!, + (json) => json is List + ? json + .map( + (i) => + AuthenticationData.fromJson(i as Map), + ) + .toList() + : List.empty(), + ); + } on Object catch (e, s) { + errorLogger?.logError(e, s, _options); + rethrow; + } + final httpResponse = HttpResponse(_value, _result); + return httpResponse; + } + @override Future>> getHxUserToken() async { final _extra = {}; diff --git a/lib/pages/mine/auth_center_page.dart b/lib/pages/mine/auth_center_page.dart index cf66016..e3033bb 100644 --- a/lib/pages/mine/auth_center_page.dart +++ b/lib/pages/mine/auth_center_page.dart @@ -111,12 +111,13 @@ class AuthCenterPage extends StatelessWidget { ) ], ), - ).onTap((){ + ).onTap(() async{ if(!item.authed){ if(item.index == 2){ Get.to(() => EditInfoPage()); } else if(item.index == 3){ - Get.to(() => RealNamePage()); + final result = await Get.to(() => RealNamePage()); + print(result); } } }); From 4d71588ba4df14294b6f9fdf3a7bb83f71698201 Mon Sep 17 00:00:00 2001 From: Jolie <412895109@qq.com> Date: Thu, 13 Nov 2025 15:27:04 +0800 Subject: [PATCH 09/10] =?UTF-8?q?feat(im):=20=E5=AE=9E=E7=8E=B0=E8=81=8A?= =?UTF-8?q?=E5=A4=A9=E6=8E=A7=E5=88=B6=E5=99=A8=E6=B3=A8=E5=86=8C=E4=B8=8E?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E9=80=9A=E7=9F=A5=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 IMManager 中添加 ChatController 的注册与注销功能- 实现消息接收时自动通知对应 ChatController 更新消息列表 - 为 ChatController 添加接收消息处理逻辑,避免重复消息插入 - 完善消息处理流程,确保新消息能及时刷新会话列表 - 添加日志记录以方便调试和追踪控制器状态变化- 优化消息通知逻辑,只处理接收到的消息并按发送者分发 --- android/app/build.gradle.kts | 9 +++++++++ pubspec.lock | 32 ++++++++++++++++++++++++++++++++ pubspec.yaml | 3 +++ 3 files changed, 44 insertions(+) diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 5d844db..77c1cb3 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -37,6 +37,15 @@ android { signingConfig = signingConfigs.getByName("debug") } } + + packaging { + jniLibs { + pickFirsts += listOf("lib/arm64-v8a/libaosl.so") + pickFirsts += listOf("lib/armeabi-v7a/libaosl.so") + pickFirsts += listOf("lib/x86/libaosl.so") + pickFirsts += listOf("lib/x86_64/libaosl.so") + } + } } flutter { diff --git a/pubspec.lock b/pubspec.lock index 2fd77cb..ec6bd42 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -9,6 +9,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "88.0.0" + agora_rtc_engine: + dependency: "direct main" + description: + name: agora_rtc_engine + sha256: "6559294d18ce4445420e19dbdba10fb58cac955cd8f22dbceae26716e194d70e" + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.5.3" analyzer: dependency: transitive description: @@ -573,6 +581,14 @@ packages: description: flutter source: sdk version: "0.0.0" + fluwx: + dependency: "direct main" + description: + name: fluwx + sha256: "7e92d2000ee49c5262a88c51ea2d22b91a753d5b29df27cc264bb0a115d65373" + url: "https://pub.flutter-io.cn" + source: hosted + version: "5.7.5" frontend_server_client: dependency: transitive description: @@ -781,6 +797,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.0.5" + iris_method_channel: + dependency: transitive + description: + name: iris_method_channel + sha256: bfb5cfc6c6eae42da8cd1b35977a72d8b8881848a5dfc3d672e4760a907d11a0 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.2.4" js: dependency: transitive description: @@ -1466,6 +1490,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "0.7.6" + tobias: + dependency: "direct main" + description: + name: tobias + sha256: "2b5520e622c0d6f04cfb5c9619211f923c97a602e1a3a8954e113e3e0e685c41" + url: "https://pub.flutter-io.cn" + source: hosted + version: "5.3.1" typed_data: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index e3ea775..61452ce 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -62,6 +62,9 @@ dependencies: video_player: ^2.9.2 chewie: ^1.8.5 # 视频播放器UI audioplayers: ^6.5.1 + fluwx: ^5.7.5 + tobias: ^5.3.1 + agora_rtc_engine: ^6.5.3 dev_dependencies: flutter_test: From 9f85413e18f28a54bbad84eba5a8c2a869fec163 Mon Sep 17 00:00:00 2001 From: Jolie <412895109@qq.com> Date: Thu, 13 Nov 2025 15:40:12 +0800 Subject: [PATCH 10/10] =?UTF-8?q?feat(rtc):=20=E6=B7=BB=E5=8A=A0RTC?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=99=A8=E5=B9=B6=E5=88=9D=E5=A7=8B=E5=8C=96?= =?UTF-8?q?-=E5=A3=B0=E7=BD=91SDK=20=E6=96=B0=E5=A2=9ERTCManager=E7=B1=BB?= =?UTF-8?q?=EF=BC=8C=E5=AE=9E=E7=8E=B0=E5=8D=95=E4=BE=8B=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E9=9F=B3=E8=A7=86=E9=A2=91=E9=80=9A=E8=AF=9D?= =?UTF-8?q?=20-=20=E9=9B=86=E6=88=90=E5=A3=B0=E7=BD=91RTC=20SDK=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E9=A2=91=E9=81=93=E5=8A=A0=E5=85=A5=E3=80=81?= =?UTF-8?q?=E9=9F=B3=E8=A7=86=E9=A2=91=E6=8E=A7=E5=88=B6=E7=AD=89=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=20-=20=E5=9C=A8main.dart=E4=B8=AD=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96RTC=E5=BC=95=E6=93=8E=EF=BC=8C=E9=85=8D=E7=BD=AEApp=20?= =?UTF-8?q?ID=20-=20=E6=8F=90=E4=BE=9B=E5=AE=8C=E6=95=B4=E7=9A=84RTC?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E5=9B=9E=E8=B0=83=E5=A4=84=E7=90=86=E6=9C=BA?= =?UTF-8?q?=E5=88=B6-=20=E5=AE=9E=E7=8E=B0=E6=9C=AC=E5=9C=B0=E5=92=8C?= =?UTF-8?q?=E8=BF=9C=E7=A8=8B=E8=A7=86=E9=A2=91=E8=A7=86=E5=9B=BE=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E5=8A=9F=E8=83=BD-=20=E6=94=AF=E6=8C=81=E6=91=84?= =?UTF-8?q?=E5=83=8F=E5=A4=B4=E5=88=87=E6=8D=A2=E3=80=81=E9=9F=B3=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E9=9D=99=E9=9F=B3=E7=AD=89=E5=9F=BA=E7=A1=80=E6=93=8D?= =?UTF-8?q?=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 2 + lib/rtc/rtc_manager.dart | 425 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 427 insertions(+) create mode 100644 lib/rtc/rtc_manager.dart diff --git a/lib/main.dart b/lib/main.dart index b2d0f4d..265b61e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,6 +6,7 @@ import 'package:dating_touchme_app/network/network_service.dart'; import 'package:dating_touchme_app/pages/main/main_page.dart'; import 'package:dating_touchme_app/pages/mine/login_page.dart'; import 'package:dating_touchme_app/pages/mine/user_info_page.dart'; +import 'package:dating_touchme_app/rtc/rtc_manager.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; @@ -21,6 +22,7 @@ void main() async { // 设置环境配置 - 根据是否为release模式 EnvConfig.setEnvironment(Environment.dev); + RTCManager.instance.initialize(appId: '4c2ea9dcb4c5440593a418df0fdd512d'); IMManager.instance.initialize('1165251016193374#demo'); // 初始化全局依赖 final networkService = NetworkService(); diff --git a/lib/rtc/rtc_manager.dart b/lib/rtc/rtc_manager.dart new file mode 100644 index 0000000..c89abe9 --- /dev/null +++ b/lib/rtc/rtc_manager.dart @@ -0,0 +1,425 @@ +import 'package:agora_rtc_engine/agora_rtc_engine.dart'; + +/// RTC 管理器,负责管理声网音视频通话功能 +class RTCManager { + // 单例模式 + static final RTCManager _instance = RTCManager._internal(); + factory RTCManager() => _instance; + // 静态getter用于instance访问 + static RTCManager get instance => _instance; + + RtcEngine? _engine; + bool _isInitialized = false; + bool _isInChannel = false; + String? _currentChannelId; + int? _currentUid; + + // 事件回调 + Function(RtcConnection connection, int elapsed)? onJoinChannelSuccess; + Function(RtcConnection connection, int remoteUid, int elapsed)? onUserJoined; + Function( + RtcConnection connection, + int remoteUid, + UserOfflineReasonType reason, + )? + onUserOffline; + Function(RtcConnection connection, RtcStats stats)? onLeaveChannel; + Function( + RtcConnection connection, + ConnectionStateType state, + ConnectionChangedReasonType reason, + )? + onConnectionStateChanged; + Function(int uid, UserInfo userInfo)? onUserInfoUpdated; + Function(RtcConnection connection, int uid, int elapsed)? + onFirstRemoteVideoDecoded; + Function( + RtcConnection connection, + VideoSourceType sourceType, + int uid, + int width, + int height, + int rotation, + )? + onVideoSizeChanged; + Function(RtcConnection connection, int uid, bool muted)? onUserMuteAudio; + Function(RtcConnection connection, int uid, bool muted)? onUserMuteVideo; + Function(RtcConnection connection)? onConnectionLost; + Function(RtcConnection connection, int code, String msg)? onError; + + RTCManager._internal() { + print('RTCManager instance created'); + } + + /// 初始化 RTC Engine + /// [appId] 声网 App ID + /// [channelProfile] 频道场景类型,默认为通信模式 + Future initialize({ + required String appId, + ChannelProfileType channelProfile = + ChannelProfileType.channelProfileCommunication, + }) async { + try { + if (_isInitialized && _engine != null) { + print('RTC Engine already initialized'); + return true; + } + + // 创建 RTC Engine + _engine = createAgoraRtcEngine(); + + // 初始化 RTC Engine + await _engine!.initialize( + RtcEngineContext(appId: appId, channelProfile: channelProfile), + ); + + // 注册事件处理器 + _registerEventHandlers(); + + _isInitialized = true; + print('RTC Engine initialized successfully'); + return true; + } catch (e) { + print('Failed to initialize RTC Engine: $e'); + return false; + } + } + + /// 注册事件处理器 + void _registerEventHandlers() { + if (_engine == null) return; + + _engine!.registerEventHandler( + RtcEngineEventHandler( + onJoinChannelSuccess: (RtcConnection connection, int elapsed) { + _isInChannel = true; + _currentChannelId = connection.channelId; + print('加入频道成功,频道名:${connection.channelId},耗时:${elapsed}ms'); + if (onJoinChannelSuccess != null) { + onJoinChannelSuccess!(connection, elapsed); + } + }, + onUserJoined: (RtcConnection connection, int remoteUid, int elapsed) { + print('用户加入,UID:$remoteUid'); + if (onUserJoined != null) { + onUserJoined!(connection, remoteUid, elapsed); + } + }, + onUserOffline: + ( + RtcConnection connection, + int remoteUid, + UserOfflineReasonType reason, + ) { + print('用户离开,UID:$remoteUid,原因:$reason'); + if (onUserOffline != null) { + onUserOffline!(connection, remoteUid, reason); + } + }, + onLeaveChannel: (RtcConnection connection, RtcStats stats) { + _isInChannel = false; + _currentChannelId = null; + print('离开频道,统计信息:${stats.duration}秒'); + if (onLeaveChannel != null) { + onLeaveChannel!(connection, stats); + } + }, + onConnectionStateChanged: + ( + RtcConnection connection, + ConnectionStateType state, + ConnectionChangedReasonType reason, + ) { + print('连接状态改变:$state,原因:$reason'); + if (onConnectionStateChanged != null) { + onConnectionStateChanged!(connection, state, reason); + } + }, + onUserInfoUpdated: (int uid, UserInfo userInfo) { + print('用户信息更新,UID:$uid'); + if (onUserInfoUpdated != null) { + onUserInfoUpdated!(uid, userInfo); + } + }, + onFirstRemoteVideoDecoded: + ( + RtcConnection connection, + int uid, + int width, + int height, + int elapsed, + ) { + print('首次远程视频解码,UID:$uid,分辨率:${width}x${height}'); + if (onFirstRemoteVideoDecoded != null) { + onFirstRemoteVideoDecoded!(connection, uid, elapsed); + } + }, + onVideoSizeChanged: + ( + RtcConnection connection, + VideoSourceType sourceType, + int uid, + int width, + int height, + int rotation, + ) { + print('视频尺寸改变,UID:$uid,分辨率:${width}x${height}'); + if (onVideoSizeChanged != null) { + onVideoSizeChanged!( + connection, + sourceType, + uid, + width, + height, + rotation, + ); + } + }, + onUserMuteAudio: (RtcConnection connection, int uid, bool muted) { + print('用户静音状态改变,UID:$uid,静音:$muted'); + if (onUserMuteAudio != null) { + onUserMuteAudio!(connection, uid, muted); + } + }, + onUserMuteVideo: (RtcConnection connection, int uid, bool muted) { + print('用户视频状态改变,UID:$uid,关闭:$muted'); + if (onUserMuteVideo != null) { + onUserMuteVideo!(connection, uid, muted); + } + }, + onConnectionLost: (RtcConnection connection) { + print('连接丢失'); + if (onConnectionLost != null) { + onConnectionLost!(connection); + } + }, + onError: (ErrorCodeType err, String msg) { + print('RTC Engine 错误:$err,消息:$msg'); + if (onError != null) { + onError!( + RtcConnection( + channelId: _currentChannelId ?? '', + localUid: _currentUid ?? 0, + ), + err.value(), + msg, + ); + } + }, + ), + ); + } + + /// 启用视频 + Future enableVideo() async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + await _engine!.enableVideo(); + print('视频已启用'); + } + + /// 禁用视频 + Future disableVideo() async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + await _engine!.disableVideo(); + print('视频已禁用'); + } + + /// 启用音频 + Future enableAudio() async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + await _engine!.enableAudio(); + print('音频已启用'); + } + + /// 禁用音频 + Future disableAudio() async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + await _engine!.disableAudio(); + print('音频已禁用'); + } + + /// 开启本地视频预览 + Future startPreview() async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + await _engine!.startPreview(); + print('本地视频预览已开启'); + } + + /// 停止本地视频预览 + Future stopPreview() async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + await _engine!.stopPreview(); + print('本地视频预览已停止'); + } + + /// 设置本地视频视图 + /// [viewId] 视图ID + /// [mirrorMode] 镜像模式 + Future setupLocalVideo({ + required int viewId, + VideoSourceType sourceType = VideoSourceType.videoSourceCameraPrimary, + VideoMirrorModeType mirrorMode = VideoMirrorModeType.videoMirrorModeAuto, + }) async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + await _engine!.setupLocalVideo( + VideoCanvas(view: viewId, sourceType: sourceType, mirrorMode: mirrorMode), + ); + print('本地视频视图已设置,viewId:$viewId'); + } + + /// 设置远程视频视图 + /// [uid] 远程用户ID + /// [viewId] 视图ID + Future setupRemoteVideo({ + required int uid, + required int viewId, + VideoSourceType sourceType = VideoSourceType.videoSourceCameraPrimary, + VideoMirrorModeType mirrorMode = + VideoMirrorModeType.videoMirrorModeDisabled, + }) async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + await _engine!.setupRemoteVideo( + VideoCanvas( + uid: uid, + view: viewId, + sourceType: sourceType, + mirrorMode: mirrorMode, + ), + ); + print('远程视频视图已设置,UID:$uid,viewId:$viewId'); + } + + /// 加入频道 + /// [token] 频道令牌(可选,如果频道未开启鉴权则可以为空字符串) + /// [channelId] 频道ID + /// [uid] 用户ID(0表示自动分配) + /// [options] 频道媒体选项 + Future joinChannel({ + String? token, + required String channelId, + int uid = 0, + ChannelMediaOptions? options, + }) async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + if (_isInChannel) { + print('已经在频道中,先离开当前频道'); + await leaveChannel(); + } + + _currentUid = uid; + await _engine!.joinChannel( + token: token ?? '', + channelId: channelId, + uid: uid, + options: options ?? const ChannelMediaOptions(), + ); + print('正在加入频道:$channelId,UID:$uid'); + } + + /// 离开频道 + Future leaveChannel() async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + if (!_isInChannel) { + print('当前不在频道中'); + return; + } + + await _engine!.leaveChannel(); + _currentUid = null; + print('已离开频道'); + } + + /// 切换摄像头 + Future switchCamera() async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + await _engine!.switchCamera(); + print('摄像头已切换'); + } + + /// 静音/取消静音本地音频 + /// [muted] true表示静音,false表示取消静音 + Future muteLocalAudio(bool muted) async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + await _engine!.muteLocalAudioStream(muted); + print('本地音频${muted ? "已静音" : "已取消静音"}'); + } + + /// 开启/关闭本地视频 + /// [enabled] true表示开启,false表示关闭 + Future muteLocalVideo(bool enabled) async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + await _engine!.muteLocalVideoStream(!enabled); + print('本地视频${enabled ? "已开启" : "已关闭"}'); + } + + /// 设置客户端角色(仅用于直播场景) + /// [role] 客户端角色:主播或观众 + Future setClientRole({ + required ClientRoleType role, + ClientRoleOptions? options, + }) async { + if (_engine == null) { + throw Exception('RTC Engine not initialized'); + } + await _engine!.setClientRole(role: role, options: options); + print('客户端角色已设置为:$role'); + } + + /// 获取当前是否在频道中 + bool get isInChannel => _isInChannel; + + /// 获取当前频道ID + String? get currentChannelId => _currentChannelId; + + /// 获取当前用户ID + int? get currentUid => _currentUid; + + /// 获取 RTC Engine 实例(用于高级操作) + RtcEngine? get engine => _engine; + + /// 释放资源 + Future dispose() async { + try { + if (_isInChannel) { + await leaveChannel(); + } + if (_engine != null) { + await _engine!.release(); + _engine = null; + } + _isInitialized = false; + _isInChannel = false; + _currentChannelId = null; + _currentUid = null; + print('RTC Engine disposed'); + } catch (e) { + print('Failed to dispose RTC Engine: $e'); + } + } +}