Files
smart-serow/pi/ui/lib/app_root.dart

118 lines
3.4 KiB
Dart
Raw Normal View History

import 'dart:convert';
2026-01-25 18:47:35 +09:00
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
2026-01-25 18:47:35 +09:00
import 'screens/splash_screen.dart';
import 'screens/dashboard_screen.dart';
import 'screens/overheat_screen.dart';
import 'services/config_service.dart';
import 'services/overheat_monitor.dart';
2026-01-25 18:47:35 +09:00
/// Root widget that manages app state transitions
class AppRoot extends StatefulWidget {
const AppRoot({super.key});
@override
State<AppRoot> createState() => _AppRootState();
}
class _AppRootState extends State<AppRoot> {
bool _initialized = false;
bool _overheatTriggered = false;
2026-01-25 18:47:35 +09:00
String _initStatus = 'Starting...';
@override
void initState() {
super.initState();
_runInitSequence();
}
@override
void dispose() {
OverheatMonitor.instance.stop();
super.dispose();
}
2026-01-25 18:47:35 +09:00
Future<void> _runInitSequence() async {
// Load config first
setState(() => _initStatus = 'Loading config...');
await ConfigService.instance.load();
2026-01-25 18:47:35 +09:00
setState(() => _initStatus = 'Checking systems...');
await Future.delayed(const Duration(milliseconds: 500));
2026-01-25 18:47:35 +09:00
// Check UART connection via backend health endpoint
setState(() => _initStatus = 'UART: connecting...');
await _waitForUart();
2026-01-25 18:47:35 +09:00
setState(() => _initStatus = 'GPS: standby');
await Future.delayed(const Duration(milliseconds: 400));
setState(() => _initStatus = 'Ready');
await Future.delayed(const Duration(milliseconds: 300));
// Start overheat monitoring
OverheatMonitor.instance.start(
onOverheat: () {
setState(() => _overheatTriggered = true);
},
);
2026-01-25 18:47:35 +09:00
setState(() => _initialized = true);
}
/// Poll backend health endpoint until Arduino is connected
Future<void> _waitForUart() async {
final backendUrl = ConfigService.instance.backendUrl;
const maxAttempts = 30; // ~30 seconds max wait
const retryDelay = Duration(seconds: 1);
for (int attempt = 0; attempt < maxAttempts; attempt++) {
try {
final response = await http
.get(Uri.parse('$backendUrl/health'))
.timeout(const Duration(seconds: 2));
if (response.statusCode == 200) {
final data = jsonDecode(response.body) as Map<String, dynamic>;
final arduinoOk = data['arduino_connected'] == true;
if (arduinoOk) {
setState(() => _initStatus = 'UART: OK');
await Future.delayed(const Duration(milliseconds: 300));
return;
}
}
} catch (e) {
// Backend not reachable yet - keep trying
}
// Not connected yet
setState(() => _initStatus = 'UART: waiting...');
await Future.delayed(retryDelay);
}
// Timeout - proceed anyway (UI will show stale data indicators)
setState(() => _initStatus = 'UART: timeout');
await Future.delayed(const Duration(milliseconds: 500));
}
2026-01-25 18:47:35 +09:00
@override
Widget build(BuildContext context) {
// Determine which screen to show (priority: overheat > splash > dashboard)
Widget child;
if (_overheatTriggered) {
child = const OverheatScreen(key: ValueKey('overheat'));
} else if (!_initialized) {
child = SplashScreen(key: const ValueKey('splash'), status: _initStatus);
} else {
child = const DashboardScreen(key: ValueKey('dashboard'));
}
2026-01-25 18:47:35 +09:00
return AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
child: child,
2026-01-25 18:47:35 +09:00
);
}
}