Initial commit
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
import "package:flutter/material.dart";
|
||||
|
||||
import "api.dart";
|
||||
import "app_settings.dart";
|
||||
import "auth.dart";
|
||||
import "biometric.dart";
|
||||
import "theme.dart";
|
||||
import "screens/lock_screen.dart";
|
||||
import "screens/login_screen.dart";
|
||||
import "screens/root_shell.dart";
|
||||
|
||||
// Simple app-wide singletons (small app — no DI framework needed).
|
||||
final apiClient = ApiClient();
|
||||
final authService = AuthService(apiClient);
|
||||
final appSettings = AppSettings();
|
||||
|
||||
Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await apiClient.loadServerUrl();
|
||||
await appSettings.loadFromStorage();
|
||||
await authService.loadFromStorage();
|
||||
// Prime the biometric-enabled cache; if a session survived from a previous
|
||||
// run and biometric login is on, start locked so the app requires an unlock
|
||||
// instead of jumping straight to the dashboard.
|
||||
final bioEnabled = await biometricAuth.isEnabled();
|
||||
if (authService.isAuthenticated && bioEnabled) {
|
||||
authService.lock();
|
||||
}
|
||||
// If a token survived from a previous run, refresh the profile so the saved
|
||||
// appearance prefs (theme/font/date format) apply on boot.
|
||||
if (authService.isAuthenticated) {
|
||||
apiClient.getMe().then((p) => appSettings.applyFromProfile(p)).catchError((_) {});
|
||||
}
|
||||
runApp(const CarControlApp());
|
||||
}
|
||||
|
||||
class CarControlApp extends StatefulWidget {
|
||||
const CarControlApp({super.key});
|
||||
@override
|
||||
State<CarControlApp> createState() => _CarControlAppState();
|
||||
}
|
||||
|
||||
class _CarControlAppState extends State<CarControlApp> with WidgetsBindingObserver {
|
||||
// Grace period: a quick app-switch (returning within this window) does NOT
|
||||
// re-lock; staying in the background longer does.
|
||||
static const _lockGrace = Duration(seconds: 30);
|
||||
DateTime? _backgroundedAt;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
// Re-lock when the app returns from the background, but only if it was away
|
||||
// longer than the grace period — so quick app-switches don't re-lock.
|
||||
// Handle only 'paused'/'resumed' (not 'inactive', which also fires
|
||||
// transiently while the OS biometric prompt is showing).
|
||||
if (state == AppLifecycleState.paused) {
|
||||
_backgroundedAt = DateTime.now();
|
||||
} else if (state == AppLifecycleState.resumed) {
|
||||
final since = _backgroundedAt;
|
||||
_backgroundedAt = null;
|
||||
if (since != null &&
|
||||
DateTime.now().difference(since) > _lockGrace &&
|
||||
authService.isAuthenticated &&
|
||||
biometricAuth.enabledCached) {
|
||||
authService.lock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListenableBuilder(
|
||||
listenable: appSettings,
|
||||
builder: (context, _) => MaterialApp(
|
||||
title: "DriverVault",
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: DriverVault.theme(Brightness.light),
|
||||
darkTheme: DriverVault.theme(Brightness.dark),
|
||||
themeMode: appSettings.themeMode,
|
||||
// Apply the user's font-size preference app-wide.
|
||||
builder: (context, child) => MediaQuery(
|
||||
data: MediaQuery.of(context).copyWith(
|
||||
textScaler: TextScaler.linear(appSettings.textScale),
|
||||
),
|
||||
child: child!,
|
||||
),
|
||||
home: ListenableBuilder(
|
||||
listenable: authService,
|
||||
builder: (context, _) {
|
||||
if (!authService.isAuthenticated) return const LoginScreen();
|
||||
if (authService.locked) return const LockScreen();
|
||||
return const RootShell();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user