Files
kaptan/ground-suite/lib/app/app_shell.dart
T

72 lines
2.2 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../features/command/presentation/tactical_command_page.dart';
import '../features/diagnostics/presentation/diagnostics_page.dart';
import '../features/observability/presentation/observability_controller.dart';
enum _ApplicationSurface { tactical, eventStream }
/// Owns the two first-class Ground Suite surfaces. Tactical planning never
/// hides decoded diagnostics or critical accessibility announcements.
class AppShell extends ConsumerStatefulWidget {
const AppShell({super.key});
@override
ConsumerState<AppShell> createState() => _AppShellState();
}
class _AppShellState extends ConsumerState<AppShell> {
_ApplicationSurface _surface = _ApplicationSurface.tactical;
@override
Widget build(BuildContext context) {
final observation = ref.watch(observabilityControllerProvider);
return Scaffold(
body: Stack(
children: [
switch (_surface) {
_ApplicationSurface.tactical => TacticalCommandPage(
sourceLabel: observation.sourceLabel,
sourceStatus: observation.status.label,
onOpenEventStream: () =>
setState(() => _surface = _ApplicationSurface.eventStream),
),
_ApplicationSurface.eventStream => _EventStreamWorkspace(
onReturnToTactical: () =>
setState(() => _surface = _ApplicationSurface.tactical),
),
},
Semantics(
liveRegion: true,
label: observation.accessibilityAnnouncement ?? '',
child: const SizedBox.shrink(),
),
],
),
);
}
}
class _EventStreamWorkspace extends StatelessWidget {
const _EventStreamWorkspace({required this.onReturnToTactical});
final VoidCallback onReturnToTactical;
@override
Widget build(BuildContext context) => Stack(
children: [
const Positioned.fill(child: DiagnosticsPage()),
Positioned(
top: 16,
right: 20,
child: FilledButton.icon(
onPressed: onReturnToTactical,
icon: const Icon(Icons.map_outlined),
label: const Text('Tactical workspace'),
),
),
],
);
}