Initial commit: monorepo Match Live TV.
Rails API, app Flutter, infrastruttura Docker/MediaMTX, sito marketing e documentazione di deploy. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
188
mobile/lib/features/controller_mode/controller_landscape.dart
Normal file
188
mobile/lib/features/controller_mode/controller_landscape.dart
Normal file
@@ -0,0 +1,188 @@
|
||||
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'),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user