72 lines
2.2 KiB
Dart
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'),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|