Browse Source

增加修改头像和用户名功能

weicu
lxm 2 years ago
parent
commit
1b83fdfa68
  1. 39
      lib/data/models/user_model.dart
  2. 133
      lib/modules/profile/controllers/profile_controller.dart
  3. 104
      lib/modules/profile/views/profile_view.dart
  4. 62
      lib/modules/settings/controllers/settings_controller.dart
  5. 57
      lib/modules/settings/views/settings_view.dart
  6. 5
      lib/routes/app_pages.dart
  7. 1
      lib/routes/app_routes.dart

39
lib/data/models/user_model.dart

@ -0,0 +1,39 @@
class UserModel {
String name; // 用户名
String avatar; // 头像
String id;
//final String deviceName;
String membershipLevel; // 会员等级
UserModel({
required this.name,
required this.avatar,
required this.id,
//required this.deviceName,
required this.membershipLevel,
});
Map<String, dynamic> toJson() => {
'name': name,
'avatar': avatar,
'id': id,
//'deviceName': deviceName,
'membershipLevel': membershipLevel,
};
factory UserModel.fromJson(Map<String, dynamic> json) => UserModel(
name: json['name'] ?? '未设置昵称',
avatar: json['avatar'] ?? '',
id: json['id'] ?? '',
//deviceName: json['deviceName'] ?? '',
membershipLevel: json['membershipLevel'] ?? '普通会员',
);
factory UserModel.defaultUser() => UserModel(
name: '未设置昵称',
avatar: '',
id: 'deepvoice_${DateTime.now().millisecondsSinceEpoch.toString()}',
//deviceName: '',
membershipLevel: '普通会员',
);
}

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

@ -1,40 +1,47 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart'; import 'package:get_storage/get_storage.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import '../../../data/models/user_model.dart';
class ProfileController extends GetxController { class ProfileController extends GetxController {
static ProfileController get to => Get.find(); static ProfileController get to => Get.find();
final _userName = ''.obs; // final _userName = ''.obs;
final _email = ''.obs; // final _email = ''.obs;
final _avatar = ''.obs; // final _avatar = ''.obs;
final _isEditing = false.obs; // final _isEditing = false.obs;
final _storage = GetStorage(); // final _storage = GetStorage();
//用户信息
static const String userKey='user_info';
final _storage=GetStorage();
final ImagePicker _picker=ImagePicker();
final Rx<UserModel> userInfo=UserModel.defaultUser().obs;
// 使用响应式变量 // 使用响应式变量
final currentLanguage = 'zh'.obs; final currentLanguage = 'zh'.obs;
String get userName => _userName.value; // String get userName => _userName.value;
String get email => _email.value; // String get email => _email.value;
String get avatar => _avatar.value; // String get avatar => _avatar.value;
bool get isEditing => _isEditing.value; // bool get isEditing => _isEditing.value;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
loadUserData(); loadUserInfo();
loadLanguagePreference(); loadLanguagePreference();
} }
void loadUserData() { // void loadUserData() {
try { // try {
_userName.value = _storage.read('userName') ?? 'User Name'; // _userName.value = _storage.read('userName') ?? 'User Name';
_email.value = _storage.read('userEmail') ?? 'user@example.com'; // _email.value = _storage.read('userEmail') ?? 'user@example.com';
_avatar.value = _storage.read('userAvatar') ?? ''; // _avatar.value = _storage.read('userAvatar') ?? '';
} catch (e) { // } catch (e) {
Get.snackbar('Error', 'Failed to load user data'); // Get.snackbar('Error', 'Failed to load user data');
} // }
} // }
// 加载用户语言偏好设置 // 加载用户语言偏好设置
void loadLanguagePreference() { void loadLanguagePreference() {
@ -72,31 +79,71 @@ class ProfileController extends GetxController {
}); });
} }
Future<void> updateUserData({ // Future<void> updateUserData({
String? userName, // String? userName,
String? email, // String? email,
String? avatar, // String? avatar,
}) async { // }) async {
try { // try {
if (userName != null) { // if (userName != null) {
await _storage.write('userName', userName); // await _storage.write('userName', userName);
_userName.value = userName; // _userName.value = userName;
} // }
if (email != null) { // if (email != null) {
await _storage.write('userEmail', email); // await _storage.write('userEmail', email);
_email.value = email; // _email.value = email;
} // }
if (avatar != null) { // if (avatar != null) {
await _storage.write('userAvatar', avatar); // await _storage.write('userAvatar', avatar);
_avatar.value = avatar; // _avatar.value = avatar;
} // }
Get.snackbar('Success', 'Profile updated successfully'); // Get.snackbar('Success', 'Profile updated successfully');
} catch (e) { // } catch (e) {
Get.snackbar('Error', 'Failed to update profile'); // Get.snackbar('Error', 'Failed to update profile');
// }
// }
// void toggleEditing() {
// _isEditing.value = !_isEditing.value;
// }
void loadUserInfo() {
final userJson = _storage.read(userKey);
if (userJson != null) {
userInfo.value = UserModel.fromJson(userJson);
} }
} }
void toggleEditing() { Future<void> saveUserInfo() async {
_isEditing.value = !_isEditing.value; 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> updateUserName(String name) async {
if (name.trim().isNotEmpty) {
userInfo.update((user) {
if (user != null) {
user.name = name.trim();
}
});
await saveUserInfo();
}
} }
} }

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

