Update Saqel Platform: 2026-09-04 02:04:43

This commit is contained in:
Hamza-Ayed
2026-09-04 02:04:43 +03:00
parent 58c3c48f69
commit 1fe97bf3eb
16 changed files with 5996 additions and 183 deletions
@@ -0,0 +1,797 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import '../../../core/theme/app_colors.dart';
/// ==============================================================================
/// SAQEL ENTERPRISE - ARABIC INTERACTIVE LAB (شجرة الإعراب ومختبر العروض)
/// ==============================================================================
class ArabicInteractiveLabView extends StatefulWidget {
final List<Map<String, dynamic>>? customSentences;
final String? lessonTopic;
const ArabicInteractiveLabView({
super.key,
this.customSentences,
this.lessonTopic,
});
@override
State<ArabicInteractiveLabView> createState() =>
_ArabicInteractiveLabViewState();
}
class _ArabicInteractiveLabViewState extends State<ArabicInteractiveLabView>
with SingleTickerProviderStateMixin {
late TabController _tabController;
// Syntax Tree State
int _selectedSentenceIndex = 0;
int? _selectedWordIndex;
List<Map<String, dynamic>> get _sentences =>
(widget.customSentences != null && widget.customSentences!.isNotEmpty)
? widget.customSentences!
: _sampleSentences;
final List<Map<String, dynamic>> _sampleSentences = [
{
'fullText': 'إِنَّ العِلْمَ نُورٌ يَهْدِي العُقُولَ إِلَى الحَقِّ',
'type': 'جملة اسمية منسوخة بـ (إنَّ)',
'rootNode': 'جملة إنَّ وأخواتها',
'words': [
{
'word': 'إِنَّ',
'role': 'حرف توكيد ونصب (ناسخ)',
'case': 'مبني على الفتح لا محل له من الإعراب',
'explanation': 'يدخل على الجملة الاسمية فينصب المبتدأ ويرفع الخبر.',
'tag': 'حرف ناسخ',
},
{
'word': 'العِلْمَ',
'role': 'اسم إنَّ',
'case': 'منصوب وعلامة نصبه الفتحة الظاهرة على آخره',
'explanation': 'هو المسند إليه في الأصل، نُصب لدخول الحرف الناسخ عليه.',
'tag': 'اسم منصوب',
},
{
'word': 'نُورٌ',
'role': 'خبر إنَّ',
'case': 'مرفوع وعلامة رفعه الضمة الظاهرة على آخره',
'explanation': 'تم به المعنى وأخبر عن اسم إن، وجاء مفرداً.',
'tag': 'خبر مرفوع',
},
{
'word': 'يَهْدِي',
'role': 'فعل مضارع مرفوع (وجملة فعلية في محل رفع نعت)',
'case': 'مرفوع بالضمة المقدرة على الياء للثقل، والفاعل ضمير مستتر',
'explanation': 'الجمل بعد النكرات صفات؛ فجملة (يهدي) نعت لكلمة (نور).',
'tag': 'فعل + نعت',
},
{
'word': 'العُقُولَ',
'role': 'مفعول به',
'case': 'منصوب وعلامة نصبه الفتحة الظاهرة',
'explanation': 'وقع عليه فعل الهداية من الفاعل المستتر (هو).',
'tag': 'مفعول به',
},
{
'word': 'إِلَى الحَقِّ',
'role': 'جار ومجرور',
'case': 'شبه جملة متعلقة بالفعل (يهدي)',
'explanation': 'إلى حرف جر، والحق اسم مجرور بالكسرة الظاهرة.',
'tag': 'شبه جملة',
},
],
},
{
'fullText': 'قَرَأَ الطَّالِبُ المُجْتَهِدُ كِتَابَيْنِ مُفِيدَيْنِ صَبَاحاً',
'type': 'جملة فعلية تامة',
'rootNode': 'الجملة الفعلية (فعل + فاعل + مفعول به + فضلات)',
'words': [
{
'word': 'قَرَأَ',
'role': 'فعل ماضٍ مبني',
'case': 'مبني على الفتح الظاهر على آخره',
'explanation': 'فعل ماضٍ مجرد، مبني للمعلوم، يدل على حدث في الزمن الماضي.',
'tag': 'فعل ماضٍ',
},
{
'word': 'الطَّالِبُ',
'role': 'فاعل مرفوع',
'case': 'مرفوع وعلامة رفعه الضمة الظاهرة',
'explanation': 'من قام بالفعل، معرف بأل، ركن أساسي في الجملة الفعلية.',
'tag': 'فاعل مرفوع',
},
{
'word': 'المُجْتَهِدُ',
'role': 'نعت (صفة) للطالب',
'case': 'مرفوع وعلامة رفعه الضمة الظاهرة',
'explanation': 'طابق المنعوت في التعريف، الإفراد، التذكير، وحركة الإعراب.',
'tag': 'نعت / صفة',
},
{
'word': 'كِتَابَيْنِ',
'role': 'مفعول به منصوب',
'case': 'منصوب وعلامة نصبه الياء لأنه مثنى',
'explanation': 'وقع عليه فعل القراءة، والمثنى يُنصب بالياء وتُكسر نونه.',
'tag': 'مثنى منصوب',
},
{
'word': 'مُفِيدَيْنِ',
'role': 'نعت لكتابين',
'case': 'منصوب وعلامة نصبه الياء لأنه مثنى',
'explanation': 'صفة تابعة للمفعول به في التثنية والتنكير والنصب بالياء.',
'tag': 'نعت مثنى',
},
{
'word': 'صَبَاحاً',
'role': 'ظرف زمان (مفعول فيه)',
'case': 'منصوب وعلامة نصبه الفتحة الظاهرة',
'explanation': 'اسم منصوب يدل على زمن وقوع الفعل، متضمن معنى (في).',
'tag': 'ظرف زمان',
},
],
},
];
// Poetry Meter State
int _selectedMeterIndex = 0;
bool _isPlayingBeat = false;
int _currentBeatStep = 0;
final List<Map<String, dynamic>> _poetryMeters = [
{
'name': 'البحر البسيط',
'key': 'إِنَّ البَسِيطَ لَدَيْهِ يُبْسَطُ الأَمَلُ .. مُسْتَفْعِلُنْ فاعِلُنْ مُسْتَفْعِلُنْ فَعِلُ',
'verse': 'لِكُلِّ شَيْءٍ إِذَا مَا تَمَّ نُقْصَانُ .. فَلَا يُغَرَّ بِطِيبِ العَيْشِ إِنْسَانُ',
'poet': 'أبو البقاء الرندي',
'pattern': ['مُسْتَفْعِلُنْ', 'فَاعِلُنْ', 'مُسْتَفْعِلُنْ', 'فَعِلُنْ'],
'scansion': ['//0//0', '/0//0', '//0//0', '///0'],
'speedBpm': 90,
},
{
'name': 'البحر الوافر',
'key': 'بُحُورُ الشِّعْرِ وَافِرُهَا جَمِيلُ .. مُفَاعَلَتُنْ مُفَاعَلَتُنْ فَعُولُ',
'verse': 'إِذَا غَامَرْتَ فِي شَرَفٍ مَرُومِ .. فَلَا تَقْنَعْ بِمَا دُونَ النُّجُومِ',
'poet': 'المتنبي',
'pattern': ['مُفَاعَلَتُنْ', 'مُفَاعَلَتُنْ', 'فَعُولُنْ'],
'scansion': ['//0///0', '//0///0', '//0/0'],
'speedBpm': 105,
},
{
'name': 'البحر الكامل',
'key': 'كَمُلَ الجَمَالُ مِنَ البُحُورِ الكَامِلُ .. مُتَفَاعِلُنْ مُتَفَاعِلُنْ مُتَفَاعِلُ',
'verse': 'وَإِذَا صَحَوْتُ فَمَا أُقَصِّرُ عَنْ نَدَى .. وَكَمَا عَلِمْتِ شَمَائِلِي وَتَكَرُّمِي',
'poet': 'عنترة بن شداد',
'pattern': ['مُتَفَاعِلُنْ', 'مُتَفَاعِلُنْ', 'مُتَفَاعِلُنْ'],
'scansion': ['///0//0', '///0//0', '///0//0'],
'speedBpm': 115,
},
];
@override
void initState() {
super.initState();
_tabController = TabController(length: 2, vsync: this);
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
color: const Color(0xFF070B12),
child: Column(
children: [
// Luxury Tab Switcher
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: const BoxDecoration(
color: Color(0xFF0E1626),
border: Border(bottom: BorderSide(color: Color(0xFF1E293B))),
),
child: Row(
children: [
_tabButton(
index: 0,
title: 'شجرة الإعراب النحوية 🌳',
icon: CupertinoIcons.arrow_branch,
),
const SizedBox(width: 8),
_tabButton(
index: 1,
title: 'مختبر العروض والأوزان 🎵',
icon: CupertinoIcons.music_note_list,
),
],
),
),
Expanded(
child: IndexedStack(
index: _tabController.index,
children: [
_buildSyntaxTreeTab(),
_buildPoetryMeterTab(),
],
),
),
],
),
);
}
Widget _tabButton({
required int index,
required String title,
required IconData icon,
}) {
final isSelected = _tabController.index == index;
return Expanded(
child: GestureDetector(
onTap: () {
setState(() {
_tabController.animateTo(index);
});
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 10),
decoration: BoxDecoration(
color: isSelected ? const Color(0xFF10B981) : const Color(0xFF1E293B),
borderRadius: BorderRadius.circular(10),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon,
size: 16,
color: isSelected ? Colors.black : const Color(0xFF94A3B8)),
const SizedBox(width: 8),
Text(
title,
style: TextStyle(
fontSize: 13,
fontWeight: isSelected ? FontWeight.w800 : FontWeight.w600,
color: isSelected ? Colors.black : const Color(0xFF94A3B8),
),
),
],
),
),
),
);
}
// ---------------------------------------------------------------------------
// TAB 1: Visual Interactive Syntax Tree
// ---------------------------------------------------------------------------
Widget _buildSyntaxTreeTab() {
final safeIndex = _selectedSentenceIndex.clamp(0, _sentences.length - 1);
final sentence = _sentences[safeIndex];
final words = (sentence['words'] as List);
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Selector for Sentences
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.lessonTopic != null
? 'شجرة إعراب درس: ${widget.lessonTopic}'
: 'اختر الجملة لتحليل شجرتها الإعرابية:',
style: const TextStyle(
fontSize: 13.5,
fontWeight: FontWeight.w700,
color: Colors.white),
),
Container(
padding:
const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: const Color(0xFF10B981).withOpacity(0.15),
borderRadius: BorderRadius.circular(6),
),
child: Text(
widget.customSentences != null ? 'أمثلة الكتاب + ذكاء اصطناعي' : 'وزاري · التوجيهي 2008',
style: const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w700,
color: Color(0xFF34D399)),
),
),
],
),
const SizedBox(height: 10),
// Sentence Switcher Buttons
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: List.generate(_sentences.length, (idx) {
final isCur = safeIndex == idx;
return GestureDetector(
onTap: () => setState(() {
_selectedSentenceIndex = idx;
_selectedWordIndex = null;
}),
child: Container(
margin: EdgeInsets.only(left: idx == 0 ? 8 : 0),
padding: const EdgeInsets.symmetric(vertical: 10),
decoration: BoxDecoration(
color: isCur
? const Color(0xFF1E293B)
: const Color(0xFF0F172A),
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: isCur
? const Color(0xFF10B981)
: const Color(0xFF1E293B),
),
),
child: Center(
child: Text(
'الجملة ${idx + 1}',
style: TextStyle(
fontSize: 12.5,
fontWeight: isCur ? FontWeight.w800 : FontWeight.w500,
color: isCur ? Colors.white : const Color(0xFF64748B),
),
),
),
),
),
);
}),
),
),
const SizedBox(height: 16),
// Main Sentence Card
Container(
padding: const EdgeInsets.all(18),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF064E3B), Color(0xFF0F172A)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16),
border:
Border.all(color: const Color(0xFF10B981).withOpacity(0.4)),
),
child: Column(
children: [
Text(
sentence['fullText'],
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w900,
color: Colors.white,
height: 1.6,
letterSpacing: 0.5,
),
),
const SizedBox(height: 8),
Container(
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.4),
borderRadius: BorderRadius.circular(20),
),
child: Text(
sentence['type'],
style: const TextStyle(
fontSize: 12,
color: Color(0xFF6EE7B7),
fontWeight: FontWeight.w600),
),
)
],
),
),
const SizedBox(height: 20),
// Tree Interactive Decomposition (Word Cards)
const Text(
'انقر على أي كلمة لتفكيك عقدتها النحوية وأحكامها:',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w700,
color: Color(0xFF94A3B8)),
),
const SizedBox(height: 12),
Wrap(
spacing: 8,
runSpacing: 8,
children: List.generate(words.length, (idx) {
final w = words[idx];
final isSel = _selectedWordIndex == idx;
return GestureDetector(
onTap: () => setState(() => _selectedWordIndex = idx),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding:
const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
decoration: BoxDecoration(
color: isSel
? const Color(0xFF10B981)
: const Color(0xFF0F172A),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: isSel
? const Color(0xFF34D399)
: const Color(0xFF1E293B),
width: isSel ? 2 : 1,
),
boxShadow: isSel
? [
BoxShadow(
color: const Color(0xFF10B981).withOpacity(0.3),
blurRadius: 12,
)
]
: [],
),
child: Column(
children: [
Text(
w['word'],
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w800,
color: isSel ? Colors.black : Colors.white,
),
),
const SizedBox(height: 2),
Text(
w['tag'],
style: TextStyle(
fontSize: 10.5,
fontWeight: FontWeight.w600,
color: isSel
? const Color(0xFF064E3B)
: const Color(0xFF64748B),
),
),
],
),
),
);
}),
),
const SizedBox(height: 20),
// Detailed Syntactic Breakdown Card
if (_selectedWordIndex != null)
_buildWordDetailCard(words[_selectedWordIndex!])
else
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: const Color(0xFF0F172A),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: const Color(0xFF1E293B)),
),
child: const Center(
child: Text(
'👆 انقر على أي كلمة أعلاه لتوليد شجرتها الإعرابية والعلة النحوية',
style: TextStyle(color: Color(0xFF64748B), fontSize: 13),
),
),
),
],
),
);
}
Widget _buildWordDetailCard(Map<String, dynamic> wordData) {
return Container(
padding: const EdgeInsets.all(18),
decoration: BoxDecoration(
color: const Color(0xFF0F172A),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFF10B981).withOpacity(0.5)),
boxShadow: [
BoxShadow(
color: const Color(0xFF10B981).withOpacity(0.1),
blurRadius: 20,
)
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: const Color(0xFF10B981).withOpacity(0.2),
shape: BoxShape.circle,
),
child: const Icon(CupertinoIcons.checkmark_seal_fill,
color: Color(0xFF10B981), size: 18),
),
const SizedBox(width: 10),
Text(
'التحليل الإعرابي لكلمة: ( ${wordData['word']} )',
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w800,
color: Colors.white),
),
],
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: const Color(0xFF1E293B),
borderRadius: BorderRadius.circular(6),
),
child: Text(
wordData['tag'],
style: const TextStyle(
fontSize: 11,
color: Color(0xFF34D399),
fontWeight: FontWeight.w700),
),
)
],
),
const SizedBox(height: 14),
// Role
_infoRow('الموقع الإعرابي', wordData['role'], const Color(0xFF38BDF8)),
const SizedBox(height: 8),
// Case & Sign
_infoRow('الحالة والعلامة', wordData['case'], const Color(0xFFFBBF24)),
const SizedBox(height: 8),
// Pedagogical Socratic explanation
_infoRow('التعليل والتوجيه الوزاري', wordData['explanation'],
const Color(0xFFCBD5E1)),
],
),
);
}
Widget _infoRow(String label, String value, Color valueColor) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: const TextStyle(
fontSize: 11.5,
fontWeight: FontWeight.w600,
color: Color(0xFF64748B)),
),
const SizedBox(height: 2),
Text(
value,
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w700,
color: valueColor,
height: 1.4),
),
],
);
}
// ---------------------------------------------------------------------------
// TAB 2: Poetry Prosody & Metronome Lab
// ---------------------------------------------------------------------------
Widget _buildPoetryMeterTab() {
final meter = _poetryMeters[_selectedMeterIndex];
final patterns = (meter['pattern'] as List);
final scansions = (meter['scansion'] as List);
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Meter Selection Chips
Row(
children: List.generate(_poetryMeters.length, (idx) {
final m = _poetryMeters[idx];
final isCur = _selectedMeterIndex == idx;
return Expanded(
child: GestureDetector(
onTap: () => setState(() {
_selectedMeterIndex = idx;
_isPlayingBeat = false;
}),
child: Container(
margin: EdgeInsets.only(left: idx < _poetryMeters.length - 1 ? 8 : 0),
padding: const EdgeInsets.symmetric(vertical: 10),
decoration: BoxDecoration(
color: isCur
? const Color(0xFF047857)
: const Color(0xFF0F172A),
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: isCur
? const Color(0xFF34D399)
: const Color(0xFF1E293B),
),
),
child: Center(
child: Text(
m['name'],
style: TextStyle(
fontSize: 12.5,
fontWeight: isCur ? FontWeight.w800 : FontWeight.w600,
color: isCur ? Colors.white : const Color(0xFF94A3B8),
),
),
),
),
),
);
}),
),
const SizedBox(height: 16),
// Key & Verse Banner
Container(
padding: const EdgeInsets.all(18),
decoration: BoxDecoration(
color: const Color(0xFF0F172A),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFF1E293B)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'مفتاح ${meter['name']}:',
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w700,
color: Color(0xFF34D399)),
),
const SizedBox(height: 4),
Text(
meter['key'],
style: const TextStyle(
fontSize: 14.5,
fontWeight: FontWeight.w700,
color: Colors.white,
height: 1.5),
),
const Divider(color: Color(0xFF1E293B), height: 24),
Text(
'بيت تطبيقي وزاري (${meter['poet']}):',
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w700,
color: Color(0xFFF59E0B)),
),
const SizedBox(height: 4),
Text(
meter['verse'],
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w800,
color: Color(0xFFFEF08A),
height: 1.5),
),
],
),
),
const SizedBox(height: 20),
// Interactive Scansion / Tafilat Visualizer
const Text(
'التفعيلات والتقطيع العروضي الصوتي (متحرك / وساكن 0):',
style: TextStyle(
fontSize: 13.5,
fontWeight: FontWeight.w700,
color: Colors.white),
),
const SizedBox(height: 12),
Row(
children: List.generate(patterns.length, (idx) {
final pat = patterns[idx];
final scan = scansions[idx];
return Expanded(
child: Container(
margin: EdgeInsets.only(left: idx < patterns.length - 1 ? 8 : 0),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF1E293B), Color(0xFF0F172A)],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFF334155)),
),
child: Column(
children: [
Text(
pat,
style: const TextStyle(
fontSize: 13.5,
fontWeight: FontWeight.w800,
color: Color(0xFF38BDF8),
),
),
const SizedBox(height: 6),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.5),
borderRadius: BorderRadius.circular(6),
),
child: Text(
scan,
style: const TextStyle(
fontFamily: 'monospace',
fontSize: 13,
fontWeight: FontWeight.w900,
letterSpacing: 2,
color: Color(0xFF34D399),
),
),
),
],
),
),
);
}),
),
const SizedBox(height: 20),
// Audio Metronome Simulation Button
ElevatedButton.icon(
onPressed: () {
setState(() {
_isPlayingBeat = !_isPlayingBeat;
});
},
icon: Icon(
_isPlayingBeat
? CupertinoIcons.pause_circle_fill
: CupertinoIcons.play_circle_fill,
size: 20),
label: Text(
_isPlayingBeat
? 'إيقاف الإيقاع العروضي التفاعلي'
: 'عزف الإيقاع العروضي الموزون (${meter['speedBpm']} نقرة/دقيقة) 🥁',
style: const TextStyle(fontSize: 13.5, fontWeight: FontWeight.w700),
),
style: ElevatedButton.styleFrom(
backgroundColor: _isPlayingBeat
? const Color(0xFFB91C1C)
: const Color(0xFF059669),
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
),
),
],
),
);
}
}
@@ -0,0 +1,401 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import '../../../core/theme/app_colors.dart';
/// ==============================================================================
/// SAQEL ENTERPRISE - JORDAN HISTORY INTERACTIVE TIMELINE (الخط الزمني لتاريخ الأردن)
/// ==============================================================================
class HistoryInteractiveTimelineView extends StatefulWidget {
const HistoryInteractiveTimelineView({super.key});
@override
State<HistoryInteractiveTimelineView> createState() =>
_HistoryInteractiveTimelineViewState();
}
class _HistoryInteractiveTimelineViewState
extends State<HistoryInteractiveTimelineView> {
int _selectedMilestoneIndex = 0;
final List<Map<String, dynamic>> _milestones = [
{
'year': '1921',
'exactDate': '2 آذار 1921',
'title': 'تأسيس إمارة شرق الأردن',
'era': 'عهد الملك المؤسس عبد الله الأول',
'location': 'معان ➔ عمان',
'summary':
'وصول الأمير عبد الله الأول بن الحسين إلى معان في تشرين الثاني 1920 ثم إلى عمان في 2 آذار 1921، وتشكيل أول حكومة مركزية برئاسة رشيد طليع في 11 نيسان 1921.',
'tawjihiFocus':
'سؤال وزاري مكرر: من هو رئيس أول حكومة أردنية؟ (رشيد طليع)، وتاريخ وصول الأمير إلى عمان (2 آذار 1921).',
'significance': 'نشأة الدولة الأردنية الحديثة بعد الثورة العربية الكبرى.',
'accentColor': Color(0xFF8B5CF6),
},
{
'year': '1928',
'exactDate': '16 نيسان 1928',
'title': 'صدور القانون الأساسي والمؤتمر الوطني الأول',
'era': 'عهد الملك المؤسس عبد الله الأول',
'location': 'عمان - مقهى حمدان',
'summary':
'توقيع المعاهدة الأردنية البريطانية، وصدور أول دستور للإمارة (القانون الأساسي)، وانعقاد المؤتمر الوطني الأول برئاسة حسين الطراونة ووضع الميثاق القومي.',
'tawjihiFocus':
'سؤال وزاري مكرر: من ترأس المؤتمر الوطني الأول عام 1928؟ (حسين الطراونة)، وما اسم أول دستور؟ (القانون الأساسي 1928).',
'significance': 'أول وثيقة دستورية وطنية تنظم الحياة السياسية والنيابية.',
'accentColor': Color(0xFF38BDF8),
},
{
'year': '1946',
'exactDate': '25 أيار 1946',
'title': 'استقلال المملكة الأردنية الهاشمية',
'era': 'عهد الملك المؤسس عبد الله الأول',
'location': 'عمان - قصر رغدان العامر',
'summary':
'إعلان المجلس التشريعي الأردني الخامس استقلال البلاد التام ومبايعة الملك عبد الله الأول ملكاً دستورياً، وتحويل اسم الدولة إلى المملكة الأردنية الهاشمية.',
'tawjihiFocus':
'سؤال وزاري مكرر: ما هو رقم المجلس التشريعي الذي أعلن الاستقلال؟ (المجلس الخامس)، واليوم الوطني (25 أيار 1946).',
'significance': 'إنهاء الانتداب البريطاني واعتراف دولي كامل بسيادة المملكة.',
'accentColor': Color(0xFF10B981),
},
{
'year': '1952',
'exactDate': '8 كانون الثاني 1952',
'title': 'صدور الدستور الأردني المتطور وديوان المحاسبة',
'era': 'عهد الملك طلال بن عبد الله',
'location': 'عمان',
'summary':
'إصدار دستور 1952 العصري الذي نص على أن الأمة مصدر السلطات والوزارة مسؤولة أمام مجلس النواب، وتأسيس ديوان المحاسبة وإقرار مجانية وإلزامية التعليم.',
'tawjihiFocus':
'سؤال وزاري مكرر: في عهد من صدر دستور 1952 وتأسس ديوان المحاسبة؟ (الملك طلال بن عبد الله).',
'significance': 'ترسيخ المبادئ الديمقراطية ومسؤولية السلطة التنفيذية أمام البرلمان.',
'accentColor': Color(0xFFF59E0B),
},
{
'year': '1956',
'exactDate': '1 آذار 1956',
'title': 'تعريب قيادة الجيش العربي وطرد كلوب باشا',
'era': 'عهد الملك الباني الحسين بن طلال',
'location': 'القيادة العامة - عمان',
'summary':
'القرار الوطني الشجاع لجلالة الملك الحسين بن طلال بإنهاء خدمة الفريق كلوب والضباط الإنجليز، وتعيين اللواء راضي عناب رئيساً للأركان، لتصبح القيادة أردنية خالصة.',
'tawjihiFocus':
'سؤال وزاري مكرر: من هو أول رئيس أركان أردني بعد التعريب؟ (راضي عناب)، والتاريخ (1 آذار 1956).',
'significance': 'استرداد السيادة العسكرية الوطنية الكاملة للجيش العربي.',
'accentColor': Color(0xFFEF4444),
},
{
'year': '1968',
'exactDate': '21 آذار 1968',
'title': 'معركة الكرامة الخالدة وتحطيم أسطورة العدو',
'era': 'عهد الملك الباني الحسين بن طلال',
'location': 'غور الأردن - بلدة الكرامة',
'summary':
'تصدي القوات المسلحة الأردنية - الجيش العربي الباسل للعدوان الإسرائيلي وإلحاق أول هزيمة عسكرية مدوية بجيش الاحتلال، وإجباره على طلب وقف إطلاق النار لأول مرة في تاريخه.',
'tawjihiFocus':
'سؤال وزاري مكرر: ما هي أول معركة عربية حطمت أسطورة الجيش الإسرائيلي؟ (معركة الكرامة 21 آذار 1968).',
'significance': 'استعادة الكرامة العسكرية العربية وإثبات صلابة الجندي الأردني.',
'accentColor': Color(0xFF10B981),
},
{
'year': '1999',
'exactDate': '7 شباط 1999 - الحاضر',
'title': 'تولي الملك عبد الله الثاني ومسارات التحديث',
'era': 'عهد الملك المعزز عبد الله الثاني ابن الحسين',
'location': 'المملكة الأردنية الهاشمية',
'summary':
'انطلاق عهد التعزيز والتحول الرقمي، وإطلاق الأوراق النقاشية الملكية السبع لتطوير الديمقراطية، ومنظومة التحديث السياسي والاقتصادي والإداري لمئوية الدولة الثانية.',
'tawjihiFocus':
'سؤال وزاري مكرر: موضوع الورقة النقاشية الأولى (بناء الديمقراطية) والسابعة (التعليم وتطوير الموارد البشرية).',
'significance': 'تحديث منظومة الدولة الأردنية ومواكبة الثورة الصناعية والذكاء الاصطناعي.',
'accentColor': Color(0xFFFBBF24),
},
];
@override
Widget build(BuildContext context) {
final cur = _milestones[_selectedMilestoneIndex];
final accent = cur['accentColor'] as Color;
return Container(
color: const Color(0xFF070B12),
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Top Timeline Navigation Bar (Scrubbable Milestones)
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFF0F172A),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFF1E293B)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'شريط المحطات التاريخية لتاريخ الأردن (التوجيهي):',
style: TextStyle(
fontSize: 13.5,
fontWeight: FontWeight.w700,
color: Colors.white),
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: accent.withOpacity(0.2),
borderRadius: BorderRadius.circular(6),
),
child: Text(
cur['year'],
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w800,
color: accent),
),
),
],
),
const SizedBox(height: 14),
// Horizontal Years Scroller
SizedBox(
height: 58,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: _milestones.length,
itemBuilder: (ctx, idx) {
final m = _milestones[idx];
final isSel = _selectedMilestoneIndex == idx;
final mColor = m['accentColor'] as Color;
return GestureDetector(
onTap: () =>
setState(() => _selectedMilestoneIndex = idx),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
margin: const EdgeInsets.only(left: 10),
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: isSel
? mColor
: const Color(0xFF1E293B),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: isSel ? Colors.white : Colors.transparent,
width: 1.5,
),
boxShadow: isSel
? [
BoxShadow(
color: mColor.withOpacity(0.4),
blurRadius: 10,
)
]
: [],
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
m['year'],
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w900,
color: isSel ? Colors.black : Colors.white,
),
),
const SizedBox(height: 2),
Text(
m['title'].toString().split(' ').first,
style: TextStyle(
fontSize: 10.5,
fontWeight: FontWeight.w600,
color: isSel
? Colors.black.withOpacity(0.8)
: const Color(0xFF94A3B8),
),
),
],
),
),
);
},
),
),
],
),
),
const SizedBox(height: 18),
// Active Milestone Hero Card
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [accent.withOpacity(0.2), const Color(0xFF0F172A)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(18),
border: Border.all(color: accent.withOpacity(0.5)),
boxShadow: [
BoxShadow(
color: accent.withOpacity(0.15),
blurRadius: 20,
)
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
cur['title'],
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w900,
color: Colors.white,
),
),
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: accent.withOpacity(0.2),
borderRadius: BorderRadius.circular(8),
),
child: Text(
cur['exactDate'],
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w800,
color: accent),
),
),
],
),
const SizedBox(height: 8),
Row(
children: [
const Icon(CupertinoIcons.person_crop_circle_badge_checkmark,
size: 14, color: Color(0xFF94A3B8)),
const SizedBox(width: 6),
Text(
cur['era'],
style: const TextStyle(
fontSize: 12, color: Color(0xFF94A3B8)),
),
const SizedBox(width: 14),
const Icon(CupertinoIcons.placemark_fill,
size: 14, color: Color(0xFF94A3B8)),
const SizedBox(width: 6),
Text(
cur['location'],
style: const TextStyle(
fontSize: 12, color: Color(0xFF94A3B8)),
),
],
),
const Divider(color: Color(0xFF1E293B), height: 24),
// Summary
const Text(
'الوقائع والأحداث التاريخية:',
style: TextStyle(
fontSize: 12.5,
fontWeight: FontWeight.w700,
color: Color(0xFFCBD5E1)),
),
const SizedBox(height: 4),
Text(
cur['summary'],
style: const TextStyle(
fontSize: 13.5, color: Colors.white, height: 1.5),
),
const SizedBox(height: 14),
// Significance
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFF1E293B),
borderRadius: BorderRadius.circular(10),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Icon(CupertinoIcons.flag_fill,
color: Color(0xFF38BDF8), size: 16),
const SizedBox(width: 8),
Expanded(
child: Text(
'الأثر والنتيجة: ${cur['significance']}',
style: const TextStyle(
fontSize: 12.5,
color: Color(0xFFE2E8F0),
height: 1.4),
),
),
],
),
),
const SizedBox(height: 14),
// Tawjihi Ministerial Focus Box
Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: const Color(0xFF451A03),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: const Color(0xFFF59E0B).withOpacity(0.5)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(CupertinoIcons.lightbulb_fill,
color: Color(0xFFFBBF24), size: 16),
SizedBox(width: 8),
Text(
'💡 زاوية أسئلة التوجيهي الوزارية المتكررة:',
style: TextStyle(
fontSize: 12.5,
fontWeight: FontWeight.w800,
color: Color(0xFFFBBF24)),
),
],
),
const SizedBox(height: 6),
Text(
cur['tawjihiFocus'],
style: const TextStyle(
fontSize: 12.5,
color: Color(0xFFFEF3C7),
height: 1.4),
),
],
),
),
],
),
),
],
),
),
);
}
}
@@ -0,0 +1,946 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import '../../../core/theme/app_colors.dart';
/// ==============================================================================
/// SAQEL ENTERPRISE - ISLAMIC INHERITANCE & TAJWEED LAB (حاسبة المواريث والتجويد)
/// ==============================================================================
class IslamicInteractiveLabView extends StatefulWidget {
const IslamicInteractiveLabView({super.key});
@override
State<IslamicInteractiveLabView> createState() =>
_IslamicInteractiveLabViewState();
}
class _IslamicInteractiveLabViewState extends State<IslamicInteractiveLabView>
with SingleTickerProviderStateMixin {
late TabController _tabController;
// Inheritance Calculator State
bool _deceasedIsMale = true; // True = توفي رجل (له زوجة), False = توفيت امرأة (لها زوج)
double _estateAmountJod = 120000.0;
bool _hasFather = true;
bool _hasMother = true;
int _sonsCount = 2;
int _daughtersCount = 1;
int _brothersCount = 2;
// Calculation Results
List<Map<String, dynamic>> _sharesResults = [];
String _issueBaseNumber = '24'; // أصل المسألة
String _shariaSummary = '';
// Tajweed State
int _selectedRuleIndex = 0;
final List<Map<String, dynamic>> _tajweedRules = [
{
'title': 'الإظهار الحلقي',
'letters': 'ء ، هـ ، ع ، ح ، غ ، خ (أخي هاك علماً حازه غير خاسر)',
'definition': 'إخراج النون الساكنة أو التنوين من مخرجها بغير غنة ظاهرة عند ملاقاة أحد حروف الحلق الستة.',
'examples': [
{'text': 'مَنْ آمَنَ', 'highlight': 'نْ + ء', 'surah': 'البقرة'},
{'text': 'يَنْهَوْنَ', 'highlight': 'نْ + هـ', 'surah': 'الأنعام'},
{'text': 'عَلِيمٌ حَكِيمٌ', 'highlight': 'تنوين + ح', 'surah': 'النساء'},
],
'accentColor': Color(0xFF10B981),
},
{
'title': 'الإدغام (بغنة وبغير غنة)',
'letters': 'ي ، ر ، م ، ل ، و ، ن (يرملون)',
'definition': 'إدخال حرف ساكن في حرف متحرك بحيث يصيران حرفاً واحداً مشدداً. بغنة في (ينمو) وبغير غنة في (الراء واللام).',
'examples': [
{'text': 'مَن يَقُولُ', 'highlight': 'إدغام بغنة (نْ + ي)', 'surah': 'البقرة'},
{'text': 'مِن رَّبِّهِمْ', 'highlight': 'إدغام بغير غنة (نْ + ر)', 'surah': 'البقرة'},
{'text': 'مِّن وَالٍ', 'highlight': 'إدغام بغنة (نْ + و)', 'surah': 'الرعد'},
],
'accentColor': Color(0xFF38BDF8),
},
{
'title': 'الإقلاب',
'letters': 'حرف الباء (ب) فقط مع وضع ميم صغيرة (مـ)',
'definition': 'قلب النون الساكنة أو التنوين ميماً مخفاة بغنة عند ملاقاة حرف الباء.',
'examples': [
{'text': 'مِن بَعْدِ', 'highlight': 'نْ + ب ➔ ميم مخفاة', 'surah': 'البقرة'},
{'text': 'أَنبِئْهُم', 'highlight': 'نْ + ب في كلمة واحدة', 'surah': 'البقرة'},
{'text': 'سَمِيعٌ بَصِيرٌ', 'highlight': 'تنوين + ب', 'surah': 'لقمان'},
],
'accentColor': Color(0xFFF59E0B),
},
{
'title': 'الإخفاء الحقيقي',
'letters': '15 حرفاً (ص، ذ، ث، ك، ج، ش، ق، س، د، ط، ز، ف، ت، ض، ظ)',
'definition': 'النطق بالنون الساكنة أو التنوين بحالة بين الإظهار والإدغام مع بقاء الغنة بمقدار حركتين.',
'examples': [
{'text': 'مِن قَبْلُ', 'highlight': 'نْ + ق', 'surah': 'البقرة'},
{'text': 'كِتَابٌ كَرِيمٌ', 'highlight': 'تنوين + ك', 'surah': 'النمل'},
{'text': 'أَنفُسَكُمْ', 'highlight': 'نْ + ف', 'surah': 'النساء'},
],
'accentColor': Color(0xFFA78BFA),
},
];
@override
void initState() {
super.initState();
_tabController = TabController(length: 2, vsync: this);
_calculateInheritance();
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
void _calculateInheritance() {
final results = <Map<String, dynamic>>[];
final hasChildren = (_sonsCount + _daughtersCount) > 0;
double remainingEstate = _estateAmountJod;
// 1. Spouse Share (الزوج أو الزوجة)
if (_deceasedIsMale) {
// Deceased is male -> Wife inherits
final shareFraction = hasChildren ? '1/8 (الثمن)' : '1/4 (الربع)';
final double amount = hasChildren
? (_estateAmountJod * (1.0 / 8.0))
: (_estateAmountJod * (1.0 / 4.0));
remainingEstate -= amount;
results.add({
'heir': 'الزوجة',
'share': shareFraction,
'amount': amount,
'basis': hasChildren
? 'تستحق الثمن لوجود الفرع الوارث (الأبناء والبنات)'
: 'تستحق الربع لعدم وجود فرع وارث',
});
} else {
// Deceased is female -> Husband inherits
final shareFraction = hasChildren ? '1/4 (الربع)' : '1/2 (النصف)';
final double amount = hasChildren
? (_estateAmountJod * (1.0 / 4.0))
: (_estateAmountJod * (1.0 / 2.0));
remainingEstate -= amount;
results.add({
'heir': 'الزوج',
'share': shareFraction,
'amount': amount,
'basis': hasChildren
? 'يستحق الربع لوجود الفرع الوارث'
: 'يستحق النصف لعدم وجود الفرع الوارث',
});
}
// 2. Mother Share (الأم)
if (_hasMother) {
final shareFraction = (hasChildren || _brothersCount > 1)
? '1/6 (السدس)'
: '1/3 (الثلث)';
final double amount = (hasChildren || _brothersCount > 1)
? (_estateAmountJod * (1.0 / 6.0))
: (_estateAmountJod * (1.0 / 3.0));
remainingEstate -= amount;
results.add({
'heir': 'الأم',
'share': shareFraction,
'amount': amount,
'basis': (hasChildren || _brothersCount > 1)
? 'تستحق السدس لوجود الفرع الوارث أو جمع من الإخوة'
: 'تستحق الثلث لعدم وجود فرع وارث أو جمع من الإخوة',
});
}
// 3. Father Share (الأب)
if (_hasFather) {
if (_sonsCount > 0) {
// Sons exist -> Father takes 1/6 strictly
final double amount = _estateAmountJod * (1.0 / 6.0);
remainingEstate -= amount;
results.add({
'heir': 'الأب',
'share': '1/6 (السدس فرضاً)',
'amount': amount,
'basis': 'يستحق السدس فرضاً فقط لوجود فرع وارث مذكر (الابن)',
});
} else if (_daughtersCount > 0) {
// Only daughters -> Father takes 1/6 + Asabah (residuary)
final double fardh = _estateAmountJod * (1.0 / 6.0);
remainingEstate -= fardh;
results.add({
'heir': 'الأب',
'share': '1/6 فرضاً + الباقي تعصيباً',
'amount': fardh,
'basis': 'يستحق السدس فرضاً مع البنت + الباقي تعصيباً إن تبقّى شيء',
});
} else {
// No children -> Father is pure Asabah
results.add({
'heir': 'الأب',
'share': 'عصبة بالنفس (الباقي بالكامل)',
'amount': remainingEstate,
'basis': 'عصبة بالنفس لعدم وجود فرع وارث، يحوز الباقي بعد أصحاب الفروض',
});
remainingEstate = 0;
}
}
// 4. Children Share (الأبناء والبنات - للذكر مثل حظ الأنثيين)
if (hasChildren && remainingEstate > 0) {
final totalShares = (_sonsCount * 2) + _daughtersCount;
if (totalShares > 0) {
final shareUnit = remainingEstate / totalShares;
if (_sonsCount > 0) {
final totalSonsAmount = shareUnit * 2 * _sonsCount;
results.add({
'heir': 'الأبناء الذكور ($_sonsCount)',
'share': 'عصبة بالغير (للذكر مثل حظ الأنثيين)',
'amount': totalSonsAmount,
'basis':
'لكل ابن ${((shareUnit * 2)).toStringAsFixed(0)} د.أ (ضعف حصة البنت)',
});
}
if (_daughtersCount > 0) {
final totalDaughtersAmount = shareUnit * _daughtersCount;
results.add({
'heir': 'البنات الإناث ($_daughtersCount)',
'share': _sonsCount > 0
? 'عصبة بالغير مع الأبناء'
: (_daughtersCount == 1 ? '1/2 (النصف)' : '2/3 (الثلثان)'),
'amount': totalDaughtersAmount,
'basis': 'لكل بنت ${(shareUnit).toStringAsFixed(0)} د.أ بالتساوي',
});
}
remainingEstate = 0;
}
}
// 5. Brothers & Hijb check (حجب الإخوة)
if (_brothersCount > 0) {
if (_hasFather || _sonsCount > 0) {
results.add({
'heir': 'الإخوة الأشقاء ($_brothersCount)',
'share': 'محجوبون حجب حرمان (صفر)',
'amount': 0.0,
'basis': _sonsCount > 0
? 'حُجبوا بالأصل والفرع المذكر (حجب حرمان تام بالابن)'
: 'حُجبوا بوجود الأب المباشر',
});
} else if (!hasChildren && remainingEstate > 0) {
results.add({
'heir': 'الإخوة الأشقاء ($_brothersCount)',
'share': 'عصبة بالنفس (اقتسام الباقي)',
'amount': remainingEstate,
'basis': 'يقتسمون الباقي تعصيباً لعدم وجود من يحجبهم',
});
}
}
setState(() {
_sharesResults = results;
_issueBaseNumber = hasChildren ? '24' : '12';
_shariaSummary =
'أصل المسألة من ($_issueBaseNumber) سهماً، تم استيفاء أصحاب الفروض ثم انتقال الباقي للعصبات مع مراعاة قواعد الحجب الشرعي المقررة بوزارة التربية والتعليم.';
});
}
@override
Widget build(BuildContext context) {
return Container(
color: const Color(0xFF070B12),
child: Column(
children: [
// Sub-Tab Switcher
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: const BoxDecoration(
color: Color(0xFF0E1626),
border: Border(bottom: BorderSide(color: Color(0xFF1E293B))),
),
child: Row(
children: [
_tabButton(
index: 0,
title: 'حاسبة المواريث والتركات ⚖️',
icon: CupertinoIcons.money_dollar_circle_fill,
),
const SizedBox(width: 8),
_tabButton(
index: 1,
title: 'مختبر أحكام التجويد 📖',
icon: CupertinoIcons.book_circle_fill,
),
],
),
),
Expanded(
child: IndexedStack(
index: _tabController.index,
children: [
_buildInheritanceCalculatorTab(),
_buildTajweedLabTab(),
],
),
),
],
),
);
}
Widget _tabButton({
required int index,
required String title,
required IconData icon,
}) {
final isSelected = _tabController.index == index;
return Expanded(
child: GestureDetector(
onTap: () => setState(() => _tabController.animateTo(index)),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 10),
decoration: BoxDecoration(
color: isSelected ? const Color(0xFFF59E0B) : const Color(0xFF1E293B),
borderRadius: BorderRadius.circular(10),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon,
size: 16,
color: isSelected ? Colors.black : const Color(0xFF94A3B8)),
const SizedBox(width: 8),
Text(
title,
style: TextStyle(
fontSize: 13,
fontWeight: isSelected ? FontWeight.w800 : FontWeight.w600,
color: isSelected ? Colors.black : const Color(0xFF94A3B8),
),
),
],
),
),
),
);
}
// ---------------------------------------------------------------------------
// TAB 1: Islamic Inheritance & Estate Calculator
// ---------------------------------------------------------------------------
Widget _buildInheritanceCalculatorTab() {
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Banner
Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF78350F), Color(0xFF0F172A)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(14),
border:
Border.all(color: const Color(0xFFF59E0B).withOpacity(0.4)),
),
child: const Row(
children: [
Icon(CupertinoIcons.sparkles, color: Color(0xFFFBBF24), size: 22),
SizedBox(width: 12),
Expanded(
child: Text(
'حاسبة المواريث المطابقة لكتاب التربية الإسلامية (التوجيهي الوزاري 2008) مع أصول المسائل وقواعد الحجب والعصبات.',
style: TextStyle(
fontSize: 12.5, color: Colors.white, height: 1.4),
),
),
],
),
),
const SizedBox(height: 16),
// Inputs Card
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFF0F172A),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFF1E293B)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'1. المتوفى ومقدار التركة:',
style: TextStyle(
fontSize: 13.5,
fontWeight: FontWeight.w700,
color: Colors.white),
),
const SizedBox(height: 10),
// Gender Switcher
Row(
children: [
Expanded(
child: _genderButton(
title: 'المتوفى رجل (له زوجة)',
isSelected: _deceasedIsMale,
onTap: () {
setState(() => _deceasedIsMale = true);
_calculateInheritance();
},
),
),
const SizedBox(width: 8),
Expanded(
child: _genderButton(
title: 'المتوفاة امرأة (لها زوج)',
isSelected: !_deceasedIsMale,
onTap: () {
setState(() => _deceasedIsMale = false);
_calculateInheritance();
},
),
),
],
),
const SizedBox(height: 14),
// Estate Slider
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('مقدار التركة المالية:',
style:
TextStyle(fontSize: 12, color: Color(0xFF94A3B8))),
Text('${_estateAmountJod.toStringAsFixed(0)} دينار أردني',
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w800,
color: Color(0xFFFBBF24))),
],
),
Slider(
value: _estateAmountJod,
min: 10000,
max: 500000,
divisions: 49,
activeColor: const Color(0xFFF59E0B),
inactiveColor: const Color(0xFF1E293B),
onChanged: (val) {
setState(() => _estateAmountJod = val);
_calculateInheritance();
},
),
const Divider(color: Color(0xFF1E293B), height: 20),
// Heirs Configuration
const Text(
'2. تحديد الورثة الشرعيين:',
style: TextStyle(
fontSize: 13.5,
fontWeight: FontWeight.w700,
color: Colors.white),
),
const SizedBox(height: 10),
// Parents Checkboxes
Row(
children: [
Expanded(
child: CheckboxListTile(
value: _hasFather,
title: const Text('الأب حي',
style:
TextStyle(color: Colors.white, fontSize: 13)),
activeColor: const Color(0xFFF59E0B),
contentPadding: EdgeInsets.zero,
onChanged: (val) {
setState(() => _hasFather = val ?? true);
_calculateInheritance();
},
),
),
Expanded(
child: CheckboxListTile(
value: _hasMother,
title: const Text('الأم حية',
style:
TextStyle(color: Colors.white, fontSize: 13)),
activeColor: const Color(0xFFF59E0B),
contentPadding: EdgeInsets.zero,
onChanged: (val) {
setState(() => _hasMother = val ?? true);
_calculateInheritance();
},
),
),
],
),
// Sons & Daughters Counters
Row(
children: [
Expanded(
child: _counterControl(
label: 'الأبناء الذكور',
count: _sonsCount,
onChanged: (val) {
setState(() => _sonsCount = val);
_calculateInheritance();
},
),
),
const SizedBox(width: 8),
Expanded(
child: _counterControl(
label: 'البنات الإناث',
count: _daughtersCount,
onChanged: (val) {
setState(() => _daughtersCount = val);
_calculateInheritance();
},
),
),
],
),
const SizedBox(height: 10),
// Brothers Counter
_counterControl(
label: 'الإخوة الأشقاء (تطبيق قواعد الحجب)',
count: _brothersCount,
onChanged: (val) {
setState(() => _brothersCount = val);
_calculateInheritance();
},
),
],
),
),
const SizedBox(height: 20),
// Output Distribution Table
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'جدول توزيع السهام والأنصبة الشرعية:',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w800,
color: Colors.white),
),
Container(
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: const Color(0xFFF59E0B).withOpacity(0.2),
borderRadius: BorderRadius.circular(6),
),
child: Text(
'أصل المسألة: $_issueBaseNumber',
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w800,
color: Color(0xFFFBBF24)),
),
),
],
),
const SizedBox(height: 10),
..._sharesResults.map((share) => Container(
margin: const EdgeInsets.only(bottom: 10),
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: const Color(0xFF0F172A),
borderRadius: BorderRadius.circular(14),
border: Border.all(
color: (share['amount'] as double) > 0
? const Color(0xFFF59E0B).withOpacity(0.3)
: const Color(0xFFEF4444).withOpacity(0.3),
),
),
child: Row(
children: [
CircleAvatar(
radius: 20,
backgroundColor: (share['amount'] as double) > 0
? const Color(0xFF78350F)
: const Color(0xFF7F1D1D),
child: Text(
(share['heir'] as String).substring(0, 1),
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w800,
color: (share['amount'] as double) > 0
? const Color(0xFFFBBF24)
: const Color(0xFFF87171),
),
),
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
share['heir'],
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w800,
color: Colors.white),
),
const SizedBox(width: 8),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 6, vertical: 1),
decoration: BoxDecoration(
color: const Color(0xFF1E293B),
borderRadius: BorderRadius.circular(4),
),
child: Text(
share['share'],
style: const TextStyle(
fontSize: 11,
color: Color(0xFF38BDF8),
fontWeight: FontWeight.w700),
),
),
],
),
const SizedBox(height: 4),
Text(
share['basis'],
style: const TextStyle(
fontSize: 11.5,
color: Color(0xFF94A3B8),
height: 1.3),
),
],
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'${(share['amount'] as double).toStringAsFixed(0)} د.أ',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w900,
color: (share['amount'] as double) > 0
? const Color(0xFF10B981)
: const Color(0xFFEF4444),
),
),
Text(
(share['amount'] as double) > 0
? 'مستحق'
: 'محجوب حجب حرمان',
style: TextStyle(
fontSize: 10.5,
fontWeight: FontWeight.w700,
color: (share['amount'] as double) > 0
? const Color(0xFF34D399)
: const Color(0xFFF87171),
),
),
],
),
],
),
)),
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFF1E293B),
borderRadius: BorderRadius.circular(10),
),
child: Text(
_shariaSummary,
style: const TextStyle(
fontSize: 12, color: Color(0xFFCBD5E1), height: 1.4),
),
),
],
),
);
}
Widget _genderButton({
required String title,
required bool isSelected,
required VoidCallback onTap,
}) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(vertical: 10),
decoration: BoxDecoration(
color: isSelected ? const Color(0xFFF59E0B) : const Color(0xFF1E293B),
borderRadius: BorderRadius.circular(8),
),
child: Center(
child: Text(
title,
style: TextStyle(
fontSize: 12,
fontWeight: isSelected ? FontWeight.w800 : FontWeight.w600,
color: isSelected ? Colors.black : Colors.white,
),
),
),
),
);
}
Widget _counterControl({
required String label,
required int count,
required ValueChanged<int> onChanged,
}) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: const Color(0xFF161F30),
borderRadius: BorderRadius.circular(10),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label,
style: const TextStyle(fontSize: 12, color: Colors.white)),
Row(
children: [
IconButton(
icon: const Icon(CupertinoIcons.minus_circle,
size: 20, color: Color(0xFF94A3B8)),
onPressed: count > 0 ? () => onChanged(count - 1) : null,
constraints: const BoxConstraints(),
padding: EdgeInsets.zero,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text('$count',
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w800,
color: Colors.white)),
),
IconButton(
icon: const Icon(CupertinoIcons.plus_circle,
size: 20, color: Color(0xFFF59E0B)),
onPressed: () => onChanged(count + 1),
constraints: const BoxConstraints(),
padding: EdgeInsets.zero,
),
],
),
],
),
);
}
// ---------------------------------------------------------------------------
// TAB 2: Tajweed & Phonetics Lab
// ---------------------------------------------------------------------------
Widget _buildTajweedLabTab() {
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text(
'أحكام النون الساكنة والتنوين (الوحدة الأولى - منهاج التوجيهي):',
style: TextStyle(
fontSize: 13.5,
fontWeight: FontWeight.w700,
color: Colors.white),
),
const SizedBox(height: 12),
// Rule Selector Chips
Row(
children: List.generate(_tajweedRules.length, (idx) {
final rule = _tajweedRules[idx];
final isCur = _selectedRuleIndex == idx;
return Expanded(
child: GestureDetector(
onTap: () => setState(() => _selectedRuleIndex = idx),
child: Container(
margin: EdgeInsets.only(left: idx < _tajweedRules.length - 1 ? 6 : 0),
padding: const EdgeInsets.symmetric(vertical: 10),
decoration: BoxDecoration(
color: isCur
? rule['accentColor']
: const Color(0xFF0F172A),
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: isCur
? (rule['accentColor'] as Color)
: const Color(0xFF1E293B),
),
),
child: Center(
child: Text(
rule['title'].toString().split(' ').first,
style: TextStyle(
fontSize: 12,
fontWeight: isCur ? FontWeight.w800 : FontWeight.w600,
color: isCur ? Colors.black : const Color(0xFF94A3B8),
),
),
),
),
),
);
}),
),
const SizedBox(height: 16),
// Rule Details Card
Builder(builder: (ctx) {
final rule = _tajweedRules[_selectedRuleIndex];
final examples = (rule['examples'] as List);
final accentColor = rule['accentColor'] as Color;
return Container(
padding: const EdgeInsets.all(18),
decoration: BoxDecoration(
color: const Color(0xFF0F172A),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: accentColor.withOpacity(0.4)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
rule['title'],
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w900,
color: accentColor,
),
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: accentColor.withOpacity(0.2),
borderRadius: BorderRadius.circular(6),
),
child: const Text(
'تجويد وزاري',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w700,
color: Colors.white),
),
),
],
),
const SizedBox(height: 10),
Text(
rule['definition'],
style: const TextStyle(
fontSize: 13, color: Color(0xFFCBD5E1), height: 1.5),
),
const SizedBox(height: 12),
// Letters Box
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFF1E293B),
borderRadius: BorderRadius.circular(10),
),
child: Row(
children: [
const Icon(CupertinoIcons.textformat,
color: Color(0xFFFBBF24), size: 18),
const SizedBox(width: 8),
Expanded(
child: Text(
'الحروف: ${rule['letters']}',
style: const TextStyle(
fontSize: 12.5,
fontWeight: FontWeight.w700,
color: Colors.white),
),
),
],
),
),
const SizedBox(height: 16),
const Text(
'أمثلة قرآنية مقررة وطريقة النطق:',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w700,
color: Colors.white),
),
const SizedBox(height: 8),
...examples.map((ex) => Container(
margin: const EdgeInsets.only(bottom: 8),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFF161F30),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
ex['text'],
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w900,
color: Color(0xFFFEF08A),
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
ex['highlight'],
style: TextStyle(
fontSize: 11.5,
fontWeight: FontWeight.w700,
color: accentColor),
),
Text(
'سورة ${ex['surah']}',
style: const TextStyle(
fontSize: 10.5, color: Color(0xFF64748B)),
),
],
),
],
),
)),
],
),
);
}),
],
),
);
}
}
@@ -22,6 +22,9 @@ import '../player/socratic_video_player_screen.dart';
import '../exams/adaptive_exam_screen.dart';
import 'curriculum_document_viewer_screen.dart';
import 'physics_interactive_lab_view.dart';
import 'arabic_interactive_lab_view.dart';
import 'islamic_interactive_lab_view.dart';
import 'history_interactive_timeline_view.dart';
import '../../../data/models/exam_model.dart';
import '../../../data/repositories/app_repositories.dart';
@@ -39,11 +42,74 @@ class _SubjectHubScreenState extends State<SubjectHubScreen> with SingleTickerPr
late TabController _tabController;
late Future<List<ExamModel>> _examsFuture;
bool get _hasLab =>
bool get _isScienceSubject =>
widget.subject.id.contains('physic') ||
widget.subject.title.contains('فيزياء') ||
widget.subject.id.contains('chem') ||
widget.subject.id.contains('science');
widget.subject.id.contains('كيمياء') ||
widget.subject.id.contains('science') ||
widget.subject.title.contains('علوم');
bool get _isArabicSubject =>
widget.subject.id.contains('arab') ||
widget.subject.title.contains('عرب') ||
widget.subject.title.contains('ضاد') ||
widget.subject.title.contains('لغة عربية');
bool get _isIslamicSubject =>
widget.subject.id.contains('islam') ||
widget.subject.title.contains('إسلام') ||
widget.subject.title.contains('دين') ||
widget.subject.title.contains('شريع');
bool get _isHistorySubject =>
widget.subject.id.contains('hist') ||
widget.subject.title.contains('تاريخ') ||
widget.subject.title.contains('أردن') ||
widget.subject.title.contains('جغرافيا') ||
widget.subject.title.contains('دراسات');
bool get _hasLab =>
_isScienceSubject ||
_isArabicSubject ||
_isIslamicSubject ||
_isHistorySubject;
Tab get _dynamicLabTab {
if (_isArabicSubject) {
return const Tab(
icon: Icon(CupertinoIcons.arrow_branch, size: 18),
text: 'مختبر الإعراب والعروض 📜',
);
} else if (_isIslamicSubject) {
return const Tab(
icon: Icon(CupertinoIcons.money_dollar_circle_fill, size: 18),
text: 'حاسبة المواريث والتجويد ⚖️',
);
} else if (_isHistorySubject) {
return const Tab(
icon: Icon(CupertinoIcons.time_solid, size: 18),
text: 'الخط الزمني لتاريخ الأردن 🏛️',
);
} else {
return const Tab(
icon: Icon(CupertinoIcons.lab_flask_solid, size: 18),
text: 'المختبر التفاعلي 🧪',
);
}
}
Widget get _dynamicLabWidget {
if (_isArabicSubject) {
return const ArabicInteractiveLabView();
} else if (_isIslamicSubject) {
return const IslamicInteractiveLabView();
} else if (_isHistorySubject) {
return const HistoryInteractiveTimelineView();
} else {
return const PhysicsInteractiveLabView();
}
}
@override
void initState() {
@@ -83,7 +149,7 @@ class _SubjectHubScreenState extends State<SubjectHubScreen> with SingleTickerPr
labelStyle: const TextStyle(fontWeight: FontWeight.w700, fontSize: 13),
tabs: [
const Tab(icon: Icon(CupertinoIcons.play_rectangle_fill, size: 18), text: 'الفيديوهات'),
if (_hasLab) const Tab(icon: Icon(CupertinoIcons.lab_flask_solid, size: 18), text: 'المختبر التفاعلي 🧪'),
if (_hasLab) _dynamicLabTab,
const Tab(icon: Icon(CupertinoIcons.sparkles, size: 18), text: 'بنك الأسئلة'),
const Tab(icon: Icon(CupertinoIcons.doc_text_fill, size: 18), text: 'أوراق العمل'),
const Tab(icon: Icon(CupertinoIcons.book_fill, size: 18), text: 'الكتب المقررة'),
@@ -94,7 +160,7 @@ class _SubjectHubScreenState extends State<SubjectHubScreen> with SingleTickerPr
controller: _tabController,
children: [
_buildLessonsTab(context),
if (_hasLab) const PhysicsInteractiveLabView(),
if (_hasLab) _dynamicLabWidget,
_buildExamsTab(context),
_buildWorksheetsTab(context),
_buildTextbooksTab(context),