Rails API, app Flutter, infrastruttura Docker/MediaMTX, sito marketing e documentazione di deploy. Co-authored-by: Cursor <cursoragent@cursor.com>
189 lines
5.8 KiB
Dart
189 lines
5.8 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import '../../app/theme.dart';
|
|
import '../../providers/session_provider.dart';
|
|
import '../../shared/models/score_state.dart';
|
|
import '../../shared/widgets/connected_badge.dart';
|
|
import '../../shared/widgets/destructive_cta.dart';
|
|
import 'controller_screen.dart';
|
|
|
|
/// Layout gamepad a 3 colonne per uso landscape.
|
|
class ControllerLandscape extends StatelessWidget {
|
|
const ControllerLandscape({
|
|
super.key,
|
|
required this.homeName,
|
|
required this.awayName,
|
|
required this.score,
|
|
required this.sessionState,
|
|
required this.onHomePoint,
|
|
required this.onAwayPoint,
|
|
required this.onHomeMinus,
|
|
required this.onAwayMinus,
|
|
required this.onCloseSet,
|
|
required this.onInterrupt,
|
|
required this.onCloseStream,
|
|
required this.pointsTarget,
|
|
});
|
|
|
|
final String homeName;
|
|
final String awayName;
|
|
final ScoreState score;
|
|
final SessionState sessionState;
|
|
final VoidCallback onHomePoint;
|
|
final VoidCallback onAwayPoint;
|
|
final VoidCallback onHomeMinus;
|
|
final VoidCallback onAwayMinus;
|
|
final VoidCallback onCloseSet;
|
|
final VoidCallback onInterrupt;
|
|
final Future<void> Function() onCloseStream;
|
|
final int pointsTarget;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final theme = Theme.of(context);
|
|
|
|
return SingleChildScrollView(
|
|
padding: const EdgeInsets.all(12),
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Expanded(
|
|
child: _TeamColumn(
|
|
teamLabel: 'CASA',
|
|
teamName: homeName,
|
|
sets: score.homeSets,
|
|
points: score.homePoints,
|
|
onPoint: onHomePoint,
|
|
onMinus: onHomeMinus,
|
|
),
|
|
),
|
|
Expanded(
|
|
flex: 2,
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Text('REGIA', style: theme.textTheme.titleMedium),
|
|
const SizedBox(width: 12),
|
|
ConnectedBadge(connected: sessionState.connected),
|
|
],
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
'SET ${score.currentSet} → $pointsTarget pt',
|
|
style: theme.textTheme.headlineMedium?.copyWith(
|
|
color: AppTheme.accentYellow,
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
YellowActionButton(label: 'CHIUDI SET', onPressed: onCloseSet),
|
|
const SizedBox(height: 16),
|
|
Text('ULTIME AZIONI', style: theme.textTheme.labelLarge),
|
|
const SizedBox(height: 8),
|
|
StreamMetricsFooter(
|
|
elapsed: sessionState.elapsed,
|
|
connected: sessionState.connected,
|
|
cameraDevice: sessionState.cameraDevice,
|
|
viewerCount: sessionState.viewerCount,
|
|
compact: true,
|
|
),
|
|
const SizedBox(height: 8),
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: OutlinedButton(
|
|
onPressed: onInterrupt,
|
|
child: const Text('Interrompi'),
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
Expanded(
|
|
child: DestructiveCta(
|
|
label: 'Chiudi',
|
|
compact: true,
|
|
onPressed: () => onCloseStream(),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
Expanded(
|
|
child: _TeamColumn(
|
|
teamLabel: 'OSPITI',
|
|
teamName: awayName,
|
|
sets: score.awaySets,
|
|
points: score.awayPoints,
|
|
onPoint: onAwayPoint,
|
|
onMinus: onAwayMinus,
|
|
alignRight: true,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _TeamColumn extends StatelessWidget {
|
|
const _TeamColumn({
|
|
required this.teamLabel,
|
|
required this.teamName,
|
|
required this.sets,
|
|
required this.points,
|
|
required this.onPoint,
|
|
required this.onMinus,
|
|
this.alignRight = false,
|
|
});
|
|
|
|
final String teamLabel;
|
|
final String teamName;
|
|
final int sets;
|
|
final int points;
|
|
final VoidCallback onPoint;
|
|
final VoidCallback onMinus;
|
|
final bool alignRight;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final theme = Theme.of(context);
|
|
final crossAlign =
|
|
alignRight ? CrossAxisAlignment.end : CrossAxisAlignment.start;
|
|
|
|
return Column(
|
|
crossAxisAlignment: crossAlign,
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Text(teamLabel, style: theme.textTheme.labelLarge),
|
|
Text(
|
|
teamName.toUpperCase(),
|
|
style: theme.textTheme.titleLarge,
|
|
textAlign: alignRight ? TextAlign.right : TextAlign.left,
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text('SETS $sets', style: theme.textTheme.bodyMedium),
|
|
Text(
|
|
'$points',
|
|
style: theme.textTheme.displayLarge?.copyWith(
|
|
color: AppTheme.primaryRed,
|
|
fontSize: 56,
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
ScoreTapButton(label: '+1', onTap: onPoint, large: true),
|
|
const SizedBox(height: 8),
|
|
OutlinedButton(
|
|
onPressed: points > 0 ? onMinus : null,
|
|
child: const Text('-1'),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|