@ -4,7 +4,9 @@ import '../controllers/profile_controller.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../../core/widgets/common_bottom_nav.dart'; import '../../../core/widgets/common_bottom_nav.dart';
import '../../../routes/app_routes.dart'; import '../../../routes/app_routes.dart';
import 'dart:io';
// ProfileView: 用户设置页面
class ProfileView extends GetView<ProfileController> { class ProfileView extends GetView<ProfileController> {
const ProfileView({Key? key}) : super(key: key); const ProfileView({Key? key}) : super(key: key);
@ -132,4 +134,104 @@ class ProfileView extends GetView<ProfileController> {
bottomNavigationBar: const CommonBottomNav(currentIndex: 2), bottomNavigationBar: const CommonBottomNav(currentIndex: 2),
); );
} }
} }
// EditProfileView: 编辑个人资料页面
class EditProfileView extends GetView<ProfileController> {
const EditProfileView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
final nameController = TextEditingController(text: controller.userInfo.value.name);
return Scaffold(
appBar: AppBar(
title: const Text('个人资料'),
actions: [
TextButton(
onPressed: () {
// 保存用户名
controller.updateUserName(nameController.text);
// 返回上一级页面
Get.back();
},
child: Text(
'保存',
style: TextStyle(
fontSize: 16.sp,
color: isDarkMode ? Colors.blue[300] : Colors.blue,
fontWeight: FontWeight.w500,
),
),
),
],
),
body: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
children: [
// 头像编辑
Center(
child: GestureDetector(
onTap: () => controller.updateUserAvatar(),
child: Obx(() => CircleAvatar(
radius: 50.r,
backgroundImage: controller.userInfo.value.avatar.isNotEmpty
? FileImage(File(controller.userInfo.value.avatar))
: null,
child: controller.userInfo.value.avatar.isEmpty
? Icon(Icons.person, size: 50.r)
: null,
// onBackgroundImageError: hasAvatar
// ? (_, __) {}
// : null,
)),
),
),
SizedBox(height: 20.h),
// 昵称编辑 - 使用外部定义的controller而不是每次重建
TextField(
controller: nameController,
decoration: const InputDecoration(
labelText: '昵称',
border: OutlineInputBorder(),
),
),
// ... 其他编辑项 ...
SizedBox(height: 30.h),
// 添加一个大的保存按钮
SizedBox(
width: double.infinity,
height: 50.h,
child: ElevatedButton(
onPressed: () {
// 保存用户名
controller.updateUserName(nameController.text);
// 返回上一级页面
Get.back();
},
style: ElevatedButton.styleFrom(
backgroundColor: isDarkMode ? Colors.blue[700] : Colors.blue,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.r),
),
),
child: Text(
'保存',
style: TextStyle(
fontSize: 16.sp,
color: Colors.white,
fontWeight: FontWeight.w500,
),
),
),
),
],
),
),
);
}
}

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

