208 lines
7.5 KiB
Dart
208 lines
7.5 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'dart:math' as math;
|
|
|
|
class CalibradorPage extends StatefulWidget {
|
|
const CalibradorPage({super.key});
|
|
|
|
@override
|
|
State<CalibradorPage> createState() => _CalibradorPageState();
|
|
}
|
|
|
|
class _CalibradorPageState extends State<CalibradorPage> {
|
|
// --- 👇 VALORES INICIAIS 👇 ---
|
|
double hoopBaseX = 0.08;
|
|
double arcRadius = 0.28;
|
|
double cornerY = 0.40;
|
|
// -----------------------------------------------------
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
SystemChrome.setPreferredOrientations([
|
|
DeviceOrientation.landscapeRight,
|
|
DeviceOrientation.landscapeLeft,
|
|
]);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final double wScreen = MediaQuery.of(context).size.width;
|
|
final double hScreen = MediaQuery.of(context).size.height;
|
|
|
|
// O MESMO CÁLCULO EXATO DO PLACAR
|
|
final double sf = math.min(wScreen / 1150, hScreen / 720);
|
|
|
|
return Scaffold(
|
|
backgroundColor: const Color(0xFF266174),
|
|
body: SafeArea(
|
|
top: false,
|
|
bottom: false,
|
|
child: Stack(
|
|
children: [
|
|
// 👇 1. O CAMPO COM AS MARGENS EXATAS DO PLACAR 👇
|
|
Container(
|
|
margin: EdgeInsets.only(left: 65 * sf, right: 65 * sf, bottom: 55 * sf),
|
|
decoration: BoxDecoration(
|
|
border: Border.all(color: Colors.white, width: 2.5),
|
|
image: const DecorationImage(
|
|
image: AssetImage('assets/campo.png'),
|
|
fit: BoxFit.fill,
|
|
),
|
|
),
|
|
child: LayoutBuilder(
|
|
builder: (context, constraints) {
|
|
return CustomPaint(
|
|
painter: LinePainter(
|
|
hoopBaseX: hoopBaseX,
|
|
arcRadius: arcRadius,
|
|
cornerY: cornerY,
|
|
color: Colors.redAccent,
|
|
width: constraints.maxWidth,
|
|
height: constraints.maxHeight,
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
|
|
// 👇 2. TOPO: MOSTRADORES DE VALORES COM FITTEDBOX (Não transborda) 👇
|
|
Positioned(
|
|
top: 0, left: 0, right: 0,
|
|
child: Container(
|
|
color: Colors.black87.withOpacity(0.8),
|
|
padding: EdgeInsets.symmetric(vertical: 5 * sf, horizontal: 15 * sf),
|
|
child: FittedBox( // Isto impede o ecrã de dar o erro dos 179 pixels!
|
|
fit: BoxFit.scaleDown,
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
_buildValueDisplay("Aro X", hoopBaseX, sf),
|
|
SizedBox(width: 20 * sf),
|
|
_buildValueDisplay("Raio", arcRadius, sf),
|
|
SizedBox(width: 20 * sf),
|
|
_buildValueDisplay("Canto", cornerY, sf),
|
|
SizedBox(width: 30 * sf),
|
|
ElevatedButton.icon(
|
|
onPressed: () => Navigator.pop(context),
|
|
icon: Icon(Icons.check, size: 18 * sf),
|
|
label: Text("FECHAR", style: TextStyle(fontSize: 14 * sf, fontWeight: FontWeight.bold)),
|
|
style: ElevatedButton.styleFrom(backgroundColor: Colors.green),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
// 👇 3. FUNDO: SLIDERS (Com altura fixa para não dar o erro "hasSize") 👇
|
|
Positioned(
|
|
bottom: 0, left: 0, right: 0,
|
|
child: Container(
|
|
color: Colors.black87.withOpacity(0.8),
|
|
height: 80 * sf, // Altura segura para os sliders
|
|
child: Row(
|
|
children: [
|
|
Expanded(child: _buildSlider("Pos. do Aro", hoopBaseX, 0.0, 0.25, (val) => setState(() => hoopBaseX = val), sf)),
|
|
Expanded(child: _buildSlider("Tam. da Curva", arcRadius, 0.1, 0.5, (val) => setState(() => arcRadius = val), sf)),
|
|
Expanded(child: _buildSlider("Pos. do Canto", cornerY, 0.2, 0.5, (val) => setState(() => cornerY = val), sf)),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildValueDisplay(String label, double value, double sf) {
|
|
return Row(
|
|
children: [
|
|
Text("$label: ", style: TextStyle(color: Colors.white70, fontSize: 16 * sf)),
|
|
Text(value.toStringAsFixed(3), style: TextStyle(color: Colors.yellow, fontSize: 20 * sf, fontWeight: FontWeight.bold)),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildSlider(String label, double value, double min, double max, ValueChanged<double> onChanged, double sf) {
|
|
return Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Text(label, style: TextStyle(color: Colors.white, fontSize: 12 * sf)),
|
|
SizedBox(
|
|
height: 40 * sf, // Altura exata para o Slider não crashar
|
|
child: Slider(
|
|
value: value, min: min, max: max,
|
|
activeColor: Colors.yellow, inactiveColor: Colors.white24,
|
|
onChanged: onChanged,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
// ==============================================================
|
|
// 📐 PINTOR: DESENHA A LINHA MATEMÁTICA NA TELA
|
|
// ==============================================================
|
|
class LinePainter extends CustomPainter {
|
|
final double hoopBaseX;
|
|
final double arcRadius;
|
|
final double cornerY;
|
|
final Color color;
|
|
final double width;
|
|
final double height;
|
|
|
|
LinePainter({
|
|
required this.hoopBaseX, required this.arcRadius, required this.cornerY,
|
|
required this.color, required this.width, required this.height,
|
|
});
|
|
|
|
@override
|
|
void paint(Canvas canvas, Size size) {
|
|
final paint = Paint()
|
|
..color = color
|
|
..style = PaintingStyle.stroke
|
|
..strokeWidth = 4;
|
|
|
|
double aspectRatio = width / height;
|
|
double hoopY = 0.50 * height;
|
|
|
|
// O cornerY controla a que distância do meio (50%) estão as linhas retas
|
|
double cornerDistY = cornerY * height;
|
|
|
|
// --- CESTO ESQUERDO ---
|
|
double hoopLX = hoopBaseX * width;
|
|
|
|
canvas.drawLine(Offset(0, hoopY - cornerDistY), Offset(width * 0.35, hoopY - cornerDistY), paint); // Cima
|
|
canvas.drawLine(Offset(0, hoopY + cornerDistY), Offset(width * 0.35, hoopY + cornerDistY), paint); // Baixo
|
|
|
|
canvas.drawArc(
|
|
Rect.fromCenter(center: Offset(hoopLX, hoopY), width: arcRadius * width * 2 / aspectRatio, height: arcRadius * height * 2),
|
|
-math.pi / 2, math.pi, false, paint,
|
|
);
|
|
|
|
// --- CESTO DIREITO ---
|
|
double hoopRX = (1.0 - hoopBaseX) * width;
|
|
|
|
canvas.drawLine(Offset(width, hoopY - cornerDistY), Offset(width * 0.65, hoopY - cornerDistY), paint); // Cima
|
|
canvas.drawLine(Offset(width, hoopY + cornerDistY), Offset(width * 0.65, hoopY + cornerDistY), paint); // Baixo
|
|
|
|
canvas.drawArc(
|
|
Rect.fromCenter(center: Offset(hoopRX, hoopY), width: arcRadius * width * 2 / aspectRatio, height: arcRadius * height * 2),
|
|
math.pi / 2, math.pi, false, paint,
|
|
);
|
|
}
|
|
|
|
@override
|
|
bool shouldRepaint(covariant LinePainter oldDelegate) {
|
|
return oldDelegate.hoopBaseX != hoopBaseX || oldDelegate.arcRadius != arcRadius || oldDelegate.cornerY != cornerY;
|
|
}
|
|
} |