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
import 'inscription_page.dart';
import 'sheet_view_page.dart';
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();
// Dernières informations lues (UID etc.)
final Map<String, String> _info = {};
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(
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
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(
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),
),
),
),
const SizedBox(height: 10),
],