@ -1,7 +1,10 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import '../../../core/themes/theme_service.dart'; import '../../../core/themes/theme_service.dart';
import '../../../data/services/speech_factory.dart'; import '../../../data/services/speech_factory.dart';
import 'package:get_storage/get_storage.dart';
import '../../../data/models/user_model.dart';
class SettingsController extends GetxController { class SettingsController extends GetxController {
// 主题服务 // 主题服务
@ -21,6 +24,12 @@ class SettingsController extends GetxController {
{'type': SpeechServiceType.azure, 'name': 'Azure 语音服务', 'description': '微软云端语音服务,支持高质量语音合成和识别'}, {'type': SpeechServiceType.azure, 'name': 'Azure 语音服务', 'description': '微软云端语音服务,支持高质量语音合成和识别'},
{'type': SpeechServiceType.flutter, 'name': 'Flutter 本地语音服务', 'description': '设备本地语音服务,无需联网即可使用'}, {'type': SpeechServiceType.flutter, 'name': 'Flutter 本地语音服务', 'description': '设备本地语音服务,无需联网即可使用'},
]; ];
//用户信息
static const String userKey='user_info';
final _storage=GetStorage();
final ImagePicker _picker=ImagePicker();
final Rx<UserModel> userInfo=UserModel.defaultUser().obs;
@override @override
void onInit() { void onInit() {
@ -34,7 +43,60 @@ class SettingsController extends GetxController {
// 获取当前语音服务类型 // 获取当前语音服务类型
_loadCurrentSpeechServiceType(); _loadCurrentSpeechServiceType();
//获取用户信息
loadUserInfo();
}
void loadUserInfo() {
final userJson = _storage.read(userKey);
if (userJson != null) {
userInfo.value = UserModel.fromJson(userJson);
}
}
Future<void> saveUserInfo() async {
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> updateUserName(String name) async {
if (name.trim().isNotEmpty) {
userInfo.update((user) {
if (user != null) {
user.name = name.trim();
}
});
await saveUserInfo();
}
}
// Future<void> updateDeviceName(String deviceName) async {
// userInfo.update((user) {
// if (user != null) {
// user.deviceName = deviceName;
// }
// });
// await saveUserInfo();
// }
// }
// 加载当前语音服务类型 // 加载当前语音服务类型
void _loadCurrentSpeechServiceType() { void _loadCurrentSpeechServiceType() {

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

@ -5,6 +5,7 @@ import 'dart:ui';
import '../controllers/settings_controller.dart'; import '../controllers/settings_controller.dart';
import '../../../core/widgets/common_bottom_nav.dart'; import '../../../core/widgets/common_bottom_nav.dart';
import '../../../routes/app_routes.dart'; import '../../../routes/app_routes.dart';
import 'dart:io';
class SettingsView extends GetView<SettingsController> { class SettingsView extends GetView<SettingsController> {
const SettingsView({Key? key}) : super(key: key); const SettingsView({Key? key}) : super(key: key);
@ -74,13 +75,15 @@ class SettingsView extends GetView<SettingsController> {
), ),
], ],
), ),
child: ClipRRect( child: ClipRRect(//裁剪圆角矩形
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
child: BackdropFilter( child: BackdropFilter(// 背景滤镜(实现毛玻璃效果)
filter: isDarkMode ? ImageFilter.blur(sigmaX: 10, sigmaY: 10) : ImageFilter.blur(sigmaX: 0, sigmaY: 0), filter: isDarkMode ? ImageFilter.blur(sigmaX: 10, sigmaY: 10) : ImageFilter.blur(sigmaX: 0, sigmaY: 0),
child: Row( child: Row(
children: [ children: [
Container( Obx(()=>GestureDetector(
onTap: ()=>controller.updateUserAvatar(),
child:Container(//用户头像部分
width: 60.w, width: 60.w,
height: 60.w, height: 60.w,
decoration: BoxDecoration( decoration: BoxDecoration(
@ -93,21 +96,27 @@ class SettingsView extends GetView<SettingsController> {
), ),
], ],
), ),
child: ClipRRect( child: ClipRRect(// 裁剪圆形头像
borderRadius: BorderRadius.circular(30.r), borderRadius: BorderRadius.circular(30.r),
child: Image.network( 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', 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?ixlib=rb-1.2.1&auto=format&fit=crop&w=200&q=80',
fit: BoxFit.cover, fit: BoxFit.cover,// 图片填充方式
), )
:Image.file(
File(controller.userInfo.value.avatar),
fit: BoxFit.cover,// 图片填充方式
)
), ),
), ),
)),
SizedBox(width: 16.w), SizedBox(width: 16.w),
Expanded( Expanded(// 占据剩余空间
child: Column( child: Obx(()=>Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,// 左对齐
children: [ children: [
Text( Text(//用户名
'林美珍', controller.userInfo.value.name,
style: TextStyle( style: TextStyle(
fontSize: 18.sp, fontSize: 18.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -118,14 +127,14 @@ class SettingsView extends GetView<SettingsController> {
SizedBox(height: 4.h), SizedBox(height: 4.h),
Row( Row(
children: [ children: [
Container( Container(// 会员等级
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 2.h), padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 2.h),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.blue[600], color: Colors.blue[600],
borderRadius: BorderRadius.circular(10.r), borderRadius: BorderRadius.circular(10.r),
), ),
child: Text( child: Text(
'高级会员', controller.userInfo.value.membershipLevel,
style: TextStyle( style: TextStyle(
fontSize: 10.sp, fontSize: 10.sp,
color: Colors.white, color: Colors.white,
@ -147,7 +156,7 @@ class SettingsView extends GetView<SettingsController> {
), ),
SizedBox(height: 4.h), SizedBox(height: 4.h),
Text( Text(
'账号ID: deepvoice_834291', controller.userInfo.value.id,
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.black54, color: isDarkMode ? Colors.white70 : Colors.black54,
@ -155,8 +164,8 @@ class SettingsView extends GetView<SettingsController> {
), ),
], ],
), ),
), ),),
Container( Container(// 编辑按钮
width: 32.w, width: 32.w,
height: 32.w, height: 32.w,
decoration: BoxDecoration( decoration: BoxDecoration(
@ -172,11 +181,15 @@ class SettingsView extends GetView<SettingsController> {
), ),
], ],
), ),
child: Icon( child: InkWell(
Icons.edit, onTap: () => Get.toNamed(Routes.editProfile),
size: 16.w, child: Icon(
color: isDarkMode ? Colors.white70 : Colors.grey[600], Icons.edit,
), size: 16.w,
color: isDarkMode ? Colors.white70 : Colors.grey[600],
),
)
), ),
], ],
), ),

5
lib/routes/app_pages.dart

@ -64,6 +64,11 @@ abstract class AppPages {
page: () => const ProfileView(), page: () => const ProfileView(),
binding: ProfileBinding(), binding: ProfileBinding(),
), ),
GetPage(
name: Routes.editProfile,
page: () => const EditProfileView(),
binding: ProfileBinding(),
),
GetPage( GetPage(
name: Routes.translation, name: Routes.translation,

1
lib/routes/app_routes.dart

@ -18,4 +18,5 @@ abstract class Routes {
static const translationHistory = '/translation_history'; static const translationHistory = '/translation_history';
static const FLUTTER_TTS_TEST = '/flutter_tts_test'; static const FLUTTER_TTS_TEST = '/flutter_tts_test';
static const FLUTTER_ASR_TEST = '/flutter_asr_test'; static const FLUTTER_ASR_TEST = '/flutter_asr_test';
static const editProfile='/edit_profile';
} }
Loading…
Cancel
Save