diff --git a/lib/modules/profile/controllers/profile_controller.dart b/lib/modules/profile/controllers/profile_controller.dart index 1dae96f4f..2480174d3 100644 --- a/lib/modules/profile/controllers/profile_controller.dart +++ b/lib/modules/profile/controllers/profile_controller.dart @@ -28,6 +28,13 @@ class ProfileController extends GetxController { final Rx userInfo = User.instance.obs; // 响应式用户信息对象,使用单例模式确保全局唯一性 final RxString localAvatar = ''.obs; // 本地头像路径,便于响应式更新UI final RxBool hasEdits = false.obs; // 是否有编辑 + final RxBool isUpdating = false.obs; // 是否正在更新用户信息 + final RxBool isEditingNickname = false.obs; // 是否正在编辑昵称(内联编辑状态) + + // 用于乐观更新的原始状态备份 + String _originalName = ''; + String _originalAvatar = ''; + String _originalLocalAvatar = ''; // 使用响应式变量 final currentLanguage = 'zh'.obs; @@ -35,6 +42,33 @@ class ProfileController extends GetxController { // 账号服务 late final AccountService _accountService; + // 错误处理工具方法 + String _getErrorMessage(dynamic error) { + final errorStr = error.toString().toLowerCase(); + + if (errorStr.contains('network') || + errorStr.contains('socket') || + errorStr.contains('timeout') || + errorStr.contains('connection')) { + return '网络连接失败,请检查网络设置后重试'; + } else if (errorStr.contains('server') || + errorStr.contains('500') || + errorStr.contains('502') || + errorStr.contains('503')) { + return '服务器暂时无法响应,请稍后重试'; + } else if (errorStr.contains('401') || errorStr.contains('unauthorized')) { + return '登录已过期,请重新登录'; + } else if (errorStr.contains('403') || errorStr.contains('forbidden')) { + return '没有权限执行此操作'; + } else if (errorStr.contains('404')) { + return '请求的资源不存在'; + } else if (errorStr.contains('file') || errorStr.contains('image')) { + return '图片处理失败,请重新选择图片'; + } else { + return '操作失败,请重试'; + } + } + @override void onInit() { super.onInit(); @@ -158,6 +192,8 @@ class ProfileController extends GetxController { // 更新用户信息到后端 Future _syncWithBackend() async { try { + Logger().d('开始同步用户信息到后端'); + // 创建FormData对象用于上传 dio.FormData formData = dio.FormData(); @@ -182,10 +218,12 @@ class ProfileController extends GetxController { ); Logger().d('已添加头像文件到请求'); } else { - Logger().e('头像文件不存在'); + Logger().e('头像文件不存在: ${localAvatar.value}'); + throw Exception('头像文件不存在'); } } catch (e) { Logger().e('读取头像文件失败: $e'); + throw Exception('读取头像文件失败: $e'); } } @@ -194,25 +232,53 @@ class ProfileController extends GetxController { // 调用API,使用FormData final response = await Api.updateUserSetting(formData); Logger().d('--更新用户信息响应: $response'); - if (response != null) { + + if (response != null && response is Map) { + // 检查响应是否包含错误 + if (response.containsKey('error') || response.containsKey('msg')) { + final errorMsg = response['error'] ?? response['msg'] ?? '服务器返回错误'; + Logger().e('服务器返回错误: $errorMsg'); + throw Exception('服务器错误: $errorMsg'); + } + Logger().d('更新成功: $response'); - Get.snackbar('成功', '同步到服务器成功'); + + // 更新用户信息为服务器返回的最新数据 userInfo.update((u) { if (u != null) { - u.name = response['name'] ?? ''; - u.avatar = response['avatar'] ?? ''; + u.name = response['name'] ?? u.name; + u.avatar = response['avatar'] ?? u.avatar; } }); + + // 如果服务器返回了新的头像URL,下载并保存到本地 + if (response['avatar'] != null && response['avatar'].toString().isNotEmpty) { + final serverAvatar = response['avatar'].toString(); + if (serverAvatar.startsWith('http')) { + // 异步下载新头像,不阻塞当前流程 + _downloadAndSaveAvatar(serverAvatar); + } + } + Logger().d('更新后的用户信息: ${userInfo.value.toJson()}'); } else { - Logger().e('更新失败: $response'); - Get.snackbar('错误', '同步到服务器失败,请重试'); - return; + Logger().e('服务器响应为空或格式错误: $response'); + throw Exception('服务器响应异常'); } } catch (e) { Logger().e('同步到后端失败: $e'); - Get.snackbar('Error', '同步到服务器失败,请重试'); - throw e; + + // 根据错误类型抛出更具体的异常 + if (e.toString().contains('SocketException') || + e.toString().contains('TimeoutException') || + e.toString().contains('network')) { + throw Exception('network: 网络连接失败,请检查网络设置'); + } else if (e.toString().contains('500') || + e.toString().contains('server')) { + throw Exception('server: 服务器内部错误,请稍后重试'); + } else { + throw Exception('unknown: ${e.toString()}'); + } } } @@ -221,12 +287,127 @@ class ProfileController extends GetxController { if (userJson != null) { userInfo.value = User.instance; Logger().d('从本地加载用户信息: $userJson'); + + // 加载本地头像路径并验证文件有效性 + final savedLocalAvatar = _storage.read('local_avatar'); + if (savedLocalAvatar != null && savedLocalAvatar.toString().isNotEmpty) { + final avatarPath = savedLocalAvatar.toString(); + // 验证文件是否存在 + final file = File(avatarPath); + file.exists().then((exists) { + if (exists) { + localAvatar.value = avatarPath; + Logger().d('加载本地头像路径: ${localAvatar.value}'); + } else { + // 文件不存在,清除无效路径 + localAvatar.value = ''; + _storage.remove('local_avatar'); + Logger().w('本地头像文件不存在,已清除路径: $avatarPath'); + } + }); + } else { + // 确保本地头像路径为空 + localAvatar.value = ''; + Logger().d('没有本地头像路径'); + } + + // 保存原始状态用于回滚 + _saveOriginalState(); + // 如果有远程头像地址,下载并保存到本地 if (userInfo.value.avatar.isNotEmpty) { Logger().d('远程头像地址: ${userInfo.value.avatar}'); _downloadAndSaveAvatar(userInfo.value.avatar); } + + // 确保状态一致性 + _ensureStateConsistency(); + } + } + + // 确保本地状态与服务器状态的一致性 + void _ensureStateConsistency() { + // 检查本地头像文件是否存在 + if (localAvatar.value.isNotEmpty) { + final file = File(localAvatar.value); + file.exists().then((exists) { + if (!exists) { + Logger().w('本地头像文件不存在,清除本地头像路径: ${localAvatar.value}'); + localAvatar.value = ''; + _storage.remove('local_avatar'); + + // 如果有远程头像,重新下载 + if (userInfo.value.avatar.isNotEmpty) { + _downloadAndSaveAvatar(userInfo.value.avatar); + } + } + }); + } + } + + // 保存原始状态用于乐观更新回滚 + void _saveOriginalState() { + _originalName = userInfo.value.name; + _originalAvatar = userInfo.value.avatar; + _originalLocalAvatar = localAvatar.value; + Logger().d('已保存原始状态 - 名称: $_originalName, 头像: $_originalAvatar, 本地头像: $_originalLocalAvatar'); + } + + // 回滚到原始状态 + Future _rollbackToOriginalState() async { + Logger().d('开始回滚到原始状态'); + + // 回滚内存中的状态 + userInfo.update((user) { + if (user != null) { + user.name = _originalName; + user.avatar = _originalAvatar; + } + }); + localAvatar.value = _originalLocalAvatar; + + // 重置编辑状态 + hasEdits.value = false; + + // 关键修复:回滚后立即同步到本地存储 + try { + await _syncLocalStorage(); + Logger().d('回滚状态已同步到本地存储'); + } catch (e) { + Logger().e('回滚状态同步到本地存储失败: $e'); } + + Logger().d('已回滚到原始状态 - 名称: $_originalName, 头像: $_originalAvatar, 本地头像: $_originalLocalAvatar'); + } + + // 开始编辑昵称(内联编辑) + void startEditingNickname() { + isEditingNickname.value = true; + Logger().d('开始内联编辑昵称'); + } + + // 完成编辑昵称(内联编辑) + void finishEditingNickname(String newName) { + isEditingNickname.value = false; + + if (newName.trim().isNotEmpty && newName.trim() != _originalName) { + // 乐观更新 + userInfo.update((user) { + if (user != null) { + user.name = newName.trim(); + } + }); + hasEdits.value = true; + Logger().d('昵称内联编辑完成,新名称: ${newName.trim()}'); + } else { + Logger().d('昵称内联编辑取消或无变化'); + } + } + + // 取消编辑昵称(内联编辑) + void cancelEditingNickname() { + isEditingNickname.value = false; + Logger().d('取消内联编辑昵称'); } // 下载并保存远程头像 @@ -273,6 +454,29 @@ class ProfileController extends GetxController { Future saveUserInfo() async { await _storage.write(userKey, userInfo.value.toJson()); + Logger().d('用户信息已保存到本地存储'); + } + + // 同步所有相关状态到本地存储 + Future _syncLocalStorage() async { + try { + // 保存用户信息 + await saveUserInfo(); + + // 同步本地头像路径 + if (localAvatar.value.isNotEmpty) { + await _storage.write('local_avatar', localAvatar.value); + Logger().d('本地头像路径已保存: ${localAvatar.value}'); + } else { + // 如果本地头像为空,清除存储中的路径 + await _storage.remove('local_avatar'); + Logger().d('本地头像路径已清除'); + } + + Logger().d('所有状态已同步到本地存储'); + } catch (e) { + Logger().e('同步本地存储失败: $e'); + } } // 更新用户头像 @@ -285,82 +489,152 @@ class ProfileController extends GetxController { ); if (image != null) { - // 检查文件大小 - final file = File(image.path); - var fileSize = await file.length(); - var quality = 85; - - // 如果文件大于1MB,进行压缩 - while (fileSize > 1024 * 1024 && quality > 30) { - final compressedImage = await FlutterImageCompress.compressAndGetFile( - file.absolute.path, - file.absolute.path, // 覆盖原文件 - quality: quality, - minWidth: 800, - minHeight: 800, - ); + try { + // 保存当前状态用于可能的回滚 + _saveOriginalState(); + + // 检查文件大小 + final file = File(image.path); + var fileSize = await file.length(); + var quality = 85; + + // 如果文件大于1MB,进行压缩 + while (fileSize > 1024 * 1024 && quality > 30) { + final compressedImage = await FlutterImageCompress.compressAndGetFile( + file.absolute.path, + file.absolute.path, // 覆盖原文件 + quality: quality, + minWidth: 800, + minHeight: 800, + ); + + if (compressedImage == null) { + Get.snackbar('错误', '图片压缩失败'); + return; + } - if (compressedImage == null) { - Get.snackbar('错误', '图片压缩失败'); - return; + //更新文件大小和质量 + fileSize = await compressedImage.length(); + quality -= 5; + Logger().d('压缩后文件大小:$fileSize'); } - //更新文件大小和质量 - fileSize = await compressedImage.length(); - quality -= 5; - Logger().d('压缩后文件大小:$fileSize'); - } - - userInfo.update((user) { - if (user != null) { - user.avatar = image.path; - } - }); + // 乐观更新:立即更新UI + userInfo.update((user) { + if (user != null) { + user.avatar = image.path; + } + }); - // 更新本地头像路径 - localAvatar.value = image.path; - await _storage.write('local_avatar', image.path); - // await saveUserInfo(); //保存到本地 - //await _syncWithBackend(); //同步到后端 - - // 通知 SettingsController 更新 - // if (Get.isRegistered()) { - // Get.find().loadUserInfo(); - // } - hasEdits.value = true; //表示有编辑 + // 更新本地头像路径 + localAvatar.value = image.path; + await _storage.write('local_avatar', image.path); + hasEdits.value = true; //表示有编辑 + + Logger().d('头像已乐观更新,等待用户保存'); + } catch (e) { + Logger().e('更新头像失败: $e'); + final errorMessage = _getErrorMessage(e); + Get.snackbar('头像更新失败', errorMessage, + duration: const Duration(seconds: 3), + snackPosition: SnackPosition.TOP, + ); + // 发生错误时回滚状态 + await _rollbackToOriginalState(); + } } } - // 更新用户名 + // 更新用户名和头像(保存操作) Future updateUserNameAndAvatar(String name) async { if (name.trim().isEmpty) { Logger().e('无效名称: $name'); + Get.snackbar('错误', '用户名不能为空'); return; } + // 显示加载状态 + isUpdating.value = true; + try { - userInfo.update((user) { - if (user != null) { - user.name = name.trim(); - Logger().d('名称更新为: ${user.name}'); - } - }); + Logger().d('开始保存用户信息 - 名称: ${name.trim()}, 本地头像: ${localAvatar.value}'); - //await saveUserInfo(); //保存到本地 - await _syncWithBackend(); //同步到后端 + // 同步到后端 + await _syncWithBackend(); - //同步更新 SettingsController + // 同步成功后更新原始状态和本地存储 + _saveOriginalState(); + await _syncLocalStorage(); + + // 同步更新 SettingsController if (Get.isRegistered()) { final settingsController = Get.find(); settingsController.userInfo.value.name = userInfo.value.name; settingsController.userInfo.value.avatar = '${userInfo.value.avatar}?time=${DateTime.now().millisecondsSinceEpoch}'; - //settingsController.loadUserInfo(); settingsController.userInfo.refresh(); } + + // 重置编辑状态 + hasEdits.value = false; + + Logger().d('用户信息保存成功'); + + // 显示成功提示 + Get.snackbar( + '保存成功', + '个人信息已更新', + duration: const Duration(seconds: 3), + snackPosition: SnackPosition.TOP, + backgroundColor: Colors.green.withOpacity(0.1), + colorText: Colors.green.shade700, + icon: Icon( + Icons.check_circle_outline, + color: Colors.green.shade700, + ), + margin: const EdgeInsets.all(16), + borderRadius: 8, + isDismissible: true, + forwardAnimationCurve: Curves.easeOutBack, + ); + + Logger().d('成功提示已显示'); + + // 备用成功提示机制 - 使用ScaffoldMessenger确保用户看到 + Future.delayed(const Duration(milliseconds: 100), () { + if (Get.context != null) { + ScaffoldMessenger.of(Get.context!).showSnackBar( + const SnackBar( + content: Row( + children: [ + Icon(Icons.check_circle, color: Colors.white), + SizedBox(width: 8), + Text('个人信息已更新'), + ], + ), + backgroundColor: Colors.green, + duration: Duration(seconds: 2), + behavior: SnackBarBehavior.floating, + ), + ); + } + }); + } catch (e) { Logger().e('更新用户名和头像错误: $e'); - Get.snackbar('错误', '更新用户名和头像失败'); + + // 同步失败时回滚到原始状态 + await _rollbackToOriginalState(); + + // 显示错误提示 + final errorMessage = _getErrorMessage(e); + Get.snackbar('保存失败', errorMessage, + duration: const Duration(seconds: 4), + snackPosition: SnackPosition.TOP, + ); + } finally { + // 隐藏加载状态 + isUpdating.value = false; } } diff --git a/lib/modules/profile/views/profile_view.dart b/lib/modules/profile/views/profile_view.dart index 678135fd6..76ed99bea 100644 --- a/lib/modules/profile/views/profile_view.dart +++ b/lib/modules/profile/views/profile_view.dart @@ -170,22 +170,46 @@ class EditProfileView extends GetView { actions: [ Obx(() => Visibility( visible: controller.hasEdits.value, - child: TextButton( - onPressed: () { - // 保存用户名 - controller.updateUserNameAndAvatar(nameController.text); - // 返回上一级页面 - Get.back(); - }, - child: Text( - 'save'.tr, - style: TextStyle( - fontSize: 16.sp, - color: isDarkMode ? Colors.blue[300] : Colors.blue, - fontWeight: FontWeight.w500, - ), - ), - ), + child: controller.isUpdating.value + ? Padding( + padding: EdgeInsets.all(16.w), + child: SizedBox( + width: 20.w, + height: 20.w, + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: AlwaysStoppedAnimation( + isDarkMode ? Colors.blue[300]! : Colors.blue, + ), + ), + ), + ) + : TextButton( + onPressed: () async { + // 保存用户名和头像 + await controller + .updateUserNameAndAvatar(nameController.text); + // 如果保存成功(没有编辑状态),延迟返回上一级页面让用户看到成功提示 + if (!controller.hasEdits.value) { + // 延迟跳转,让用户看到成功提示 + Future.delayed(const Duration(milliseconds: 500), + () { + if (Get.isRegistered()) { + Get.back(); + } + }); + } + }, + child: Text( + 'save'.tr, + style: TextStyle( + fontSize: 16.sp, + color: + isDarkMode ? Colors.blue[300] : Colors.blue, + fontWeight: FontWeight.w500, + ), + ), + ), )) ], ), @@ -341,41 +365,54 @@ class EditProfileView extends GetView { ); } - // 新增:构建昵称设置项 + // 新增:构建昵称设置项(双模式内联编辑) Widget _buildNicknameSetting(BuildContext context, bool isDarkMode, TextEditingController nameController) { + return Obx(() { + // 根据编辑状态显示不同的UI + if (controller.isEditingNickname.value) { + return _buildNicknameEditingMode(context, isDarkMode, nameController); + } else { + return _buildNicknameDisplayMode(context, isDarkMode, nameController); + } + }); + } + + // 构建昵称显示模式 + Widget _buildNicknameDisplayMode(BuildContext context, bool isDarkMode, + TextEditingController nameController) { return InkWell( - onTap: () => _showEditNicknameDialog(context, isDarkMode, nameController), + key: Key('nickname_edit_button'), // 添加测试Key + onTap: () { + // 开始内联编辑 + nameController.text = controller.userInfo.value.name; + controller.startEditingNickname(); + }, child: Padding( - padding: - EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), // 调整垂直间距 + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), child: Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, // 移除 children: [ - // --- 添加图标 --- + // 图标 Container( width: 32.w, height: 32.w, decoration: BoxDecoration( color: isDarkMode - ? Colors.green[900]!.withOpacity(0.3) // 使用示例颜色 - : Colors.green[50]!, // 使用示例颜色 - borderRadius: BorderRadius.circular(16.r), // 图标背景圆角 + ? Colors.green[900]!.withOpacity(0.3) + : Colors.green[50]!, + borderRadius: BorderRadius.circular(16.r), ), child: Icon( - Icons.edit_outlined, // 编辑图标 + Icons.edit_outlined, size: 18.w, - color: isDarkMode - ? Colors.green[300]! - : Colors.green[600]!, // 图标颜色 + color: isDarkMode ? Colors.green[300]! : Colors.green[600]!, ), ), - SizedBox(width: 12.w), // 图标和文字间距 - // --- --- + SizedBox(width: 12.w), + // 标签 Expanded( - // 让文字占据剩余空间 child: Text( - 'nickname'.tr, // 名字 + 'nickname'.tr, style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w500, @@ -383,14 +420,13 @@ class EditProfileView extends GetView { ), ), ), - // --- 右侧内容保持不变 --- + // 右侧内容 Row( children: [ - // 使用 Obx 监听 userInfo 的变化来显示昵称 Obx(() => Text( controller.userInfo.value.name.isNotEmpty ? controller.userInfo.value.name - : 'notSet'.tr, // 未设置 + : 'notSet'.tr, style: TextStyle( fontSize: 14.sp, color: isDarkMode ? Colors.white70 : Colors.grey[600], @@ -408,87 +444,111 @@ class EditProfileView extends GetView { borderRadius: BorderRadius.circular(16.r), ), child: Icon( - Icons.chevron_right, + Icons.edit, size: 16.w, color: isDarkMode ? Colors.white70 : Colors.grey[500], ), ), ], ), - // --- --- ], ), ), ); } - // 新增:显示编辑昵称对话框的方法 - void _showEditNicknameDialog(BuildContext context, bool isDarkMode, + // 构建昵称编辑模式 + Widget _buildNicknameEditingMode(BuildContext context, bool isDarkMode, TextEditingController nameController) { - // 创建一个临时的 Controller,用于对话框内的编辑,避免直接修改主 nameController - final dialogNameController = - TextEditingController(text: nameController.text); - - Get.dialog( - AlertDialog( - backgroundColor: isDarkMode ? Colors.grey[900] : Colors.white, - title: Text( - 'editNickname'.tr, // 编辑昵称 - style: TextStyle(color: isDarkMode ? Colors.white : Colors.black87), - ), - content: TextField( - controller: dialogNameController, // 使用对话框的 Controller - autofocus: true, - decoration: InputDecoration( - hintText: 'enterNickname'.tr, // 请输入昵称 - hintStyle: - TextStyle(color: isDarkMode ? Colors.white54 : Colors.black38), - // 可以添加其他样式,如边框 - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8.r), - borderSide: BorderSide( - color: isDarkMode ? Colors.white38 : Colors.grey[400]!), + return Padding( + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), // 与显示模式保持一致的padding + child: Row( + children: [ + // 图标 - 保持与显示模式相同的绿色主题 + Container( + width: 32.w, + height: 32.w, + decoration: BoxDecoration( + color: isDarkMode + ? Colors.green[900]!.withOpacity(0.3) + : Colors.green[50]!, + borderRadius: BorderRadius.circular(16.r), ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(8.r), - borderSide: BorderSide( - color: isDarkMode ? Colors.white38 : Colors.grey[400]!), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(8.r), - borderSide: BorderSide( - color: isDarkMode ? Colors.blue[300]! : Colors.blue), + child: Icon( + Icons.edit_outlined, // 保持与显示模式相同的图标 + size: 18.w, + color: isDarkMode ? Colors.green[300]! : Colors.green[600]!, ), ), - style: TextStyle(color: isDarkMode ? Colors.white : Colors.black87), - ), - actions: [ - TextButton( - onPressed: () => Get.back(), + SizedBox(width: 12.w), + // 标签 - 保持与显示模式相同的布局 + Expanded( child: Text( - 'cancel'.tr, + 'nickname'.tr, style: TextStyle( - color: isDarkMode ? Colors.white70 : Colors.black54), + fontSize: 16.sp, + fontWeight: FontWeight.w500, + color: isDarkMode ? Colors.white : Colors.black87, + ), ), ), - TextButton( - onPressed: () { - // 确认时,将对话框输入的值更新回主 nameController - nameController.text = dialogNameController.text; - controller.userInfo.update((user) { - if (user != null) { - user.name = dialogNameController.text; - } - }); - controller.hasEdits.value = true; // 标记已编辑 - Get.back(); // 关闭对话框 - // 注意:实际的保存操作由 AppBar 中的保存按钮触发 - }, - child: Text( - 'confirm'.tr, - style: - TextStyle(color: isDarkMode ? Colors.blue[300] : Colors.blue), - ), + // 右侧内容区域 - 与显示模式布局保持一致 + Row( + children: [ + // 编辑输入框 - 替换原来的昵称文本显示区域 + Container( + constraints: BoxConstraints( + minWidth: 80.w, // 确保有足够的编辑空间 + maxWidth: 120.w, // 限制最大宽度保持布局稳定 + ), + child: TextField( + controller: nameController, + autofocus: true, + textAlign: TextAlign.right, // 右对齐,与原昵称显示保持一致 + style: TextStyle( + fontSize: 14.sp, + color: isDarkMode ? Colors.white70 : Colors.grey[600], + ), + decoration: InputDecoration( + hintText: 'enterNickname'.tr, + hintStyle: TextStyle( + color: isDarkMode ? Colors.white54 : Colors.grey[500], + fontSize: 14.sp, + ), + border: InputBorder.none, // 移除边框,保持简洁 + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + contentPadding: EdgeInsets.zero, // 移除内边距 + isDense: true, + ), + onSubmitted: (value) { + // 按回车确认编辑 + controller.finishEditingNickname(value); + }, + onTapOutside: (event) { + // 点击外部区域确认编辑 + controller.finishEditingNickname(nameController.text); + }, + ), + ), + SizedBox(width: 8.w), + // 右侧箭头图标 - 保持与显示模式完全相同 + Container( + width: 32.w, + height: 32.w, + decoration: BoxDecoration( + color: isDarkMode + ? Colors.white.withOpacity(0.05) + : Colors.grey[100], + borderRadius: BorderRadius.circular(16.r), + ), + child: Icon( + Icons.chevron_right, + size: 16.w, + color: isDarkMode ? Colors.white70 : Colors.grey[500], + ), + ), + ], ), ], ), diff --git a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleConst.kt b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleConst.kt index 0da045011..29246eabf 100644 --- a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleConst.kt +++ b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleConst.kt @@ -11,13 +11,16 @@ object BleConst { val PRIMARY_SERVICE_UUID: UUID = UUID.fromString("0000abc0-0000-1111-2222-123456789abc") /** 音频服务UUID - 文档中定义为00001801-0000-1000-8000-00805f9b34fb */ - val AUDIO_SERVICE_UUID: UUID = UUID.fromString("0000ae00-0000-1000-8000-00805f9b34fb") + val AUDIO_SERVICE_UUID: UUID = UUID.fromString("0000ABC0-0001-1111-2222-123456789ABC") /** 接收音频特征UUID - 文档中定义为0000ABC1-0000-1111-2222-123456789ABC */ - val RECEIVE_AUDIO_CHAR_UUID: UUID = UUID.fromString("0000ae02-0000-1000-8000-00805f9b34fb") + val RECEIVE_AUDIO_CHAR_UUID: UUID = UUID.fromString("0000ABC2-0001-1111-2222-123456789ABC") /** 写入特征UUID - 文档中定义为0000ABC1-0000-1111-2222-123456789ABC */ val WRITE_CHAR_UUID: UUID = UUID.fromString("0000abc1-0000-1111-2222-123456789abc") + + /** 音频写入特征UUID - 文档中定义为0000ABC1-0000-1111-2222-123456789ABC */ + val AUDIO_WRITE_CHAR_UUID: UUID = UUID.fromString("0000ABC1-0001-1111-2222-123456789ABC") /** 通知特征UUID - 文档中定义为0000ABC2-0000-1111-2222-123456789ABC */ val NOTIFY_CHAR_UUID: UUID = UUID.fromString("0000abc2-0000-1111-2222-123456789abc")