Feat: Bouton de clôture de session (80/20) sur l'accueil et reset de l'espacement
This commit is contained in:
@@ -496,13 +496,13 @@ class AnalysisProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
/// Adjust target position
|
/// Adjust target position
|
||||||
void adjustTargetPosition(
|
void adjustTargetPosition(
|
||||||
double centerX,
|
double centerX,
|
||||||
double centerY,
|
double centerY,
|
||||||
double innerRadius,
|
double innerRadius,
|
||||||
double radius, {
|
double radius, {
|
||||||
int? ringCount,
|
int? ringCount,
|
||||||
List<double>? ringRadii,
|
List<double>? ringRadii,
|
||||||
}) {
|
}) {
|
||||||
_targetCenterX = centerX;
|
_targetCenterX = centerX;
|
||||||
_targetCenterY = centerY;
|
_targetCenterY = centerY;
|
||||||
_targetInnerRadius = innerRadius;
|
_targetInnerRadius = innerRadius;
|
||||||
@@ -510,9 +510,9 @@ class AnalysisProvider extends ChangeNotifier {
|
|||||||
if (ringCount != null) {
|
if (ringCount != null) {
|
||||||
_ringCount = ringCount;
|
_ringCount = ringCount;
|
||||||
}
|
}
|
||||||
if (ringRadii != null) {
|
|
||||||
_ringRadii = ringRadii;
|
// CORRECTION : On accepte désormais la valeur null pour pouvoir réinitialiser l'espacement !
|
||||||
}
|
_ringRadii = ringRadii;
|
||||||
|
|
||||||
// Recalculate all shot scores based on new target position
|
// Recalculate all shot scores based on new target position
|
||||||
_shots = _shots.map((shot) {
|
_shots = _shots.map((shot) {
|
||||||
|
|||||||
@@ -51,14 +51,14 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
|||||||
late int _ringCount;
|
late int _ringCount;
|
||||||
late List<double> _ringRadii;
|
late List<double> _ringRadii;
|
||||||
|
|
||||||
|
// CORRECTION : Variable dédiée pour piloter la jauge orange sans conflit
|
||||||
|
late double _currentEspacementRatio;
|
||||||
|
|
||||||
bool _isDraggingCenter = false;
|
bool _isDraggingCenter = false;
|
||||||
bool _isDraggingRadius = false;
|
bool _isDraggingRadius = false;
|
||||||
bool _isDraggingInnerRadius = false;
|
bool _isDraggingInnerRadius = false;
|
||||||
|
|
||||||
// Contrôle de l'affichage du scroll d'espacement (désactivé par défaut)
|
|
||||||
bool _showEspacement = false;
|
bool _showEspacement = false;
|
||||||
|
|
||||||
// Variable temporaire pour stocker la valeur du rayon au début du pincement tactile
|
|
||||||
double _baseRadiusBeforeScale = 1.0;
|
double _baseRadiusBeforeScale = 1.0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -69,6 +69,9 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
|||||||
_radius = widget.initialRadius;
|
_radius = widget.initialRadius;
|
||||||
_innerRadius = widget.initialInnerRadius;
|
_innerRadius = widget.initialInnerRadius;
|
||||||
_ringCount = widget.initialRingCount;
|
_ringCount = widget.initialRingCount;
|
||||||
|
|
||||||
|
// Initialisation du ratio par défaut
|
||||||
|
_currentEspacementRatio = (_radius > 0) ? (_innerRadius / _radius).clamp(0.01, 0.9) : 0.1;
|
||||||
_initRingRadii();
|
_initRingRadii();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,11 +80,9 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
|||||||
widget.initialRingRadii!.length == _ringCount) {
|
widget.initialRingRadii!.length == _ringCount) {
|
||||||
_ringRadii = List.from(widget.initialRingRadii!);
|
_ringRadii = List.from(widget.initialRingRadii!);
|
||||||
} else {
|
} else {
|
||||||
// Initialize with default proportional radii interpolated between inner and outer
|
|
||||||
_ringRadii = List.generate(_ringCount, (i) {
|
_ringRadii = List.generate(_ringCount, (i) {
|
||||||
if (_ringCount <= 1) return 1.0;
|
if (_ringCount <= 1) return 1.0;
|
||||||
final ratio = _innerRadius / _radius;
|
return _currentEspacementRatio + (1.0 - _currentEspacementRatio) * i / (_ringCount - 1);
|
||||||
return ratio + (1.0 - ratio) * i / (_ringCount - 1);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,12 +92,10 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
|||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
bool shouldReinit = false;
|
bool shouldReinit = false;
|
||||||
|
|
||||||
if (widget.initialCenterX != oldWidget.initialCenterX &&
|
if (widget.initialCenterX != oldWidget.initialCenterX && !_isDraggingCenter) {
|
||||||
!_isDraggingCenter) {
|
|
||||||
_centerX = widget.initialCenterX;
|
_centerX = widget.initialCenterX;
|
||||||
}
|
}
|
||||||
if (widget.initialCenterY != oldWidget.initialCenterY &&
|
if (widget.initialCenterY != oldWidget.initialCenterY && !_isDraggingCenter) {
|
||||||
!_isDraggingCenter) {
|
|
||||||
_centerY = widget.initialCenterY;
|
_centerY = widget.initialCenterY;
|
||||||
}
|
}
|
||||||
if (widget.initialRingCount != oldWidget.initialRingCount) {
|
if (widget.initialRingCount != oldWidget.initialRingCount) {
|
||||||
@@ -107,12 +106,12 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
|||||||
_radius = widget.initialRadius;
|
_radius = widget.initialRadius;
|
||||||
shouldReinit = true;
|
shouldReinit = true;
|
||||||
}
|
}
|
||||||
if (widget.initialInnerRadius != oldWidget.initialInnerRadius &&
|
if (widget.initialInnerRadius != oldWidget.initialInnerRadius && !_isDraggingInnerRadius) {
|
||||||
!_isDraggingInnerRadius) {
|
|
||||||
_innerRadius = widget.initialInnerRadius;
|
_innerRadius = widget.initialInnerRadius;
|
||||||
|
_currentEspacementRatio = (_radius > 0) ? (_innerRadius / _radius).clamp(0.01, 0.9) : 0.1;
|
||||||
shouldReinit = true;
|
shouldReinit = true;
|
||||||
}
|
}
|
||||||
if (widget.initialRingRadii != oldWidget.initialRingRadii) {
|
if (widget.initialRingRadii != oldWidget.initialRingRadii && widget.initialRingRadii != null) {
|
||||||
shouldReinit = true;
|
shouldReinit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +128,6 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
|||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
// Utilisation d'un Gesture Detector basé sur le Scale pour synchroniser à 1 ou 2 doigts
|
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onScaleStart: (details) {
|
onScaleStart: (details) {
|
||||||
_baseRadiusBeforeScale = _radius;
|
_baseRadiusBeforeScale = _radius;
|
||||||
@@ -162,7 +160,6 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Panneau de configuration des Sliders en haut
|
|
||||||
Positioned(
|
Positioned(
|
||||||
top: 10,
|
top: 10,
|
||||||
left: 40,
|
left: 40,
|
||||||
@@ -170,7 +167,6 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
// Ligne d'activation pour afficher ou masquer l'espacement
|
|
||||||
Container(
|
Container(
|
||||||
margin: const EdgeInsets.only(bottom: 8),
|
margin: const EdgeInsets.only(bottom: 8),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 2),
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 2),
|
||||||
@@ -201,7 +197,6 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Slider pour la taille (toujours visible)
|
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@@ -221,6 +216,7 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
|||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_radius = value;
|
_radius = value;
|
||||||
|
_innerRadius = _radius * _currentEspacementRatio;
|
||||||
_initRingRadii(forceRecalculate: true);
|
_initRingRadii(forceRecalculate: true);
|
||||||
});
|
});
|
||||||
_notifyChange();
|
_notifyChange();
|
||||||
@@ -232,11 +228,10 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Affichage conditionnel du slider d'espacement orange
|
|
||||||
if (_showEspacement) ...[
|
if (_showEspacement) ...[
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
padding: const EdgeInsets.fromLTRB(16, 4, 4, 4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.black54,
|
color: Colors.black54,
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
@@ -246,21 +241,51 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
|||||||
const Text('Espacement ', style: TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold)),
|
const Text('Espacement ', style: TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold)),
|
||||||
const Icon(Icons.compress, color: Colors.white, size: 16),
|
const Icon(Icons.compress, color: Colors.white, size: 16),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Slider(
|
child: Row(
|
||||||
value: (_innerRadius / _radius).clamp(0.01, 0.9),
|
children: [
|
||||||
min: 0.01,
|
Expanded(
|
||||||
max: 0.9,
|
child: Slider(
|
||||||
activeColor: Colors.orange,
|
value: _currentEspacementRatio,
|
||||||
onChanged: (value) {
|
min: 0.01,
|
||||||
setState(() {
|
max: 0.9,
|
||||||
_innerRadius = _radius * value;
|
activeColor: Colors.orange,
|
||||||
_initRingRadii(forceRecalculate: true);
|
onChanged: (value) {
|
||||||
});
|
setState(() {
|
||||||
_notifyChange();
|
_currentEspacementRatio = value;
|
||||||
},
|
_innerRadius = _radius * _currentEspacementRatio;
|
||||||
|
_initRingRadii(forceRecalculate: true);
|
||||||
|
});
|
||||||
|
_notifyChange();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Icon(Icons.expand, color: Colors.white, size: 16),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Icon(Icons.expand, color: Colors.white, size: 16),
|
// CORRECTION DU BOUTON RESET : Force visuellement le ratio par défaut
|
||||||
|
// CORRECTION RADICALE : On force le ratio à 0.1 (10% standard) en dur
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.refresh, color: Colors.white70, size: 20),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
// 1. On force la jauge orange à se remettre pile à 10% (la valeur d'usine)
|
||||||
|
_currentEspacementRatio = 0.1;
|
||||||
|
|
||||||
|
// 2. On réaligne l'innerRadius sur la taille actuelle
|
||||||
|
_innerRadius = _radius * _currentEspacementRatio;
|
||||||
|
|
||||||
|
// 3. On force la reconstruction des cercles parfaits
|
||||||
|
_initRingRadii(forceRecalculate: true);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4. On balance tout ça au provider pour qu'il s'aligne
|
||||||
|
_notifyChange();
|
||||||
|
},
|
||||||
|
tooltip: 'Réinitialiser l\'espacement',
|
||||||
|
constraints: const BoxConstraints(),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -268,7 +293,6 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -286,28 +310,16 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
_buildDirectionButton(
|
_buildDirectionButton(Icons.keyboard_arrow_up, () => _moveCenterByPixels(0, -1, size)),
|
||||||
Icons.keyboard_arrow_up,
|
|
||||||
() => _moveCenterByPixels(0, -1, size),
|
|
||||||
),
|
|
||||||
Row(
|
Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
_buildDirectionButton(
|
_buildDirectionButton(Icons.keyboard_arrow_left, () => _moveCenterByPixels(-1, 0, size)),
|
||||||
Icons.keyboard_arrow_left,
|
|
||||||
() => _moveCenterByPixels(-1, 0, size),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 40),
|
const SizedBox(width: 40),
|
||||||
_buildDirectionButton(
|
_buildDirectionButton(Icons.keyboard_arrow_right, () => _moveCenterByPixels(1, 0, size)),
|
||||||
Icons.keyboard_arrow_right,
|
|
||||||
() => _moveCenterByPixels(1, 0, size),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
_buildDirectionButton(
|
_buildDirectionButton(Icons.keyboard_arrow_down, () => _moveCenterByPixels(0, 1, size)),
|
||||||
Icons.keyboard_arrow_down,
|
|
||||||
() => _moveCenterByPixels(0, 1, size),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -315,10 +327,7 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
|||||||
|
|
||||||
Widget _buildDirectionButton(IconData icon, VoidCallback onPressed) {
|
Widget _buildDirectionButton(IconData icon, VoidCallback onPressed) {
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(color: Colors.black87, borderRadius: BorderRadius.circular(8)),
|
||||||
color: Colors.black87,
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
margin: const EdgeInsets.all(2),
|
margin: const EdgeInsets.all(2),
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
icon: Icon(icon, color: Colors.white, size: 28),
|
icon: Icon(icon, color: Colors.white, size: 28),
|
||||||
@@ -348,22 +357,19 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
|||||||
_notifyChange();
|
_notifyChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Traitement intelligent des gestes tactiles
|
|
||||||
void _onScaleUpdate(ScaleUpdateDetails details, Size size) {
|
void _onScaleUpdate(ScaleUpdateDetails details, Size size) {
|
||||||
setState(() {
|
setState(() {
|
||||||
if (details.pointerCount == 2) {
|
if (details.pointerCount == 2) {
|
||||||
// GESTE À DEUX DOIGTS : On calcule le zoom en respectant le range strict (0.3 à 1.2)
|
|
||||||
_radius = (_baseRadiusBeforeScale * details.scale).clamp(0.5, 1.1);
|
_radius = (_baseRadiusBeforeScale * details.scale).clamp(0.5, 1.1);
|
||||||
|
_innerRadius = _radius * _currentEspacementRatio;
|
||||||
_initRingRadii(forceRecalculate: true);
|
_initRingRadii(forceRecalculate: true);
|
||||||
} else if (_isDraggingCenter) {
|
} else if (_isDraggingCenter) {
|
||||||
// GESTE À UN SEUL DOIGT : Déplacement fluide du centre de la cible
|
|
||||||
final deltaX = details.focalPointDelta.dx / size.width;
|
final deltaX = details.focalPointDelta.dx / size.width;
|
||||||
final deltaY = details.focalPointDelta.dy / size.height;
|
final deltaY = details.focalPointDelta.dy / size.height;
|
||||||
_centerX = _centerX + deltaX;
|
_centerX = _centerX + deltaX;
|
||||||
_centerY = _centerY + deltaY;
|
_centerY = _centerY + deltaY;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
_notifyChange();
|
_notifyChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,31 +426,16 @@ class _CalibrationPainter extends CustomPainter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isDraggingCenter) {
|
if (isDraggingCenter) {
|
||||||
final crosshairLinePaint = Paint()
|
final crosshairLinePaint = Paint()..color = Colors.red.withValues(alpha: 0.5)..strokeWidth = 1;
|
||||||
..color = Colors.red.withValues(alpha: 0.5)
|
canvas.drawLine(Offset(0, centerPx.dy), Offset(size.width, centerPx.dy), crosshairLinePaint);
|
||||||
..strokeWidth = 1;
|
canvas.drawLine(Offset(centerPx.dx, 0), Offset(centerPx.dx, size.height), crosshairLinePaint);
|
||||||
canvas.drawLine(
|
|
||||||
Offset(0, centerPx.dy),
|
|
||||||
Offset(size.width, centerPx.dy),
|
|
||||||
crosshairLinePaint,
|
|
||||||
);
|
|
||||||
canvas.drawLine(
|
|
||||||
Offset(centerPx.dx, 0),
|
|
||||||
Offset(centerPx.dx, size.height),
|
|
||||||
crosshairLinePaint,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_drawCenterHandle(canvas, centerPx);
|
_drawCenterHandle(canvas, centerPx);
|
||||||
_drawInstructions(canvas, size);
|
_drawInstructions(canvas, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _drawConcentricZones(
|
void _drawConcentricZones(Canvas canvas, Size size, Offset center, double baseRadius) {
|
||||||
Canvas canvas,
|
|
||||||
Size size,
|
|
||||||
Offset center,
|
|
||||||
double baseRadius,
|
|
||||||
) {
|
|
||||||
List<Color> zoneColors = [];
|
List<Color> zoneColors = [];
|
||||||
for (int i = 0; i < ringCount; i++) {
|
for (int i = 0; i < ringCount; i++) {
|
||||||
final ratio = i / ringCount;
|
final ratio = i / ringCount;
|
||||||
@@ -468,9 +459,7 @@ class _CalibrationPainter extends CustomPainter {
|
|||||||
..color = Colors.red.withValues(alpha: 0.8);
|
..color = Colors.red.withValues(alpha: 0.8);
|
||||||
|
|
||||||
for (int i = ringCount - 1; i >= 0; i--) {
|
for (int i = ringCount - 1; i >= 0; i--) {
|
||||||
final ringRadius = ringRadii.length > i
|
final ringRadius = ringRadii.length > i ? ringRadii[i] : (i + 1) / ringCount;
|
||||||
? ringRadii[i]
|
|
||||||
: (i + 1) / ringCount;
|
|
||||||
final zoneRadius = baseRadius * ringRadius;
|
final zoneRadius = baseRadius * ringRadius;
|
||||||
|
|
||||||
zonePaint.color = zoneColors[i];
|
zonePaint.color = zoneColors[i];
|
||||||
@@ -481,12 +470,8 @@ class _CalibrationPainter extends CustomPainter {
|
|||||||
final textPainter = TextPainter(textDirection: TextDirection.ltr);
|
final textPainter = TextPainter(textDirection: TextDirection.ltr);
|
||||||
|
|
||||||
for (int i = 0; i < ringCount; i++) {
|
for (int i = 0; i < ringCount; i++) {
|
||||||
final ringRadius = ringRadii.length > i
|
final ringRadius = ringRadii.length > i ? ringRadii[i] : (i + 1) / ringCount;
|
||||||
? ringRadii[i]
|
final prevRingRadius = i > 0 ? (ringRadii.length > i - 1 ? ringRadii[i - 1] : i / ringCount) : 0.0;
|
||||||
: (i + 1) / ringCount;
|
|
||||||
final prevRingRadius = i > 0
|
|
||||||
? (ringRadii.length > i - 1 ? ringRadii[i - 1] : i / ringCount)
|
|
||||||
: 0.0;
|
|
||||||
final zoneRadius = baseRadius * (ringRadius + prevRingRadius) / 2;
|
final zoneRadius = baseRadius * (ringRadius + prevRingRadius) / 2;
|
||||||
|
|
||||||
final score = 10 - i;
|
final score = 10 - i;
|
||||||
@@ -506,53 +491,28 @@ class _CalibrationPainter extends CustomPainter {
|
|||||||
|
|
||||||
final labelY = center.dy - textPainter.height / 2;
|
final labelY = center.dy - textPainter.height / 2;
|
||||||
if (labelY >= 0 && labelY <= size.height) {
|
if (labelY >= 0 && labelY <= size.height) {
|
||||||
textPainter.paint(
|
textPainter.paint(canvas, Offset(labelX - textPainter.width / 2, labelY));
|
||||||
canvas,
|
|
||||||
Offset(labelX - textPainter.width / 2, labelY),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _drawSilhouetteZones(
|
void _drawSilhouetteZones(Canvas canvas, Size size, Offset center, double radius) {
|
||||||
Canvas canvas,
|
final paint = Paint()..style = PaintingStyle.stroke..strokeWidth = 2;
|
||||||
Size size,
|
|
||||||
Offset center,
|
|
||||||
double radius,
|
|
||||||
) {
|
|
||||||
final paint = Paint()
|
|
||||||
..style = PaintingStyle.stroke
|
|
||||||
..strokeWidth = 2;
|
|
||||||
|
|
||||||
final silhouetteWidth = radius * 0.8;
|
final silhouetteWidth = radius * 0.8;
|
||||||
final silhouetteHeight = radius * 2;
|
final silhouetteHeight = radius * 2;
|
||||||
|
|
||||||
paint.color = Colors.green.withValues(alpha: 0.5);
|
paint.color = Colors.green.withValues(alpha: 0.5);
|
||||||
canvas.drawRect(
|
canvas.drawRect(Rect.fromCenter(center: center, width: silhouetteWidth, height: silhouetteHeight), paint);
|
||||||
Rect.fromCenter(
|
|
||||||
center: center,
|
|
||||||
width: silhouetteWidth,
|
|
||||||
height: silhouetteHeight,
|
|
||||||
),
|
|
||||||
paint,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _drawCenterHandle(Canvas canvas, Offset center) {
|
void _drawCenterHandle(Canvas canvas, Offset center) {
|
||||||
final outerPaint = Paint()
|
final outerPaint = Paint()..color = isDraggingCenter ? Colors.redAccent : Colors.red..style = PaintingStyle.stroke..strokeWidth = 3;
|
||||||
..color = isDraggingCenter ? Colors.redAccent : Colors.red
|
|
||||||
..style = PaintingStyle.stroke
|
|
||||||
..strokeWidth = 3;
|
|
||||||
canvas.drawCircle(center, 15, outerPaint);
|
canvas.drawCircle(center, 15, outerPaint);
|
||||||
|
|
||||||
final innerPaint = Paint()
|
final innerPaint = Paint()..color = isDraggingCenter ? Colors.redAccent : Colors.red..style = PaintingStyle.fill;
|
||||||
..color = isDraggingCenter ? Colors.redAccent : Colors.red
|
|
||||||
..style = PaintingStyle.fill;
|
|
||||||
canvas.drawCircle(center, 5, innerPaint);
|
canvas.drawCircle(center, 5, innerPaint);
|
||||||
|
|
||||||
final crossPaint = Paint()
|
final crossPaint = Paint()..color = isDraggingCenter ? Colors.redAccent : Colors.red..strokeWidth = 2;
|
||||||
..color = isDraggingCenter ? Colors.redAccent : Colors.red
|
|
||||||
..strokeWidth = 2;
|
|
||||||
canvas.drawLine(Offset(center.dx - 20, center.dy), Offset(center.dx - 8, center.dy), crossPaint);
|
canvas.drawLine(Offset(center.dx - 20, center.dy), Offset(center.dx - 8, center.dy), crossPaint);
|
||||||
canvas.drawLine(Offset(center.dx + 8, center.dy), Offset(center.dx + 20, center.dy), crossPaint);
|
canvas.drawLine(Offset(center.dx + 8, center.dy), Offset(center.dx + 20, center.dy), crossPaint);
|
||||||
canvas.drawLine(Offset(center.dx, center.dy - 20), Offset(center.dx, center.dy - 8), crossPaint);
|
canvas.drawLine(Offset(center.dx, center.dy - 20), Offset(center.dx, center.dy - 8), crossPaint);
|
||||||
@@ -561,23 +521,12 @@ class _CalibrationPainter extends CustomPainter {
|
|||||||
|
|
||||||
void _drawInstructions(Canvas canvas, Size size) {
|
void _drawInstructions(Canvas canvas, Size size) {
|
||||||
const instruction = 'Deplacez le centre ou ajustez le rayon';
|
const instruction = 'Deplacez le centre ou ajustez le rayon';
|
||||||
|
|
||||||
final textPainter = TextPainter(
|
final textPainter = TextPainter(
|
||||||
text: TextSpan(
|
text: TextSpan(text: instruction, style: TextStyle(color: Colors.white.withValues(alpha: 0.9), fontSize: 12, backgroundColor: Colors.black54)),
|
||||||
text: instruction,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white.withValues(alpha: 0.9),
|
|
||||||
fontSize: 12,
|
|
||||||
backgroundColor: Colors.black54,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
);
|
);
|
||||||
textPainter.layout();
|
textPainter.layout();
|
||||||
textPainter.paint(
|
textPainter.paint(canvas, Offset((size.width - textPainter.width) / 2, size.height - 30));
|
||||||
canvas,
|
|
||||||
Offset((size.width - textPainter.width) / 2, size.height - 30),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||||||
final repository = context.read<SessionRepository>();
|
final repository = context.read<SessionRepository>();
|
||||||
final stats = await repository.getStatistics();
|
final stats = await repository.getStatistics();
|
||||||
final sessions = await repository.getAllSessions();
|
final sessions = await repository.getAllSessions();
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_stats = stats;
|
_stats = stats;
|
||||||
@@ -125,21 +125,100 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||||||
final sessionProvider = context.watch<SessionProvider>();
|
final sessionProvider = context.watch<SessionProvider>();
|
||||||
final isSessionActive = sessionProvider.isSessionActive;
|
final isSessionActive = sessionProvider.isSessionActive;
|
||||||
|
|
||||||
return ElevatedButton.icon(
|
// Si aucune session n'est active, on affiche juste le bouton de départ habituel
|
||||||
onPressed: () => _navigateToSessionSetup(context),
|
if (!isSessionActive) {
|
||||||
style: ElevatedButton.styleFrom(
|
return ElevatedButton.icon(
|
||||||
backgroundColor: isSessionActive ? AppTheme.secondaryColor : AppTheme.primaryColor,
|
onPressed: () => _navigateToSessionSetup(context),
|
||||||
foregroundColor: Colors.white,
|
style: ElevatedButton.styleFrom(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 20),
|
backgroundColor: AppTheme.primaryColor,
|
||||||
shape: RoundedRectangleBorder(
|
foregroundColor: Colors.white,
|
||||||
borderRadius: BorderRadius.circular(AppConstants.borderRadius),
|
padding: const EdgeInsets.symmetric(vertical: 20),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(AppConstants.borderRadius),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
icon: const Icon(Icons.add_circle_outline, size: 28),
|
||||||
icon: Icon(isSessionActive ? Icons.play_circle_outline : Icons.add_circle_outline, size: 28),
|
label: const Text(
|
||||||
label: Text(
|
'Démarrer la session',
|
||||||
isSessionActive ? 'Continuer la session' : 'Démarrer la session',
|
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
),
|
||||||
),
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MODIFICATION : Alignement sur une seule ligne (80% / 20%) avec une croix pour l'annulation
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
// 80% de largeur pour continuer la session
|
||||||
|
Expanded(
|
||||||
|
flex: 8,
|
||||||
|
child: ElevatedButton.icon(
|
||||||
|
onPressed: () => _navigateToSessionSetup(context),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: AppTheme.secondaryColor,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 20),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(AppConstants.borderRadius),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
icon: const Icon(Icons.play_circle_outline, size: 28),
|
||||||
|
label: const Text(
|
||||||
|
'Continuer la session',
|
||||||
|
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
// 20% de largeur pour terminer/annuler la session (Bouton Croix)
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: OutlinedButton(
|
||||||
|
onPressed: () {
|
||||||
|
// Boite de dialogue de confirmation pour éviter les erreurs
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text('Terminer la session ?'),
|
||||||
|
content: const Text('Êtes-vous sûr de vouloir clôturer la session en cours ?'),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
child: const Text('ANNULER'),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
// Clôture propre de la session dans le State global
|
||||||
|
context.read<SessionProvider>().endSession();
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(
|
||||||
|
content: Text('Session terminée avec succès !'),
|
||||||
|
backgroundColor: AppTheme.successColor,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: const Text('TERMINER', style: TextStyle(color: Colors.redAccent)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
style: OutlinedButton.styleFrom(
|
||||||
|
foregroundColor: Colors.redAccent,
|
||||||
|
side: const BorderSide(color: Colors.redAccent, width: 1.5),
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 20), // Même hauteur que le bouton continuer
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(AppConstants.borderRadius),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const Icon(Icons.close, size: 28),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,17 +276,17 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||||||
),
|
),
|
||||||
child: _recentSessions.length < 2
|
child: _recentSessions.length < 2
|
||||||
? const Center(
|
? const Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.show_chart, size: 40, color: Colors.grey),
|
Icon(Icons.show_chart, size: 40, color: Colors.grey),
|
||||||
Text(
|
Text(
|
||||||
'Pas assez de données',
|
'Pas assez de données',
|
||||||
style: TextStyle(color: Colors.grey, fontSize: 12),
|
style: TextStyle(color: Colors.grey, fontSize: 12),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: _buildHomeTrendChart(),
|
: _buildHomeTrendChart(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -276,7 +355,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||||||
final sorted = List<Session>.from(_recentSessions)
|
final sorted = List<Session>.from(_recentSessions)
|
||||||
..sort((a, b) => a.createdAt.compareTo(b.createdAt));
|
..sort((a, b) => a.createdAt.compareTo(b.createdAt));
|
||||||
final display =
|
final display =
|
||||||
sorted.length > 7 ? sorted.sublist(sorted.length - 7) : sorted;
|
sorted.length > 7 ? sorted.sublist(sorted.length - 7) : sorted;
|
||||||
|
|
||||||
return LineChart(
|
return LineChart(
|
||||||
LineChartData(
|
LineChartData(
|
||||||
@@ -372,4 +451,4 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||||||
);
|
);
|
||||||
_loadStats();
|
_loadStats();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user