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 '../../widgets/pencil_yellow_white_background.dart'; import '../generate/generate_result_screen.dart'; /// 从 My History 点「生成中」任务进入:轮询 [ImageApi.getProgress],完成后进入 [GenerateResultScreen]。 /// /// 与 app_client [GenerateProgressScreen] 行为对齐(权威数据源为 `/v1/image/progress`)。 class HistoryTaskProgressScreen extends StatefulWidget { const HistoryTaskProgressScreen({super.key, required this.taskId}); final String taskId; @override State createState() => _HistoryTaskProgressScreenState(); } class _HistoryTaskProgressScreenState extends State { ImageProgressPollHandle? _pollHandle; String _genStatus = ''; int _genProgress = 0; String? _genResultUrl; String? _genError; bool _navigated = false; @override void initState() { super.initState(); _startPoll(); } void _startPoll() { _pollHandle?.cancel(); _pollHandle = ImageProgressPoll.start( app: currentBackendAppType(), taskId: widget.taskId, userId: UserState.userId.value, interval: const Duration(seconds: 5), onTick: _onTick, onFatalError: (msg) { if (!mounted || _navigated) return; setState(() => _genError = msg); }, ); } void _onTick(ProgressPollTick tick) { if (!mounted || _navigated) return; final res = tick.response; if (!res.isSuccess || res.data == null) { setState(() => _genError = res.msg.isNotEmpty ? res.msg : 'Progress error'); return; } final p = res.data!; setState(() { _genError = null; _genStatus = p.status ?? ''; _genProgress = p.progress ?? 0; _genResultUrl = p.resultUrl; }); if (ProgressPollSemantics.isProgressSuccess(p)) { _navigated = true; _pollHandle?.cancel(); _pollHandle = null; final url = _genResultUrl ?? ''; Navigator.of(context).pushReplacement( MaterialPageRoute( builder: (_) => GenerateResultScreen( taskId: widget.taskId, resultUrl: url, ), ), ); return; } if (ProgressPollSemantics.isProgressFailure(p)) { _pollHandle?.cancel(); _pollHandle = null; setState(() { _genError ??= 'Task failed (${p.status})'; }); } } @override void dispose() { _pollHandle?.cancel(); super.dispose(); } @override Widget build(BuildContext context) { return PencilYellowWhitePageBackground( child: Scaffold( backgroundColor: Colors.transparent, body: SafeArea( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Padding( padding: const EdgeInsets.fromLTRB(2, 0, 14, 8), 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.symmetric(horizontal: 24), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ if (_genError != null) ...[ Text( _genError!, style: GoogleFonts.inter(color: Colors.red), textAlign: TextAlign.center, ), ] else ...[ LinearProgressIndicator( value: _genProgress > 0 ? _genProgress / 100 : null, color: PencilTheme.underlineGold, ), const SizedBox(height: 16), Text( _genStatus.isEmpty ? 'Processing…' : _genStatus, textAlign: TextAlign.center, style: GoogleFonts.inter( fontSize: 14, color: PencilTheme.stone600, ), ), Text( '$_genProgress%', textAlign: TextAlign.center, style: GoogleFonts.inter( fontSize: 28, fontWeight: FontWeight.w700, color: PencilTheme.stone900, ), ), ], ], ), ), ), ], ), ), ), ); } }