Aidra Connect 10.0.2+16
Aidra Connect Mobile Application
Loading...
Searching...
No Matches
qcm_screen.dart
Go to the documentation of this file.
1import 'package:connect/core/localization/app_localizations.dart';
2import 'package:connect/core/ui/theme/color_palette.dart';
3import 'package:flutter/material.dart';
4import 'package:flutter_bloc/flutter_bloc.dart';
5import 'package:flutter_screenutil/flutter_screenutil.dart';
6
7import '../../../../core/ui/screens/views/faild_to_fetch_data_view.dart';
8import '../../../../core/ui/widgets/custom_scaffold.dart';
9import '../../domain/entities/qcm_entity.dart';
10import '../logic/cubit/elearning_v2_cubit.dart';
11
12class QcmScreen extends StatefulWidget {
13 final int courseId;
14 final double currentProgress;
15 final Function(double) onComplete;
16
17 const QcmScreen({
18 super.key,
19 required this.courseId,
20 required this.currentProgress,
21 required this.onComplete,
22 });
23
24 @override
25 State<QcmScreen> createState() => _QcmScreenState();
26}
27
28class _QcmScreenState extends State<QcmScreen> {
30 List<int?> _userAnswers = [];
31 bool _quizCompleted = false;
32 double _score = 0.0;
33
34 @override
35 void initState() {
36 super.initState();
38 }
39
41 context.read<ElearningV2Cubit>().loadCourseDetails(widget.courseId);
42 }
43
44 @override
45 Widget build(BuildContext context) {
46 return BlocBuilder<ElearningV2Cubit, ElearningV2State>(
47 builder: (context, state) {
48 return CustomScaffold(
49 title: AppLocalizations.of(context).translate('Knowledge Check'),
50 isLoading: state is LoadingCourseDetailsState,
51 isLeadingVisible: true,
52 body: SafeArea(
53 child: _buildBody(state),
54 ),
55 );
56 },
57 );
58 }
59
61 if (state is CourseDetailsLoadedState) {
62 final qcmList = state.qcmList;
63
64 // Initialize user answers if not already done
65 if (_userAnswers.isEmpty) {
66 _userAnswers = List.filled(qcmList.length, null);
67 }
68
69 if (_quizCompleted) {
71 } else {
73 }
74 } else if (state is CourseDetailsLoadingFailureState) {
75 return FailedToFetchDataView(
76 onRetry: _loadQuestions,
77 );
78 } else {
79 return const SizedBox();
80 }
81 }
82
83 Widget _buildQuizQuestion(List<QcmEntity> questions) {
84 if (questions.isEmpty) {
85 return Center(
86 child: Text(
87 AppLocalizations.of(context)
88 .translate('No questions available for this course'),
89 style: Theme.of(context).textTheme.bodyMedium,
90 ),
91 );
92 }
93
94 final currentQuestion = questions[_currentQuestionIndex];
95
96 return Padding(
97 padding: EdgeInsets.all(16.r),
98 child: Column(
99 crossAxisAlignment: CrossAxisAlignment.start,
100 children: [
101 // Progress indicator
102 LinearProgressIndicator(
103 value: (_currentQuestionIndex + 1) / questions.length,
104 backgroundColor: Colors.grey.withOpacity(0.2),
105 valueColor: AlwaysStoppedAnimation<Color>(
106 Theme.of(context).primaryColor,
107 ),
108 borderRadius: BorderRadius.circular(4.r),
109 minHeight: 3.r,
110 ),
111 SizedBox(height: 8.r),
112 Row(
113 mainAxisAlignment: MainAxisAlignment.spaceBetween,
114 children: [
115 Text(
116 '${AppLocalizations.of(context).translate('Question')} ${_currentQuestionIndex + 1} of ${questions.length}',
117 style: Theme.of(context).textTheme.bodySmall?.copyWith(
118 color: Theme.of(context).hintColor,
119 ),
120 ),
121 Text(
122 '${((_currentQuestionIndex + 1) / questions.length * 100).toInt()}%',
123 style: Theme.of(context).textTheme.displaySmall?.copyWith(
124 color: Theme.of(context).colorScheme.primary,
125 ),
126 ),
127 ],
128 ),
129 SizedBox(height: 24.r),
130
131 // Question
132 Text(
133 currentQuestion.question ??
134 AppLocalizations.of(context).translate('Question'),
135 style: Theme.of(context).textTheme.displaySmall,
136 ),
137 SizedBox(height: 24.r),
138
139 // Options
140 Expanded(
141 child: ListView.separated(
142 itemCount: currentQuestion.options?.length ?? 0,
143 separatorBuilder: (context, index) => SizedBox(height: 12.r),
144 itemBuilder: (context, index) {
146
147 return GestureDetector(
148 onTap: () {
149 setState(() {
151 });
152 },
153 child: Container(
154 padding: EdgeInsets.all(16.r),
155 decoration: BoxDecoration(
157 ? Theme.of(context).primaryColor.withOpacity(0.1)
158 : Colors.transparent,
159 borderRadius: BorderRadius.circular(10.r),
160 ),
161 child: Row(
162 children: [
163 Container(
164 width: 24.r,
165 height: 24.r,
166 decoration: BoxDecoration(
167 shape: BoxShape.circle,
169 ? Theme.of(context).primaryColor
170 : Colors.transparent,
171 border: Border.all(
173 ? Theme.of(context).primaryColor
174 : Theme.of(context).hintColor,
175 width: 1,
176 ),
177 ),
179 ? Icon(
180 Icons.check,
181 size: 16.r,
182 color:
183 Theme.of(context).colorScheme.onPrimary,
184 )
185 : null,
186 ),
187 SizedBox(width: 12.r),
188 Expanded(
189 child: Text(
190 currentQuestion.options?[index] ?? '',
191 style: Theme.of(context).textTheme.bodyMedium,
192 ),
193 ),
194 ],
195 ),
196 ),
197 );
198 },
199 ),
200 ),
201
202 // Navigation buttons
203 SizedBox(height: 16.r),
204 Row(
205 mainAxisAlignment: MainAxisAlignment.spaceBetween,
206 children: [
207 if (_currentQuestionIndex > 0)
208 ElevatedButton(
209 onPressed: () {
210 setState(() {
212 });
213 },
214 style: ElevatedButton.styleFrom(
215 padding: EdgeInsets.symmetric(horizontal: 20, vertical: 15),
216 shape: RoundedRectangleBorder(
217 borderRadius: BorderRadius.circular(200),
218 ),
219 ),
220 child:
221 Text(AppLocalizations.of(context).translate('Previous')),
222 )
223 else
224 SizedBox(width: 100.r),
225 ElevatedButton(
226 style: ElevatedButton.styleFrom(
227 padding: EdgeInsets.symmetric(horizontal: 20, vertical: 15),
228 shape: RoundedRectangleBorder(
229 borderRadius: BorderRadius.circular(200),
230 ),
231 ),
232 onPressed: _userAnswers[_currentQuestionIndex] != null
233 ? () {
234 if (_currentQuestionIndex < questions.length - 1) {
235 setState(() {
236 _currentQuestionIndex++;
237 });
238 } else {
239 // Calculate score
240 int correctAnswers = 0;
241 for (int i = 0; i < questions.length; i++) {
242 if (_userAnswers[i] ==
243 questions[i].correctAnswerIndex) {
244 correctAnswers++;
245 }
246 }
247
248 _score = correctAnswers / questions.length;
249
250 setState(() {
251 _quizCompleted = true;
252 });
253 }
254 }
255 : null,
256 child: Text(
257 _currentQuestionIndex < questions.length - 1
258 ? AppLocalizations.of(context).translate('Next')
259 : AppLocalizations.of(context).translate('Finish'),
260 ),
261 ),
262 ],
263 ),
264 ],
265 ),
266 );
267 }
268
269 Widget _buildQuizResults(List<QcmEntity> questions) {
270 final correctAnswers = _userAnswers
271 .asMap()
272 .entries
273 .where(
274 (entry) => entry.value == questions[entry.key].correctAnswerIndex,
275 )
276 .length;
277
278 return Padding(
279 padding: EdgeInsets.all(16.r),
280 child: Column(
281 crossAxisAlignment: CrossAxisAlignment.center,
282 children: [
283 SizedBox(height: 24.r),
284
285 // Score circle
286 Container(
287 width: 150.r,
288 height: 150.r,
289 decoration: BoxDecoration(
290 shape: BoxShape.circle,
291 color: Theme.of(context).primaryColor.withOpacity(0.1),
292 ),
293 child: Center(
294 child: Column(
295 mainAxisAlignment: MainAxisAlignment.center,
296 children: [
297 Text(
298 '${(_score * 100).toInt()}%',
299 style: Theme.of(context).textTheme.displayLarge?.copyWith(
300 color: Theme.of(context).primaryColor,
301 ),
302 ),
303 Text(
304 AppLocalizations.of(context).translate('Score'),
305 style: Theme.of(context).textTheme.bodyLarge,
306 ),
307 ],
308 ),
309 ),
310 ),
311 SizedBox(height: 30.r),
312
313 // Results summary
314 Center(
315 child: Text(
316 AppLocalizations.of(context).translate('Quiz Completed'),
317 style: Theme.of(context).textTheme.displayMedium,
318 ),
319 ),
320 SizedBox(height: 15.r),
321 Text(
322 '${AppLocalizations.of(context).translate('You answered')} $correctAnswers ${AppLocalizations.of(context).translate('out of')} ${questions.length} ${AppLocalizations.of(context).translate('questions correctly')}',
323 style: Theme.of(context).textTheme.bodyMedium?.copyWith(
324 color: Theme.of(context).hintColor,
325 ),
326 textAlign: TextAlign.center,
327 ),
328 SizedBox(height: 32.r),
329
330 // Review answers button
331 ElevatedButton.icon(
332 onPressed: () {
333 setState(() {
334 _quizCompleted = false;
335 _currentQuestionIndex = 0;
336 });
337 },
338 style: ElevatedButton.styleFrom(
339 padding: EdgeInsets.symmetric(
340 horizontal: 30,
341 vertical: 20,
342 ),
343 shape: RoundedRectangleBorder(
344 borderRadius: BorderRadius.circular(200),
345 ),
346 backgroundColor: Theme.of(context).primaryColor.withOpacity(0.11),
347 foregroundColor: Theme.of(context).primaryColor.withOpacity(0.8),
348 ),
349 icon: Icon(Icons.refresh),
350 label: Text(
351 AppLocalizations.of(context).translate('Review Answers'),
352 ),
353 ),
354 SizedBox(height: 16.r),
355
356 // Complete button
357 ElevatedButton.icon(
358 onPressed: () {
359 widget.onComplete(_score);
360 Navigator.pop(context);
361 },
362 style: ElevatedButton.styleFrom(
363 padding: EdgeInsets.symmetric(
364 horizontal: 30,
365 vertical: 20,
366 ),
367 // foregroundColor: ColorPalette.lightGreen,
368 backgroundColor: Theme.of(context).primaryColor,
369 shape: RoundedRectangleBorder(
370 borderRadius: BorderRadius.circular(200),
371 ),
372 ),
373 icon: Icon(Icons.check_circle),
374 label: Text(
375 AppLocalizations.of(context).translate('Complete'),
376 ),
377 ),
378 ],
379 ),
380 );
381 }
382}
static AppLocalizations of(BuildContext context)
String translate(String key)
static AppLocalizations of(BuildContext context)
override State< QcmScreen > createState()
final Function(double) onComplete
final double currentProgress
const QcmScreen({ super.key, required this.courseId, required this.currentProgress, required this.onComplete, })
Widget _buildBody(ElearningV2State state)
final Widget child
final EdgeInsets padding
override void initState()
final List< QcmEntity > qcmList
const CourseDetailsLoadedState(this.course, this.pdfPath, this.qcmList)
final Color backgroundColor
final String label
final VoidCallback onTap
final bool isSelected
final Color color
Definition failures.dart:1
class GetPdfPathUseCase implements UseCase< String, PdfParams > courseId
override Widget build(BuildContext context)
bool _quizCompleted
void _loadQuestions()
List< int?> _userAnswers
double _score
class QcmScreen extends StatefulWidget _currentQuestionIndex
Widget _buildQuizResults(List< QcmEntity > questions)
Widget _buildQuizQuestion(List< QcmEntity > questions)
final double value
final String title