Newer
Older
import 'package:nfc_manager/nfc_manager.dart';
import 'dart:typed_data'; // Pour convertir l'UID
import 'google_sheets_api.dart'; // <-- ON IMPORTE NOTRE API
WidgetsFlutterBinding.ensureInitialized();
runApp(const MaterialApp(
debugShowCheckedModeBanner: false,
home: SimpleNfcReader(),
));
class SimpleNfcReader extends StatefulWidget {
const SimpleNfcReader({super.key});
@override
State<SimpleNfcReader> createState() => _SimpleNfcReaderState();
}
class _SimpleNfcReaderState extends State<SimpleNfcReader> {
bool _isReading = false;
String _status = 'Appuyez sur le bouton pour vérifier un étudiant';
// Instance de notre API Google Sheets
final GoogleSheetsApi _sheetsApi = GoogleSheetsApi();
Future<void> _startNfc() async {
bool available = await NfcManager.instance.isAvailable();
if (!available) {
setState(() => _status = '❌ NFC non disponible sur cet appareil');
return;
}
setState(() {
_isReading = true;
_status = '🔄 Approchez une carte étudiant...';
});
NfcManager.instance.startSession(
pollingOptions: {
NfcPollingOption.iso14443,
NfcPollingOption.iso15693,
NfcPollingOption.iso18092,
},
onDiscovered: (NfcTag tag) async {
String? uid;
try {
// --- 1. Récupérer le numéro de série (UID) ---
dynamic data;
try { data = (tag as dynamic).data; } catch (_) {}
final idBytes = _safeLookup(data, 'id');
if (idBytes != null) {
uid = _bytesToHexString(idBytes);
}
if (uid == null) {
setState(() => _status = '❌ Impossible de lire le N° de série.');
await NfcManager.instance.stopSession();
setState(() => _isReading = false);
return;
}
// --- 2. Vérifier dans Google Sheets ---
setState(() => _status = '🔄 N° de série: $uid\nVérification en cours...');
// On arrête le scan NFC pendant qu'on contacte la base de données
await NfcManager.instance.stopSession();
// Appel de notre NOUVELLE fonction
String? nomEtudiant = await _sheetsApi.findStudentNameByNfcUid(uid);
// --- 3. Afficher le résultat final ---
setState(() {
if (nomEtudiant != null) {
_status = '✅ Bienvenue, $nomEtudiant !';
} else {
_status = '❌ Étudiant inexistant.\nN°: $uid';
}
});
} catch (e) {
setState(() {
_status = 'Erreur: ${e.toString()}';
});
await NfcManager.instance.stopSession();
}
// On a fini, on réactive le bouton
setState(() => _isReading = false);
},
);
}
// --- Fonctions utilitaires (inchangées) ---
String _bytesToHexString(dynamic maybeBytes) {
try {
Uint8List bytes;
if (maybeBytes is Uint8List) bytes = maybeBytes;
else if (maybeBytes is List<int>) bytes = Uint8List.fromList(maybeBytes);
else return maybeBytes.toString();
return bytes.map((b) => b.toRadixString(16).padLeft(2, '0').toUpperCase()).join('');
} catch (_) {
return maybeBytes.toString();
}
}
dynamic _safeLookup(dynamic obj, String key) {
if (obj == null) return null;
try {
if (obj is Map) {
if (obj.containsKey(key)) return obj[key];
final lower = key.toLowerCase();
if (obj.containsKey(lower)) return obj[lower];
} catch (_) {}
try {
return (obj as dynamic)[key];
} catch (_) {}
try {
if (key == 'id') return (obj as dynamic).id;
} catch (_) {}
return null;
/// L'interface utilisateur
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('📡 Vérification Étudiant')),
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Icon(
_isReading ? Icons.nfc : Icons.school,
size: 100,
color: Colors.blueAccent,
),
const SizedBox(height: 30),
Text(
_status,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
const Spacer(),
Center(
child: ElevatedButton.icon(
onPressed: _isReading ? null : _startNfc,
icon: _isReading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(color: Colors.white, strokeWidth: 2)
)
: const Icon(Icons.nfc),
label: Text(_isReading ? 'Lecture en cours...' : 'Vérifier la carte'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 15),
),
),
),
],