import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../blocs/wall/wall_bloc.dart'; import '../blocs/wall/wall_event.dart'; import '../blocs/wall/wall_state.dart'; import '../widgets/overview/wall_overview.dart'; import '../widgets/section/section_view.dart'; class MainScreen extends StatefulWidget { const MainScreen({super.key}); @override State createState() => _MainScreenState(); } class _MainScreenState extends State { String? _selectedSectionId; final FocusNode _focusNode = FocusNode(); WallBloc? _wallBloc; @override void dispose() { _focusNode.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return BlocBuilder( builder: (blocContext, state) { // Store bloc reference for keyboard handler _wallBloc = BlocProvider.of(blocContext, listen: false); return KeyboardListener( focusNode: _focusNode, autofocus: true, onKeyEvent: _handleKeyEvent, child: Scaffold( backgroundColor: const Color(0xFF0A0E14), body: _buildBody(blocContext, state), ), ); }, ); } Widget _buildBody(BuildContext context, WallState state) { if (state.isLoading || state.config == null) { return const Center( child: CircularProgressIndicator( color: Color(0xFF00D4FF), ), ); } // Show section view or wall overview if (_selectedSectionId != null) { final section = state.config!.sections.firstWhere( (s) => s.id == _selectedSectionId, orElse: () => state.config!.sections.first, ); return SectionView( section: section, wallState: state, onBack: () => setState(() => _selectedSectionId = null), onViewerTap: (viewerId) { context.read().add(SelectViewer(viewerId)); // Re-request focus for keyboard input after tile tap _focusNode.requestFocus(); }, ); } return WallOverview( config: state.config!, wallState: state, onSectionTap: (sectionId) { setState(() => _selectedSectionId = sectionId); }, ); } void _handleKeyEvent(KeyEvent event) { if (event is! KeyDownEvent) return; final bloc = _wallBloc; if (bloc == null) return; final state = bloc.state; final key = event.logicalKey; // Escape - go back or deselect if (key == LogicalKeyboardKey.escape) { if (state.selectedViewerId != null) { bloc.add(const DeselectViewer()); } else if (_selectedSectionId != null) { setState(() => _selectedSectionId = null); } return; } // Only handle camera input when a viewer is selected if (state.selectedViewerId == null) return; // Digit keys 0-9 if (key.keyId >= LogicalKeyboardKey.digit0.keyId && key.keyId <= LogicalKeyboardKey.digit9.keyId) { final digit = key.keyId - LogicalKeyboardKey.digit0.keyId; bloc.add(AddCameraDigit(digit)); return; } // Numpad digits if (key.keyId >= LogicalKeyboardKey.numpad0.keyId && key.keyId <= LogicalKeyboardKey.numpad9.keyId) { final digit = key.keyId - LogicalKeyboardKey.numpad0.keyId; bloc.add(AddCameraDigit(digit)); return; } // Enter - execute CrossSwitch if (key == LogicalKeyboardKey.enter || key == LogicalKeyboardKey.numpadEnter) { if (state.canExecuteCrossSwitch) { bloc.add(const ExecuteCrossSwitch()); } return; } // Backspace - remove last digit if (key == LogicalKeyboardKey.backspace) { bloc.add(const BackspaceCameraDigit()); return; } // Delete - cancel edit if (key == LogicalKeyboardKey.delete) { bloc.add(const CancelCameraEdit()); return; } // Escape - cancel edit or deselect if (key == LogicalKeyboardKey.escape) { if (state.isEditing) { bloc.add(const CancelCameraEdit()); } else { bloc.add(const DeselectViewer()); } return; } // F1-F3 for prefix selection if (key == LogicalKeyboardKey.f1) { bloc.add(const SetCameraPrefix(500)); return; } if (key == LogicalKeyboardKey.f2) { bloc.add(const SetCameraPrefix(501)); return; } if (key == LogicalKeyboardKey.f3) { bloc.add(const SetCameraPrefix(502)); return; } } }