这是CaiYouHui前端,一个关于flutter的安卓app,前端使用flutter实现

profile_detail_screen.dart 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. import 'package:flutter/material.dart';
  2. import 'package:provider/provider.dart';
  3. import '../../../data/models/user.dart';
  4. import '../../providers/auth_provider.dart';
  5. import '../../providers/user_provider.dart';
  6. import '../../widgets/common/app_button.dart';
  7. import '../../widgets/common/app_text_field.dart';
  8. class ProfileDetailScreen extends StatefulWidget {
  9. const ProfileDetailScreen({super.key});
  10. @override
  11. State<ProfileDetailScreen> createState() => _ProfileDetailScreenState();
  12. }
  13. class _ProfileDetailScreenState extends State<ProfileDetailScreen> {
  14. late User _currentUser;
  15. late TextEditingController _nameController;
  16. late TextEditingController _emailController;
  17. late TextEditingController _phoneController;
  18. final _formKey = GlobalKey<FormState>();
  19. @override
  20. void initState() {
  21. super.initState();
  22. final userProvider = Provider.of<UserProvider>(context, listen: false);
  23. _currentUser = userProvider.user ?? User(id: -1, email: '', username: '', createdAt: DateTime.now());
  24. _nameController = TextEditingController(text: _currentUser.fullName);
  25. _emailController = TextEditingController(text: _currentUser.email);
  26. _phoneController = TextEditingController(text: _currentUser.phone ?? '');
  27. }
  28. @override
  29. void dispose() {
  30. _nameController.dispose();
  31. _emailController.dispose();
  32. _phoneController.dispose();
  33. super.dispose();
  34. }
  35. @override
  36. Widget build(BuildContext context) {
  37. final userProvider = Provider.of<UserProvider>(context);
  38. return Scaffold(
  39. appBar: AppBar(
  40. title: const Text('个人信息'),
  41. actions: [
  42. if (userProvider.isLoading)
  43. const Padding(
  44. padding: EdgeInsets.only(right: 16),
  45. child: Center(
  46. child: SizedBox(
  47. width: 24,
  48. height: 24,
  49. child: CircularProgressIndicator(strokeWidth: 2),
  50. ),
  51. ),
  52. ),
  53. ],
  54. ),
  55. body: SingleChildScrollView(
  56. padding: const EdgeInsets.all(20),
  57. child: Form(
  58. key: _formKey,
  59. child: Column(
  60. children: [
  61. // 头像编辑
  62. GestureDetector(
  63. onTap: () {
  64. // 选择头像
  65. },
  66. child: Stack(
  67. alignment: Alignment.center,
  68. children: [
  69. CircleAvatar(
  70. radius: 60,
  71. backgroundColor: Colors.blue[100],
  72. backgroundImage: _currentUser.avatar != null
  73. ? NetworkImage(_currentUser.avatar!)
  74. : null,
  75. child: _currentUser.avatar == null
  76. ? const Icon(
  77. Icons.person,
  78. size: 80,
  79. color: Colors.blue,
  80. )
  81. : null,
  82. ),
  83. Positioned(
  84. bottom: 0,
  85. right: 0,
  86. child: Container(
  87. padding: const EdgeInsets.all(8),
  88. decoration: BoxDecoration(
  89. color: Colors.blue,
  90. borderRadius: BorderRadius.circular(20),
  91. border: Border.all(
  92. color: Colors.white,
  93. width: 2,
  94. ),
  95. ),
  96. child: const Icon(
  97. Icons.camera_alt,
  98. size: 20,
  99. color: Colors.white,
  100. ),
  101. ),
  102. ),
  103. ],
  104. ),
  105. ),
  106. const SizedBox(height: 20),
  107. TextButton(
  108. onPressed: () {},
  109. child: const Text(
  110. '更换头像',
  111. style: TextStyle(color: Colors.blue),
  112. ),
  113. ),
  114. const SizedBox(height: 40),
  115. // 表单字段
  116. AppTextField(
  117. controller: _nameController,
  118. labelText: '姓名',
  119. hintText: '请输入您的姓名',
  120. validator: (value) {
  121. if (value == null || value.isEmpty) {
  122. return '请输入姓名';
  123. }
  124. return null;
  125. },
  126. ),
  127. const SizedBox(height: 20),
  128. AppTextField(
  129. controller: _emailController,
  130. labelText: '邮箱地址',
  131. hintText: '请输入邮箱',
  132. keyboardType: TextInputType.emailAddress,
  133. enabled: false, // 邮箱通常不可修改
  134. validator: (value) {
  135. if (value == null || value.isEmpty) {
  136. return '请输入邮箱地址';
  137. }
  138. if (!value.contains('@')) {
  139. return '请输入有效的邮箱地址';
  140. }
  141. return null;
  142. },
  143. ),
  144. const SizedBox(height: 20),
  145. AppTextField(
  146. controller: _phoneController,
  147. labelText: '手机号码',
  148. hintText: '请输入手机号码',
  149. keyboardType: TextInputType.phone,
  150. ),
  151. const SizedBox(height: 40),
  152. // 错误提示
  153. if (userProvider.error != null)
  154. Padding(
  155. padding: const EdgeInsets.only(bottom: 16),
  156. child: Text(
  157. userProvider.error!,
  158. style: const TextStyle(color: Colors.red),
  159. ),
  160. ),
  161. // 保存按钮
  162. AppButton(
  163. text: '保存修改',
  164. isLoading: userProvider.isLoading, // 传入加载状态
  165. enabled: !userProvider.isLoading, // 加载时禁用按钮
  166. onPressed: () async {
  167. if (_formKey.currentState!.validate()) {
  168. final updatedUser = _currentUser.copyWith(
  169. fullName: _nameController.text.trim(),
  170. // phone: _phoneController.text.trim().isNotEmpty
  171. // ? _phoneController.text.trim()
  172. // : null,
  173. );
  174. await userProvider.updateUserProfile(updatedUser);
  175. if (userProvider.error == null && mounted) {
  176. // ✅ 同时更新 AuthProvider
  177. final authProvider = Provider.of<AuthProvider>(context, listen: false);
  178. authProvider.updateUser(userProvider.user); // 使用接口返回的完整用户数据
  179. ScaffoldMessenger.of(context).showSnackBar(
  180. const SnackBar(
  181. content: Text('个人信息已更新'),
  182. duration: Duration(seconds: 2),
  183. ),
  184. );
  185. // ✅ 直接返回 - 数据已同步更新
  186. Navigator.of(context).pop();
  187. }
  188. }
  189. },
  190. ),
  191. const SizedBox(height: 20),
  192. ],
  193. ),
  194. ),
  195. ),
  196. );
  197. }
  198. }