Flutter web app replacing legacy WPF CCTV surveillance keyboard controller. Includes wall overview, section view with monitor grid, camera input, PTZ control, alarm/lock/sequence BLoCs, and legacy-matching UI styling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
99 lines
3.0 KiB
Dart
99 lines
3.0 KiB
Dart
import 'package:flutter/material.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 'wall_section_widget.dart';
|
|
|
|
/// Main video wall grid displaying all sections
|
|
class WallGrid extends StatelessWidget {
|
|
const WallGrid({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return BlocBuilder<WallBloc, WallState>(
|
|
builder: (context, state) {
|
|
if (state.isLoading) {
|
|
return const Center(
|
|
child: CircularProgressIndicator(),
|
|
);
|
|
}
|
|
|
|
if (state.error != null) {
|
|
return Center(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
const Icon(Icons.error_outline, color: Colors.red, size: 48),
|
|
const SizedBox(height: 16),
|
|
Text(
|
|
state.error!,
|
|
style: const TextStyle(color: Colors.red),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
const SizedBox(height: 16),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
context.read<WallBloc>().add(const LoadWallConfig());
|
|
},
|
|
child: const Text('Retry'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
final config = state.config;
|
|
if (config == null) {
|
|
return const Center(
|
|
child: Text(
|
|
'No wall configuration loaded',
|
|
style: TextStyle(color: Colors.white54),
|
|
),
|
|
);
|
|
}
|
|
|
|
return SingleChildScrollView(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Wall name header
|
|
Padding(
|
|
padding: const EdgeInsets.only(bottom: 16),
|
|
child: Text(
|
|
config.name,
|
|
style: const TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
),
|
|
// Sections
|
|
...config.sections.map((section) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(bottom: 16),
|
|
child: WallSectionWidget(
|
|
section: section,
|
|
wallState: state,
|
|
isExpanded: state.isSectionExpanded(section.id),
|
|
onToggleExpanded: () {
|
|
context
|
|
.read<WallBloc>()
|
|
.add(ToggleSectionExpanded(section.id));
|
|
},
|
|
onViewerTap: (viewerId) {
|
|
context.read<WallBloc>().add(SelectViewer(viewerId));
|
|
},
|
|
),
|
|
);
|
|
}),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|