import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../core/theme/app_theme.dart'; import '../../models/result.dart'; import '../../providers/result_provider.dart'; class ResultsScreen extends ConsumerWidget { const ResultsScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final resultsAsync = ref.watch(resultProvider); return Scaffold( appBar: AppBar( title: const Text('Examination Results'), ), body: RefreshIndicator( onRefresh: () async { ref.invalidate(resultProvider); await ref.read(resultProvider.future); }, child: resultsAsync.when( loading: () => const Center(child: CircularProgressIndicator()), error: (err, _) => Center( child: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.error_outline_rounded, size: 48, color: AppTheme.errorColor), const SizedBox(height: 12), const Text('Failed to load examination results', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), const SizedBox(height: 8), Text(err.toString(), textAlign: TextAlign.center, style: const TextStyle(color: AppTheme.textSecondary, fontSize: 13)), const SizedBox(height: 16), ElevatedButton( onPressed: () => ref.refresh(resultProvider), child: const Text('Retry'), ), ], ), ), ), data: (results) { if (results.isEmpty) { return const Center( child: Padding( padding: EdgeInsets.all(32), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.grade_outlined, size: 60, color: AppTheme.textMuted), SizedBox(height: 12), Text( 'No published results', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600), ), SizedBox(height: 6), Text( 'Your exam results will appear here once published by the controller of examinations.', textAlign: TextAlign.center, style: TextStyle(color: AppTheme.textSecondary, fontSize: 13), ), ], ), ), ); } return ListView.separated( padding: const EdgeInsets.all(16), itemCount: results.length, separatorBuilder: (_, __) => const SizedBox(height: 12), itemBuilder: (context, index) => _buildResultCard(results[index]), ); }, ), ), ); } Widget _buildResultCard(ExamResult result) { final isPass = result.status.toLowerCase() == 'pass'; return Card( child: ExpansionTile( tilePadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), leading: CircleAvatar( backgroundColor: isPass ? AppTheme.successColor.withOpacity(0.12) : AppTheme.errorColor.withOpacity(0.12), child: Text( result.grade ?? (isPass ? 'P' : 'F'), style: TextStyle( fontWeight: FontWeight.bold, color: isPass ? AppTheme.successColor : AppTheme.errorColor, ), ), ), title: Text( result.exam?.name ?? 'Examination', style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 15), ), subtitle: Padding( padding: const EdgeInsets.only(top: 4), child: Text( 'Score: ${result.totalObtainedMarks.toStringAsFixed(0)} / ${result.totalMaxMarks.toStringAsFixed(0)} (${result.percentage.toStringAsFixed(1)}%)', style: const TextStyle(fontSize: 13, color: AppTheme.textSecondary), ), ), trailing: Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( color: isPass ? AppTheme.successColor.withOpacity(0.12) : AppTheme.errorColor.withOpacity(0.12), borderRadius: BorderRadius.circular(6), ), child: Text( result.status.toUpperCase(), style: TextStyle( fontSize: 11, fontWeight: FontWeight.bold, color: isPass ? AppTheme.successColor : AppTheme.errorColor, ), ), ), children: [ const Divider(height: 1), if (result.lines.isEmpty) const Padding( padding: EdgeInsets.all(16), child: Text( 'No detailed subject breakdown available', style: TextStyle(color: AppTheme.textSecondary, fontSize: 12), ), ) else Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Subject Marks Breakdown', style: TextStyle( fontSize: 13, fontWeight: FontWeight.bold, color: AppTheme.textPrimary, ), ), const SizedBox(height: 10), ...result.lines.map((line) => _buildLineRow(line)), ], ), ), ], ), ); } Widget _buildLineRow(ResultLine line) { final linePass = line.status.toLowerCase() == 'pass'; return Padding( padding: const EdgeInsets.symmetric(vertical: 6), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( line.subject?.name ?? 'Subject', style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w600), ), Text( 'Max: ${line.maxMarks.toStringAsFixed(0)} | Min Pass: ${line.passMarks.toStringAsFixed(0)}', style: const TextStyle(fontSize: 11, color: AppTheme.textSecondary), ), ], ), ), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( '${line.obtainedMarks.toStringAsFixed(0)} (${line.percentage.toStringAsFixed(0)}%)', style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold), ), Text( line.grade != null ? 'Grade: ${line.grade}' : line.status.toUpperCase(), style: TextStyle( fontSize: 11, fontWeight: FontWeight.w600, color: linePass ? AppTheme.successColor : AppTheme.errorColor, ), ), ], ), ], ), ); } }