- 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
65 lines
2.3 KiB
Dart
65 lines
2.3 KiB
Dart
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(),
|
||
);
|
||
}
|
||
}
|