feat(app): 形象照片上传 + 身形设置 + 化身构建 + 衣橱管理

- photo_upload_provider/photo_guide_page:4 类照片(大头照/正面/侧面/背面)拍照或相册上传
- body_provider/body_tune_page:身高 145-200cm / 体重 / 肤色 1-5 滑杆设置
- avatar_provider:化身构建(同步 + 异步轮询兜底)
- profile_page:形象主页聚合化身/照片/身形状态 + 登出
- wardrobe_provider/wardrobe_page/wardrobe_upload_page:网格展示 + 分类筛选 + 长按删除 + 表单上传
- AppConfig.resolveUrl 相对路径拼接;新增 /photo-guide /body-tune /wardrobe-upload 路由
This commit is contained in:
2026-07-31 12:44:10 +08:00
parent acbe60c13c
commit ceef7bc64e
14 changed files with 1214 additions and 4 deletions
+33
View File
@@ -0,0 +1,33 @@
import 'package:flutter/material.dart';
/// 空态 + 引导动作
class EmptyView extends StatelessWidget {
final String message;
final String? actionText;
final VoidCallback? onAction;
const EmptyView({
super.key,
required this.message,
this.actionText,
this.onAction,
});
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.inbox_outlined, size: 48, color: Colors.grey.shade400),
const SizedBox(height: 12),
Text(message, style: const TextStyle(color: Colors.grey)),
if (actionText != null && onAction != null) ...[
const SizedBox(height: 16),
FilledButton(onPressed: onAction, child: Text(actionText!)),
],
],
),
);
}
}
+33
View File
@@ -0,0 +1,33 @@
import 'package:flutter/material.dart';
/// 错误态 + 重试
class ErrorView extends StatelessWidget {
final String message;
final VoidCallback? onRetry;
const ErrorView({super.key, required this.message, this.onRetry});
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.error_outline, size: 48, color: Colors.grey.shade400),
const SizedBox(height: 12),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 32),
child: Text(message,
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.grey)),
),
if (onRetry != null) ...[
const SizedBox(height: 16),
OutlinedButton.icon(
onPressed: onRetry, icon: const Icon(Icons.refresh), label: const Text('重试')),
],
],
),
);
}
}
+24
View File
@@ -0,0 +1,24 @@
import 'package:flutter/material.dart';
/// 加载骨架屏
class LoadingView extends StatelessWidget {
final String? text;
const LoadingView({super.key, this.text});
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const CircularProgressIndicator(),
if (text != null) ...[
const SizedBox(height: 12),
Text(text!, style: const TextStyle(color: Colors.grey)),
],
],
),
);
}
}