Files
slogan/app/lib/features/wardrobe/wardrobe_upload_page.dart
T
2026-08-04 17:04:18 +08:00

163 lines
5.3 KiB
Dart

import 'dart:io';
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 '../../shared/app_toast.dart';
import 'wardrobe_provider.dart';
/// 服装上传:选图 + 分类/季节/风格/颜色信息
class WardrobeUploadPage extends ConsumerStatefulWidget {
const WardrobeUploadPage({super.key});
@override
ConsumerState<WardrobeUploadPage> createState() => _WardrobeUploadPageState();
}
class _WardrobeUploadPageState extends ConsumerState<WardrobeUploadPage> {
final _picker = ImagePicker();
final _styleCtrl = TextEditingController();
final _colorCtrl = TextEditingController();
String? _imagePath;
String? _category;
String? _season;
bool _uploading = false;
@override
void dispose() {
_styleCtrl.dispose();
_colorCtrl.dispose();
super.dispose();
}
Future<void> _pickImage() async {
final file = await _picker.pickImage(
source: ImageSource.gallery, maxWidth: 2048, imageQuality: 85);
if (file == null) return;
setState(() => _imagePath = file.path);
}
Future<void> _submit() async {
if (_imagePath == null) {
showToast('请先选择服装照片');
return;
}
if (_category == null) {
showToast('请选择分类');
return;
}
setState(() => _uploading = true);
try {
await ref.read(wardrobeProvider.notifier).upload(
_imagePath!,
category: _category!,
season: _season ?? '',
styleTags: _styleCtrl.text.trim(),
colorInfo: _colorCtrl.text.trim(),
);
if (!mounted) return;
showToast('上传成功');
context.go('/wardrobe');
} catch (e) {
if (!mounted) return;
showToast('上传失败:${e.toString().replaceFirst('Exception: ', '')}');
} finally {
if (mounted) setState(() => _uploading = false);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('上传服装')),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
GestureDetector(
onTap: _uploading ? null : _pickImage,
child: Container(
height: 220,
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.shade300),
),
clipBehavior: Clip.antiAlias,
child: _imagePath == null
? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.add_photo_alternate_outlined,
size: 48, color: Colors.grey.shade500),
const SizedBox(height: 8),
const Text('点击选择服装照片',
style: TextStyle(color: Colors.grey)),
],
)
: Image.file(File(_imagePath!), fit: BoxFit.cover),
),
),
const SizedBox(height: 16),
DropdownButtonFormField<String>(
initialValue: _category,
decoration: const InputDecoration(
labelText: '分类', border: OutlineInputBorder()),
items: [
for (final c in wardrobeCategories)
DropdownMenuItem(value: c, child: Text(c)),
],
onChanged: _uploading
? null
: (v) => setState(() => _category = v),
),
const SizedBox(height: 12),
DropdownButtonFormField<String>(
initialValue: _season,
decoration: const InputDecoration(
labelText: '适用季节(选填)', border: OutlineInputBorder()),
items: [
for (final s in wardrobeSeasons)
DropdownMenuItem(value: s, child: Text(s)),
],
onChanged:
_uploading ? null : (v) => setState(() => _season = v),
),
const SizedBox(height: 12),
TextField(
controller: _styleCtrl,
enabled: !_uploading,
decoration: const InputDecoration(
labelText: '风格标签(选填,如:通勤、休闲)',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 12),
TextField(
controller: _colorCtrl,
enabled: !_uploading,
decoration: const InputDecoration(
labelText: '颜色描述(选填,如:黑色)',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 24),
FilledButton(
onPressed: _uploading ? null : _submit,
style: FilledButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 14)),
child: _uploading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2))
: const Text('上传到衣橱'),
),
],
),
);
}
}