整个课程和数据UI重新设计版本
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../core/app_state.dart';
|
||||
import '../../core/app_theme.dart';
|
||||
import '../../core/courses/courses.dart';
|
||||
import '../../widgets/app_widgets.dart';
|
||||
|
||||
/// 课程路径:全部课程按顺序排列,显示完成、当前与锁定状态。
|
||||
class LessonPath extends StatelessWidget {
|
||||
const LessonPath({
|
||||
super.key,
|
||||
required this.state,
|
||||
required this.onOpenLesson,
|
||||
});
|
||||
final AppState state;
|
||||
final ValueChanged<String> onOpenLesson;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final done = allLessons
|
||||
.where((lesson) => state.completedLessonIds.contains(lesson.id))
|
||||
.length;
|
||||
return SpacedColumn(
|
||||
spacing: 10,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
'课程路径',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'$done/${allLessons.length} 完成',
|
||||
style: TextStyle(fontSize: 13, color: AppColors.muted),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (state.reviewBacklog)
|
||||
Text(
|
||||
'复习有积压:先完成到期复习,新课暂时锁定;已学课程仍可重温。',
|
||||
style: TextStyle(fontSize: 13, color: AppColors.warmInk),
|
||||
),
|
||||
for (final lesson in allLessons)
|
||||
_LessonTile(
|
||||
lesson: lesson,
|
||||
state: state,
|
||||
onTap: () => onOpenLesson(lesson.id),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _LessonTile extends StatelessWidget {
|
||||
const _LessonTile({
|
||||
required this.lesson,
|
||||
required this.state,
|
||||
required this.onTap,
|
||||
});
|
||||
final SeedLesson lesson;
|
||||
final AppState state;
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final completed = state.completedLessonIds.contains(lesson.id);
|
||||
final current = lesson.id == state.activeLessonId && !completed;
|
||||
final open =
|
||||
state.isLessonUnlocked(lesson.id) &&
|
||||
(!state.reviewBacklog || completed);
|
||||
final segments = lesson.segments.length;
|
||||
final segmentsDone = lesson.segments
|
||||
.where((segment) => state.isSegmentComplete(segment.id))
|
||||
.length;
|
||||
return SectionCard(
|
||||
tint: current ? AppColors.softGreen : null,
|
||||
onTap: open ? onTap : null,
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
completed
|
||||
? Icons.check_circle
|
||||
: open
|
||||
? Icons.play_circle_outline
|
||||
: Icons.lock_outline,
|
||||
color: completed || current ? 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(
|
||||
segments > 1
|
||||
? '${lesson.outcome} · 小段 $segmentsDone/$segments'
|
||||
: lesson.outcome,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user