import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:image_picker/image_picker.dart'; import '../../core/network/api_exception.dart'; import '../../core/theme/app_theme.dart'; import '../../providers/auth_provider.dart'; import '../../services/user_service.dart'; import '../feed/widgets/feed_overlays.dart'; /// 编辑资料(《03-接口文档》4.2 / 4.3): /// 头像点击即选图上传(jpg/png,≤5MB);昵称/简介/性别点「保存」提交。 class ProfileEditScreen extends ConsumerStatefulWidget { const ProfileEditScreen({super.key}); @override ConsumerState createState() => _ProfileEditScreenState(); } class _ProfileEditScreenState extends ConsumerState { late final TextEditingController _nicknameCtrl; late final TextEditingController _bioCtrl; late String _avatarUrl; late int _gender; bool _uploadingAvatar = false; bool _saving = false; final _picker = ImagePicker(); @override void initState() { super.initState(); final user = ref.read(authProvider).user; _nicknameCtrl = TextEditingController(text: user?.nickname ?? ''); _bioCtrl = TextEditingController(text: user?.bio ?? ''); _avatarUrl = user?.avatarUrl ?? ''; _gender = user?.gender ?? 0; } @override void dispose() { _nicknameCtrl.dispose(); _bioCtrl.dispose(); super.dispose(); } void _toast(String message) { if (mounted) showTopToast(context, message); } /// 选头像 → 立即上传(上传成功即生效,无需再点保存) Future _pickAvatar() async { if (_uploadingAvatar) return; final picked = await _picker.pickImage( source: ImageSource.gallery, maxWidth: 1024, maxHeight: 1024, ); if (picked == null) return; setState(() => _uploadingAvatar = true); try { final url = await UserService.uploadAvatar( filePath: picked.path, fileName: picked.name, ); if (!mounted) return; setState(() => _avatarUrl = url); // 拉取最新用户信息同步我的页资料卡 ref.read(authProvider.notifier).updateUser(await UserService.fetchMe()); _toast('头像已更新'); } on ApiException catch (e) { _toast(e.message); } catch (_) { _toast('网络异常,请稍后重试'); } finally { if (mounted) setState(() => _uploadingAvatar = false); } } Future _save() async { final nickname = _nicknameCtrl.text.trim(); if (nickname.isEmpty) { _toast('请填写昵称'); return; } setState(() => _saving = true); try { final updated = await UserService.updateMe( nickname: nickname, bio: _bioCtrl.text.trim(), gender: _gender, ); ref.read(authProvider.notifier).updateUser(updated); _toast('已保存'); if (mounted) Navigator.of(context).maybePop(); } on ApiException catch (e) { _toast(e.message); } catch (_) { _toast('网络异常,请稍后重试'); } finally { if (mounted) setState(() => _saving = false); } } @override Widget build(BuildContext context) { // 未登录兜底(正常由入口守卫拦截) if (!ref.watch(authProvider).isLoggedIn) { return Scaffold( appBar: AppBar(title: const Text('编辑资料')), body: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.person_outline, size: 64, color: AppColors.textWeak), const SizedBox(height: 12), const Text('登录后编辑资料', style: TextStyle(color: AppColors.textSecondary)), const SizedBox(height: 16), ElevatedButton( onPressed: () => context.push('/login'), style: ElevatedButton.styleFrom(minimumSize: const Size(160, 44)), child: const Text('去登录 / 注册'), ), ], ), ), ); } final busy = _saving || _uploadingAvatar; return Scaffold( appBar: AppBar(title: const Text('编辑资料')), body: ListView( padding: const EdgeInsets.all(16), children: [ // 头像(点击即选图上传) Center( child: GestureDetector( onTap: _pickAvatar, child: Stack( alignment: Alignment.center, children: [ CircleAvatar( radius: 44, backgroundColor: AppColors.surface, backgroundImage: _avatarUrl.isNotEmpty ? CachedNetworkImageProvider(_avatarUrl) : null, child: _avatarUrl.isEmpty ? const Icon(Icons.person_outline, size: 40, color: AppColors.textWeak) : null, ), if (_uploadingAvatar) const SizedBox( width: 88, height: 88, child: CircularProgressIndicator( color: AppColors.primary, strokeWidth: 2), ), ], ), ), ), const SizedBox(height: 8), const Center( child: Text('点击更换头像(jpg/png,≤5MB)', style: TextStyle(color: AppColors.textWeak, fontSize: 12)), ), const SizedBox(height: 24), TextField( controller: _nicknameCtrl, enabled: !busy, maxLength: 20, decoration: const InputDecoration(labelText: '昵称'), ), const SizedBox(height: 8), TextField( controller: _bioCtrl, enabled: !busy, maxLength: 200, maxLines: 3, decoration: const InputDecoration(labelText: '简介'), ), const SizedBox(height: 8), const Text('性别', style: TextStyle(color: AppColors.textSecondary)), const SizedBox(height: 8), Wrap( spacing: 8, children: [ for (final option in const [ (0, '不公开'), (1, '男'), (2, '女'), ]) ChoiceChip( label: Text(option.$2), selected: _gender == option.$1, onSelected: busy ? null : (_) => setState(() => _gender = option.$1), ), ], ), const SizedBox(height: 24), ElevatedButton( onPressed: busy ? null : _save, style: ElevatedButton.styleFrom(minimumSize: const Size(0, 48)), child: const Text('保存'), ), ], ), ); } }