part of '../lesson_flow.dart'; class _WritingStep extends StatefulWidget { const _WritingStep({ required this.state, required this.lessonId, required this.segmentId, required this.activity, required this.controller, required this.showHelp, required this.canContinue, required this.onChanged, required this.onToggleHelp, required this.onContinue, }); final String lessonId; final AppState state; final String segmentId; final LessonActivity activity; final TextEditingController controller; final bool showHelp; final bool canContinue; final VoidCallback onChanged; final VoidCallback onToggleHelp; final ValueChanged onContinue; @override State<_WritingStep> createState() => _WritingStepState(); } class _WritingStepState extends State<_WritingStep> { WritingCheckResult? result; WritingAiFeedback? aiFeedback; String? aiFeedbackError; bool requestingAiFeedback = false; void _checkOrContinue() { if (result?.complete == true) { widget.onContinue(aiFeedback != null); return; } setState( () => result = WritingFeedback.check( widget.lessonId, widget.controller.text, segmentId: widget.segmentId, ), ); } Future _requestAiFeedback() async { if (widget.controller.text.trim().isEmpty || requestingAiFeedback) return; if (widget.state.aiProvider == AiProviderType.mock) { setState(() => aiFeedbackError = '请先在“我的”配置 AI 服务;本地检查仍可继续学习。'); return; } setState(() { requestingAiFeedback = true; aiFeedbackError = null; }); final feedback = await AiService.instance.writingFeedback( provider: widget.state.aiProvider, endpoint: widget.state.aiEndpoint, model: widget.state.aiModel, lessonId: widget.lessonId, taskPrompt: widget.activity.writingPrompt, answer: widget.controller.text.trim(), ); if (!mounted) return; setState(() { requestingAiFeedback = false; aiFeedback = feedback; aiFeedbackError = feedback == null ? '暂时无法获得 AI 反馈。你的答案保留在这里,可稍后重试或继续本地练习。' : null; }); } @override Widget build(BuildContext context) => _LessonScaffold( step: 5, child: SpacedColumn( children: [ const Eyebrow('写一写'), Text( widget.activity.writingPrompt, style: Theme.of(context).textTheme.headlineMedium, ), SectionCard( tint: AppColors.surfaceMuted, child: Text( '小提示:${grammarNoteForSegment(widget.segmentId, widget.lessonId)}', ), ), if (widget.showHelp) SectionCard( tint: AppColors.softGreen, child: Text( widget.activity.writingExample, style: TextStyle(fontSize: 18, height: 1.5), ), ), TextField( controller: widget.controller, onChanged: (_) { setState(() { result = null; aiFeedback = null; aiFeedbackError = null; }); widget.onChanged(); }, minLines: 3, maxLines: 4, decoration: InputDecoration( labelText: '你的答案', hintText: widget.showHelp ? widget.activity.writingExample : '请输入完整英文答案', filled: true, fillColor: AppColors.surface, border: const OutlineInputBorder(), ), ), TextButton( onPressed: widget.onToggleHelp, child: Text(widget.showHelp ? '收起示例,自己试一次' : '需要帮助,查看示例'), ), OutlinedButton.icon( onPressed: widget.canContinue && !requestingAiFeedback ? _requestAiFeedback : null, icon: requestingAiFeedback ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.auto_awesome_outlined), label: Text(requestingAiFeedback ? '正在获取反馈…' : '获取 AI 写作建议(可选)'), ), if (aiFeedback != null) SectionCard( tint: aiFeedback!.verdict == 'accepted' ? AppColors.softGreen : AppColors.warm, child: SpacedColumn( spacing: 6, children: [ Text('AI 建议:${aiFeedback!.feedback}'), if (aiFeedback!.missing.isNotEmpty) Text('还可补充:${aiFeedback!.missing.join('、')}'), if (aiFeedback!.suggestion != null) Text('可参考改写:${aiFeedback!.suggestion}'), const Text( '这是学习帮助;请按自己的意思重写后再检查,系统不会仅凭 AI 建议记为掌握。', style: TextStyle(fontSize: 12, color: AppColors.muted), ), ], ), ), if (aiFeedbackError != null) SectionCard( tint: AppColors.warm, child: Text( aiFeedbackError!, style: const TextStyle(color: AppColors.warmInk), ), ), if (result != null) SectionCard( tint: result!.complete ? AppColors.softGreen : AppColors.warm, child: Text( result!.message, style: TextStyle( color: result!.complete ? AppColors.green : AppColors.warmInk, ), ), ), PrimaryButton( label: result?.complete == true ? '进入课程对话' : '检查句子', onPressed: widget.canContinue ? _checkOrContinue : null, ), ], ), ); }