Files
timemaster-kiosk/lib/main.dart
T
patrickandClaude Sonnet 5 cea8ba8f08 fix: Fehlerbehandlung + NFC-Session-Leak (flutter-expert-Review)
- NFC onDiscovered: try/finally um stopSession(), sonst bleibt die native
  NFC-Session bei einer Exception in _extractUid offen und jeder weitere
  Scan schlägt dauerhaft fehl (nur App-Neustart behebt es).
- Alle Login-/Stamp-Handler fangen jetzt auch generische Exceptions (nicht
  nur DioException) – sonst wirkt der Kiosk bei unerwarteter Serverantwort
  nur "eingefroren" ohne Hinweis.
- _runAction: expliziter Re-Entrancy-Guard gegen Doppel-Tap, Status-Refresh
  nach fehlgeschlagener Stempel-Aktion (kein veralteter Button-Zustand).
- ApiClient.init(): Secure-Storage-Reads einzeln try/catch, dio wird immer
  konstruiert auch wenn Keystore beim Kaltstart noch nicht bereit ist.
- main(): ApiClient.init() in try/catch – Fehler dort ließ die App vorher
  komplett schwarz bleiben (runApp() nie erreicht).

v0.1.2+3. Review von flutter-expert-Subagent, Diffs manuell gegengeprüft.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dzdm3EWeNdYFUuXFRJryA1
2026-09-10 11:55:02 +02:00

65 lines
2.3 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:async';
import 'package:flutter/material.dart';
import 'app_version.dart';
import 'services/api_client.dart';
import 'screens/enrollment_screen.dart';
import 'screens/login_screen.dart';
Future<void> main() async {
// Release-Build zeigt bei unbehandelten Fehlern sonst nur eine leere
// Fläche ohne Text – siehe timemaster-mobile v0.1.1+2 (gleiches Problem,
// gleicher Fix hier von Anfang an).
ErrorWidget.builder = (FlutterErrorDetails details) {
return Material(
color: Colors.red[900],
child: SafeArea(
child: Padding(
padding: const EdgeInsets.all(16),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Fehler (Kiosk $kAppVersion)',
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 16)),
const SizedBox(height: 8),
Text(details.exceptionAsString(), style: const TextStyle(color: Colors.white70, fontSize: 12)),
],
),
),
),
),
);
};
runZonedGuarded(() async {
WidgetsFlutterBinding.ensureInitialized();
try {
await ApiClient.instance.init();
} catch (e, st) {
// Scheitert z.B. flutter_secure_storage/Keystore beim Kaltstart, blieb
// die App sonst komplett schwarz stehen (runApp() nie erreicht,
// ErrorWidget.builder greift nur für Fehler im Widget-Tree, nicht
// hier) – auf einem unbeaufsichtigten Kiosk ohne angeschlossene
// Konsole nicht von einem simplen Hänger zu unterscheiden. Lieber
// sichtbar machen und mit leerem Enrollment-Status weiterstarten.
debugPrint('ApiClient.init() fehlgeschlagen (Kiosk $kAppVersion): $e\n$st');
}
runApp(const TimeMasterKioskApp());
}, (error, stack) {
debugPrint('Unbehandelter Fehler (Kiosk $kAppVersion): $error\n$stack');
});
}
class TimeMasterKioskApp extends StatelessWidget {
const TimeMasterKioskApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'TimeMaster Kiosk',
theme: ThemeData(colorSchemeSeed: Colors.indigo, useMaterial3: true),
home: ApiClient.instance.isEnrolled ? const LoginScreen() : const EnrollmentScreen(),
);
}
}