Enhance video playback cubit resilience and K-12 curriculum flow

This commit is contained in:
Hamza-Ayed
2026-08-31 17:46:03 +03:00
parent 78b703f625
commit d8a50bb105
34 changed files with 2934 additions and 75 deletions
@@ -0,0 +1,388 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import '../../../core/theme/app_colors.dart';
import '../../../core/theme/app_theme.dart';
import '../../../core/utils/saqel_toast.dart';
import '../../../data/models/subject_model.dart';
import '../../widgets/luxury_widgets.dart';
import '../player/socratic_video_player_screen.dart';
class SubjectHubScreen extends StatefulWidget {
final SubjectModel subject;
const SubjectHubScreen({super.key, required this.subject});
@override
State<SubjectHubScreen> createState() => _SubjectHubScreenState();
}
class _SubjectHubScreenState extends State<SubjectHubScreen> with SingleTickerProviderStateMixin {
late TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(length: 4, vsync: this);
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.darkBackground,
appBar: AppBar(
backgroundColor: AppColors.darkSurface,
elevation: 0,
leading: IconButton(
icon: const Icon(CupertinoIcons.back, color: Colors.white),
onPressed: () => Navigator.of(context).pop(),
),
title: Text(
widget.subject.title,
style: AppTypography.titleLarge.copyWith(color: Colors.white, fontSize: 18),
),
bottom: TabBar(
controller: _tabController,
indicatorColor: widget.subject.primaryColor,
indicatorWeight: 3,
labelColor: Colors.white,
unselectedLabelColor: AppColors.textSecondaryDark,
labelStyle: const TextStyle(fontWeight: FontWeight.w700, fontSize: 13),
tabs: const [
Tab(icon: Icon(CupertinoIcons.play_rectangle_fill, size: 18), text: 'الفيديوهات'),
Tab(icon: Icon(CupertinoIcons.doc_text_fill, size: 18), text: 'أوراق العمل'),
Tab(icon: Icon(CupertinoIcons.sparkles, size: 18), text: 'بنك الأسئلة'),
Tab(icon: Icon(CupertinoIcons.book_fill, size: 18), text: 'الكتب المقررة'),
],
),
),
body: TabBarView(
controller: _tabController,
children: [
_buildLessonsTab(context),
_buildWorksheetsTab(context),
_buildExamsTab(context),
_buildTextbooksTab(context),
],
),
);
}
/// Tab 1: Interactive Video Lessons by Unit
Widget _buildLessonsTab(BuildContext context) {
final units = widget.subject.units;
if (units.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(CupertinoIcons.play_circle, size: 56, color: widget.subject.primaryColor.withAlpha(120)),
const SizedBox(height: 16),
Text(
'جاري تحضير دروس ${widget.subject.title} بواسطة الذكاء الاصطناعي',
style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 14),
),
],
),
);
}
return ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 20),
itemCount: units.length,
itemBuilder: (context, uIdx) {
final unit = units[uIdx];
return Container(
margin: const EdgeInsets.only(bottom: 20),
decoration: BoxDecoration(
color: AppColors.darkSurface,
borderRadius: BorderRadius.circular(20),
border: Border.all(color: AppColors.darkCardBorder),
),
child: ExpansionTile(
initiallyExpanded: uIdx == 0,
shape: const Border(),
collapsedShape: const Border(),
leading: Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: widget.subject.primaryColor.withAlpha(30),
shape: BoxShape.circle,
),
child: Center(
child: Text(
'${uIdx + 1}',
style: TextStyle(
color: widget.subject.primaryColor,
fontWeight: FontWeight.w800,
fontSize: 15,
),
),
),
),
title: Text(
unit.name,
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w700, fontSize: 15),
),
subtitle: Text(
'${unit.lessons.length} دروس تفاعلية • فحص فهم سقراطي',
style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 12),
),
children: unit.lessons.map((lesson) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 14, vertical: 6),
decoration: BoxDecoration(
color: const Color(0xFF09111E),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: AppColors.darkCardBorder.withAlpha(80)),
),
child: ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 4),
leading: Container(
width: 38,
height: 38,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [widget.subject.primaryColor, widget.subject.secondaryColor],
),
borderRadius: BorderRadius.circular(10),
),
child: const Icon(CupertinoIcons.play_fill, color: Colors.black, size: 18),
),
title: Text(
lesson.title,
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w600, fontSize: 13),
),
subtitle: Row(
children: [
Text(
'${lesson.durationSeconds ~/ 60} دقيقة',
style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 11),
),
const SizedBox(width: 8),
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: AppColors.saqelCyan.withAlpha(25),
borderRadius: BorderRadius.circular(6),
),
child: Text(
'${lesson.checkpointsCount} فحوصات',
style: const TextStyle(color: AppColors.saqelCyan, fontSize: 10, fontWeight: FontWeight.w700),
),
),
],
),
trailing: const Icon(CupertinoIcons.chevron_back, color: AppColors.textSecondaryDark, size: 16),
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => SocraticVideoPlayerScreen(
lesson: lesson,
subject: widget.subject,
),
),
);
},
),
);
}).toList(),
),
);
},
);
}
/// Tab 2: Worksheets & Summaries
Widget _buildWorksheetsTab(BuildContext context) {
final worksheets = widget.subject.worksheets.isNotEmpty
? widget.subject.worksheets
: [
const ResourceItemModel(title: 'ورقة عمل 1: المفاهيم الأساسية والتطبيقات', filePath: 'ws1.pdf', type: 'worksheet'),
const ResourceItemModel(title: 'ملخص شامل: القوانين والمعادلات الوزارية المقررة', filePath: 'summary.pdf', type: 'summary'),
const ResourceItemModel(title: 'مراجعة ختامية ونماذج تدريبية شاملة', filePath: 'exam_prep.pdf', type: 'worksheet'),
];
return ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 20),
itemCount: worksheets.length,
itemBuilder: (context, idx) {
final ws = worksheets[idx];
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: LuxuryCard(
child: Row(
children: [
Container(
width: 44,
height: 44,
decoration: BoxDecoration(
color: AppColors.crimsonRed.withAlpha(25),
borderRadius: BorderRadius.circular(12),
),
child: const Icon(CupertinoIcons.doc_fill, color: AppColors.crimsonRed, size: 22),
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
ws.title,
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w700, fontSize: 14),
),
const SizedBox(height: 4),
const Text(
'ملف PDF جاهز للطباعة والمراجعة السريعة',
style: TextStyle(color: AppColors.textSecondaryDark, fontSize: 12),
),
],
),
),
IconButton(
icon: const Icon(CupertinoIcons.arrow_down_circle_fill, color: AppColors.saqelCyan, size: 28),
onPressed: () {
SaqelToast.showSuccess(context, 'تم تجهيز ملف ${ws.title} للتحميل', title: 'تحميل المذكرة 📄');
},
),
],
),
),
);
},
);
}
/// Tab 3: Question Bank & Adaptive Unit Exams
Widget _buildExamsTab(BuildContext context) {
final exams = [
const SubjectExamModel(id: 'ex1', title: 'اختبار الفهم الشامل: الوحدة الأولى', questionsCount: 25, durationMinutes: 45, targetScore: 100),
const SubjectExamModel(id: 'ex2', title: 'نماذج أسئلة الوزارة للسنوات السابقة', questionsCount: 30, durationMinutes: 60, targetScore: 100),
const SubjectExamModel(id: 'ex3', title: 'اختبار تشخيص الثغرات التكيفي بالذكاء الاصطناعي', questionsCount: 15, durationMinutes: 25, targetScore: 100),
];
return ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 20),
itemCount: exams.length,
itemBuilder: (context, idx) {
final exam = exams[idx];
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: LuxuryCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
exam.title,
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w700, fontSize: 14),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: AppColors.emeraldGreen.withAlpha(25),
borderRadius: BorderRadius.circular(8),
),
child: const Text(
'جاهز للتقديم ✍️',
style: TextStyle(color: AppColors.emeraldGreen, fontSize: 11, fontWeight: FontWeight.w700),
),
),
],
),
const SizedBox(height: 12),
Row(
children: [
const Icon(CupertinoIcons.question_circle, color: AppColors.textSecondaryDark, size: 14),
const SizedBox(width: 4),
Text('${exam.questionsCount} سؤال اختياري', style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 12)),
const SizedBox(width: 14),
const Icon(CupertinoIcons.time, color: AppColors.textSecondaryDark, size: 14),
const SizedBox(width: 4),
Text('${exam.durationMinutes} دقيقة', style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 12)),
const Spacer(),
LuxuryButton(
text: 'بدء الاختبار 🚀',
onPressed: () {
SaqelToast.showInfo(context, 'جاري توليد أسئلة الاختبار التكيفي من بنك الوزارة', title: 'بدء الامتحان 📝');
},
),
],
),
],
),
),
);
},
);
}
/// Tab 4: Official Ministry Textbooks
Widget _buildTextbooksTab(BuildContext context) {
final textbooks = widget.subject.textbooks.isNotEmpty
? widget.subject.textbooks
: [
const ResourceItemModel(title: 'كتاب الطالب المقرّر — منهاج وزارة التربية والتعليم', filePath: 'book.pdf', type: 'textbook'),
const ResourceItemModel(title: 'كتاب التجارب والأنشطة العلمية والعملية', filePath: 'workbook.pdf', type: 'textbook'),
];
return ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 20),
itemCount: textbooks.length,
itemBuilder: (context, idx) {
final tb = textbooks[idx];
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: LuxuryCard(
child: Row(
children: [
Container(
width: 46,
height: 46,
decoration: BoxDecoration(
color: AppColors.appleBlue.withAlpha(25),
borderRadius: BorderRadius.circular(12),
),
child: const Icon(CupertinoIcons.book_fill, color: AppColors.appleBlue, size: 22),
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
tb.title,
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w700, fontSize: 14),
),
const SizedBox(height: 4),
const Text(
'منهاج وزارة التربية والتعليم الأردنية المعتمد (PDF كامل)',
style: TextStyle(color: AppColors.textSecondaryDark, fontSize: 12),
),
],
),
),
IconButton(
icon: const Icon(CupertinoIcons.eye_fill, color: AppColors.saqelCyan, size: 24),
onPressed: () {
SaqelToast.showInfo(context, 'جاري فتح ${tb.title} عبر عارض الكتب الذكي', title: 'الكتاب الوزاري 📚');
},
),
],
),
),
);
},
);
}
}
@@ -0,0 +1,413 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../core/theme/app_colors.dart';
import '../../../core/theme/app_theme.dart';
import '../../../data/models/subject_model.dart';
import '../../../data/repositories/curriculum_repository.dart';
import '../../../logic/cubits/curriculum_cubit.dart';
import '../../widgets/luxury_widgets.dart';
import 'subject_hub_screen.dart';
class SubjectsGridScreen extends StatefulWidget {
final String? initialGrade;
const SubjectsGridScreen({super.key, this.initialGrade});
@override
State<SubjectsGridScreen> createState() => _SubjectsGridScreenState();
}
class _SubjectsGridScreenState extends State<SubjectsGridScreen> {
final TextEditingController _searchController = TextEditingController();
@override
void initState() {
super.initState();
final grade = widget.initialGrade ?? 'grade_10';
context.read<CurriculumCubit>().fetchSubjects(gradeLevel: grade);
}
@override
void dispose() {
_searchController.dispose();
super.dispose();
}
IconData _getCupertinoIcon(String iconCode) {
switch (iconCode) {
case 'calculator':
return CupertinoIcons.number_square_fill;
case 'book_closed_fill':
return CupertinoIcons.book_fill;
case 'chat_bubble_2_fill':
return CupertinoIcons.chat_bubble_2_fill;
case 'moon_stars_fill':
return CupertinoIcons.moon_stars_fill;
case 'bolt_fill':
return CupertinoIcons.bolt_fill;
case 'flame_fill':
return CupertinoIcons.flame_fill;
case 'tree_fill':
return CupertinoIcons.tree;
case 'globe':
return CupertinoIcons.globe;
case 'building_columns_fill':
return CupertinoIcons.building_2_fill;
case 'map_fill':
return CupertinoIcons.map_fill;
case 'person_2_fill':
return CupertinoIcons.person_2_fill;
case 'desktopcomputer':
return CupertinoIcons.desktopcomputer;
case 'creditcard_fill':
return CupertinoIcons.money_dollar_circle_fill;
case 'wrench_fill':
return CupertinoIcons.wrench_fill;
case 'paintbrush_fill':
return CupertinoIcons.paintbrush_fill;
case 'sportscourt_fill':
return CupertinoIcons.sportscourt_fill;
default:
return CupertinoIcons.book_fill;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.darkBackground,
appBar: AppBar(
backgroundColor: AppColors.darkSurface,
elevation: 0,
title: Row(
children: [
Container(
width: 32,
height: 32,
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [AppColors.appleBlue, AppColors.saqelCyan],
),
borderRadius: BorderRadius.circular(8),
),
child: const Center(
child: Text('ص', style: TextStyle(color: Colors.black, fontWeight: FontWeight.w900, fontSize: 18)),
),
),
const SizedBox(width: 10),
const Text(
'المناهج والمباحث الدراسية',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.w800, fontSize: 17),
),
],
),
actions: [
Container(
margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 14),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
decoration: BoxDecoration(
color: AppColors.saqelCyan.withAlpha(25),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: AppColors.saqelCyan.withAlpha(80)),
),
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(CupertinoIcons.checkmark_seal_fill, color: AppColors.saqelCyan, size: 14),
SizedBox(width: 6),
Text(
'المنهاج الوزاري الأردني 🇯🇴',
style: TextStyle(color: AppColors.saqelCyan, fontSize: 11, fontWeight: FontWeight.w700),
),
],
),
),
],
),
body: BlocBuilder<CurriculumCubit, CurriculumState>(
builder: (context, state) {
if (state is CurriculumLoading) {
return const Center(child: CircularProgressIndicator(color: AppColors.saqelCyan));
}
if (state is CurriculumError) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(CupertinoIcons.wifi_exclamationmark, color: AppColors.crimsonRed, size: 48),
const SizedBox(height: 14),
Text(state.message, style: const TextStyle(color: Colors.white)),
const SizedBox(height: 14),
LuxuryButton(
text: 'إعادة المحاولة',
onPressed: () => context.read<CurriculumCubit>().fetchSubjects(),
),
],
),
);
}
if (state is CurriculumLoaded) {
return SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Grade Level Header & Stage Switcher
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'المواد المقررة — ${GradeLevelModel.getGradeName(state.selectedGrade)} 📚',
style: AppTypography.displayLarge.copyWith(color: Colors.white, fontSize: 20),
),
const SizedBox(height: 4),
const Text(
'اختر المادة لتصفح شروحات الفيديو التفاعلية، المذكرات، وبنك الأسئلة',
style: TextStyle(color: AppColors.textSecondaryDark, fontSize: 13),
),
],
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: AppColors.darkSurface,
borderRadius: BorderRadius.circular(14),
border: Border.all(color: AppColors.darkCardBorder),
),
child: Text(
'${state.filteredSubjects.length} مباحث متوفرة',
style: const TextStyle(color: AppColors.saqelCyan, fontSize: 12, fontWeight: FontWeight.w700),
),
),
],
),
const SizedBox(height: 16),
// K-12 Grade Level Selector Bar (الصف الأول -> التوجيهي)
SizedBox(
height: 42,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: (state.availableGrades.isNotEmpty ? state.availableGrades : GradeLevelModel.allK12Grades).length,
itemBuilder: (context, gIdx) {
final grade = (state.availableGrades.isNotEmpty ? state.availableGrades : GradeLevelModel.allK12Grades)[gIdx];
final isSelected = state.selectedGrade == grade.key || state.selectedGrade.replaceAll('grade_', '') == grade.key.replaceAll('grade_', '');
return Padding(
padding: const EdgeInsets.only(left: 8),
child: InkWell(
onTap: () => context.read<CurriculumCubit>().selectGrade(grade.key),
borderRadius: BorderRadius.circular(14),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: isSelected ? AppColors.saqelCyan.withAlpha(30) : AppColors.darkSurface,
borderRadius: BorderRadius.circular(14),
border: Border.all(
color: isSelected ? AppColors.saqelCyan : AppColors.darkCardBorder,
width: isSelected ? 1.5 : 1.0,
),
),
child: Center(
child: Text(
grade.name,
style: TextStyle(
color: isSelected ? AppColors.saqelCyan : AppColors.textSecondaryDark,
fontWeight: isSelected ? FontWeight.w800 : FontWeight.w500,
fontSize: 12.5,
),
),
),
),
),
);
},
),
),
const SizedBox(height: 20),
// Search Bar
LuxuryTextField(
controller: _searchController,
hint: 'ابحث عن مادة دراسية (مثلاً: الفيزياء، الرياضيات، الإنجليزي)...',
prefixIcon: const Icon(CupertinoIcons.search, color: AppColors.saqelCyan, size: 18),
onChanged: (q) => context.read<CurriculumCubit>().searchSubjects(q),
),
const SizedBox(height: 24),
// Dynamic Live Subjects Bento Grid or Empty State
if (state.filteredSubjects.isEmpty)
Container(
padding: const EdgeInsets.symmetric(vertical: 60, horizontal: 20),
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 64,
height: 64,
decoration: BoxDecoration(
color: AppColors.saqelCyan.withAlpha(20),
shape: BoxShape.circle,
),
child: const Icon(CupertinoIcons.square_grid_2x2, size: 32, color: AppColors.saqelCyan),
),
const SizedBox(height: 16),
const Text(
'لا توجد مواد مضافة بعد في السيرفر',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.w700, fontSize: 16),
),
const SizedBox(height: 6),
const Text(
'يتم إدراج المواد تلقائياً بمجرد رفع وتحليل كتب المناهج في الخادم',
textAlign: TextAlign.center,
style: TextStyle(color: AppColors.textSecondaryDark, fontSize: 13),
),
],
),
)
else
LayoutBuilder(
builder: (context, constraints) {
int crossAxisCount = 4;
if (constraints.maxWidth < 600) {
crossAxisCount = 2;
} else if (constraints.maxWidth < 900) {
crossAxisCount = 3;
}
return GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
childAspectRatio: 1.15,
),
itemCount: state.filteredSubjects.length,
itemBuilder: (context, idx) {
final sub = state.filteredSubjects[idx];
return _buildSubjectCard(context, sub);
},
);
},
),
],
),
);
}
return const SizedBox.shrink();
},
),
);
}
/// Individual Apple Bento Card for Subject
Widget _buildSubjectCard(BuildContext context, SubjectModel subject) {
final iconData = _getCupertinoIcon(subject.iconCode);
return InkWell(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => SubjectHubScreen(subject: subject),
),
);
},
borderRadius: BorderRadius.circular(22),
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColors.darkSurface,
borderRadius: BorderRadius.circular(22),
border: Border.all(color: AppColors.darkCardBorder, width: 1.2),
boxShadow: const [
BoxShadow(
color: Color(0x33000000),
blurRadius: 16,
offset: Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
subject.primaryColor.withAlpha(50),
subject.secondaryColor.withAlpha(20),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: subject.primaryColor.withAlpha(120), width: 1.5),
),
child: Icon(iconData, color: subject.primaryColor, size: 24),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: Colors.black.withAlpha(80),
borderRadius: BorderRadius.circular(8),
),
child: Text(
'${subject.totalUnits} وحدات',
style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 10, fontWeight: FontWeight.w700),
),
),
],
),
const Spacer(),
Text(
subject.title,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w800,
fontSize: 15,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 2),
Text(
subject.englishTitle,
style: const TextStyle(
color: AppColors.textSecondaryDark,
fontSize: 11,
fontWeight: FontWeight.w500,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 10),
// Mini Mastery Progress Bar
ClipRRect(
borderRadius: BorderRadius.circular(4),
child: LinearProgressIndicator(
value: subject.masteryScore / 100.0,
backgroundColor: Colors.white10,
valueColor: AlwaysStoppedAnimation<Color>(subject.primaryColor),
minHeight: 4,
),
),
],
),
),
);
}
}
@@ -10,6 +10,7 @@ import '../../../data/models/lesson_model.dart';
import '../../../logic/cubits/auth_cubit.dart';
import '../../../logic/cubits/dashboard_cubits.dart';
import '../../widgets/luxury_widgets.dart';
import '../curriculum/subjects_grid_screen.dart';
class UnifiedHomeScreen extends StatefulWidget {
final UserModel user;
@@ -209,13 +210,43 @@ class _UnifiedHomeScreenState extends State<UnifiedHomeScreen> {
);
} else if (state is StudentEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(CupertinoIcons.book, color: AppColors.textMutedDark, size: 48),
const SizedBox(height: 12),
Text(AppStrings.noLessonsFound, style: const TextStyle(color: AppColors.textSecondaryDark)),
],
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 72,
height: 72,
decoration: BoxDecoration(
color: AppColors.saqelCyan.withAlpha(25),
shape: BoxShape.circle,
border: Border.all(color: AppColors.saqelCyan.withAlpha(80), width: 1.5),
),
child: const Icon(CupertinoIcons.book_fill, color: AppColors.saqelCyan, size: 36),
),
const SizedBox(height: 18),
const Text(
'المنهاج الوزاري المعتمد — صَقِل 🇯🇴',
style: TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.w800),
),
const SizedBox(height: 8),
const Text(
'اختر من بين 16 مبحثاً دراسياً لبدء شروحات الفيديو التفاعلية، أوراق العمل، وبنك الأسئلة السقراطي',
textAlign: TextAlign.center,
style: TextStyle(color: AppColors.textSecondaryDark, fontSize: 13, height: 1.4),
),
const SizedBox(height: 24),
LuxuryButton(
text: 'تصفح المناهج والمواد 📚',
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (_) => SubjectsGridScreen(initialGrade: widget.user.gradeLevel)),
);
},
),
],
),
),
);
} else if (state is StudentLoaded) {
@@ -224,6 +255,69 @@ class _UnifiedHomeScreenState extends State<UnifiedHomeScreen> {
child: ListView(
padding: const EdgeInsets.all(20),
children: [
// Subjects Catalog Banner (Top Gateway)
InkWell(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (_) => SubjectsGridScreen(initialGrade: widget.user.gradeLevel)),
);
},
borderRadius: BorderRadius.circular(20),
child: Container(
padding: const EdgeInsets.all(18),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF0F2B48), Color(0xFF0A192F)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: AppColors.saqelCyan.withAlpha(100), width: 1.5),
boxShadow: [
BoxShadow(
color: AppColors.saqelCyan.withAlpha(30),
blurRadius: 20,
offset: const Offset(0, 6),
),
],
),
child: Row(
children: [
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [AppColors.appleBlue, AppColors.saqelCyan],
),
borderRadius: BorderRadius.circular(14),
),
child: const Icon(CupertinoIcons.square_grid_2x2_fill, color: Colors.black, size: 24),
),
const SizedBox(width: 14),
const Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'تصفح المناهج والمواد (16 مادة) 📚',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.w800, fontSize: 15),
),
SizedBox(height: 4),
Text(
'الرياضيات، الفيزياء، الإنجليزي، الكيمياء، والمزيد',
style: TextStyle(color: AppColors.textSecondaryDark, fontSize: 12),
),
],
),
),
const Icon(CupertinoIcons.chevron_back, color: AppColors.saqelCyan, size: 18),
],
),
),
),
const SizedBox(height: 20),
// Real Readiness Score Card
LuxuryCard(
borderColor: AppColors.saqelCyan.withAlpha(50),
@@ -0,0 +1,463 @@
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../core/theme/app_colors.dart';
import '../../../core/theme/app_theme.dart';
import '../../../core/utils/saqel_toast.dart';
import '../../../data/models/subject_model.dart';
import '../../../logic/cubits/auth_cubit.dart';
import '../../../logic/cubits/video_playback_cubit.dart';
import '../../widgets/luxury_widgets.dart';
import '../../widgets/socratic_dialog.dart';
class SocraticVideoPlayerScreen extends StatefulWidget {
final CurriculumLessonItemModel lesson;
final SubjectModel? subject;
const SocraticVideoPlayerScreen({
super.key,
required this.lesson,
this.subject,
});
@override
State<SocraticVideoPlayerScreen> createState() => _SocraticVideoPlayerScreenState();
}
class _SocraticVideoPlayerScreenState extends State<SocraticVideoPlayerScreen> with SingleTickerProviderStateMixin {
Timer? _playbackTicker;
late AnimationController _watermarkController;
@override
void initState() {
super.initState();
context.read<VideoPlaybackCubit>().loadLesson(widget.lesson, subject: widget.subject);
// Dynamic Floating Forensic Anti-Piracy Watermark Animation
_watermarkController = AnimationController(
vsync: this,
duration: const Duration(seconds: 18),
)..repeat(reverse: true);
// Ticker simulating video playback time progression and Socratic checkpoint gatekeeping
_playbackTicker = Timer.periodic(const Duration(seconds: 1), (timer) {
final cubit = context.read<VideoPlaybackCubit>();
final state = cubit.state;
if (state is VideoPlaybackReady && state.isPlaying && state.activeCheckpoint == null) {
if (state.currentPositionSeconds < state.playbackData.durationSeconds) {
cubit.updatePosition(state.currentPositionSeconds + 1);
}
}
});
}
@override
void dispose() {
_playbackTicker?.cancel();
_watermarkController.dispose();
super.dispose();
}
String _formatTime(int seconds) {
final m = seconds ~/ 60;
final s = seconds % 60;
return '${m.toString().padLeft(2, '0')}:${s.toString().padLeft(2, '0')}';
}
@override
Widget build(BuildContext context) {
final authState = context.read<AuthCubit>().state;
final studentName = authState is Authenticated ? authState.user.name : 'طالب صَقِل';
final studentPhone = authState is Authenticated ? authState.user.phone : '0798583052';
return Scaffold(
backgroundColor: AppColors.darkBackground,
appBar: AppBar(
backgroundColor: AppColors.darkSurface,
elevation: 0,
leading: IconButton(
icon: const Icon(CupertinoIcons.back, color: Colors.white),
onPressed: () => Navigator.of(context).pop(),
),
title: Text(
widget.lesson.title,
style: AppTypography.titleLarge.copyWith(color: Colors.white, fontSize: 16),
),
centerTitle: false,
actions: [
Container(
margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 14),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
decoration: BoxDecoration(
color: AppColors.saqelCyan.withAlpha(30),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: AppColors.saqelCyan.withAlpha(80)),
),
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(CupertinoIcons.shield_lefthalf_fill, color: AppColors.saqelCyan, size: 14),
SizedBox(width: 6),
Text(
'حماية جنائية DRM',
style: TextStyle(color: AppColors.saqelCyan, fontSize: 11, fontWeight: FontWeight.w700),
),
],
),
),
],
),
body: BlocConsumer<VideoPlaybackCubit, VideoPlaybackState>(
listener: (context, state) {
if (state is VideoPlaybackReady) {
// Trigger Socratic Checkpoint Modal automatically when active
if (state.activeCheckpoint != null) {
showDialog(
context: context,
barrierDismissible: false,
builder: (modalContext) => SocraticCheckpointModal(
checkpoint: state.activeCheckpoint!,
onOptionSelected: (opt) {
final isCorrect = context.read<VideoPlaybackCubit>().submitCheckpointAnswer(opt);
if (isCorrect) {
SaqelToast.showSuccess(context, 'إجابة نموذجية! تم تعزيز الجاهزية (+0.5%)', title: 'فحص الفهم السقراطي ✨');
} else {
SaqelToast.showError(context, 'تم إرجاع الفيديو لإعادة الاستماع للنقطة الجوهرية 🔄', title: 'سد الثغرة العلاجية');
}
},
),
);
}
}
},
builder: (context, state) {
if (state is VideoPlaybackLoading) {
return const Center(
child: CircularProgressIndicator(color: AppColors.saqelCyan),
);
}
if (state is VideoPlaybackError) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(CupertinoIcons.exclamationmark_triangle_fill, color: AppColors.crimsonRed, size: 48),
const SizedBox(height: 16),
Text(state.message, style: const TextStyle(color: Colors.white)),
const SizedBox(height: 16),
LuxuryButton(
text: 'إعادة المحاولة',
onPressed: () => context.read<VideoPlaybackCubit>().loadLesson(widget.lesson),
),
],
),
);
}
if (state is VideoPlaybackReady) {
final duration = state.playbackData.durationSeconds > 0 ? state.playbackData.durationSeconds : 960;
final current = state.currentPositionSeconds;
final progress = (current / duration).clamp(0.0, 1.0);
return SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 1000),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Video Viewport Canvas with Anti-Piracy Floating Watermark
Container(
height: 380,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(24),
border: Border.all(color: AppColors.darkCardBorder),
boxShadow: const [
BoxShadow(
color: Color(0x66000000),
blurRadius: 30,
offset: Offset(0, 12),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(24),
child: Stack(
children: [
// Video Background Scene Simulation
Container(
decoration: const BoxDecoration(
gradient: RadialGradient(
center: Alignment(0.0, -0.2),
radius: 1.2,
colors: [
Color(0xFF0F1E36),
Color(0xFF050B14),
],
),
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
color: AppColors.saqelCyan.withAlpha(25),
shape: BoxShape.circle,
border: Border.all(color: AppColors.saqelCyan.withAlpha(100), width: 2),
),
child: IconButton(
icon: Icon(
state.isPlaying ? CupertinoIcons.pause_fill : CupertinoIcons.play_fill,
color: AppColors.saqelCyan,
size: 36,
),
onPressed: () => context.read<VideoPlaybackCubit>().togglePlayPause(),
),
),
const SizedBox(height: 16),
Text(
widget.lesson.title,
style: const TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w700,
),
),
const SizedBox(height: 6),
const Text(
'بث مشفر فائق الجودة HLS 1080p — منصة صَقِل',
style: TextStyle(
color: AppColors.textSecondaryDark,
fontSize: 12,
),
),
],
),
),
),
// Dynamic Forensic Anti-Piracy Watermark (Moves continuously)
AnimatedBuilder(
animation: _watermarkController,
builder: (context, child) {
return Align(
alignment: Alignment(
-0.8 + (1.6 * _watermarkController.value),
-0.7 + (1.4 * _watermarkController.value),
),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: Colors.black.withAlpha(100),
borderRadius: BorderRadius.circular(8),
),
child: Text(
'$studentName • $studentPhone',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w700,
color: Colors.white.withAlpha(90),
letterSpacing: 0.5,
),
),
),
);
},
),
// Video Controls Overlay (Bottom)
Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.transparent, Color(0xE6000000)],
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Socratic Timeline Progress Bar with Checkpoint Markers
Stack(
alignment: Alignment.centerLeft,
children: [
SliderTheme(
data: SliderTheme.of(context).copyWith(
trackHeight: 5,
thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 7),
overlayShape: const RoundSliderOverlayShape(overlayRadius: 14),
activeTrackColor: AppColors.saqelCyan,
inactiveTrackColor: Colors.white24,
thumbColor: AppColors.saqelCyan,
),
child: Slider(
value: progress,
onChanged: (val) {
final targetSec = (val * duration).toInt();
context.read<VideoPlaybackCubit>().seekTo(targetSec);
},
),
),
// Checkpoint markers on the progress track
...state.playbackData.checkpoints.map((cp) {
final posFraction = (cp.timestampSeconds / duration).clamp(0.0, 1.0);
final isPassed = state.passedCheckpointIds.contains(cp.id);
return Positioned(
left: (MediaQuery.of(context).size.width * 0.85 * posFraction).clamp(20.0, 700.0),
child: Container(
width: 10,
height: 10,
decoration: BoxDecoration(
color: isPassed ? AppColors.emeraldGreen : AppColors.guardianAmber,
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 1.5),
boxShadow: [
BoxShadow(
color: (isPassed ? AppColors.emeraldGreen : AppColors.guardianAmber).withAlpha(120),
blurRadius: 6,
),
],
),
),
);
}),
],
),
Row(
children: [
IconButton(
icon: Icon(
state.isPlaying ? CupertinoIcons.pause_fill : CupertinoIcons.play_fill,
color: Colors.white,
size: 20,
),
onPressed: () => context.read<VideoPlaybackCubit>().togglePlayPause(),
),
const SizedBox(width: 8),
Text(
'${_formatTime(current)} / ${_formatTime(duration)}',
style: const TextStyle(color: Colors.white70, fontSize: 12, fontWeight: FontWeight.w600),
),
const Spacer(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: AppColors.darkSurface,
borderRadius: BorderRadius.circular(8),
),
child: Text(
'${state.passedCheckpointIds.length}/${state.playbackData.checkpoints.length} فحص منجز',
style: const TextStyle(color: AppColors.saqelCyan, fontSize: 11, fontWeight: FontWeight.w700),
),
),
],
),
],
),
),
),
],
),
),
),
const SizedBox(height: 20),
// Remediation Feedback Alert (if triggered)
if (state.remediationNotice != null) ...[
Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: AppColors.saqelCyan.withAlpha(20),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: AppColors.saqelCyan.withAlpha(60)),
),
child: Row(
children: [
const Icon(CupertinoIcons.sparkles, color: AppColors.saqelCyan, size: 20),
const SizedBox(width: 12),
Expanded(
child: Text(
state.remediationNotice!,
style: const TextStyle(color: Colors.white, fontSize: 13, fontWeight: FontWeight.w600),
),
),
],
),
),
const SizedBox(height: 20),
],
// Lesson Meta & Learning Outcomes
LuxuryCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'نتاجات التعلم والمفاهيم الوزارية 🎯',
style: AppTypography.titleLarge.copyWith(color: Colors.white, fontSize: 16),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: AppColors.appleBlue.withAlpha(30),
borderRadius: BorderRadius.circular(12),
),
child: Text(
widget.subject?.title ?? 'المنهاج الرسمي',
style: const TextStyle(color: AppColors.appleBlue, fontSize: 12, fontWeight: FontWeight.w700),
),
),
],
),
const SizedBox(height: 14),
if (widget.lesson.outcomes.isNotEmpty)
...widget.lesson.outcomes.map((out) {
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Icon(CupertinoIcons.check_mark_circled_solid, color: AppColors.saqelCyan, size: 16),
const SizedBox(width: 10),
Expanded(
child: Text(
out,
style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 13, height: 1.4),
),
),
],
),
);
})
else
const Text(
'شرح المفاهيم والقواعد الوزارية المقررة مع التطبيقات الحياتية ونماذج الامتحانات.',
style: TextStyle(color: AppColors.textSecondaryDark, fontSize: 13),
),
],
),
),
],
),
),
);
}
return const SizedBox.shrink();
},
),
);
}
}
@@ -120,15 +120,17 @@ class LuxuryTextField extends StatelessWidget {
final TextInputType keyboardType;
final Widget? prefixIcon;
final bool enabled;
final ValueChanged<String>? onChanged;
const LuxuryTextField({
super.key,
required this.controller,
required this.label,
this.label = '',
required this.hint,
this.keyboardType = TextInputType.text,
this.prefixIcon,
this.enabled = true,
this.onChanged,
});
@override
@@ -136,18 +138,21 @@ class LuxuryTextField extends StatelessWidget {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: AppTypography.titleMedium.copyWith(
color: AppColors.textSecondaryDark,
fontSize: 13,
if (label.isNotEmpty) ...[
Text(
label,
style: AppTypography.titleMedium.copyWith(
color: AppColors.textSecondaryDark,
fontSize: 13,
),
),
),
const SizedBox(height: 8),
const SizedBox(height: 8),
],
TextField(
controller: controller,
keyboardType: keyboardType,
enabled: enabled,
onChanged: onChanged,
style: AppTypography.bodyMedium.copyWith(
color: Colors.white,
fontSize: 15,
@@ -0,0 +1,255 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import '../../core/theme/app_colors.dart';
import '../../core/theme/app_theme.dart';
import '../../data/models/socratic_checkpoint_model.dart';
import 'luxury_widgets.dart';
class SocraticCheckpointModal extends StatefulWidget {
final SocraticCheckpointModel checkpoint;
final Function(SocraticOptionModel) onOptionSelected;
const SocraticCheckpointModal({
super.key,
required this.checkpoint,
required this.onOptionSelected,
});
@override
State<SocraticCheckpointModal> createState() => _SocraticCheckpointModalState();
}
class _SocraticCheckpointModalState extends State<SocraticCheckpointModal> {
SocraticOptionModel? _selectedOption;
bool _submitted = false;
bool _showHint = false;
@override
Widget build(BuildContext context) {
return Dialog(
backgroundColor: Colors.transparent,
insetPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 24),
child: Container(
constraints: const BoxConstraints(maxWidth: 540),
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: const Color(0xFF0C1424),
borderRadius: BorderRadius.circular(28),
border: Border.all(color: AppColors.saqelCyan.withAlpha(90), width: 1.5),
boxShadow: [
BoxShadow(
color: AppColors.saqelCyan.withAlpha(40),
blurRadius: 40,
spreadRadius: 2,
),
const BoxShadow(
color: Colors.black,
blurRadius: 30,
offset: Offset(0, 10),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Top Badge: Socratic Gatekeeping Indicator
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: AppColors.saqelCyan.withAlpha(30),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: AppColors.saqelCyan.withAlpha(80)),
),
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(CupertinoIcons.sparkles, color: AppColors.saqelCyan, size: 14),
SizedBox(width: 6),
Text(
'فحص الفهم السقراطي الذكي 🧠',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w700,
color: AppColors.saqelCyan,
),
),
],
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: AppColors.darkSurface,
borderRadius: BorderRadius.circular(12),
),
child: const Text(
'+0.5% جاهزية',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w800,
color: AppColors.emeraldGreen,
),
),
),
],
),
const SizedBox(height: 20),
// Question Text
Text(
widget.checkpoint.questionText,
style: AppTypography.titleLarge.copyWith(
color: Colors.white,
fontWeight: FontWeight.w800,
height: 1.4,
),
textAlign: TextAlign.right,
),
const SizedBox(height: 20),
// Options List
...widget.checkpoint.options.map((opt) {
final isSelected = _selectedOption?.id == opt.id;
Color borderColor = AppColors.darkCardBorder;
Color bgColor = AppColors.darkSurface;
if (_submitted) {
if (opt.isCorrect) {
borderColor = AppColors.emeraldGreen;
bgColor = AppColors.emeraldGreen.withAlpha(30);
} else if (isSelected && !opt.isCorrect) {
borderColor = AppColors.crimsonRed;
bgColor = AppColors.crimsonRed.withAlpha(30);
}
} else if (isSelected) {
borderColor = AppColors.saqelCyan;
bgColor = AppColors.saqelCyan.withAlpha(20);
}
return Padding(
padding: const EdgeInsets.only(bottom: 10),
child: InkWell(
onTap: _submitted
? null
: () {
setState(() => _selectedOption = opt);
},
borderRadius: BorderRadius.circular(16),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
decoration: BoxDecoration(
color: bgColor,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: borderColor, width: isSelected ? 1.8 : 1.0),
),
child: Row(
children: [
Container(
width: 24,
height: 24,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: isSelected ? AppColors.saqelCyan : AppColors.textSecondaryDark,
width: 2,
),
color: isSelected ? AppColors.saqelCyan : Colors.transparent,
),
child: isSelected
? const Icon(Icons.check, size: 16, color: Colors.black)
: null,
),
const SizedBox(width: 14),
Expanded(
child: Text(
opt.text,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: isSelected ? Colors.white : AppColors.textSecondaryDark,
),
textAlign: TextAlign.right,
),
),
],
),
),
),
);
}),
// Hint Drawer (if requested)
if (_showHint && widget.checkpoint.hint.isNotEmpty) ...[
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: AppColors.guardianAmber.withAlpha(20),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: AppColors.guardianAmber.withAlpha(60)),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Icon(CupertinoIcons.lightbulb_fill, color: AppColors.guardianAmber, size: 18),
const SizedBox(width: 10),
Expanded(
child: Text(
widget.checkpoint.hint,
style: const TextStyle(
color: Color(0xFFFDE68A),
fontSize: 13,
fontWeight: FontWeight.w600,
height: 1.4,
),
textAlign: TextAlign.right,
),
),
],
),
),
],
const SizedBox(height: 20),
// Bottom Actions
Row(
children: [
if (!_showHint && widget.checkpoint.hint.isNotEmpty)
TextButton.icon(
onPressed: () => setState(() => _showHint = true),
icon: const Icon(CupertinoIcons.lightbulb, size: 16, color: AppColors.guardianAmber),
label: const Text(
'تلميح الذكاء الاصطناعي',
style: TextStyle(color: AppColors.guardianAmber, fontSize: 12, fontWeight: FontWeight.w700),
),
),
const Spacer(),
Expanded(
flex: 2,
child: LuxuryButton(
text: _submitted ? 'متابعة ⚡' : 'تأكيد الإجابة 🚀',
onPressed: _selectedOption == null
? null
: () {
if (!_submitted) {
setState(() => _submitted = true);
widget.onOptionSelected(_selectedOption!);
} else {
Navigator.of(context).pop();
}
},
),
),
],
),
],
),
),
);
}
}