import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:go_router/go_router.dart'; import 'avatar_provider.dart'; import 'body_provider.dart'; import 'photo_upload_provider.dart'; /// 我的形象:化身 + 照片 + 身形(退出登录在「我的」页) class ProfilePage extends ConsumerWidget { const ProfilePage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final avatar = ref.watch(avatarProvider); final photos = ref.watch(photoProvider); final body = ref.watch(bodyProvider); return Scaffold( appBar: AppBar(title: const Text('我的形象')), body: ListView( padding: const EdgeInsets.all(16), children: [ _buildAvatarCard(context, ref, avatar), const SizedBox(height: 12), _buildPhotoCard(context, ref, photos), const SizedBox(height: 12), _buildBodyCard(context, ref, body), ], ), ); } Widget _buildAvatarCard(BuildContext context, WidgetRef ref, AsyncValue avatar) { return _SectionCard( title: '3D 化身', icon: Icons.face_retouching_natural, onActionText: null, child: avatar.when( loading: () => const Padding( padding: EdgeInsets.all(16), child: Center( child: SizedBox( width: 24, height: 24, child: CircularProgressIndicator(strokeWidth: 2))), ), error: (e, _) => _ActionRow( text: '构建失败:${e.toString().replaceFirst('Exception: ', '')}', actionText: '重试', onAction: () => ref.read(avatarProvider.notifier).buildAvatar(), ), data: (a) => _ActionRow( text: a.built ? '已构建(模板 ${a.faceTemplateId}/${a.bodyTemplateId})' : '尚未构建化身,需要先完成照片与身形', actionText: a.built ? '查看' : '立即构建', onAction: a.built ? () => context.go('/avatar-viewer') : () async { await ref.read(avatarProvider.notifier).buildAvatar(); if (context.mounted) { Fluttertoast.showToast(msg: '化身构建完成'); } }, ), ), ); } Widget _buildPhotoCard( BuildContext context, WidgetRef ref, AsyncValue> photos) { return _SectionCard( title: '形象照片', icon: Icons.photo_camera_outlined, onActionText: '去拍摄', onAction: () => context.go('/photo-guide'), child: photos.when( loading: () => const _InfoText('加载中...'), error: (e, _) => _InfoText('加载失败:$e'), data: (list) => Column( children: [ for (final type in PhotoType.all) _InfoText( '${PhotoType.labels[type]}:' '${list.any((p) => p.type == type) ? '已上传' : '未上传'}'), ], ), ), ); } Widget _buildBodyCard( BuildContext context, WidgetRef ref, AsyncValue body) { return _SectionCard( title: '身形参数', icon: Icons.accessibility_new, onActionText: '去设置', onAction: () => context.go('/body-tune'), child: body.when( loading: () => const _InfoText('加载中...'), error: (e, _) => _InfoText('加载失败:$e'), data: (b) => _InfoText( '身高 ${b.height}cm · 体重 ${b.weight}kg · 肤色 ${b.skinTone}'), ), ); } } class _SectionCard extends StatelessWidget { final String title; final IconData icon; final Widget child; final String? onActionText; final VoidCallback? onAction; const _SectionCard({ required this.title, required this.icon, required this.child, this.onActionText, this.onAction, }); @override Widget build(BuildContext context) { return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(icon, size: 20, color: Theme.of(context).colorScheme.primary), const SizedBox(width: 8), Text(title, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold)), ], ), const SizedBox(height: 12), child, if (onActionText != null) ...[ const SizedBox(height: 12), Align( alignment: Alignment.centerRight, child: TextButton( onPressed: onAction, child: Text(onActionText!), ), ), ], ], ), ), ); } } class _ActionRow extends StatelessWidget { final String text; final String actionText; final VoidCallback? onAction; const _ActionRow({ required this.text, required this.actionText, required this.onAction, }); @override Widget build(BuildContext context) { return Row( children: [ Expanded(child: Text(text, style: const TextStyle(fontSize: 13))), if (onAction != null) TextButton(onPressed: onAction, child: Text(actionText)), ], ); } } class _InfoText extends StatelessWidget { final String text; const _InfoText(this.text); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(vertical: 2), child: Text(text, style: const TextStyle(fontSize: 13)), ); } }