Commits (2)
...@@ -42,6 +42,6 @@ ...@@ -42,6 +42,6 @@
<data android:mimeType="text/plain"/> <data android:mimeType="text/plain"/>
</intent> </intent>
</queries> </queries>
<uses-permission android:name="android.permission.NFC"/>
<uses-permission android:name="android.permission.NFC" /> <uses-feature android:name="android.hardware.nfc" android:required="true"/>
</manifest> </manifest>
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:nfc_manager/nfc_manager.dart'; import 'package:nfc_manager/nfc_manager.dart';
import 'dart:typed_data'; import 'dart:typed_data'; // Pour convertir l'UID
import 'google_sheets_api.dart'; import 'google_sheets_api.dart'; // <-- ON IMPORTE NOTRE API
import 'inscription_page.dart'; // <-- 1. IMPORTER LA NOUVELLE PAGE import 'inscription_page.dart';
import 'sheet_view_page.dart';
void main() { void main() {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
...@@ -22,9 +23,11 @@ class SimpleNfcReader extends StatefulWidget { ...@@ -22,9 +23,11 @@ class SimpleNfcReader extends StatefulWidget {
class _SimpleNfcReaderState extends State<SimpleNfcReader> { class _SimpleNfcReaderState extends State<SimpleNfcReader> {
bool _isReading = false; bool _isReading = false;
String _status = 'Appuyez sur le bouton pour vérifier un étudiant'; String _status = 'Appuyez sur le bouton pour vérifier un étudiant';
String? _lastScannedUid; // <-- 2. GARDER EN MÉMOIRE L'UID SCANNÉ
// Instance de notre API Google Sheets
final GoogleSheetsApi _sheetsApi = GoogleSheetsApi(); final GoogleSheetsApi _sheetsApi = GoogleSheetsApi();
// Dernières informations lues (UID etc.)
final Map<String, String> _info = {};
Future<void> _startNfc() async { Future<void> _startNfc() async {
bool available = await NfcManager.instance.isAvailable(); bool available = await NfcManager.instance.isAvailable();
...@@ -36,7 +39,6 @@ class _SimpleNfcReaderState extends State<SimpleNfcReader> { ...@@ -36,7 +39,6 @@ class _SimpleNfcReaderState extends State<SimpleNfcReader> {
setState(() { setState(() {
_isReading = true; _isReading = true;
_status = '🔄 Approchez une carte étudiant...'; _status = '🔄 Approchez une carte étudiant...';
_lastScannedUid = null; // Réinitialiser à chaque scan
}); });
NfcManager.instance.startSession( NfcManager.instance.startSession(
...@@ -48,6 +50,7 @@ class _SimpleNfcReaderState extends State<SimpleNfcReader> { ...@@ -48,6 +50,7 @@ class _SimpleNfcReaderState extends State<SimpleNfcReader> {
onDiscovered: (NfcTag tag) async { onDiscovered: (NfcTag tag) async {
String? uid; String? uid;
try { try {
// --- 1. Récupérer le numéro de série (UID) ---
dynamic data; dynamic data;
try { data = (tag as dynamic).data; } catch (_) {} try { data = (tag as dynamic).data; } catch (_) {}
...@@ -63,18 +66,21 @@ class _SimpleNfcReaderState extends State<SimpleNfcReader> { ...@@ -63,18 +66,21 @@ class _SimpleNfcReaderState extends State<SimpleNfcReader> {
return; return;
} }
// --- 2. Vérifier dans Google Sheets ---
setState(() => _status = '🔄 N° de série: $uid\nVérification en cours...'); 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(); await NfcManager.instance.stopSession();
// Appel de notre NOUVELLE fonction
String? nomEtudiant = await _sheetsApi.findStudentNameByNfcUid(uid); String? nomEtudiant = await _sheetsApi.findStudentNameByNfcUid(uid);
// --- 3. Afficher le résultat final ---
setState(() { setState(() {
if (nomEtudiant != null) { if (nomEtudiant != null) {
_status = '✅ Bienvenue, $nomEtudiant !'; _status = '✅ Bienvenue, $nomEtudiant !';
_lastScannedUid = null; // Trouvé, donc on n'inscrit pas
} else { } else {
_status = '❌ Étudiant inexistant.\nN°: $uid'; _status = '❌ Étudiant inexistant.\nN°: $uid';
_lastScannedUid = uid; // <-- 3. ON SAUVEGARDE L'UID POUR L'INSCRIPTION
} }
}); });
...@@ -85,33 +91,14 @@ class _SimpleNfcReaderState extends State<SimpleNfcReader> { ...@@ -85,33 +91,14 @@ class _SimpleNfcReaderState extends State<SimpleNfcReader> {
await NfcManager.instance.stopSession(); await NfcManager.instance.stopSession();
} }
// On a fini, on réactive le bouton
setState(() => _isReading = false); setState(() => _isReading = false);
}, },
); );
} }
// --- 4. FONCTION POUR OUVRIR LA PAGE D'INSCRIPTION ---
void _goToInscriptionPage() {
if (_lastScannedUid == null) return;
// Naviguer vers la page d'inscription et lui passer l'UID
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => InscriptionPage(prefilledMac: _lastScannedUid),
),
).then((inscriptionReussie) {
// Ce code s'exécute quand on revient de la page d'inscription
if (inscriptionReussie == true) {
setState(() {
_status = '✅ Nouvel étudiant enregistré !';
_lastScannedUid = null; // On nettoie
});
}
});
}
// --- Fonctions utilitaires (inchangées) --- // --- Fonctions utilitaires (inchangées) ---
String _bytesToHexString(dynamic maybeBytes) { String _bytesToHexString(dynamic maybeBytes) {
try { try {
Uint8List bytes; Uint8List bytes;
...@@ -119,7 +106,6 @@ class _SimpleNfcReaderState extends State<SimpleNfcReader> { ...@@ -119,7 +106,6 @@ class _SimpleNfcReaderState extends State<SimpleNfcReader> {
else if (maybeBytes is List<int>) bytes = Uint8List.fromList(maybeBytes); else if (maybeBytes is List<int>) bytes = Uint8List.fromList(maybeBytes);
else return maybeBytes.toString(); else return maybeBytes.toString();
// SANS DEUX-POINTS
return bytes.map((b) => b.toRadixString(16).padLeft(2, '0').toUpperCase()).join(''); return bytes.map((b) => b.toRadixString(16).padLeft(2, '0').toUpperCase()).join('');
} catch (_) { } catch (_) {
return maybeBytes.toString(); return maybeBytes.toString();
...@@ -144,10 +130,51 @@ class _SimpleNfcReaderState extends State<SimpleNfcReader> { ...@@ -144,10 +130,51 @@ class _SimpleNfcReaderState extends State<SimpleNfcReader> {
return null; return null;
} }
/// L'interface utilisateur
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar(title: const Text('📡 Vérification Étudiant')), appBar: AppBar(title: const Text('📡 Lecteur NFC simple')),
drawer: Drawer(
child: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
DrawerHeader(
decoration: BoxDecoration(color: Theme.of(context).colorScheme.primaryContainer),
child: const Center(child: Text('Menu', style: TextStyle(fontSize: 20))),
),
ListTile(
leading: const Icon(Icons.person_add),
title: const Text('Inscription'),
onTap: () async {
Navigator.of(context).pop();
final prefMac = _info['Numéro de série'] ?? _info['UID brute'];
await Navigator.of(context).push(MaterialPageRoute(builder: (_) => InscriptionPage(prefilledMac: prefMac)));
},
),
ListTile(
leading: const Icon(Icons.table_chart),
title: const Text('Liste des données'),
onTap: () async {
Navigator.of(context).pop();
await Navigator.of(context).push(MaterialPageRoute(builder: (_) => const SheetViewPage()));
},
),
const Divider(),
ListTile(
leading: const Icon(Icons.info_outline),
title: const Text('A propos'),
onTap: () => showAboutDialog(
context: context,
applicationName: 'NFC Reader',
children: [const Text('Lecteur NFC et gestion des inscriptions')],
),
),
],
),
),
),
body: Padding( body: Padding(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
child: Column( child: Column(
...@@ -165,32 +192,16 @@ class _SimpleNfcReaderState extends State<SimpleNfcReader> { ...@@ -165,32 +192,16 @@ class _SimpleNfcReaderState extends State<SimpleNfcReader> {
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
// --- 5. AFFICHAGE DU BOUTON D'INSCRIPTION ---
if (_lastScannedUid != null && !_isReading)
Padding(
padding: const EdgeInsets.only(top: 24.0),
child: ElevatedButton.icon(
onPressed: _goToInscriptionPage,
icon: const Icon(Icons.person_add),
label: const Text('Inscrire ce nouvel étudiant'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green, // Couleur différente
padding: const EdgeInsets.symmetric(vertical: 12),
),
),
),
const Spacer(), const Spacer(),
Center( Center(
child: ElevatedButton.icon( child: ElevatedButton.icon(
onPressed: _isReading ? null : _startNfc, onPressed: _isReading ? null : _startNfc,
icon: _isReading icon: _isReading
? const SizedBox( ? const SizedBox(
width: 20, width: 20,
height: 20, height: 20,
child: CircularProgressIndicator(color: Colors.white, strokeWidth: 2) child: CircularProgressIndicator(color: Colors.white, strokeWidth: 2),
) )
: const Icon(Icons.nfc), : const Icon(Icons.nfc),
label: Text(_isReading ? 'Lecture en cours...' : 'Vérifier la carte'), label: Text(_isReading ? 'Lecture en cours...' : 'Vérifier la carte'),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
...@@ -198,6 +209,7 @@ class _SimpleNfcReaderState extends State<SimpleNfcReader> { ...@@ -198,6 +209,7 @@ class _SimpleNfcReaderState extends State<SimpleNfcReader> {
), ),
), ),
), ),
const SizedBox(height: 10),
], ],
), ),
), ),
......
...@@ -22,6 +22,8 @@ class _SheetViewPageState extends State<SheetViewPage> { ...@@ -22,6 +22,8 @@ class _SheetViewPageState extends State<SheetViewPage> {
Future<void> _load() async { Future<void> _load() async {
setState(() => _loading = true); setState(() => _loading = true);
final data = await _api.readData(); final data = await _api.readData();
// afficher les données dans la console pour le débogage
print('Données chargées : $data');
setState(() { setState(() {
_rows = data ?? []; _rows = data ?? [];
_loading = false; _loading = false;
......