FunyMeeAI/lib/features/generate/generate_progress_screen.dart

214 lines
6.4 KiB
Dart

import 'dart:async';
import 'dart:io';
import 'package:client_proxy_framework/client_proxy_framework.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import '../../core/app_env.dart';
import '../../core/user/user_state.dart';
import '../../design/pencil_theme.dart';
import '../../widgets/pencil_chrome.dart';
import 'generate_result_screen.dart';
/// `YoZaK` 生成中 — 与 EYsUi 同款顶栏结构,标题「生成中」。
class GenerateProgressScreen extends StatefulWidget {
const GenerateProgressScreen({
super.key,
required this.taskId,
this.localPreviewPath,
});
final String taskId;
final String? localPreviewPath;
@override
State<GenerateProgressScreen> createState() => _GenerateProgressScreenState();
}
class _GenerateProgressScreenState extends State<GenerateProgressScreen> {
Timer? _timer;
String _status = '';
int _progress = 0;
String? _resultUrl;
String? _error;
bool _finished = false;
@override
void initState() {
super.initState();
_poll();
_timer = Timer.periodic(const Duration(seconds: 2), (_) => _poll());
}
Future<void> _poll() async {
if (_error != null || _finished) return;
final uid = UserState.userId.value;
final res = await ImageApi.getProgress(
app: currentBackendAppType(),
taskId: widget.taskId,
userId: uid,
);
if (!mounted) return;
if (!res.isSuccess || res.data == null) {
setState(() => _error = res.msg.isNotEmpty ? res.msg : 'Progress error');
return;
}
final p = res.data!;
setState(() {
_status = p.status ?? '';
_progress = p.progress ?? 0;
_resultUrl = p.resultUrl;
});
if (_isTerminal(_status) || _hasUsableResult(_resultUrl)) {
_timer?.cancel();
if (_isSuccess(_status) || _hasUsableResult(_resultUrl)) {
_finished = true;
if (!mounted) return;
Navigator.of(context).pushReplacement(
MaterialPageRoute<void>(
builder: (_) => GenerateResultScreen(
taskId: widget.taskId,
resultUrl: _resultUrl ?? '',
),
),
);
} else if (_isFailure(_status)) {
setState(() => _error ??= 'Task failed ($_status)');
}
}
}
bool _hasUsableResult(String? url) {
if (url == null || url.isEmpty) return false;
return url.startsWith('http://') || url.startsWith('https://');
}
bool _isTerminal(String s) {
final t = s.toLowerCase();
return t == 'success' ||
t == 'completed' ||
t == 'complete' ||
t == 'failed' ||
t == 'failure' ||
t == 'error' ||
t == 'cancelled' ||
t == 'canceled';
}
bool _isSuccess(String s) {
final t = s.toLowerCase();
return t == 'success' || t == 'completed' || t == 'complete';
}
bool _isFailure(String s) {
final t = s.toLowerCase();
return t == 'failed' ||
t == 'failure' ||
t == 'error' ||
t == 'cancelled' ||
t == 'canceled';
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
decoration: const BoxDecoration(
gradient: PencilTheme.yellowWhitePageGradient,
),
child: Scaffold(
backgroundColor: Colors.transparent,
body: SafeArea(
bottom: false,
child: Column(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(2, 0, 14, 10),
child: SizedBox(
height: 56,
child: Row(
children: [
PencilRoundBackButton(
onPressed: () => Navigator.of(context).pop(),
),
Expanded(
child: Center(
child: Text(
'Generating',
style: GoogleFonts.inter(
fontSize: 19,
fontWeight: FontWeight.w700,
fontStyle: FontStyle.italic,
color: PencilTheme.stone900,
),
),
),
),
const SizedBox(width: 44),
],
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
children: [
if (widget.localPreviewPath != null)
ClipRRect(
borderRadius: BorderRadius.circular(12),
child: AspectRatio(
aspectRatio: 1,
child: Image.file(
File(widget.localPreviewPath!),
fit: BoxFit.cover,
),
),
)
else
const SizedBox(height: 48),
const SizedBox(height: 24),
if (_error != null)
Text(
_error!,
style: GoogleFonts.inter(color: Colors.red),
textAlign: TextAlign.center,
)
else ...[
LinearProgressIndicator(
value: _progress > 0 ? _progress / 100 : null,
color: PencilTheme.underlineGold,
),
const SizedBox(height: 16),
Text(
_status.isEmpty ? 'Processing…' : _status,
style: GoogleFonts.inter(color: PencilTheme.stone600),
),
Text(
'$_progress%',
style: GoogleFonts.inter(
fontSize: 28,
fontWeight: FontWeight.w700,
color: PencilTheme.stone900,
),
),
],
],
),
),
),
],
),
),
),
);
}
}