import 'package:flutter/material.dart'; import '../../core/app_state.dart'; import '../../core/app_theme.dart'; import '../../core/courses/courses.dart'; import '../../core/sync/sync_coordinator.dart'; import '../../widgets/app_widgets.dart'; import 'ai_service_page.dart'; import 'assessment_list_page.dart'; import 'learning_records_page.dart'; import 'level_progress_page.dart'; import 'preference_pages.dart'; import 'profile_widgets.dart'; import 'recordings_page.dart'; import 'sync_settings_sheet.dart'; /// 我的:概览卡、升级进度,以及进入各二级页面的入口。 class ProfilePage extends StatelessWidget { const ProfilePage({ super.key, required this.state, required this.onOpenAssessment, required this.onOpenReview, }); final AppState state; final ValueChanged onOpenAssessment; final VoidCallback onOpenReview; void _open(BuildContext context, String title, WidgetBuilder builder) => openProfilePage(context, state: state, title: title, builder: builder); void _openLevel(BuildContext context) => _open(context, '升级进度', (_) => LevelProgressView(state: state)); void _openRecords(BuildContext context) => _open(context, '学习记录', (_) => LearningRecordsView(state: state)); @override Widget build(BuildContext context) { final passedPacks = state.passedAssessmentPackCount; return AppPage( child: SpacedColumn( spacing: 16, children: [ Text('我的', style: Theme.of(context).textTheme.headlineMedium), _OverviewCard( state: state, onOpenLevel: () => _openLevel(context), onOpenRecords: () => _openRecords(context), onOpenReview: onOpenReview, ), _StageCard(state: state, onTap: () => _openLevel(context)), MenuGroup( title: '学习', children: [ MenuTile( icon: Icons.trending_up, title: '升级进度', value: '${(state.a0StageProgress * 100).round()}%', onTap: () => _openLevel(context), ), MenuTile( icon: Icons.quiz_outlined, title: '四技能评估', value: '$passedPacks/2 通过', onTap: () => _open( context, '四技能评估', (context) => AssessmentListView( state: state, onOpenAssessment: (pack) { Navigator.of(context).pop(); onOpenAssessment(pack); }, ), ), ), MenuTile( icon: Icons.history, title: '学习记录', onTap: () => _openRecords(context), ), MenuTile( icon: Icons.library_music_outlined, title: '我的录音', onTap: () => _open(context, '我的录音', (_) => RecordingsView(state: state)), ), ], ), MenuGroup( title: '设置', children: [ MenuTile( icon: Icons.tune, title: '学习偏好', value: '${state.dailyMinutes} 分钟', onTap: () => _open( context, '学习偏好', (_) => LearningPreferencesView(state: state), ), ), MenuTile( icon: Icons.palette_outlined, title: '外观', value: themeModeLabel(state.themeMode), onTap: () => _open(context, '外观', (_) => AppearanceView(state: state)), ), ListenableBuilder( listenable: SyncCoordinator.instance, builder: (context, _) { final loggedIn = SyncCoordinator.instance.isLoggedIn; return MenuTile( icon: loggedIn ? Icons.cloud_done_outlined : Icons.cloud_queue_outlined, iconColor: loggedIn ? AppColors.green : null, title: '云同步', value: loggedIn ? '已登录' : '未登录', onTap: () => SyncSettingsSheet.show(context, state), ); }, ), MenuTile( icon: Icons.smart_toy_outlined, title: 'AI 服务', value: aiProviderLabel(state.aiProvider), onTap: () => _open(context, 'AI 服务', (_) => AiServiceView(state: state)), ), ], ), MenuGroup( children: [ DangerAction( label: '清除学习进度', message: '这会清除本机的课程/分段进度、草稿、复习队列、学习作答证据、掌握记录、对话草稿、AI 补练缓存和阶段评估结果,且不可恢复。', details: '不会清除:已保存录音、AI API Key 与应用设置。录音请在“我的录音”中单独删除。', acknowledgement: '我了解这些本机学习数据无法恢复', onConfirm: state.clearProgress, ), ], ), ], ), ); } } class _OverviewCard extends StatelessWidget { const _OverviewCard({ required this.state, required this.onOpenLevel, required this.onOpenRecords, required this.onOpenReview, }); final AppState state; final VoidCallback onOpenLevel; final VoidCallback onOpenRecords; final VoidCallback onOpenReview; @override Widget build(BuildContext context) { final due = state.dueReviewCount; return SectionCard( padding: const EdgeInsets.all(16), child: Column( children: [ Row( children: [ CircleAvatar( radius: 24, backgroundColor: AppColors.softGreen, child: Icon(Icons.person, color: AppColors.green), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( state.a0Passed ? 'A0 已通过' : 'A0 起步', style: Theme.of(context).textTheme.titleMedium, ), Text( '每天 ${state.dailyMinutes} 分钟', style: Theme.of(context).textTheme.bodyMedium, ), ], ), ), ], ), Divider(height: 28, color: AppColors.line), Row( children: [ _Stat( value: state.completedLessonIds.length, label: '已学课', onTap: onOpenRecords, ), _Stat( value: state.coreUsableCount, label: '可使用', onTap: onOpenLevel, ), _Stat( value: state.coreMasteredCount, label: '已掌握', onTap: onOpenLevel, ), _Stat( value: due, label: '待复习', color: due > 0 ? AppColors.warmInk : null, onTap: onOpenReview, ), ], ), ], ), ); } } class _Stat extends StatelessWidget { const _Stat({ required this.value, required this.label, required this.onTap, this.color, }); final int value; final String label; final VoidCallback onTap; final Color? color; @override Widget build(BuildContext context) => Expanded( child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(10), child: Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Column( children: [ Text( '$value', style: TextStyle( fontSize: 22, fontWeight: FontWeight.w700, color: color ?? AppColors.ink, ), ), Text(label, style: TextStyle(fontSize: 12, color: AppColors.muted)), ], ), ), ), ); } /// 距离下一阶段的总进度,点击进入升级进度。 class _StageCard extends StatelessWidget { const _StageCard({required this.state, required this.onTap}); final AppState state; final VoidCallback onTap; @override Widget build(BuildContext context) { final missing = [ if (state.coreUsableCount < a0UsableGoal) '${a0UsableGoal - state.coreUsableCount} 项可使用', if (state.coreMasteredCount < a0MasteredGoal) '${a0MasteredGoal - state.coreMasteredCount} 项掌握', if (state.passedAssessmentPackCount < 2) '${2 - state.passedAssessmentPackCount} 套评估', ]; return SectionCard( tint: AppColors.softGreen, onTap: onTap, child: SpacedColumn( spacing: 8, children: [ Row( children: [ const Expanded( child: Text( '距离 A1', style: TextStyle(fontWeight: FontWeight.w600), ), ), Text( '${(state.a0StageProgress * 100).round()}%', style: TextStyle(color: AppColors.green), ), Icon(Icons.chevron_right, color: AppColors.green), ], ), ProgressBar( value: state.a0StageProgress, background: AppColors.surface, ), Text( missing.isEmpty ? 'A0 已通过。A1 主线内容尚未提供,可继续进行 A0 巩固。' : '还需:${missing.join(' · ')}', style: Theme.of(context).textTheme.bodyMedium, ), ], ), ); } }