/*import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class PlacarPage extends StatefulWidget { final String gameId, myTeam, opponentTeam; const PlacarPage({super.key, required this.gameId, required this.myTeam, required this.opponentTeam}); @override State createState() => _PlacarPageState(); } class _PlacarPageState extends State { int _myScore = 0; int _opponentScore = 0; int _myFouls = 0; int _opponentFouls = 0; int _currentQuarter = 1; Duration _duration = const Duration(minutes: 10); Timer? _timer; bool _isRunning = false; @override void initState() { super.initState(); SystemChrome.setPreferredOrientations([ DeviceOrientation.landscapeRight, DeviceOrientation.landscapeLeft, ]); } void _toggleTimer() { if (_isRunning) { _timer?.cancel(); } else { _timer = Timer.periodic(const Duration(seconds: 1), (timer) { setState(() { if (_duration.inSeconds > 0) { _duration -= const Duration(seconds: 1); } else { _timer?.cancel(); _isRunning = false; } }); }); } setState(() => _isRunning = !_isRunning); } String _formatTime(Duration d) => "${d.inMinutes.toString().padLeft(2, '0')}:${d.inSeconds.remainder(60).toString().padLeft(2, '0')}"; @override void dispose() { _timer?.cancel(); SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); super.dispose(); } @override Widget build(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; final screenHeight = MediaQuery.of(context).size.height; return Scaffold( body: Stack( children: [ // Fundo do Campo Container( decoration: const BoxDecoration( image: DecorationImage( image: AssetImage('assets/campo.png'), fit: BoxFit.cover, alignment: Alignment(0.0, 0.2), ), ), ), // Posicionamento Tático (Jogadores como DragTargets) ..._buildTacticalFormation(screenWidth, screenHeight), // Botão Central Play/Pause Center( child: GestureDetector( onTap: _toggleTimer, child: CircleAvatar( radius: 35, backgroundColor: Colors.white, child: Icon(_isRunning ? Icons.pause : Icons.play_arrow, color: const Color(0xFF1E2A38), size: 40), ), ), ), // Placard Superior Positioned(top: 0, left: 0, right: 0, child: Center(child: _buildTopScoreboard())), // Painel de Ações Inferior (Botões de pontos como Draggables) Positioned(bottom: 20, left: 0, right: 0, child: _buildActionButtonsPanel()), // Botão Fechar Positioned( bottom: 20, right: 20, child: FloatingActionButton( backgroundColor: const Color(0xFF1E2A38), mini: true, onPressed: () => Navigator.pop(context), child: const Icon(Icons.close, color: Colors.white), ), ), ], ), ); } List _buildTacticalFormation(double w, double h) { return [ // CASA (Azul) Positioned(top: h * 0.25, left: w * 0.02, child: _buildPlayerCard("1", "Russell", false)), Positioned(top: h * 0.68, left: w * 0.02, child: _buildPlayerCard("15", "Reaves", false)), Positioned(top: h * 0.48, left: w * 0.20, child: _buildPlayerCard("3", "Davis", false)), Positioned(top: h * 0.15, left: w * 0.20, child: _buildPlayerCard("6", "James", false)), Positioned(top: h * 0.80, left: w * 0.20, child: _buildPlayerCard("28", "Hachimura", false)), // VISITANTE (Vermelho) Positioned(top: h * 0.25, right: w * 0.02, child: _buildPlayerCard("7", "Kyle", true)), Positioned(top: h * 0.68, right: w * 0.02, child: _buildPlayerCard("9", "Serge", true)), Positioned(top: h * 0.48, right: w * 0.20, child: _buildPlayerCard("2", "Kawhi", true)), Positioned(top: h * 0.15, right: w * 0.20, child: _buildPlayerCard("14", "Danny", true)), Positioned(top: h * 0.80, right: w * 0.20, child: _buildPlayerCard("23", "Fred", true)), ]; } // JOGADOR COMO DRAG TARGET (Recebe os pontos) Widget _buildPlayerCard(String number, String name, bool isOpponent) { final teamColor = isOpponent ? const Color(0xFFD92C2C) : const Color(0xFF1E5BB2); return DragTarget( onWillAcceptWithDetails: (details) => true, onAcceptWithDetails: (details) { setState(() { if (isOpponent) { _opponentScore += details.data; } else { _myScore += details.data; } }); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("$name marcou ${details.data} pontos!"), duration: const Duration(seconds: 1)), ); }, builder: (context, candidateData, rejectedData) { return AnimatedContainer( duration: const Duration(milliseconds: 200), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), decoration: BoxDecoration( color: candidateData.isNotEmpty ? Colors.yellow.shade100 : Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all( color: candidateData.isNotEmpty ? Colors.orange : Colors.transparent, width: 2, ), boxShadow: const [BoxShadow(color: Colors.black26, blurRadius: 6, offset: Offset(0, 3))], ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 40, height: 40, decoration: BoxDecoration(color: teamColor, borderRadius: BorderRadius.circular(8)), alignment: Alignment.center, child: Text(number, style: const TextStyle(color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold)), ), const SizedBox(width: 12), Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text(name, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black87)), const Text("0 Pts | 0 Rbs | 0 Ast", style: TextStyle(fontSize: 12, color: Colors.grey)), ], ), ], ), ); }, ); } Widget _buildTopScoreboard() { return Container( padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 30), decoration: BoxDecoration( color: const Color(0xFF16202C), borderRadius: const BorderRadius.only(bottomLeft: Radius.circular(15), bottomRight: Radius.circular(15)), border: Border.all(color: Colors.white, width: 2), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ _buildTeamSection(widget.myTeam, _myScore, _myFouls, const Color(0xFF1E5BB2), false), const SizedBox(width: 25), Column( children: [ _timeDisplay(), const SizedBox(height: 5), Text("PERÍODO $_currentQuarter", style: const TextStyle(color: Colors.orangeAccent, fontSize: 14, fontWeight: FontWeight.bold)), ], ), const SizedBox(width: 25), _buildTeamSection(widget.opponentTeam, _opponentScore, _opponentFouls, const Color(0xFFD92C2C), true), ], ), ); } Widget _buildTeamSection(String name, int score, int fouls, Color color, bool isOpponent) { final info = Column( children: [ _scoreBox(score, color), Text("FALTAS: $fouls", style: const TextStyle(color: Colors.yellowAccent, fontSize: 12)), ], ); final teamName = Text(name.toUpperCase(), style: const TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold)); return Row( children: isOpponent ? [info, const SizedBox(width: 15), teamName] : [teamName, const SizedBox(width: 15), info], ); } Widget _timeDisplay() => Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 5), decoration: BoxDecoration(color: const Color(0xFF2C3E50), borderRadius: BorderRadius.circular(5)), child: Text(_formatTime(_duration), style: const TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold, fontFamily: 'monospace')), ); Widget _scoreBox(int score, Color color) => Container( width: 55, height: 45, alignment: Alignment.center, decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(8)), child: Text(score.toString(), style: const TextStyle(color: Colors.white, fontSize: 26, fontWeight: FontWeight.bold)), ); // PAINEL DE AÇÕES (Botões de pontos são DRAGGABLE) Widget _buildActionButtonsPanel() { return Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ _draggableActionBtn("1", Colors.orange, 1), _draggableActionBtn("2", Colors.orange, 2), _draggableActionBtn("3", Colors.orange, 3), _simpleActionBtn(null, const Color(0xFF1E2A38), Icons.shopping_basket), ], ), const SizedBox(height: 10), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ _simpleActionBtn("1", Colors.orange, null, isX: true), _simpleActionBtn("2", Colors.orange, null, isX: true), _simpleActionBtn("3", Colors.orange, null, isX: true), _simpleActionBtn(null, const Color(0xFF1E2A38), Icons.shield), ], ), ], ); } // BOTÃO ARRASTÁVEL (PONTOS) Widget _draggableActionBtn(String label, Color color, int points) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 6), child: Draggable( data: points, feedback: _buildCircleContent(label, color, null, true), // O que voa com o dedo childWhenDragging: Opacity(opacity: 0.3, child: _buildCircleContent(label, color, null, false)), child: _buildCircleContent(label, color, null, false), ), ); } // BOTÃO SIMPLES (REBOTES/DEFESA) Widget _simpleActionBtn(String? label, Color color, IconData? icon, {bool isX = false}) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 6), child: Stack( alignment: Alignment.bottomRight, children: [ _buildCircleContent(label, color, icon, false), if (isX) const Icon(Icons.cancel, color: Colors.red, size: 25), ], ), ); } Widget _buildCircleContent(String? label, Color color, IconData? icon, bool isFeedback) { return Container( width: isFeedback ? 70 : 60, height: isFeedback ? 70 : 60, decoration: BoxDecoration( color: color, shape: BoxShape.circle, boxShadow: const [BoxShadow(color: Colors.black26, blurRadius: 4)], ), alignment: Alignment.center, child: icon != null ? Icon(icon, color: Colors.white, size: 35) : Text(label!, style: const TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold, decoration: TextDecoration.none)), ); } }*/ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class PlacarPage extends StatefulWidget { final String gameId, myTeam, opponentTeam; const PlacarPage({super.key, required this.gameId, required this.myTeam, required this.opponentTeam}); @override State createState() => _PlacarPageState(); } class _PlacarPageState extends State { int _myScore = 0; int _opponentScore = 0; int _myFouls = 0; int _opponentFouls = 0; int _currentQuarter = 1; int _myTimeoutsUsed = 0; int _opponentTimeoutsUsed = 0; final Map> _playerStats = { "Russell": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0}, "Reaves": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0}, "Davis": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0}, "James": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0}, "Hachimura": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0}, "Kyle": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0}, "Serge": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0}, "Kawhi": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0}, "Danny": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0}, "Fred": {"pts": 0, "rbs": 0, "ast": 0, "stl": 0, "tov": 0, "blk": 0}, }; Duration _duration = const Duration(minutes: 10); Timer? _timer; bool _isRunning = false; @override void initState() { super.initState(); SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeRight, DeviceOrientation.landscapeLeft]); } void _toggleTimer() { if (_isRunning) { _timer?.cancel(); } else { _timer = Timer.periodic(const Duration(seconds: 1), (timer) { setState(() { if (_duration.inSeconds > 0) { _duration -= const Duration(seconds: 1); } else { // O TEMPO ESGOTOU (00:00) _timer?.cancel(); _isRunning = false; // 1. MUDAR O PERÍODO if (_currentQuarter < 4) { _currentQuarter++; // 2. RESETAR O TEMPO PARA 10 MINUTOS _duration = const Duration(minutes: 10); // 3. RESETAR AS FALTAS DE EQUIPA (O PONTO MAIS IMPORTANTE) _myFouls = 0; _opponentFouls = 0; ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Período $_currentQuarter iniciado. Faltas resetadas!'), backgroundColor: Colors.blue, ), ); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('FIM DO JOGO!'), backgroundColor: Colors.red), ); } } }); }); } setState(() => _isRunning = !_isRunning); } void _useTimeout(bool isOpponent) { setState(() { if (isOpponent) { if (_opponentTimeoutsUsed < 3) _opponentTimeoutsUsed++; } else { if (_myTimeoutsUsed < 3) _myTimeoutsUsed++; } _isRunning = false; _timer?.cancel(); }); } String _formatTime(Duration d) => "${d.inMinutes.toString().padLeft(2, '0')}:${d.inSeconds.remainder(60).toString().padLeft(2, '0')}"; @override void dispose() { _timer?.cancel(); SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFF266174), body: Stack( children: [ Container( margin: const EdgeInsets.only(left: 60, right: 60, bottom: 50), decoration: BoxDecoration( border: Border.all(color: Colors.white, width: 2.0), ), child: LayoutBuilder( builder: (context, constraints) { final innerWidth = constraints.maxWidth; final innerHeight = constraints.maxHeight; return Stack( children: [ Container( decoration: const BoxDecoration( image: DecorationImage( image: AssetImage('assets/campo.png'), fit: BoxFit.cover, alignment: Alignment(0.0, 0.2) ), ), ), ..._buildTacticalFormation(innerWidth, innerHeight), Positioned( top: innerHeight * 0.32, left: 0, right: 0, child: Column( mainAxisSize: MainAxisSize.min, children: [ Row( mainAxisSize: MainAxisSize.min, children: [ _dragBtn("F", Colors.orange, "add_foul", icon: Icons.sports), const SizedBox(width: 40), _dragBtn("F", Colors.orange, "sub_foul", icon: Icons.block), ], ), const SizedBox(height: 15), GestureDetector( onTap: _toggleTimer, child: CircleAvatar( radius: 60, backgroundColor: Colors.grey.withOpacity(0.5), child: Icon(_isRunning ? Icons.pause : Icons.play_arrow, color: Colors.white, size: 50), ), ), ], ), ), Positioned(top: 0, left: 0, right: 0, child: Center(child: _buildTopScoreboard())), Positioned(bottom: 10, left: 0, right: 0, child: _buildActionButtonsPanel()), ], ); }, ), ), // BOTÕES LATERAIS Positioned( top: 20, left: 10, child: FloatingActionButton( heroTag: 'btn_save', backgroundColor: const Color(0xFF16202C), mini: true, onPressed: () => ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Jogo Guardado!'))), child: const Icon(Icons.save, color: Colors.white), ), ), Positioned( top: 70, left: 10, child: FloatingActionButton( heroTag: 'btn_exit', backgroundColor: const Color(0xFFD92C2C), mini: true, onPressed: () => Navigator.pop(context), child: const Icon(Icons.exit_to_app, color: Colors.white), ), ), Positioned( bottom: 50, left: 10, child: FloatingActionButton( heroTag: 'btn_sub_home', backgroundColor: const Color(0xFF1E5BB2), mini: true, onPressed: () => ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Substituição Casa'))), child: const Icon(Icons.swap_horiz, color: Colors.white), ), ), Positioned( bottom: 50, right: 10, child: FloatingActionButton( heroTag: 'btn_sub_away', backgroundColor: const Color(0xFFD92C2C), mini: true, onPressed: () => ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Substituição Visitante'))), child: const Icon(Icons.swap_horiz, color: Colors.white), ), ), ], ), ); } List _buildTacticalFormation(double w, double h) { return [ Positioned(top: h * 0.25, left: w * 0.02, child: _buildPlayerCard("1", "Russell", false)), Positioned(top: h * 0.68, left: w * 0.02, child: _buildPlayerCard("15", "Reaves", false)), Positioned(top: h * 0.45, left: w * 0.25, child: _buildPlayerCard("3", "Davis", false)), Positioned(top: h * 0.15, left: w * 0.20, child: _buildPlayerCard("6", "James", false)), Positioned(top: h * 0.80, left: w * 0.20, child: _buildPlayerCard("28", "Hachimura", false)), Positioned(top: h * 0.25, right: w * 0.02, child: _buildPlayerCard("7", "Kyle", true)), Positioned(top: h * 0.68, right: w * 0.02, child: _buildPlayerCard("9", "Serge", true)), Positioned(top: h * 0.45, right: w * 0.25, child: _buildPlayerCard("2", "Kawhi", true)), Positioned(top: h * 0.15, right: w * 0.20, child: _buildPlayerCard("14", "Danny", true)), Positioned(top: h * 0.80, right: w * 0.20, child: _buildPlayerCard("23", "Fred", true)), ]; } Widget _buildPlayerCard(String number, String name, bool isOpponent) { final teamColor = isOpponent ? const Color(0xFFD92C2C) : const Color(0xFF1E5BB2); final stats = _playerStats[name]!; return DragTarget( onAcceptWithDetails: (details) { final action = details.data; setState(() { if (action.startsWith("add_pts_")) { int pts = int.parse(action.split("_").last); if (isOpponent) _opponentScore += pts; else _myScore += pts; stats["pts"] = stats["pts"]! + pts; } else if (action.startsWith("sub_pts_")) { int pts = int.parse(action.split("_").last); if (isOpponent) { _opponentScore = (_opponentScore - pts < 0) ? 0 : _opponentScore - pts; } else { _myScore = (_myScore - pts < 0) ? 0 : _myScore - pts; } stats["pts"] = (stats["pts"]! - pts < 0) ? 0 : stats["pts"]! - pts; } else if (action == "add_rbs") { stats["rbs"] = stats["rbs"]! + 1; } else if (action == "add_ast") { stats["ast"] = stats["ast"]! + 1; } else if (action == "add_stl") { stats["stl"] = stats["stl"]! + 1; } else if (action == "add_tov") { stats["tov"] = stats["tov"]! + 1; } else if (action == "add_blk") { stats["blk"] = stats["blk"]! + 1; } // LÓGICA DE FALTAS COM LIMITE DE 5 else if (action == "add_foul") { if (isOpponent) { if (_opponentFouls < 5) _opponentFouls++; } else { if (_myFouls < 5) _myFouls++; } } else if (action == "sub_foul") { if (isOpponent) { if (_opponentFouls > 0) _opponentFouls--; } else { if (_myFouls > 0) _myFouls--; } } }); }, builder: (context, candidateData, rejectedData) { return Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), decoration: BoxDecoration( color: candidateData.isNotEmpty ? Colors.orange.shade50 : Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all(color: candidateData.isNotEmpty ? Colors.orange : Colors.transparent, width: 2), boxShadow: const [BoxShadow(color: Colors.black26, blurRadius: 6, offset: Offset(0, 3))], ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 40, height: 40, decoration: BoxDecoration(color: teamColor, borderRadius: BorderRadius.circular(8)), alignment: Alignment.center, child: Text(number, style: const TextStyle(color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold)), ), const SizedBox(width: 12), Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text(name, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black87)), const SizedBox(height: 2), Text("${stats["pts"]} Pts | ${stats["rbs"]} Rbs | ${stats["ast"]} Ast", style: const TextStyle(fontSize: 12, color: Colors.grey, fontWeight: FontWeight.w500)), ], ), ], ), ); }, ); } Widget _buildTopScoreboard() { return Container( padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 30), decoration: BoxDecoration( color: const Color(0xFF16202C), borderRadius: const BorderRadius.only(bottomLeft: Radius.circular(15), bottomRight: Radius.circular(15)), border: Border.all(color: Colors.white, width: 2), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ _buildTeamSection(widget.myTeam, _myScore, _myFouls, _myTimeoutsUsed, const Color(0xFF1E5BB2), false), const SizedBox(width: 25), Column( children: [ _timeDisplay(), const SizedBox(height: 5), Text("PERÍODO $_currentQuarter", style: const TextStyle(color: Colors.orangeAccent, fontSize: 14, fontWeight: FontWeight.bold)), ], ), const SizedBox(width: 25), _buildTeamSection(widget.opponentTeam, _opponentScore, _opponentFouls, _opponentTimeoutsUsed, const Color(0xFFD92C2C), true), ], ), ); } Widget _buildTeamSection(String name, int score, int fouls, int timeouts, Color color, bool isOpp) { final timeoutIndicators = Row( mainAxisSize: MainAxisSize.min, children: List.generate(3, (index) => Container( margin: const EdgeInsets.symmetric(horizontal: 3), width: 12, height: 12, decoration: BoxDecoration(shape: BoxShape.circle, color: index < timeouts ? Colors.yellow : Colors.grey.shade600, border: Border.all(color: Colors.black26)), )), ); return Row( crossAxisAlignment: CrossAxisAlignment.start, children: isOpp ? [ Column(children: [_scoreBox(score, color), const SizedBox(height: 4), Text("FALTAS: $fouls", style: TextStyle(color: fouls >= 5 ? Colors.red : Colors.yellowAccent, fontSize: 12, fontWeight: FontWeight.bold)), timeoutIndicators]), const SizedBox(width: 15), Text(name.toUpperCase(), style: const TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold)) ] : [ Text(name.toUpperCase(), style: const TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(width: 15), Column(children: [_scoreBox(score, color), const SizedBox(height: 4), Text("FALTAS: $fouls", style: TextStyle(color: fouls >= 5 ? Colors.red : Colors.yellowAccent, fontSize: 12, fontWeight: FontWeight.bold)), timeoutIndicators]) ] ); } Widget _timeDisplay() => Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 6), decoration: BoxDecoration(color: const Color(0xFF2C3E50), borderRadius: BorderRadius.circular(6)), child: Text(_formatTime(_duration), style: const TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold, fontFamily: 'monospace')), ); Widget _scoreBox(int score, Color color) => Container( width: 50, height: 40, alignment: Alignment.center, decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(6)), child: Text(score.toString(), style: const TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold)), ); Widget _buildActionButtonsPanel() { return Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.end, children: [ _columnBtn([ _actionBtn("T.O", const Color(0xFF1E5BB2), () => _useTimeout(false), labelSize: 20), _dragBtn("1", Colors.orange, "add_pts_1"), _dragBtn("1", Colors.orange, "sub_pts_1", isX: true), _dragBtn("AST", Colors.blueGrey, "add_ast"), ]), const SizedBox(width: 15), _columnBtn([ _dragBtn("2", Colors.orange, "add_pts_2"), _dragBtn("2", Colors.orange, "sub_pts_2", isX: true), _dragBtn("STL", Colors.green, "add_stl"), ]), const SizedBox(width: 15), _columnBtn([ _dragBtn("3", Colors.orange, "add_pts_3"), _dragBtn("3", Colors.orange, "sub_pts_3", isX: true), _dragBtn("TOV", Colors.redAccent, "add_tov"), ]), const SizedBox(width: 15), _columnBtn([ _actionBtn("T.O", const Color(0xFFD92C2C), () => _useTimeout(true), labelSize: 20), _dragBtn("ORB", const Color(0xFF1E2A38), "add_rbs", icon: Icons.sports_basketball), _dragBtn("DRB", const Color(0xFF1E2A38), "add_rbs", icon: Icons.sports_basketball), _dragBtn("BLK", Colors.deepPurple, "add_blk"), ]), ], ); } Widget _columnBtn(List children) => Column(mainAxisSize: MainAxisSize.min, children: children.map((c) => Padding(padding: const EdgeInsets.only(bottom: 8), child: c)).toList()); Widget _dragBtn(String label, Color color, String actionData, {IconData? icon, bool isX = false}) { return Draggable( data: actionData, feedback: _circle(label, color, icon, true, isX: isX), childWhenDragging: Opacity(opacity: 0.5, child: _circle(label, color, icon, false, isX: isX)), child: _circle(label, color, icon, false, isX: isX), ); } Widget _actionBtn(String label, Color color, VoidCallback onTap, {IconData? icon, bool isX = false, double labelSize = 24}) { return GestureDetector(onTap: onTap, child: _circle(label, color, icon, false, fontSize: labelSize, isX: isX)); } Widget _circle(String label, Color color, IconData? icon, bool isFeed, {double fontSize = 20, bool isX = false}) { return Stack( alignment: Alignment.bottomRight, children: [ Container( width: isFeed ? 70 : 60, height: isFeed ? 70 : 60, decoration: BoxDecoration(color: color, shape: BoxShape.circle, boxShadow: const [BoxShadow(color: Colors.black26, blurRadius: 4)]), alignment: Alignment.center, child: icon != null ? Icon(icon, color: Colors.white, size: 35) : Text(label, style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: fontSize, decoration: TextDecoration.none)), ), if (isX) Icon(Icons.cancel, color: Colors.red, size: isFeed ? 30 : 25), ], ); } }