import "package:flutter/material.dart"; import "../../core/app_state.dart"; import "../../core/app_theme.dart"; import "../../core/models.dart"; import "../../core/courses/courses.dart"; import "../dialogue/dialogue_flow.dart"; import "../assessment/assessment_page.dart"; import "../home/home_page.dart"; import "../lesson/lesson_flow.dart"; import "../profile/profile_page.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.learn; var route = _ShellRoute.tab; _ShellRoute? previousRoute; bool dialogueInLesson = false; String sceneId = 'a0-meet'; AssessmentPack? assessmentPack; DialogueSummaryData? dialogueSummary; void showTab(AppTab value) => setState(() { tab = value; route = _ShellRoute.tab; previousRoute = null; }); void showLesson() => setState(() { previousRoute = route; route = _ShellRoute.lesson; }); void showDialogue({bool inLesson = false, String? scene}) => setState(() { previousRoute = route; dialogueInLesson = inLesson; if (scene != null) sceneId = scene; route = _ShellRoute.dialogue; }); void showSummary(DialogueSummaryData summary) => setState(() { previousRoute = route; dialogueSummary = summary; route = _ShellRoute.summary; }); void showAssessment(AssessmentPack pack) => setState(() { previousRoute = route; assessmentPack = pack; route = _ShellRoute.assessmentPreparation; }); void startAssessment() => setState(() { previousRoute = route; route = _ShellRoute.assessment; }); void showAdaptiveLesson() => setState(() { previousRoute = route; route = _ShellRoute.adaptiveLesson; }); void handleBack() { switch (route) { case _ShellRoute.lesson: showTab(tab); case _ShellRoute.dialogue: if (dialogueInLesson) { showLesson(); } else { showTab(tab); } case _ShellRoute.summary: showTab(tab); case _ShellRoute.adaptiveLesson: showTab(AppTab.review); case _ShellRoute.assessmentPreparation: showTab(AppTab.profile); case _ShellRoute.assessment: if (assessmentPack != null) { showAssessment(assessmentPack!); } else { showTab(AppTab.profile); } case _ShellRoute.tab: if (tab != AppTab.learn) { showTab(AppTab.learn); } } } @override Widget build(BuildContext context) { Widget body; switch (route) { case _ShellRoute.lesson: body = LessonFlow( // 换课或换段时重建,避免上一段的听力选择和播放状态沿用过来。 key: ValueKey( '${widget.state.activeLessonId}-' '${widget.state.activeSegmentIndexFor(widget.state.activeLessonId)}', ), state: widget.state, onOpenDialogue: () => showDialogue(inLesson: true), onFinish: () => showTab(tab), ); case _ShellRoute.dialogue: body = DialoguePage( key: ValueKey(dialogueInLesson ? 'lesson' : 'scene-$sceneId'), state: widget.state, isLessonDialogue: dialogueInLesson, sceneId: sceneId, onFinished: (summary) { if (dialogueInLesson) { showLesson(); } else if (summary != null) { showSummary(summary); } else { handleBack(); } }, ); case _ShellRoute.summary: body = DialogueSummaryPage( summary: dialogueSummary!, onHome: () => showTab(AppTab.learn), onLesson: showLesson, onRetry: () => showDialogue(scene: sceneId), onBack: () => showTab(tab), ); 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.profile), onStartReplacement: showAssessment, ); case _ShellRoute.assessmentPreparation: body = AssessmentPreparationPage( state: widget.state, pack: assessmentPack!, onStart: startAssessment, onBack: () => showTab(AppTab.profile), ); case _ShellRoute.tab: body = _tabContent(); } return PopScope( canPop: route == _ShellRoute.tab && tab == AppTab.learn, onPopInvokedWithResult: (didPop, _) { if (!didPop) { handleBack(); } }, child: 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.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.learn: return HomePage( state: widget.state, onStartLesson: showLesson, onOpenLesson: (id) { widget.state.openLesson(id); showLesson(); }, onStartReview: () => showTab(AppTab.review), onResumeLessonDialogue: () => showDialogue(inLesson: true), onStartReinforcement: () { widget.state.scheduleA0Reinforcement(); showTab(AppTab.review); }, onOpenAssessment: (packId) { final pack = a0AssessmentPacks .where((item) => item.id == packId) .firstOrNull; if (pack != null) showAssessment(pack); }, ); case AppTab.dialogue: return DialogueScenePage( state: widget.state, onStart: (id) => showDialogue(scene: id), ); case AppTab.review: return ReviewPage( state: widget.state, onFinished: () => showTab(AppTab.learn), onOpenAdaptiveLesson: showAdaptiveLesson, ); case AppTab.profile: return ProfilePage( state: widget.state, onOpenAssessment: showAssessment, onOpenReview: () => showTab(AppTab.review), ); } } } enum _ShellRoute { tab, lesson, dialogue, summary, adaptiveLesson, assessmentPreparation, assessment, }