Files
ai_english/client/lib/features/profile/screens/profile_edit_screen.dart
2025-11-17 13:39:05 +08:00

708 lines
22 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:image_picker/image_picker.dart';
import '../../../core/theme/app_colors.dart';
import '../../../shared/widgets/custom_app_bar.dart';
import '../../../core/widgets/custom_text_field.dart';
import '../../../core/widgets/custom_button.dart';
import '../../../core/providers/app_state_provider.dart';
import '../../../shared/models/user_model.dart';
/// 个人信息编辑页面
class ProfileEditScreen extends ConsumerStatefulWidget {
const ProfileEditScreen({super.key});
@override
ConsumerState<ProfileEditScreen> createState() => _ProfileEditScreenState();
}
class _ProfileEditScreenState extends ConsumerState<ProfileEditScreen> {
final _formKey = GlobalKey<FormState>();
final _usernameController = TextEditingController();
final _emailController = TextEditingController();
final _phoneController = TextEditingController();
final _bioController = TextEditingController();
final _realNameController = TextEditingController();
final _locationController = TextEditingController();
final _occupationController = TextEditingController();
String? _selectedGender;
DateTime? _selectedBirthday;
String? _selectedEducation;
String? _selectedEnglishLevel;
String? _selectedTargetLevel;
String? _selectedLearningGoal;
List<String> _interests = [];
String? _avatarPath;
bool _isLoading = false;
final List<String> _genderOptions = ['male', 'female', 'other'];
final List<String> _educationOptions = ['高中', '专科', '本科', '硕士', '博士'];
final List<String> _englishLevelOptions = ['beginner', 'elementary', 'intermediate', 'upperIntermediate', 'advanced'];
final List<String> _learningGoalOptions = ['dailyCommunication', 'businessEnglish', 'academicEnglish', 'examPreparation', 'travelEnglish'];
final List<String> _interestOptions = ['编程', '英语学习', '阅读', '音乐', '电影', '旅行', '运动', '摄影', '绘画', '游戏'];
@override
void initState() {
super.initState();
_loadUserData();
}
@override
void dispose() {
_usernameController.dispose();
_emailController.dispose();
_phoneController.dispose();
_bioController.dispose();
_realNameController.dispose();
_locationController.dispose();
_occupationController.dispose();
super.dispose();
}
void _loadUserData() {
final authProviderNotifier = ref.read(authProvider);
final user = authProviderNotifier.user;
if (user != null) {
_usernameController.text = user.username ?? '';
_emailController.text = user.email ?? '';
_phoneController.text = user.phone ?? '';
_bioController.text = user.bio ?? '';
_selectedGender = user.gender;
_selectedBirthday = user.birthday;
_selectedEnglishLevel = user.learningLevel;
_avatarPath = user.avatar;
}
}
Future<void> _pickImage() async {
final ImagePicker picker = ImagePicker();
final XFile? image = await picker.pickImage(
source: ImageSource.gallery,
maxWidth: 512,
maxHeight: 512,
imageQuality: 80,
);
if (image != null) {
setState(() {
_avatarPath = image.path;
});
}
}
Future<void> _selectBirthday() async {
final DateTime? picked = await showDatePicker(
context: context,
initialDate: _selectedBirthday ?? DateTime(1990),
firstDate: DateTime(1900),
lastDate: DateTime.now(),
builder: (context, child) {
return Theme(
data: Theme.of(context).copyWith(
colorScheme: ColorScheme.light(
primary: AppColors.primary,
onPrimary: AppColors.onPrimary,
surface: AppColors.surface,
onSurface: AppColors.onSurface,
),
),
child: child!,
);
},
);
if (picked != null) {
setState(() {
_selectedBirthday = picked;
});
}
}
void _showInterestsDialog() {
showDialog(
context: context,
builder: (context) {
List<String> tempInterests = List.from(_interests);
return StatefulBuilder(
builder: (context, setDialogState) {
return AlertDialog(
title: const Text('选择兴趣爱好'),
content: SizedBox(
width: double.maxFinite,
child: ListView.builder(
shrinkWrap: true,
itemCount: _interestOptions.length,
itemBuilder: (context, index) {
final interest = _interestOptions[index];
final isSelected = tempInterests.contains(interest);
return CheckboxListTile(
title: Text(interest),
value: isSelected,
onChanged: (bool? value) {
setDialogState(() {
if (value == true) {
tempInterests.add(interest);
} else {
tempInterests.remove(interest);
}
});
},
activeColor: AppColors.primary,
);
},
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
TextButton(
onPressed: () {
setState(() {
_interests = tempInterests;
});
Navigator.pop(context);
},
child: const Text('确定'),
),
],
);
},
);
},
);
}
Future<void> _saveProfile() async {
if (!_formKey.currentState!.validate()) return;
setState(() {
_isLoading = true;
});
try {
final authProviderNotifier = ref.read(authProvider);
final success = await authProviderNotifier.updateProfile(
nickname: _usernameController.text.trim(),
avatar: _avatarPath,
phone: _phoneController.text.trim(),
birthday: _selectedBirthday,
gender: _selectedGender,
bio: _bioController.text.trim(),
learningLevel: _selectedEnglishLevel,
targetLanguage: 'english',
nativeLanguage: 'chinese',
dailyGoal: 30,
);
if (success) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('个人信息更新成功'),
backgroundColor: Colors.green,
),
);
Navigator.pop(context);
}
} else {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('更新失败,请重试'),
backgroundColor: Colors.red,
),
);
}
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('更新失败: $e'),
backgroundColor: Colors.red,
),
);
}
} finally {
if (mounted) {
setState(() {
_isLoading = false;
});
}
}
}
String _getGenderDisplayName(String gender) {
switch (gender) {
case 'male':
return '';
case 'female':
return '';
case 'other':
return '其他';
default:
return gender;
}
}
String _getEnglishLevelDisplayName(String level) {
switch (level) {
case 'beginner':
return '初学者';
case 'elementary':
return '基础';
case 'intermediate':
return '中级';
case 'upperIntermediate':
return '中高级';
case 'advanced':
return '高级';
default:
return level;
}
}
String _getLearningGoalDisplayName(String goal) {
switch (goal) {
case 'dailyCommunication':
return '日常交流';
case 'businessEnglish':
return '商务英语';
case 'academicEnglish':
return '学术英语';
case 'examPreparation':
return '考试准备';
case 'travelEnglish':
return '旅游英语';
default:
return goal;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.surface,
appBar: CustomAppBar(
title: '编辑个人信息',
actions: [
TextButton(
onPressed: _isLoading ? null : _saveProfile,
child: Text(
'保存',
style: TextStyle(
color: _isLoading ? AppColors.onSurfaceVariant : AppColors.primary,
fontWeight: FontWeight.w600,
),
),
),
],
),
body: Stack(
children: [
Form(
key: _formKey,
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 头像部分
Center(
child: GestureDetector(
onTap: _pickImage,
child: Container(
width: 100,
height: 100,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: AppColors.primary,
width: 2,
),
),
child: CircleAvatar(
radius: 48,
backgroundColor: AppColors.surface,
backgroundImage: _avatarPath != null
? (_avatarPath!.startsWith('http')
? NetworkImage(_avatarPath!)
: FileImage(XFile(_avatarPath!).path as dynamic))
: null,
child: _avatarPath == null
? Icon(
Icons.camera_alt,
size: 30,
color: AppColors.primary,
)
: null,
),
),
),
),
const SizedBox(height: 8),
Center(
child: Text(
'点击更换头像',
style: TextStyle(
fontSize: 12,
color: AppColors.onSurfaceVariant,
),
),
),
const SizedBox(height: 32),
// 基本信息
_buildSectionTitle('基本信息'),
const SizedBox(height: 16),
CustomTextField(
controller: _usernameController,
labelText: '用户名',
hintText: '请输入用户名',
validator: (value) {
if (value == null || value.trim().isEmpty) {
return '请输入用户名';
}
return null;
},
),
const SizedBox(height: 16),
CustomTextField(
controller: _realNameController,
labelText: '真实姓名',
hintText: '请输入真实姓名',
),
const SizedBox(height: 16),
CustomTextField(
controller: _emailController,
labelText: '邮箱',
hintText: '请输入邮箱地址',
keyboardType: TextInputType.emailAddress,
enabled: false, // 邮箱通常不允许修改
),
const SizedBox(height: 16),
CustomTextField(
controller: _phoneController,
labelText: '手机号',
hintText: '请输入手机号',
keyboardType: TextInputType.phone,
),
const SizedBox(height: 16),
// 性别选择
_buildDropdownField(
label: '性别',
value: _selectedGender,
items: _genderOptions,
displayNameBuilder: _getGenderDisplayName,
onChanged: (value) {
setState(() {
_selectedGender = value;
});
},
),
const SizedBox(height: 16),
// 生日选择
_buildDateField(
label: '生日',
value: _selectedBirthday,
onTap: _selectBirthday,
),
const SizedBox(height: 16),
CustomTextField(
controller: _locationController,
labelText: '所在地',
hintText: '请输入所在地',
),
const SizedBox(height: 16),
CustomTextField(
controller: _occupationController,
labelText: '职业',
hintText: '请输入职业',
),
const SizedBox(height: 16),
// 教育背景
_buildDropdownField(
label: '教育背景',
value: _selectedEducation,
items: _educationOptions,
displayNameBuilder: (value) => value,
onChanged: (value) {
setState(() {
_selectedEducation = value;
});
},
),
const SizedBox(height: 32),
// 学习信息
_buildSectionTitle('学习信息'),
const SizedBox(height: 16),
// 当前英语水平
_buildDropdownField(
label: '当前英语水平',
value: _selectedEnglishLevel,
items: _englishLevelOptions,
displayNameBuilder: _getEnglishLevelDisplayName,
onChanged: (value) {
setState(() {
_selectedEnglishLevel = value;
});
},
),
const SizedBox(height: 16),
// 目标英语水平
_buildDropdownField(
label: '目标英语水平',
value: _selectedTargetLevel,
items: _englishLevelOptions,
displayNameBuilder: _getEnglishLevelDisplayName,
onChanged: (value) {
setState(() {
_selectedTargetLevel = value;
});
},
),
const SizedBox(height: 16),
// 学习目标
_buildDropdownField(
label: '学习目标',
value: _selectedLearningGoal,
items: _learningGoalOptions,
displayNameBuilder: _getLearningGoalDisplayName,
onChanged: (value) {
setState(() {
_selectedLearningGoal = value;
});
},
),
const SizedBox(height: 16),
// 兴趣爱好
_buildInterestsField(),
const SizedBox(height: 32),
// 个人简介
_buildSectionTitle('个人简介'),
const SizedBox(height: 16),
CustomTextField(
controller: _bioController,
labelText: '个人简介',
hintText: '介绍一下自己吧...',
maxLines: 4,
),
const SizedBox(height: 32),
// 保存按钮
CustomButton(
text: '保存修改',
onPressed: _saveProfile,
isLoading: _isLoading,
),
const SizedBox(height: 32),
],
),
),
),
if (_isLoading)
Container(
color: Colors.black.withOpacity(0.3),
child: const Center(
child: CircularProgressIndicator(),
),
),
],
),
);
}
Widget _buildSectionTitle(String title) {
return Text(
title,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: AppColors.onSurface,
),
);
}
Widget _buildDropdownField({
required String label,
required String? value,
required List<String> items,
required String Function(String) displayNameBuilder,
required void Function(String?) onChanged,
}) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.onSurface,
),
),
const SizedBox(height: 8),
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(
border: Border.all(color: AppColors.outline),
borderRadius: BorderRadius.circular(8),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: value,
hint: Text('请选择$label'),
isExpanded: true,
items: items.map((item) {
return DropdownMenuItem<String>(
value: item,
child: Text(displayNameBuilder(item)),
);
}).toList(),
onChanged: onChanged,
),
),
),
],
);
}
Widget _buildDateField({
required String label,
required DateTime? value,
required VoidCallback onTap,
}) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.onSurface,
),
),
const SizedBox(height: 8),
GestureDetector(
onTap: onTap,
child: Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 16),
decoration: BoxDecoration(
border: Border.all(color: AppColors.outline),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
value != null
? '${value.year}-${value.month.toString().padLeft(2, '0')}-${value.day.toString().padLeft(2, '0')}'
: '请选择$label',
style: TextStyle(
fontSize: 16,
color: value != null ? AppColors.onSurface : AppColors.onSurfaceVariant,
),
),
Icon(
Icons.calendar_today,
color: AppColors.onSurfaceVariant,
size: 20,
),
],
),
),
),
],
);
}
Widget _buildInterestsField() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'兴趣爱好',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.onSurface,
),
),
const SizedBox(height: 8),
GestureDetector(
onTap: _showInterestsDialog,
child: Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 16),
decoration: BoxDecoration(
border: Border.all(color: AppColors.outline),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: _interests.isEmpty
? Text(
'请选择兴趣爱好',
style: TextStyle(
fontSize: 16,
color: AppColors.onSurfaceVariant,
),
)
: Wrap(
spacing: 8,
runSpacing: 4,
children: _interests.map((interest) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: AppColors.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Text(
interest,
style: TextStyle(
fontSize: 12,
color: AppColors.primary,
),
),
);
}).toList(),
),
),
Icon(
Icons.arrow_forward_ios,
color: AppColors.onSurfaceVariant,
size: 16,
),
],
),
),
),
],
);
}
}