import 'package:flutter/material.dart'; import 'package:patbond_flutter/core/theme/app_theme.dart'; import 'package:patbond_flutter/data/demo_data.dart'; import 'package:patbond_flutter/models/models.dart'; import 'package:patbond_flutter/state/app_state.dart'; import 'package:patbond_flutter/widgets/common.dart'; enum CreationMode { image, video } class CreatePage extends StatefulWidget { const CreatePage({ required this.appState, required this.onPublished, super.key, }); final AppState appState; final ValueChanged onPublished; @override State createState() => _CreatePageState(); } class _CreatePageState extends State { final titleController = TextEditingController(); final contentController = TextEditingController(); CreationMode mode = CreationMode.image; CreationStyle selectedStyle = creationStyles.first; String selectedModel = 'Patbond-V1'; String duration = '5 秒'; String resolution = '1080P'; bool upscaling = true; bool uploaded = false; bool uploading = false; bool generating = false; int generationStep = 0; String? resultUrl; List tags = ['可爱修勾', 'AI宠物']; @override void dispose() { titleController.dispose(); contentController.dispose(); super.dispose(); } Future simulateUpload() async { setState(() => uploading = true); await Future.delayed(const Duration(milliseconds: 700)); if (!mounted) return; setState(() { uploading = false; uploaded = true; resultUrl = null; }); } Future generate() async { if (!uploaded) { ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text('请先选择一张宠物照片'))); return; } setState(() { generating = true; generationStep = 1; }); for (var step = 2; step <= 4; step++) { await Future.delayed(const Duration(milliseconds: 650)); if (!mounted) return; setState(() => generationStep = step); } await Future.delayed(const Duration(milliseconds: 500)); if (!mounted) return; setState(() { generating = false; resultUrl = selectedStyle.image; titleController.text = mode == CreationMode.image ? '豆豆的${selectedStyle.title}冒险' : '豆豆的 AI 萌宠短片'; contentController.text = '刚刚用 Patbond 创作了新作品,快来看看豆豆的新造型吧!✨'; }); } Future addTag() async { final controller = TextEditingController(); final value = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('添加话题'), content: TextField( controller: controller, autofocus: true, decoration: const InputDecoration(hintText: '输入话题名称'), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('取消'), ), FilledButton( onPressed: () => Navigator.pop(context, controller.text.trim()), child: const Text('添加'), ), ], ), ); controller.dispose(); if (value == null || value.isEmpty || tags.contains(value)) return; setState(() => tags = [...tags, value]); } void publish() { if (resultUrl == null || titleController.text.trim().isEmpty) { ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text('请先完成生成并填写标题'))); return; } final post = PostModel( id: 'post_user_${DateTime.now().millisecondsSinceEpoch}', authorName: '萌宠新手(我)', authorAvatar: userAvatar, time: '刚刚', breedTag: 'AI创作', content: '${titleController.text.trim()}\n\n${contentController.text.trim()}', mainImage: resultUrl!, likes: 1, tags: [...tags, mode == CreationMode.image ? 'AI生图' : 'AI视频'], comments: const [], hasLiked: true, ); widget.appState.publishPost(post); setState(() { uploaded = false; resultUrl = null; generationStep = 0; titleController.clear(); contentController.clear(); }); widget.onPublished(post); } @override Widget build(BuildContext context) { return ListView( padding: const EdgeInsets.fromLTRB(16, 12, 16, 28), children: [ SegmentedButton( segments: const [ ButtonSegment( value: CreationMode.image, icon: Icon(Icons.image_outlined), label: Text('AI 图片'), ), ButtonSegment( value: CreationMode.video, icon: Icon(Icons.movie_creation_outlined), label: Text('AI 视频'), ), ], selected: {mode}, showSelectedIcon: false, onSelectionChanged: (value) => setState(() { mode = value.first; resultUrl = null; }), ), const SizedBox(height: 16), _UploadCard( uploaded: uploaded, uploading: uploading, imageUrl: widget.appState.pet.avatarUrl, onTap: simulateUpload, onRemove: () => setState(() { uploaded = false; resultUrl = null; }), ), const SizedBox(height: 16), SectionCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('生成设置', style: Theme.of(context).textTheme.titleMedium), const SizedBox(height: 14), DropdownButtonFormField( initialValue: selectedModel, decoration: const InputDecoration(labelText: '创作模型'), items: ['Patbond-V1', 'Pet-Art Pro', 'Cute Motion'] .map( (model) => DropdownMenuItem(value: model, child: Text(model)), ) .toList(), onChanged: (value) { if (value != null) setState(() => selectedModel = value); }, ), if (mode == CreationMode.video) ...[ const SizedBox(height: 12), _ChoiceRow( title: '视频时长', values: const ['5 秒', '10 秒', '15 秒'], selected: duration, onChanged: (value) => setState(() => duration = value), ), ], const SizedBox(height: 12), _ChoiceRow( title: '分辨率', values: const ['720P', '1080P', '2K'], selected: resolution, onChanged: (value) => setState(() => resolution = value), ), SwitchListTile.adaptive( contentPadding: EdgeInsets.zero, title: const Text('高清增强'), subtitle: const Text('提升毛发与眼睛细节'), value: upscaling, onChanged: (value) => setState(() => upscaling = value), ), ], ), ), const SizedBox(height: 18), Text('热门风格', style: Theme.of(context).textTheme.titleLarge), const SizedBox(height: 10), SizedBox( height: 150, child: ListView.separated( scrollDirection: Axis.horizontal, itemCount: creationStyles.length, separatorBuilder: (_, _) => const SizedBox(width: 10), itemBuilder: (context, index) { final style = creationStyles[index]; final selected = style.id == selectedStyle.id; return InkWell( onTap: () => setState(() { selectedStyle = style; resultUrl = null; }), borderRadius: BorderRadius.circular(20), child: Container( width: 125, decoration: BoxDecoration( border: Border.all( color: selected ? AppColors.primary : AppColors.border, width: selected ? 2 : 1, ), borderRadius: BorderRadius.circular(20), ), child: Stack( fit: StackFit.expand, children: [ RemoteImage( url: style.image, borderRadius: BorderRadius.circular(18), ), DecoratedBox( decoration: BoxDecoration( borderRadius: BorderRadius.circular(18), gradient: const LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.transparent, Color(0xCC0F172A)], ), ), ), Positioned( left: 12, right: 12, bottom: 10, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( style.title, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.w800, ), ), Text( style.subtitle, style: const TextStyle( color: Color(0xFFE2E8F0), fontSize: 10, ), ), ], ), ), ], ), ), ); }, ), ), const SizedBox(height: 18), if (generating) _GenerationProgress(step: generationStep), FilledButton.icon( style: FilledButton.styleFrom( minimumSize: const Size.fromHeight(52), backgroundColor: AppColors.ink, ), onPressed: generating ? null : generate, icon: generating ? const SizedBox.square( dimension: 18, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white, ), ) : const Icon(Icons.auto_awesome), label: Text(generating ? '正在生成…' : '开始生成'), ), if (resultUrl != null) ...[ const SizedBox(height: 18), SectionCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon(Icons.check_circle, color: AppColors.success), const SizedBox(width: 8), Text( '生成完成', style: Theme.of(context).textTheme.titleMedium, ), ], ), const SizedBox(height: 14), AspectRatio( aspectRatio: 16 / 10, child: RemoteImage( url: resultUrl!, borderRadius: BorderRadius.circular(18), ), ), const SizedBox(height: 14), TextField( controller: titleController, decoration: const InputDecoration(labelText: '发布标题(必填)'), ), const SizedBox(height: 12), TextField( controller: contentController, maxLines: 3, decoration: const InputDecoration(labelText: '分享正文'), ), const SizedBox(height: 12), Wrap( spacing: 8, runSpacing: 8, children: [ ...tags.map( (tag) => InputChip( label: Text('#$tag'), onDeleted: () => setState( () => tags = tags.where((item) => item != tag).toList(), ), ), ), ActionChip( avatar: const Icon(Icons.add, size: 16), label: const Text('话题'), onPressed: addTag, ), ], ), const SizedBox(height: 14), const ListTile( contentPadding: EdgeInsets.zero, leading: Icon(Icons.location_on_outlined), title: Text('北京市 · 朝阳区'), trailing: Icon(Icons.chevron_right), ), FilledButton.icon( style: FilledButton.styleFrom( minimumSize: const Size.fromHeight(48), ), onPressed: publish, icon: const Icon(Icons.send_rounded), label: const Text('发布到社区'), ), ], ), ), ], ], ); } } class _UploadCard extends StatelessWidget { const _UploadCard({ required this.uploaded, required this.uploading, required this.imageUrl, required this.onTap, required this.onRemove, }); final bool uploaded; final bool uploading; final String imageUrl; final VoidCallback onTap; final VoidCallback onRemove; @override Widget build(BuildContext context) { return SectionCard( child: SizedBox( height: 190, width: double.infinity, child: uploading ? const Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator(), SizedBox(height: 12), Text('正在读取宠物照片…'), ], ) : uploaded ? Stack( fit: StackFit.expand, children: [ RemoteImage( url: imageUrl, borderRadius: BorderRadius.circular(18), ), Positioned( right: 8, top: 8, child: IconButton.filled( tooltip: '移除照片', onPressed: onRemove, icon: const Icon(Icons.close), ), ), ], ) : InkWell( onTap: onTap, borderRadius: BorderRadius.circular(18), child: const Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircleAvatar( radius: 28, child: Icon(Icons.add_photo_alternate_outlined), ), SizedBox(height: 12), Text( '选择宠物照片', style: TextStyle(fontWeight: FontWeight.w800), ), SizedBox(height: 4), Text('演示模式会读取豆豆的档案头像'), ], ), ), ), ); } } class _ChoiceRow extends StatelessWidget { const _ChoiceRow({ required this.title, required this.values, required this.selected, required this.onChanged, }); final String title; final List values; final String selected; final ValueChanged onChanged; @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: Theme.of(context).textTheme.bodySmall), const SizedBox(height: 7), Wrap( spacing: 8, children: values .map( (value) => ChoiceChip( label: Text(value), selected: value == selected, onSelected: (_) => onChanged(value), ), ) .toList(), ), ], ); } } class _GenerationProgress extends StatelessWidget { const _GenerationProgress({required this.step}); final int step; @override Widget build(BuildContext context) { const labels = ['分析宠物特征', '加载风格模型', '生成画面细节', '高清增强与合成']; return Padding( padding: const EdgeInsets.only(bottom: 14), child: SectionCard( child: Column( children: [ LinearProgressIndicator(value: step / labels.length), const SizedBox(height: 12), for (var index = 0; index < labels.length; index++) ListTile( dense: true, contentPadding: EdgeInsets.zero, leading: Icon( index < step ? Icons.check_circle : Icons.circle_outlined, color: index < step ? AppColors.success : AppColors.muted, size: 20, ), title: Text(labels[index]), ), ], ), ), ); } }