import 'package:flutter/material.dart'; import '../../core/app_state.dart'; import '../../core/app_theme.dart'; import '../../core/models.dart'; import '../../core/seed_courses.dart'; import '../../core/assessment_bank.dart'; import '../../widgets/app_widgets.dart'; import '../dialogue/dialogue_flow.dart'; import '../assessment/assessment_page.dart'; import '../home/home_page.dart'; import '../lesson/lesson_flow.dart'; import '../progress/progress_pages.dart'; import '../review/review_page.dart'; class LearningShell extends StatefulWidget { const LearningShell({super.key, required this.state}); final AppState state; @override State createState() => _LearningShellState(); } class _LearningShellState extends State { AppTab tab = AppTab.home; var route = _ShellRoute.tab; bool dialogueInLesson = false; AssessmentPack? assessmentPack; DialogueSummaryData? dialogueSummary; void showTab(AppTab value) => setState(() { tab = value; route = _ShellRoute.tab; }); void showLesson() => setState(() => route = _ShellRoute.lesson); void showDialogueScene() => setState(() => route = _ShellRoute.scene); void showDialogue({bool inLesson = false}) => setState(() { dialogueInLesson = inLesson; route = _ShellRoute.dialogue; }); void showSummary(DialogueSummaryData summary) => setState(() { dialogueSummary = summary; route = _ShellRoute.summary; }); void showSettings() => setState(() => route = _ShellRoute.settings); void showAssessment(AssessmentPack pack) => setState(() { assessmentPack = pack; route = _ShellRoute.assessmentPreparation; }); void startAssessment() => setState(() => route = _ShellRoute.assessment); void showAdaptiveLesson() => setState(() => route = _ShellRoute.adaptiveLesson); @override Widget build(BuildContext context) { Widget body; switch (route) { case _ShellRoute.lesson: body = LessonFlow( state: widget.state, onOpenDialogue: () => showDialogue(inLesson: true), onFinish: () => showTab(AppTab.home), ); case _ShellRoute.scene: body = DialogueScenePage(onStart: showDialogue); case _ShellRoute.dialogue: body = DialoguePage( state: widget.state, isLessonDialogue: dialogueInLesson, onFinished: dialogueInLesson ? (_) => showLesson() : (summary) { if (summary != null) showSummary(summary); }, ); case _ShellRoute.summary: body = DialogueSummaryPage( summary: dialogueSummary!, onHome: () => showTab(AppTab.home), onLesson: showLesson, onRetry: showDialogue, ); case _ShellRoute.settings: body = SettingsPage(state: widget.state); case _ShellRoute.adaptiveLesson: body = AdaptiveLessonPage( state: widget.state, onFinished: () => showTab(AppTab.review), ); case _ShellRoute.assessment: body = AssessmentPage( state: widget.state, pack: assessmentPack!, onFinished: () => showTab(AppTab.progress), onStartReplacement: showAssessment, ); case _ShellRoute.assessmentPreparation: body = AssessmentPreparationPage( state: widget.state, pack: assessmentPack!, onStart: startAssessment, onBack: () => showTab(AppTab.progress), ); case _ShellRoute.tab: body = _tabContent(); } return AnimatedBuilder( animation: widget.state, builder: (context, _) => Scaffold( body: body, bottomNavigationBar: route == _ShellRoute.tab ? NavigationBar( selectedIndex: tab.index, height: 70, indicatorColor: AppColors.softGreen, onDestinationSelected: (index) => showTab(AppTab.values[index]), destinations: const [ NavigationDestination( icon: Icon(Icons.home_outlined), selectedIcon: Icon(Icons.home), label: '首页', ), NavigationDestination( icon: Icon(Icons.menu_book_outlined), selectedIcon: Icon(Icons.menu_book), label: '学习', ), NavigationDestination( icon: Icon(Icons.chat_bubble_outline), selectedIcon: Icon(Icons.chat_bubble), label: '对话', ), NavigationDestination( icon: Icon(Icons.refresh_outlined), selectedIcon: Icon(Icons.refresh), label: '复习', ), NavigationDestination( icon: Icon(Icons.person_outline), selectedIcon: Icon(Icons.person), label: '我的', ), ], ) : null, ), ); } Widget _tabContent() { switch (tab) { case AppTab.home: return HomePage( state: widget.state, onStartPrimaryTask: widget.state.reviewIsPrimary ? () => showTab(AppTab.review) : showLesson, onOpenDialogue: showDialogue, onResumeLessonDialogue: () => showDialogue(inLesson: true), ); case AppTab.learn: return _LearningMap( onOpenLesson: (id) { widget.state.openLesson(id); showLesson(); }, onStartReinforcement: () { widget.state.scheduleA0Reinforcement(); showTab(AppTab.review); }, onOpenReview: () => showTab(AppTab.review), state: widget.state, ); case AppTab.dialogue: return DialogueScenePage(onStart: showDialogue); case AppTab.review: return ReviewPage( state: widget.state, onFinished: () => showTab(AppTab.home), onOpenAdaptiveLesson: showAdaptiveLesson, ); case AppTab.progress: return ProgressPage( state: widget.state, onSettings: showSettings, onOpenAssessment: showAssessment, ); } } } enum _ShellRoute { tab, lesson, scene, dialogue, summary, settings, adaptiveLesson, assessmentPreparation, assessment, } class _LearningMap extends StatelessWidget { const _LearningMap({ required this.onOpenLesson, required this.onStartReinforcement, required this.onOpenReview, required this.state, }); final ValueChanged onOpenLesson; final VoidCallback onStartReinforcement; final VoidCallback onOpenReview; final AppState state; @override Widget build(BuildContext context) { return AppPage( child: SpacedColumn( children: [ const Eyebrow('学习地图 · 按掌握状态推进'), Text('从认识到开口', style: Theme.of(context).textTheme.headlineMedium), const Text('每节课都围绕一个能完成的小任务。'), if (state.reviewBacklog) SectionCard( tint: AppColors.warm, child: SpacedColumn( children: [ Text( '复习已有积压,今天先完成 ${state.dueReviewCount} 项到期复习,再开启新课。', style: const TextStyle(color: AppColors.warmInk), ), SecondaryButton(label: '先去复习', onPressed: onOpenReview), ], ), ), for (final lesson in a0SeedLessons) SectionCard( tint: lesson.id == state.activeLessonId ? AppColors.softGreen : null, onTap: state.isLessonUnlocked(lesson.id) && (!state.reviewBacklog || state.completedLessonIds.contains(lesson.id)) ? () => onOpenLesson(lesson.id) : null, child: Row( children: [ Icon( state.completedLessonIds.contains(lesson.id) ? Icons.check_circle : state.reviewBacklog ? Icons.lock_outline : state.isLessonUnlocked(lesson.id) ? Icons.play_circle_outline : Icons.lock_outline, color: state.completedLessonIds.contains(lesson.id) ? AppColors.green : AppColors.muted, ), const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '第 ${lesson.number} 课 · ${lesson.title}', style: const TextStyle(fontWeight: FontWeight.w600), ), const SizedBox(height: 3), Text( lesson.segments.length > 1 ? '${lesson.outcome} · 小段 ${lesson.segments.where((segment) => state.isSegmentComplete(segment.id)).length}/${lesson.segments.length}' : lesson.outcome, style: Theme.of(context).textTheme.bodyMedium, ), ], ), ), ], ), ), if (state.completedLessonIds.length == a0SeedLessons.length) SectionCard( tint: AppColors.softGreen, child: SpacedColumn( children: [ const Text( 'A0 巩固变式', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600), ), const Text('换一个人物、地点或情境,继续巩固尚未稳定的核心表达。'), PrimaryButton( label: '安排一题巩固练习', onPressed: onStartReinforcement, ), ], ), ), ], ), ); } }