JOGO
This commit is contained in:
@@ -1,20 +1,25 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:playmaker/icons.dart/resaltosicon.dart';
|
||||
import 'package:playmaker/widgets/placar_widgets.dart'; // Mantém este import
|
||||
import 'dart:math' as math;
|
||||
import '../classe/theme.dart';
|
||||
import '../controllers/placar_controller.dart';
|
||||
import 'package:playmaker/zone_map_dialog.dart';
|
||||
import 'package:playmaker/widgets/share_game_dialog.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
|
||||
import '../classe/theme.dart';
|
||||
import '../controllers/game_sharing_controller.dart';
|
||||
import '../controllers/placar_controller.dart';
|
||||
|
||||
class PlacarPage extends StatefulWidget {
|
||||
final String gameId, myTeam, opponentTeam;
|
||||
|
||||
|
||||
const PlacarPage({
|
||||
super.key,
|
||||
required this.gameId,
|
||||
required this.myTeam,
|
||||
required this.opponentTeam
|
||||
super.key,
|
||||
required this.gameId,
|
||||
required this.myTeam,
|
||||
required this.opponentTeam,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -23,6 +28,12 @@ class PlacarPage extends StatefulWidget {
|
||||
|
||||
class _PlacarPageState extends State<PlacarPage> {
|
||||
late PlacarController _controller;
|
||||
final GameSharingController _sharingController = GameSharingController();
|
||||
String? _sessionId;
|
||||
String? _shareCode;
|
||||
String _sharedWithName = '';
|
||||
StreamSubscription? _syncSubscription;
|
||||
bool _isApplyingRemoteSync = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -31,23 +42,33 @@ class _PlacarPageState extends State<PlacarPage> {
|
||||
DeviceOrientation.landscapeRight,
|
||||
DeviceOrientation.landscapeLeft,
|
||||
]);
|
||||
|
||||
|
||||
_controller = PlacarController(
|
||||
gameId: widget.gameId,
|
||||
myTeam: widget.myTeam,
|
||||
myTeam: widget.myTeam,
|
||||
opponentTeam: widget.opponentTeam,
|
||||
);
|
||||
_controller.loadPlayers();
|
||||
_controller.loadPlayers().then((_) => _initializeShareForGame());
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_syncSubscription?.cancel();
|
||||
_controller.dispose();
|
||||
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Widget _buildFloatingFoulBtn(String label, Color color, String action, IconData icon, double left, double right, double top, double sf) {
|
||||
Widget _buildFloatingFoulBtn(
|
||||
String label,
|
||||
Color color,
|
||||
String action,
|
||||
IconData icon,
|
||||
double left,
|
||||
double right,
|
||||
double top,
|
||||
double sf,
|
||||
) {
|
||||
return Positioned(
|
||||
top: top,
|
||||
left: left > 0 ? left : null,
|
||||
@@ -57,39 +78,62 @@ class _PlacarPageState extends State<PlacarPage> {
|
||||
feedback: Material(
|
||||
color: Colors.transparent,
|
||||
child: CircleAvatar(
|
||||
radius: 30 * sf,
|
||||
backgroundColor: color.withOpacity(0.8),
|
||||
child: Icon(icon, color: Colors.white, size: 30 * sf)
|
||||
radius: 30 * sf,
|
||||
backgroundColor: color.withOpacity(0.8),
|
||||
child: Icon(icon, color: Colors.white, size: 30 * sf),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
CircleAvatar(
|
||||
radius: 27 * sf,
|
||||
radius: 27 * sf,
|
||||
backgroundColor: color,
|
||||
child: Icon(icon, color: Colors.white, size: 28 * sf),
|
||||
child: Icon(icon, color: Colors.white, size: 28 * sf),
|
||||
),
|
||||
SizedBox(height: 5 * sf),
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12 * sf,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5 * sf),
|
||||
Text(label, style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 12 * sf)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCornerBtn({required String heroTag, required IconData icon, required Color color, required VoidCallback? onTap, required double size, bool isLoading = false}) {
|
||||
Widget _buildCornerBtn({
|
||||
required String heroTag,
|
||||
required IconData icon,
|
||||
required Color color,
|
||||
required VoidCallback? onTap,
|
||||
required double size,
|
||||
bool isLoading = false,
|
||||
}) {
|
||||
return SizedBox(
|
||||
width: size,
|
||||
width: size,
|
||||
height: size,
|
||||
child: FloatingActionButton(
|
||||
heroTag: heroTag,
|
||||
heroTag: heroTag,
|
||||
backgroundColor: onTap == null ? Colors.grey : color,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14 * (size / 50))),
|
||||
elevation: 5,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(14 * (size / 50)),
|
||||
),
|
||||
elevation: 5,
|
||||
onPressed: isLoading ? null : onTap,
|
||||
child: isLoading
|
||||
? SizedBox(width: size * 0.45, height: size * 0.45, child: const CircularProgressIndicator(color: Colors.white, strokeWidth: 2.5))
|
||||
: Icon(icon, color: Colors.white, size: size * 0.55),
|
||||
child: isLoading
|
||||
? SizedBox(
|
||||
width: size * 0.45,
|
||||
height: size * 0.45,
|
||||
child: const CircularProgressIndicator(
|
||||
color: Colors.white,
|
||||
strokeWidth: 2.5,
|
||||
),
|
||||
)
|
||||
: Icon(icon, color: Colors.white, size: size * 0.55),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -109,25 +153,183 @@ class _PlacarPageState extends State<PlacarPage> {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _initializeShareForGame() async {
|
||||
final activeSession = await _sharingController.getActiveSessionForGame(
|
||||
widget.gameId,
|
||||
);
|
||||
if (activeSession == null) return;
|
||||
|
||||
_sessionId = activeSession['id']?.toString();
|
||||
_shareCode = activeSession['share_code']?.toString();
|
||||
final sharedWith = activeSession['shared_with_user_id']?.toString();
|
||||
|
||||
if (sharedWith != null && sharedWith.isNotEmpty) {
|
||||
_sharedWithName = await _resolveUserName(sharedWith);
|
||||
}
|
||||
|
||||
_setupSyncListener();
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
Future<String> _resolveUserName(String userId) async {
|
||||
try {
|
||||
final profile = await Supabase.instance.client
|
||||
.from('profiles')
|
||||
.select('username, full_name')
|
||||
.eq('id', userId)
|
||||
.single();
|
||||
|
||||
return profile['full_name']?.toString() ??
|
||||
profile['username']?.toString() ??
|
||||
'Parceiro';
|
||||
} catch (_) {
|
||||
return 'Parceiro';
|
||||
}
|
||||
}
|
||||
|
||||
void _setupSyncListener() {
|
||||
if (_sessionId == null) return;
|
||||
_syncSubscription?.cancel();
|
||||
_syncSubscription = _sharingController.listenToGameSync(_sessionId!).listen(
|
||||
(event) {
|
||||
if (event is List && event.isNotEmpty) {
|
||||
final record = event.last as Map<String, dynamic>?;
|
||||
if (record != null) {
|
||||
_handleSyncRecords(record);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _handleSyncRecords(Map<String, dynamic> record) {
|
||||
final triggeredBy = record['triggered_by']?.toString();
|
||||
final currentUserId = Supabase.instance.client.auth.currentUser?.id;
|
||||
if (triggeredBy == null || triggeredBy == currentUserId) return;
|
||||
|
||||
_applyRemoteSyncEvent(record);
|
||||
}
|
||||
|
||||
void _applyRemoteSyncEvent(Map<String, dynamic> record) {
|
||||
final actionType = record['action_type']?.toString();
|
||||
final actionData = Map<String, dynamic>.from(record['action_data'] ?? {});
|
||||
if (actionType == 'toggle_timer') {
|
||||
final paused = actionData['paused'] == true;
|
||||
final remainingSeconds =
|
||||
int.tryParse(actionData['remaining_seconds']?.toString() ?? '') ??
|
||||
_controller.durationNotifier.value.inSeconds;
|
||||
_controller.durationNotifier.value = Duration(seconds: remainingSeconds);
|
||||
|
||||
if (paused && _controller.isRunning) {
|
||||
_isApplyingRemoteSync = true;
|
||||
_controller.toggleTimer(context);
|
||||
_isApplyingRemoteSync = false;
|
||||
} else if (!paused && !_controller.isRunning) {
|
||||
_isApplyingRemoteSync = true;
|
||||
_controller.toggleTimer(context);
|
||||
_isApplyingRemoteSync = false;
|
||||
}
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
void _handleTimerButton(BuildContext context) {
|
||||
_controller.toggleTimer(context);
|
||||
|
||||
if (_sessionId != null && !_isApplyingRemoteSync) {
|
||||
_sharingController.sendSyncEvent(_sessionId!, 'toggle_timer', {
|
||||
'paused': !_controller.isRunning,
|
||||
'remaining_seconds': _controller.durationNotifier.value.inSeconds,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _openShareDialog(BuildContext context) async {
|
||||
final result = await showDialog<Map<String, dynamic>>(
|
||||
context: context,
|
||||
builder: (ctx) => ShareGameDialog(
|
||||
gameId: widget.gameId,
|
||||
controller: _sharingController,
|
||||
activeSessionId: _sessionId,
|
||||
activeShareCode: _shareCode,
|
||||
),
|
||||
);
|
||||
|
||||
if (result != null) {
|
||||
_sessionId = result['session_id']?.toString();
|
||||
_shareCode = result['share_code']?.toString();
|
||||
_setupSyncListener();
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _openJoinDialog(BuildContext context) async {
|
||||
final result = await showDialog<Map<String, dynamic>>(
|
||||
context: context,
|
||||
builder: (ctx) => JoinGameDialog(controller: _sharingController),
|
||||
);
|
||||
|
||||
if (result != null) {
|
||||
_sessionId = result['session_id']?.toString();
|
||||
_shareCode = result['share_code']?.toString();
|
||||
_sharedWithName = result['creator_name']?.toString() ?? '';
|
||||
_setupSyncListener();
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildShareStatus(double sf) {
|
||||
if (_sessionId == null) return const SizedBox.shrink();
|
||||
|
||||
final text = _sharedWithName.isNotEmpty
|
||||
? 'Partilhado com $_sharedWithName'
|
||||
: 'Sessão partilhada: $_shareCode';
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 14 * sf, vertical: 8 * sf),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withOpacity(0.55),
|
||||
borderRadius: BorderRadius.circular(14 * sf),
|
||||
border: Border.all(color: Colors.white24),
|
||||
),
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 13 * sf,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final double wScreen = MediaQuery.of(context).size.width;
|
||||
final double hScreen = MediaQuery.of(context).size.height;
|
||||
final double sf = math.min(wScreen / 1150, hScreen / 720);
|
||||
final double cornerBtnSize = 48 * sf;
|
||||
final double sf = math.min(wScreen / 1150, hScreen / 720);
|
||||
final double cornerBtnSize = 48 * sf;
|
||||
|
||||
return AnimatedBuilder(
|
||||
animation: _controller,
|
||||
builder: (context, child) {
|
||||
if (_controller.isLoading) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppTheme.placarDarkSurface,
|
||||
backgroundColor: AppTheme.placarDarkSurface,
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text("PREPARANDO O PAVILHÃO", style: TextStyle(color: Colors.white24, fontSize: 45 * sf, fontWeight: FontWeight.bold, letterSpacing: 2)),
|
||||
SizedBox(height: 35 * sf),
|
||||
Text(
|
||||
"PREPARANDO O PAVILHÃO",
|
||||
style: TextStyle(
|
||||
color: Colors.white24,
|
||||
fontSize: 45 * sf,
|
||||
fontWeight: FontWeight.bold,
|
||||
letterSpacing: 2,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 35 * sf),
|
||||
const CircularProgressIndicator(color: Colors.orangeAccent),
|
||||
],
|
||||
),
|
||||
@@ -136,16 +338,23 @@ class _PlacarPageState extends State<PlacarPage> {
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppTheme.placarBackground,
|
||||
backgroundColor: AppTheme.placarBackground,
|
||||
body: SafeArea(
|
||||
top: false, bottom: false,
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: IgnorePointer(
|
||||
ignoring: _controller.isSaving,
|
||||
ignoring: _controller.isSaving,
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
margin: EdgeInsets.only(left: 65 * sf, right: 65 * sf, bottom: 55 * sf),
|
||||
decoration: BoxDecoration(border: Border.all(color: Colors.white, width: 2.5)),
|
||||
margin: EdgeInsets.only(
|
||||
left: 65 * sf,
|
||||
right: 65 * sf,
|
||||
bottom: 55 * sf,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.white, width: 2.5),
|
||||
),
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final w = constraints.maxWidth;
|
||||
@@ -155,70 +364,254 @@ class _PlacarPageState extends State<PlacarPage> {
|
||||
GestureDetector(
|
||||
onTapDown: (details) {
|
||||
if (_controller.isSelectingShotLocation) {
|
||||
bool isMake = _controller.pendingAction?.startsWith("add_pts_") ?? false;
|
||||
bool isMake =
|
||||
_controller.pendingAction?.startsWith(
|
||||
"add_pts_",
|
||||
) ??
|
||||
false;
|
||||
String? pData = _controller.pendingPlayerId;
|
||||
|
||||
_controller.registerShotLocation(context, details.localPosition, Size(w, h));
|
||||
|
||||
|
||||
_controller.registerShotLocation(
|
||||
context,
|
||||
details.localPosition,
|
||||
Size(w, h),
|
||||
);
|
||||
|
||||
if (isMake && pData != null) {
|
||||
bool isOpp = pData.startsWith("player_opp_");
|
||||
String pId = pData.replaceAll("player_my_", "").replaceAll("player_opp_", "");
|
||||
showAssistDialog(context, _controller, isOpp, pId, sf);
|
||||
bool isOpp = pData.startsWith(
|
||||
"player_opp_",
|
||||
);
|
||||
String pId = pData
|
||||
.replaceAll("player_my_", "")
|
||||
.replaceAll("player_opp_", "");
|
||||
showAssistDialog(
|
||||
context,
|
||||
_controller,
|
||||
isOpp,
|
||||
pId,
|
||||
sf,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage('assets/campo.png'),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!_controller.isSelectingShotLocation && _controller.myCourt.length >= 5 && _controller.oppCourt.length >= 5) ...[
|
||||
Positioned(top: h * 0.25, left: w * 0.02, child: PlayerCourtCard(controller: _controller, playerId: _controller.myCourt[0], isOpponent: false, sf: sf)),
|
||||
Positioned(top: h * 0.68, left: w * 0.02, child: PlayerCourtCard(controller: _controller, playerId: _controller.myCourt[1], isOpponent: false, sf: sf)),
|
||||
Positioned(top: h * 0.45, left: w * 0.25, child: PlayerCourtCard(controller: _controller, playerId: _controller.myCourt[2], isOpponent: false, sf: sf)),
|
||||
Positioned(top: h * 0.15, left: w * 0.20, child: PlayerCourtCard(controller: _controller, playerId: _controller.myCourt[3], isOpponent: false, sf: sf)),
|
||||
Positioned(top: h * 0.80, left: w * 0.20, child: PlayerCourtCard(controller: _controller, playerId: _controller.myCourt[4], isOpponent: false, sf: sf)),
|
||||
|
||||
Positioned(top: h * 0.25, right: w * 0.02, child: PlayerCourtCard(controller: _controller, playerId: _controller.oppCourt[0], isOpponent: true, sf: sf)),
|
||||
Positioned(top: h * 0.68, right: w * 0.02, child: PlayerCourtCard(controller: _controller, playerId: _controller.oppCourt[1], isOpponent: true, sf: sf)),
|
||||
Positioned(top: h * 0.45, right: w * 0.25, child: PlayerCourtCard(controller: _controller, playerId: _controller.oppCourt[2], isOpponent: true, sf: sf)),
|
||||
Positioned(top: h * 0.15, right: w * 0.20, child: PlayerCourtCard(controller: _controller, playerId: _controller.oppCourt[3], isOpponent: true, sf: sf)),
|
||||
Positioned(top: h * 0.80, right: w * 0.20, child: PlayerCourtCard(controller: _controller, playerId: _controller.oppCourt[4], isOpponent: true, sf: sf)),
|
||||
],
|
||||
if (!_controller.isSelectingShotLocation) ...[
|
||||
_buildFloatingFoulBtn("FALTA +", AppTheme.actionPoints, "add_foul", Icons.sports, w * 0.39, 0.0, h * 0.31, sf),
|
||||
_buildFloatingFoulBtn("FALTA -", AppTheme.actionMiss, "sub_foul", Icons.block, 0.0, w * 0.39, h * 0.31, sf),
|
||||
],
|
||||
if (!_controller.isSelectingShotLocation)
|
||||
Positioned(
|
||||
top: (h * 0.32) + (40 * sf),
|
||||
left: 0, right: 0,
|
||||
child: Center(
|
||||
child: GestureDetector(
|
||||
onTap: () => _controller.toggleTimer(context),
|
||||
child: CircleAvatar(
|
||||
radius: 68 * sf,
|
||||
backgroundColor: Colors.grey.withOpacity(0.5),
|
||||
child: Icon(_controller.isRunning ? Icons.pause : Icons.play_arrow, color: Colors.white, size: 58 * sf)
|
||||
),
|
||||
image: AssetImage('assets/campo.png'),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(top: 0, left: 0, right: 0, child: Center(child: TopScoreboard(controller: _controller, sf: sf))),
|
||||
|
||||
if (!_controller.isSelectingShotLocation) Positioned(bottom: -10 * sf, left: 0, right: 0, child: ActionButtonsPanel(controller: _controller, sf: sf)),
|
||||
),
|
||||
if (!_controller.isSelectingShotLocation &&
|
||||
_controller.myCourt.length >= 5 &&
|
||||
_controller.oppCourt.length >= 5) ...[
|
||||
Positioned(
|
||||
top: h * 0.25,
|
||||
left: w * 0.02,
|
||||
child: PlayerCourtCard(
|
||||
controller: _controller,
|
||||
playerId: _controller.myCourt[0],
|
||||
isOpponent: false,
|
||||
sf: sf,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: h * 0.68,
|
||||
left: w * 0.02,
|
||||
child: PlayerCourtCard(
|
||||
controller: _controller,
|
||||
playerId: _controller.myCourt[1],
|
||||
isOpponent: false,
|
||||
sf: sf,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: h * 0.45,
|
||||
left: w * 0.25,
|
||||
child: PlayerCourtCard(
|
||||
controller: _controller,
|
||||
playerId: _controller.myCourt[2],
|
||||
isOpponent: false,
|
||||
sf: sf,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: h * 0.15,
|
||||
left: w * 0.20,
|
||||
child: PlayerCourtCard(
|
||||
controller: _controller,
|
||||
playerId: _controller.myCourt[3],
|
||||
isOpponent: false,
|
||||
sf: sf,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: h * 0.80,
|
||||
left: w * 0.20,
|
||||
child: PlayerCourtCard(
|
||||
controller: _controller,
|
||||
playerId: _controller.myCourt[4],
|
||||
isOpponent: false,
|
||||
sf: sf,
|
||||
),
|
||||
),
|
||||
|
||||
Positioned(
|
||||
top: h * 0.25,
|
||||
right: w * 0.02,
|
||||
child: PlayerCourtCard(
|
||||
controller: _controller,
|
||||
playerId: _controller.oppCourt[0],
|
||||
isOpponent: true,
|
||||
sf: sf,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: h * 0.68,
|
||||
right: w * 0.02,
|
||||
child: PlayerCourtCard(
|
||||
controller: _controller,
|
||||
playerId: _controller.oppCourt[1],
|
||||
isOpponent: true,
|
||||
sf: sf,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: h * 0.45,
|
||||
right: w * 0.25,
|
||||
child: PlayerCourtCard(
|
||||
controller: _controller,
|
||||
playerId: _controller.oppCourt[2],
|
||||
isOpponent: true,
|
||||
sf: sf,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: h * 0.15,
|
||||
right: w * 0.20,
|
||||
child: PlayerCourtCard(
|
||||
controller: _controller,
|
||||
playerId: _controller.oppCourt[3],
|
||||
isOpponent: true,
|
||||
sf: sf,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: h * 0.80,
|
||||
right: w * 0.20,
|
||||
child: PlayerCourtCard(
|
||||
controller: _controller,
|
||||
playerId: _controller.oppCourt[4],
|
||||
isOpponent: true,
|
||||
sf: sf,
|
||||
),
|
||||
),
|
||||
],
|
||||
if (!_controller.isSelectingShotLocation) ...[
|
||||
_buildFloatingFoulBtn(
|
||||
"FALTA +",
|
||||
AppTheme.actionPoints,
|
||||
"add_foul",
|
||||
Icons.sports,
|
||||
w * 0.39,
|
||||
0.0,
|
||||
h * 0.31,
|
||||
sf,
|
||||
),
|
||||
_buildFloatingFoulBtn(
|
||||
"FALTA -",
|
||||
AppTheme.actionMiss,
|
||||
"sub_foul",
|
||||
Icons.block,
|
||||
0.0,
|
||||
w * 0.39,
|
||||
h * 0.31,
|
||||
sf,
|
||||
),
|
||||
],
|
||||
if (!_controller.isSelectingShotLocation)
|
||||
Positioned(
|
||||
top: (h * 0.32) + (40 * sf),
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Center(
|
||||
child: GestureDetector(
|
||||
onTap: () => _handleTimerButton(context),
|
||||
child: CircleAvatar(
|
||||
radius: 68 * sf,
|
||||
backgroundColor: Colors.grey.withOpacity(
|
||||
0.5,
|
||||
),
|
||||
child: Icon(
|
||||
_controller.isRunning
|
||||
? Icons.pause
|
||||
: Icons.play_arrow,
|
||||
color: Colors.white,
|
||||
size: 58 * sf,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Center(
|
||||
child: TopScoreboard(
|
||||
controller: _controller,
|
||||
sf: sf,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_sessionId != null)
|
||||
Positioned(
|
||||
top: 90 * sf,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Center(child: _buildShareStatus(sf)),
|
||||
),
|
||||
|
||||
if (!_controller.isSelectingShotLocation)
|
||||
Positioned(
|
||||
bottom: -10 * sf,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: ActionButtonsPanel(
|
||||
controller: _controller,
|
||||
sf: sf,
|
||||
),
|
||||
),
|
||||
if (_controller.isSelectingShotLocation)
|
||||
Positioned(
|
||||
top: h * 0.4, left: 0, right: 0,
|
||||
top: h * 0.4,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Center(
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 35 * sf, vertical: 18 * sf),
|
||||
decoration: BoxDecoration(color: Colors.black87, borderRadius: BorderRadius.circular(11 * sf), border: Border.all(color: Colors.white, width: 1.5 * sf)),
|
||||
child: Text("TOQUE NO CAMPO PARA MARCAR O LOCAL", style: TextStyle(color: Colors.white, fontSize: 22 * sf, fontWeight: FontWeight.bold)),
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 35 * sf,
|
||||
vertical: 18 * sf,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black87,
|
||||
borderRadius: BorderRadius.circular(
|
||||
11 * sf,
|
||||
),
|
||||
border: Border.all(
|
||||
color: Colors.white,
|
||||
width: 1.5 * sf,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
"TOQUE NO CAMPO PARA MARCAR O LOCAL",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 22 * sf,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -229,30 +622,77 @@ class _PlacarPageState extends State<PlacarPage> {
|
||||
),
|
||||
|
||||
Positioned(
|
||||
top: 50 * sf, left: 12 * sf,
|
||||
top: 50 * sf,
|
||||
left: 12 * sf,
|
||||
child: Column(
|
||||
children: [
|
||||
_buildCornerBtn(heroTag: 'btn_save_exit', icon: Icons.save_alt, color: AppTheme.oppTeamRed, size: cornerBtnSize, isLoading: _controller.isSaving, onTap: () async { await _controller.saveGameStats(context); if (context.mounted) Navigator.pop(context); }),
|
||||
_buildCornerBtn(
|
||||
heroTag: 'btn_save_exit',
|
||||
icon: Icons.save_alt,
|
||||
color: AppTheme.oppTeamRed,
|
||||
size: cornerBtnSize,
|
||||
isLoading: _controller.isSaving,
|
||||
onTap: () async {
|
||||
await _controller.saveGameStats(context);
|
||||
if (context.mounted) Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
SizedBox(height: 10 * sf),
|
||||
_buildCornerBtn(heroTag: 'btn_history', icon: Icons.history, color: Colors.blueGrey, size: cornerBtnSize, onTap: () => showDialog(context: context, builder: (ctx) => PlayByPlayDialog(controller: _controller))),
|
||||
_buildCornerBtn(
|
||||
heroTag: 'btn_history',
|
||||
icon: Icons.history,
|
||||
color: Colors.blueGrey,
|
||||
size: cornerBtnSize,
|
||||
onTap: () => showDialog(
|
||||
context: context,
|
||||
builder: (ctx) =>
|
||||
PlayByPlayDialog(controller: _controller),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Positioned(
|
||||
top: 50 * sf, right: 12 * sf,
|
||||
top: 50 * sf,
|
||||
right: 12 * sf,
|
||||
child: Column(
|
||||
children: [
|
||||
_buildCornerBtn(heroTag: 'btn_heatmap', icon: Icons.local_fire_department, color: Colors.orange.shade800, size: cornerBtnSize, onTap: () => _showHeatmap(context)),
|
||||
_buildCornerBtn(
|
||||
heroTag: 'btn_heatmap',
|
||||
icon: Icons.local_fire_department,
|
||||
color: Colors.orange.shade800,
|
||||
size: cornerBtnSize,
|
||||
onTap: () => _showHeatmap(context),
|
||||
),
|
||||
SizedBox(height: 10 * sf),
|
||||
_buildCornerBtn(heroTag: 'btn_boxscore', icon: Icons.table_chart, color: Colors.indigo, size: cornerBtnSize, onTap: () => showDialog(context: context, builder: (ctx) => BoxScoreDialog(controller: _controller, sf: sf))),
|
||||
_buildCornerBtn(
|
||||
heroTag: 'btn_boxscore',
|
||||
icon: Icons.table_chart,
|
||||
color: Colors.indigo,
|
||||
size: cornerBtnSize,
|
||||
onTap: () => showDialog(
|
||||
context: context,
|
||||
builder: (ctx) =>
|
||||
BoxScoreDialog(controller: _controller, sf: sf),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 10 * sf),
|
||||
_buildCornerBtn(
|
||||
heroTag: 'btn_share',
|
||||
icon: Icons.share,
|
||||
color: Colors.green,
|
||||
size: cornerBtnSize,
|
||||
onTap: () => _openShareDialog(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// BOTÕES INFERIORES: SUBSTITUIÇÕES E TIMEOUTS
|
||||
Positioned(
|
||||
bottom: 55 * sf, left: 12 * sf,
|
||||
bottom: 55 * sf,
|
||||
left: 12 * sf,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
@@ -270,14 +710,23 @@ class _PlacarPageState extends State<PlacarPage> {
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12 * sf),
|
||||
_buildCornerBtn(heroTag: 'btn_to_home', icon: Icons.timer, color: AppTheme.myTeamBlue, size: cornerBtnSize, onTap: _controller.myTimeoutsUsed >= 3 ? null : () => _controller.useTimeout(false)),
|
||||
SizedBox(height: 12 * sf),
|
||||
_buildCornerBtn(
|
||||
heroTag: 'btn_to_home',
|
||||
icon: Icons.timer,
|
||||
color: AppTheme.myTeamBlue,
|
||||
size: cornerBtnSize,
|
||||
onTap: _controller.myTimeoutsUsed >= 3
|
||||
? null
|
||||
: () => _controller.useTimeout(false),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Positioned(
|
||||
bottom: 55 * sf, right: 12 * sf,
|
||||
bottom: 55 * sf,
|
||||
right: 12 * sf,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
@@ -295,14 +744,29 @@ class _PlacarPageState extends State<PlacarPage> {
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12 * sf),
|
||||
_buildCornerBtn(heroTag: 'btn_to_away', icon: Icons.timer, color: AppTheme.oppTeamRed, size: cornerBtnSize, onTap: _controller.opponentTimeoutsUsed >= 3 ? null : () => _controller.useTimeout(true)),
|
||||
SizedBox(height: 12 * sf),
|
||||
_buildCornerBtn(
|
||||
heroTag: 'btn_to_away',
|
||||
icon: Icons.timer,
|
||||
color: AppTheme.oppTeamRed,
|
||||
size: cornerBtnSize,
|
||||
onTap: _controller.opponentTimeoutsUsed >= 3
|
||||
? null
|
||||
: () => _controller.useTimeout(true),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
if (_controller.isSaving)
|
||||
Positioned.fill(child: Container(color: Colors.black.withOpacity(0.4), child: const Center(child: CircularProgressIndicator(color: Colors.white)))),
|
||||
Positioned.fill(
|
||||
child: Container(
|
||||
color: Colors.black.withOpacity(0.4),
|
||||
child: const Center(
|
||||
child: CircularProgressIndicator(color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -311,4 +775,4 @@ class _PlacarPageState extends State<PlacarPage> {
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user