Browse Source

优化头像更新

newdev_shunjiawei
lxm 2 years ago
parent
commit
bd062c2acd
  1. 8
      lib/core/translations/language/en_us.dart
  2. 8
      lib/core/translations/language/zh_cn.dart
  3. 2
      lib/modules/home/bindings/home_binding.dart
  4. 2
      lib/modules/home/controllers/home_controller.dart
  5. 2
      lib/modules/pairing/controllers/pairing_controller.dart
  6. 142
      lib/modules/profile/controllers/profile_controller.dart
  7. 10
      lib/modules/profile/views/profile_view.dart
  8. 92
      lib/modules/settings/controllers/settings_controller.dart
  9. 59
      lib/modules/settings/views/settings_view.dart
  10. 8
      lib/modules/timbre/controllers/timbre_controller.dart
  11. 6
      lib/modules/timbre/views/timbre_view.dart

8
lib/core/translations/language/en_us.dart

@ -43,6 +43,7 @@ const Map<String, String> enUS = {
"and": " and ",
"userAgreement": "User Agreement",
"privacyPolicy": "Privacy Policy",
'selectCountry':'Select Country/Region',
// 首页
"deviceStatus": "Device Status",
@ -85,6 +86,8 @@ const Map<String, String> enUS = {
"fine": "Great",
"excellent": "Excellent",
"unknown": "Unknown",
'bluetoothTurnedOff': 'Bluetooth is turned off, please turn it on',
'bluetoothOff':'bluetooth is off',
// 发现页
"searchAi": "Search AI features...",
@ -148,6 +151,11 @@ const Map<String, String> enUS = {
"flutterVoiceService": "Local voice service",
"flutterVoiceServiceInfo":
"Device-local voice service with no internet required",
'timbreService': 'Timbre Service',
'timbreServiceInfo': 'Select voice synthesis service',
'defaultTimbre': 'Default Timbre',
'microsoftCloudTimbreService': 'Microsoft Cloud Timbre Service',
'voiceClone': 'Voice Clone',
// 聊天页
"clearChatHistory": "Clear Chat History",

8
lib/core/translations/language/zh_cn.dart

@ -84,6 +84,8 @@ const Map<String, String> zhCN = {
"fine": "很好",
"excellent": "极佳",
"unknown": "未知",
'bluetoothTurnedOff': '蓝牙已关闭,请打开蓝牙',
'bluetoothOff': '蓝牙已关闭',
// 发现页
"searchAi": "搜索AI功能...",
@ -143,6 +145,12 @@ const Map<String, String> zhCN = {
"azureVoiceServiceInfo": "微软云端语音服务,支持高质量语音合成和识别",
"flutterVoiceService": "Flutter 本地语音服务",
"flutterVoiceServiceInfo": "设备本地语音服务,无需联网即可使用",
'timbreService': '音色服务',
'timbreServiceInfo': '选择音色语音合成服务',
'defaultTimbre': '默认音色',
'microsoftCloudTimbreService': '微软云端音色服务',
'voiceClone': '声音复刻',
'selectCountry': '选择国家/地区',
// 聊天页
"clearChatHistory": "清空聊天记录",

2
lib/modules/home/bindings/home_binding.dart

@ -1,4 +1,4 @@
import 'package:deep_voice/modules/explore/controllers/explore_controller.dart';
import '../../explore/controllers/explore_controller.dart';
import 'package:get/get.dart';
import '../controllers/home_controller.dart';

2
lib/modules/home/controllers/home_controller.dart

@ -278,7 +278,7 @@ class HomeController extends GetxController {
String getConnectionStatusText() {
// 如果蓝牙关闭,优先显示蓝牙状态
if (_bluetoothService.bluetoothState.value == 'off') {
return '蓝牙已关闭';
return 'bluetoothOff'.tr; // 蓝牙已关闭
}
switch (_connectionStatus.value) {

2
lib/modules/pairing/controllers/pairing_controller.dart

@ -82,7 +82,7 @@ class PairingController extends GetxController {
/// 更新状态描述
void _updateStatusDescription() {
if (_bluetoothService.bluetoothState.value == 'off') {
statusDescription.value = '蓝牙已关闭,请打开蓝牙';
statusDescription.value = 'bluetoothTurnedOff'.tr;// 蓝牙已关闭, 请打开蓝牙
return;
}

142
lib/modules/profile/controllers/profile_controller.dart

@ -13,6 +13,7 @@ import 'package:path_provider/path_provider.dart';
import '../../../data/services/network/api.dart';
import 'package:dio/dio.dart';
class ProfileController extends GetxController {
static ProfileController get to => Get.find();
@ -115,6 +116,53 @@ class ProfileController extends GetxController {
// void toggleEditing() {
// _isEditing.value = !_isEditing.value;
// }
// 验证base64图片数据
// Future<void> _verifyBase64Image(String base64String) async {
// try {
// // 解码base64数据
// List<int> imageBytes = base64Decode(base64String);
// // 获取应用文档目录
// final appDir = await getApplicationDocumentsDirectory();
// final verifyPath = '${appDir.path}/verify_base64_${DateTime.now().millisecondsSinceEpoch}.jpg';
// // 将解码后的数据写入文件
// final file = File(verifyPath);
// await file.writeAsBytes(imageBytes);
// // 记录验证文件的路径
// Logger().d('Base64验证图片已保存到: $verifyPath');
// // 显示图片预览对话框
// Get.dialog(
// Dialog(
// child: Column(
// mainAxisSize: MainAxisSize.min,
// children: [
// Padding(
// padding: const EdgeInsets.all(16.0),
// child: Text('Base64 图片验证', style: TextStyle(fontWeight: FontWeight.bold)),
// ),
// Image.file(File(verifyPath)),
// Padding(
// padding: const EdgeInsets.all(16.0),
// child: ElevatedButton(
// onPressed: () => Get.back(),
// child: Text('关闭'),
// ),
// ),
// ],
// ),
// ),
// );
// Get.snackbar('验证', '已将Base64转换回图片,请查看预览');
// } catch (e) {
// Logger().e('验证Base64图片失败: $e');
// Get.snackbar('错误', 'Base64验证失败: $e');
// }
// }
// 更新用户信息到后端
@ -123,26 +171,29 @@ class ProfileController extends GetxController {
// 准备数据
Map<String, dynamic> data = {
"image": "", // 默认空字符串
"language": currentLanguage.value,
"name": userInfo.value.name,
"language": currentLanguage.value,//当前用户语言
"name": userInfo.value.name,//当前用户名
};
// // 如果有头像,转换为base64
// if (localAvatar.value.isNotEmpty) {
// try{
// File imageFile = File(localAvatar.value);
// if(await imageFile.exists()){
// List<int> imageBytes = await imageFile.readAsBytes();
// String base64Image = base64Encode(imageBytes);
// data["image"] = base64Image;
// }else{
// Logger().e('头像文件不存在');
// }
// }catch(e){
// Logger().e('读取头像文件失败: $e');
// }
// }
// 如果有头像,转换为base64
if (localAvatar.value.isNotEmpty) {
//如果本地头像路径不为空,即代表有头像
try {
File imageFile = File(localAvatar.value);
if (await imageFile.exists()) {
List<int> imageBytes = await imageFile.readAsBytes();
String base64Image = base64Encode(imageBytes);
data["image"] = base64Image;
// // 验证base64数据 - 将base64转回图片并保存到本地
// await _verifyBase64Image(base64Image);
} else {
Logger().e('头像文件不存在');
}
} catch (e) {
Logger().e('读取头像文件失败: $e');
}
}
final dio = Dio(BaseOptions(
baseUrl: 'https://www.deapsound.com:443',
@ -160,6 +211,8 @@ class ProfileController extends GetxController {
'/api/home/user_setting',
data: data,
);
// 调用API
//final response = await Api.updateUserSetting(data);
@ -167,6 +220,13 @@ class ProfileController extends GetxController {
if (response.data['code'] == 0) {
Logger().d('更新成功: ${response.data}');
Get.snackbar('成功', '同步到服务器成功');
userInfo.update((u) {
if (u != null) {
u.name = response.data['data']['name'] ?? '';
u.avatar = response.data['data']['avatar'] ?? '';
}
});
Logger().d('更新后的用户信息: ${userInfo.value.toJson()}');
} else {
throw '更新失败:${response.data['msg'] ?? '未知错误'}';
}
@ -184,18 +244,35 @@ class ProfileController extends GetxController {
final userJson = _storage.read(userKey);
if (userJson != null) {
userInfo.value = User.instance;
Logger().d('从本地加载用户信息: $userJson');
// 如果有远程头像地址,下载并保存到本地
if (userInfo.value.avatar.isNotEmpty) {
Logger().d('远程头像地址: ${userInfo.value.avatar}');
_downloadAndSaveAvatar(userInfo.value.avatar);
}
}
}
// 下载并保存远程头像
Future<void> _downloadAndSaveAvatar(String url) async {
Future<void> _downloadAndSaveAvatar(String url) async {
try {
final response = await GetConnect().get(url);
if (response.status.isOk && response.body != null) {
Logger().d('开始下载头像: $url');
final dio = Dio(BaseOptions(
headers: {
'accept': '*/*',
'Authorization': _storage.read<String>("logintoken"),
},
));
final response = await dio.get(
url,
options: Options(responseType: ResponseType.bytes),
);
Logger().d('下载状态: ${response.statusCode}');
if (response.statusCode == 200) {
// 保存到应用文档目录
final appDir = await getApplicationDocumentsDirectory();
final fileName = 'avatar_${DateTime.now().millisecondsSinceEpoch}.jpg';
@ -203,15 +280,13 @@ class ProfileController extends GetxController {
// 写入文件
final file = File(localPath);
final List<int> bytes = response.body.runtimeType == String
? base64Decode(response.body.toString())
: response.body;
await file.writeAsBytes(bytes);
await file.writeAsBytes(response.data);
// 更新本地头像路径
localAvatar.value = localPath;
await _storage.write('local_avatar', localPath);
Logger().d('头像已下载到本地: $localPath');
//Get.snackbar('头像', '头像已下载到本地');
}
} catch (e) {
Logger().e('下载头像失败: $e');
@ -225,12 +300,12 @@ class ProfileController extends GetxController {
// 更新用户头像
Future<void> updateUserAvatar() async {
final XFile? image = await _picker.pickImage(
source: ImageSource.gallery,
source: ImageSource.gallery,// 从相册选择
maxWidth: 800,
maxHeight: 800,
imageQuality: 85,
);
if (image != null) {
// 检查文件大小
final file = File(image.path);
@ -268,7 +343,7 @@ class ProfileController extends GetxController {
localAvatar.value = image.path;
await _storage.write('local_avatar', image.path);
// await saveUserInfo(); //保存到本地
await _syncWithBackend(); //同步到后端
//await _syncWithBackend(); //同步到后端
// 通知 SettingsController 更新
// if (Get.isRegistered<SettingsController>()) {
@ -278,7 +353,7 @@ class ProfileController extends GetxController {
}
// 更新用户名
Future<void> updateUserName(String name) async {
Future<void> updateUserNameAndAvatar(String name) async {
if (name.trim().isEmpty) {
Logger().e('无效名称: $name');
return;
@ -298,11 +373,14 @@ class ProfileController extends GetxController {
//同步更新 SettingsController
if (Get.isRegistered<SettingsController>()) {
final settingsController = Get.find<SettingsController>();
settingsController.loadUserInfo();
settingsController.userInfo.value.name = userInfo.value.name;
settingsController.userInfo.value.avatar = '${userInfo.value.avatar}?time=${DateTime.now().millisecondsSinceEpoch}';
//settingsController.loadUserInfo();
settingsController.userInfo.refresh();
}
} catch (e) {
Logger().e('更新名称错误: $e');
Get.snackbar('错误', '更新用户名失败');
Logger().e('更新用户名和头像错误: $e');
Get.snackbar('错误', '更新用户名和头像失败');
}
}
}

10
lib/modules/profile/views/profile_view.dart

@ -148,6 +148,14 @@ class EditProfileView extends GetView<ProfileController> {
centerTitle: true,
elevation: 0,
backgroundColor: isDarkMode ? Colors.black : Colors.white,
leading: IconButton(
icon: Icon(
Icons.arrow_back,
size: 24.sp,
color: isDarkMode? Colors.white : Colors.black,
),
onPressed: () => Get.back(),
),
title: Text(
'editProfile'.tr,
style: TextStyle(
@ -231,7 +239,7 @@ class EditProfileView extends GetView<ProfileController> {
child: ElevatedButton(
onPressed: () {
// 保存用户名
controller.updateUserName(nameController.text);
controller.updateUserNameAndAvatar(nameController.text);
// 返回上一级页面
Get.back();
},

92
lib/modules/settings/controllers/settings_controller.dart

@ -1,15 +1,17 @@
import 'package:firebase_auth/firebase_auth.dart' as firebase;
import 'package:get/get.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:dio/dio.dart';
import 'package:get_storage/get_storage.dart';
import '../../../core/themes/theme_service.dart';
import '../../../data/services/speech_factory.dart';
import '../models/language_model.dart';
//import '../../../data/models/user_model.dart';
import 'package:logger/logger.dart';
import '../../../data/models/user_Info.dart';
import '../../../modules/login/controllers/googleLogin.dart';
import '../../../modules/login/controllers/facebookLogin.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:io';
class SettingsController extends GetxController {
// 语言设置
@ -61,12 +63,12 @@ class SettingsController extends GetxController {
},
];
RxString timbreName = '默认音色'.obs;
RxString timbreName = 'defaultTimbre'.obs;
//用户信息
static const String userKey = 'user_info';
//final _storage=GetStorage();
final ImagePicker _picker = ImagePicker();
//final ImagePicker _picker = ImagePicker();
final Rx<User> userInfo = User.instance.obs; //使用单例实例
@override
@ -93,8 +95,20 @@ class SettingsController extends GetxController {
void loadUserInfo() {
final userJson = _storage.read(userKey);
Logger().d('加载用户信息: $userJson');
if (userJson != null) {
userInfo.value = User.instance;
Logger().d('用户信息已加载: ${userInfo.value.toJson()}');
if (userInfo.value.avatar.isNotEmpty) {
Logger().d('开始下载头像');
_downloadAndSaveAvatar(userInfo.value.avatar);
} else {
Logger().d('用户没有头像');
}
} else {
Logger().d('没有找到用户信息');
}
}
@ -102,24 +116,64 @@ class SettingsController extends GetxController {
await _storage.write(userKey, userInfo.value.toJson());
}
Future<void> updateUserAvatar() async {
final XFile? image = await _picker.pickImage(
source: ImageSource.gallery,
maxWidth: 800,
maxHeight: 800,
imageQuality: 85,
);
if (image != null) {
userInfo.update((user) {
if (user != null) {
user.avatar = image.path;
}
});
await saveUserInfo();
// 下载并保存远程头像
Future<void> _downloadAndSaveAvatar(String url) async {
try {
Logger().d('开始下载头像: $url');
final dio = Dio(BaseOptions(
headers: {
'accept': '*/*',
'Authorization': _storage.read<String>("logintoken"),
},
));
final response = await dio.get(
url,
options: Options(responseType: ResponseType.bytes),
);
Logger().d('下载状态: ${response.statusCode}');
if (response.statusCode == 200) {
// 保存到应用文档目录
final appDir = await getApplicationDocumentsDirectory();
final fileName = 'avatar_${DateTime.now().millisecondsSinceEpoch}.jpg';
final localPath = '${appDir.path}/$fileName';
// 写入文件
final file = File(localPath);
await file.writeAsBytes(response.data);
// 更新本地头像路径
//localAvatar.value = localPath;
await _storage.write('local_avatar', localPath);
Logger().d('头像已下载到本地: $localPath');
//Get.snackbar('头像', '头像已下载到本地');
}
} catch (e) {
Logger().e('下载头像失败: $e');
}
}
// Future<void> updateUserAvatar() async {
// final XFile? image = await _picker.pickImage(
// source: ImageSource.gallery,
// maxWidth: 800,
// maxHeight: 800,
// imageQuality: 85,
// );
// if (image != null) {
// userInfo.update((user) {
// if (user != null) {
// user.avatar = image.path;
// }
// });
// await saveUserInfo();
// }
// }
Future<void> updateUserName(String name) async {
if (name.trim().isNotEmpty) {
userInfo.update((user) {

59
lib/modules/settings/views/settings_view.dart

@ -110,13 +110,55 @@ class SettingsView extends GetView<SettingsController> {
// 裁剪圆形头像
borderRadius: BorderRadius.circular(30.r),
child: controller.userInfo.value.avatar.isEmpty
? Image.network(
'https://images.unsplash.com/photo-1534528741775-53994a69daeb?ixlib=rb-1.2.1&auto=format&fit=crop&w=200&q=80',
fit: BoxFit.cover, // 图片填充方式
?
// Image.network(
// 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?ixlib=rb-1.2.1&auto=format&fit=crop&w=200&q=80',
// fit: BoxFit.cover, // 图片填充方式
// )
Container(
decoration: BoxDecoration(
color: Colors.blue[600],
borderRadius: BorderRadius.circular(30.r),
),
child: Center(
child: Text(
controller.userInfo.value.name.isNotEmpty
? controller.userInfo.value.name[0]
.toUpperCase()
: '?',
style: TextStyle(
color: Colors.white,
fontSize: 24.sp,
fontWeight: FontWeight.bold,
),
),
),
)
: Image.network(
controller.userInfo.value.avatar,
fit: BoxFit.cover, // 图片填充方式
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
// 当图片加载失败时显示用户名首字母
return Container(
decoration: BoxDecoration(
color: Colors.blue[600],
borderRadius: BorderRadius.circular(30.r),
),
child: Center(
child: Text(
controller.userInfo.value.name.isNotEmpty
? controller.userInfo.value.name[0]
.toUpperCase()
: '?',
style: TextStyle(
color: Colors.white,
fontSize: 24.sp,
fontWeight: FontWeight.bold,
),
),
),
);
},
)),
),
)),
@ -328,6 +370,9 @@ class SettingsView extends GetView<SettingsController> {
),
onTap: () {
// 打开备份设置页面
print('---------用户头像-${controller.userInfo.value.avatar}');
print('---------用户名-${controller.userInfo.value.name}');
//print('user${User.instance.value}');
},
isDarkMode: isDarkMode,
),
@ -346,15 +391,15 @@ class SettingsView extends GetView<SettingsController> {
: Colors.grey[200]),
Obx(
() => _buildNavigationSetting(
title: '音色服务',
subtitle: '选择音色语音合成服务',
title: 'timbreService'.tr, // 音色服务
subtitle: 'timbreServiceInfo'.tr, // 选择音色服务
icon: Icons.photo_camera_front_outlined,
iconBgColor: isDarkMode
? Colors.cyan[900]!.withOpacity(0.3)
: Colors.cyan[100]!,
iconColor:
isDarkMode ? Colors.cyan[300]! : Colors.cyan[600]!,
value: controller.timbreName.value,
value: controller.timbreName.value.tr,
valueStyle: TextStyle(
fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.grey[500],

8
lib/modules/timbre/controllers/timbre_controller.dart

@ -51,15 +51,15 @@ class TimbreController extends GetxController {
} else {
dataList.add({
'type': '1',
'name': '默认音色',
'description': '微软云端音色服务',
'name': 'defaultTimbre',
'description': 'microsoftCloudTimbreService',
'isUsable': true
});
_storage.write('timbreList', [
{
'type': '1',
'name': '默认音色',
'description': '微软云端音色服务',
'name': 'defaultTimbre',
'description': 'microsoftCloudTimbreService',
'isUsable': true
}
]);

6
lib/modules/timbre/views/timbre_view.dart

@ -137,7 +137,7 @@ class TimbreView extends GetView<TimbreController> {
const Icon(Icons.keyboard_voice, color: Colors.white),
5.horizontalSpace,
Text(
'声音复刻',
'voiceClone'.tr,
style: TextStyle(color: Colors.white, fontSize: 16.sp),
),
],
@ -180,7 +180,7 @@ class TimbreView extends GetView<TimbreController> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
data['name'],
'${data['name']}'.tr,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w500,
@ -190,7 +190,7 @@ class TimbreView extends GetView<TimbreController> {
),
SizedBox(height: 4.h),
Text(
data['description'],
'${data['description']}'.tr,
style: TextStyle(
fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.black54,

Loading…
Cancel
Save