home_page.dart 6,32 ko
Newer Older
import 'package:flutter/material.dart';
import 'package:nfc_google_sheet/components/selection_indicator.dart';
import 'package:nfc_google_sheet/components/student_form.dart';
Jérémy DEZETREE's avatar
Jérémy DEZETREE a validé
import 'package:nfc_google_sheet/components/title.dart';
import 'package:nfc_google_sheet/components/wide_button.dart';
import 'package:flutter_nfc_kit/flutter_nfc_kit.dart';
import 'package:nfc_google_sheet/context/app_colors.dart';
import 'package:nfc_google_sheet/database/student_dao.dart';
import 'package:nfc_google_sheet/database/attendance_dao.dart';
import 'package:nfc_google_sheet/pages/sheet_page.dart';
class HomePage extends StatefulWidget {
  const HomePage({super.key});
  @override
  State<HomePage> createState() => _HomePageState();
class _HomePageState extends State<HomePage> {
  String nfcData = "Appuyez sur une carte NFC";
  ScanStatus _scanStatus = ScanStatus.idle;

  void _handleAttendanceResult(AttendanceResult result, String promotion) {
    switch (result) {
      case AttendanceResult.success:
        setState(() {
          nfcData = "Émargement enregistré !";
          _scanStatus = ScanStatus.success;
        });
      case AttendanceResult.alreadyScanned:
        setState(() {
          nfcData = "Cet étudiant a déjà émargé pour cet examen.";
          _scanStatus = ScanStatus.error;
        });
      case AttendanceResult.wrongPromotion:
        setState(() {
          nfcData = "Mauvaise promotion : $promotion (attendu : ${SheetPage.selectedProgram})";
          _scanStatus = ScanStatus.error;
        });
    }
  }

Jérémy DEZETREE's avatar
Jérémy DEZETREE a validé
  Future<void> startNFC() async {
    if (SheetPage.selectedProgram == null || SheetPage.selectedSubject == null) {
      setState(() {
        nfcData = "Veuillez d'abord sélectionner une filière et une matière.";
        _scanStatus = ScanStatus.error;
      });
      return;
    }

    try {
      var availability = await FlutterNfcKit.nfcAvailability;
      if (availability != NFCAvailability.available) {
        setState(() {
          nfcData = "NFC non disponible ou désactivé.";
          _scanStatus = ScanStatus.error;
        });
        return;
      }

Jérémy DEZETREE's avatar
Jérémy DEZETREE a validé
      setState(() {
        nfcData = "En attente d'une carte NFC...";
        _scanStatus = ScanStatus.scanning;
Jérémy DEZETREE's avatar
Jérémy DEZETREE a validé
      });

      NFCTag tag = await FlutterNfcKit.poll(timeout: Duration(seconds: 15));
      final cardId = tag.id;
      final existingStudent = await StudentDao.instance.getByCardId(cardId);
      if (existingStudent != null) {
        final result = await AttendanceDao.instance.register(
          cardId, existingStudent.promotion, SheetPage.selectedProgram!, SheetPage.selectedSubject!,
        );
        _handleAttendanceResult(result, existingStudent.promotion);
      } else {
        setState(() {
          nfcData = "Nouvel étudiant détecté";
          _scanStatus = ScanStatus.success;
        });
        showDialog(
          context: context,
          builder: (BuildContext context) {
            return StudentForm(
              cardId: cardId,
              onStudentSaved: (student) async {
                final result = await AttendanceDao.instance.register(
                  cardId, student.promotion, SheetPage.selectedProgram!, SheetPage.selectedSubject!,
                );
                _handleAttendanceResult(result, student.promotion);
              },
            );
          },
        );
      }
    } catch (e) {
      setState(() {
        nfcData = "Erreur ou scan annulé.";
        _scanStatus = ScanStatus.error;
      });
    } finally {
      await FlutterNfcKit.finish();
      if (_scanStatus == ScanStatus.scanning) {
        setState(() {
          _scanStatus = ScanStatus.idle;
          nfcData = "Appuyez sur une carte NFC";
        });
Jérémy DEZETREE's avatar
Jérémy DEZETREE a validé
      }
    }
  }

  Widget _buildStatusIcon() {
    switch (_scanStatus) {
      case ScanStatus.scanning:
        return SizedBox(
          width: 120,
          height: 120,
          child: CircularProgressIndicator(
            strokeWidth: 6,
          ),
        );
      case ScanStatus.success:
        return Icon(Icons.check_circle_outline, color: AppColors.selected, size: 120);
      case ScanStatus.error:
        return Icon(Icons.error_outline, color: Colors.red.shade400, size: 120);
      default:
        return Icon(Icons.nfc, color: AppColors.unselected.withValues(alpha: 0.7), size: 120);
    }
  }

  Widget _buildStatusText() {
    Color textColor;
    switch (_scanStatus) {
      case ScanStatus.success:
      case ScanStatus.error:
        textColor = Colors.red.shade400;
      default:
    }

    return Container(
      padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
      decoration: BoxDecoration(
        color: AppColors.subtitle.withValues(alpha: 0.5),
        borderRadius: BorderRadius.circular(12),
      ),
      child: Text(
        nfcData,
        textAlign: TextAlign.center,
        style: TextStyle(color: textColor, fontSize: 16, fontWeight: FontWeight.w500),
  @override
  Widget build(BuildContext context) {
    bool isScanning = _scanStatus == ScanStatus.scanning;

    return Scaffold(
      backgroundColor: AppColors.background,
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.all(24.0),
          child: Column(
            children: [
              TitleCustom(content: 'ENREGISTREZ - VOUS'),
              SizedBox(height: 12),
              SelectionIndicator(),
              Expanded(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    _buildStatusIcon(),
                    SizedBox(height: 30),
                    _buildStatusText(),
                  ],
                ),
              ),
              WideButton(
                onPressed: isScanning ? null : startNFC,
                icon: Icons.nfc,
                label: isScanning ? "SCAN EN COURS..." : "ENREGISTRER SA CARTE",
                backgroundColor: AppColors.selected,
                foregroundColor: AppColors.background,
                disabledBackgroundColor: AppColors.selected.withValues(alpha: 0.5),
                letterSpacing: 1.1,
Jérémy DEZETREE's avatar
Jérémy DEZETREE a validé
          ),
        ),
}

enum ScanStatus {
  idle,
  scanning,
  success,
  error,