170 lines
4.9 KiB
Dart
170 lines
4.9 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import 'package:arcx/app/app_theme.dart';
|
|
import 'package:arcx/core/constants/app_defaults.dart';
|
|
import 'package:arcx/data/local/models/models.dart';
|
|
import 'package:arcx/features/profile/application/profile_controller.dart';
|
|
|
|
class ProfileEditDialog extends ConsumerStatefulWidget {
|
|
const ProfileEditDialog({super.key});
|
|
|
|
@override
|
|
ConsumerState<ProfileEditDialog> createState() => _ProfileEditDialogState();
|
|
}
|
|
|
|
class _ProfileEditDialogState extends ConsumerState<ProfileEditDialog> {
|
|
late final TextEditingController _controller;
|
|
bool _saving = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
final profile = ref.read(profileProvider).value;
|
|
_controller = TextEditingController(text: profile?.nickname ?? '');
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_controller.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final profile = ref.watch(profileProvider).value;
|
|
return AlertDialog(
|
|
title: const Text('编辑个人资料'),
|
|
content: SingleChildScrollView(
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
GestureDetector(
|
|
onTap: _saving ? null : () => _pickAvatar(context),
|
|
child: _AvatarPreview(profile: profile),
|
|
),
|
|
if (profile?.avatarSource == AvatarSource.localFile)
|
|
TextButton.icon(
|
|
onPressed: _saving ? null : _resetAvatar,
|
|
icon: const Icon(Icons.refresh, size: 18),
|
|
label: const Text('恢复默认头像'),
|
|
),
|
|
const SizedBox(height: 8),
|
|
TextField(
|
|
controller: _controller,
|
|
maxLength: 12,
|
|
decoration: const InputDecoration(
|
|
labelText: '昵称',
|
|
hintText: '1~12 个字符',
|
|
counterText: '',
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: _saving ? null : () => Navigator.pop(context),
|
|
child: const Text('取消'),
|
|
),
|
|
FilledButton(
|
|
onPressed: _saving ? null : _save,
|
|
child: const Text('保存'),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Future<void> _pickAvatar(BuildContext context) async {
|
|
final choice = await showModalBottomSheet<AvatarAction>(
|
|
context: context,
|
|
builder: (_) => SafeArea(
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
ListTile(
|
|
leading: const Icon(Icons.photo_outlined),
|
|
title: const Text('从相册选择'),
|
|
onTap: () => Navigator.pop(context, AvatarAction.gallery),
|
|
),
|
|
ListTile(
|
|
leading: const Icon(Icons.camera_alt_outlined),
|
|
title: const Text('拍摄照片'),
|
|
onTap: () => Navigator.pop(context, AvatarAction.camera),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
if (choice == null) return;
|
|
setState(() => _saving = true);
|
|
await ref
|
|
.read(profileProvider.notifier)
|
|
.updateAvatar(fromCamera: choice == AvatarAction.camera);
|
|
if (mounted) setState(() => _saving = false);
|
|
}
|
|
|
|
Future<void> _resetAvatar() async {
|
|
setState(() => _saving = true);
|
|
await ref.read(profileProvider.notifier).resetAvatar();
|
|
if (mounted) setState(() => _saving = false);
|
|
}
|
|
|
|
Future<void> _save() async {
|
|
final name = _controller.text.trim();
|
|
if (name.isEmpty) {
|
|
ScaffoldMessenger.of(
|
|
context,
|
|
).showSnackBar(const SnackBar(content: Text('昵称不能为空')));
|
|
return;
|
|
}
|
|
setState(() => _saving = true);
|
|
await ref.read(profileProvider.notifier).updateNickname(name);
|
|
if (mounted) {
|
|
setState(() => _saving = false);
|
|
Navigator.pop(context);
|
|
}
|
|
}
|
|
}
|
|
|
|
enum AvatarAction { gallery, camera }
|
|
|
|
class _AvatarPreview extends StatelessWidget {
|
|
const _AvatarPreview({required this.profile});
|
|
final UserProfile? profile;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final p = profile;
|
|
final ImageProvider image;
|
|
if (p != null &&
|
|
p.avatarSource == AvatarSource.localFile &&
|
|
p.avatarLocalPath != null &&
|
|
p.avatarLocalPath!.isNotEmpty) {
|
|
image = FileImage(File(p.avatarLocalPath!));
|
|
} else {
|
|
image = const AssetImage(AppDefaults.defaultAvatarAssetPath);
|
|
}
|
|
return Stack(
|
|
alignment: Alignment.bottomRight,
|
|
children: [
|
|
CircleAvatar(
|
|
radius: 44,
|
|
backgroundColor: AppTheme.surfaceMuted,
|
|
backgroundImage: image,
|
|
),
|
|
Container(
|
|
padding: const EdgeInsets.all(6),
|
|
decoration: const BoxDecoration(
|
|
color: AppTheme.primary,
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: const Icon(Icons.edit, color: Colors.white, size: 16),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|