Files
English/kouyu_english/lib/features/shell/learning_shell.dart
T

262 lines
7.7 KiB
Dart

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<LearningShell> createState() => _LearningShellState();
}
class _LearningShellState extends State<LearningShell> {
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,
}