feat(session): ajout du sélecteur de date et persistance dans l'historique de tir
This commit is contained in:
@@ -4,6 +4,8 @@ import '../../data/models/target_analysis.dart';
|
||||
import '../../data/models/weapon.dart';
|
||||
|
||||
class SessionProvider extends ChangeNotifier {
|
||||
DateTime? _sessionDate;
|
||||
DateTime? get sessionDate => _sessionDate;
|
||||
String? _currentWeaponName;
|
||||
String? _currentWeaponId;
|
||||
int _shotsPerTarget = 5;
|
||||
@@ -23,11 +25,12 @@ class SessionProvider extends ChangeNotifier {
|
||||
int get totalSessionScore => _currentAnalyses.fold(0, (sum, a) => sum + a.totalScore);
|
||||
int get targetCount => _currentAnalyses.length;
|
||||
|
||||
void startSession(String weaponName, int shots, String sessionId, {String? weaponId, int distance = 25}) {
|
||||
void startSession(String weaponName, int shots, String sessionId, {String? weaponId, int distance = 25, DateTime? date}) {
|
||||
_currentWeaponName = weaponName;
|
||||
_currentWeaponId = weaponId;
|
||||
_shotsPerTarget = shots;
|
||||
_distance = distance;
|
||||
_sessionDate = date ?? DateTime.now();
|
||||
_activeSessionId = sessionId;
|
||||
_currentAnalyses.clear();
|
||||
_isSessionActive = true;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:intl/intl.dart'; // Utile pour formater proprement la date en français
|
||||
import '../../core/constants/app_constants.dart';
|
||||
import '../../core/theme/app_theme.dart';
|
||||
import '../../data/models/weapon.dart';
|
||||
@@ -18,13 +19,15 @@ class SessionSetupScreen extends StatefulWidget {
|
||||
class _SessionSetupScreenState extends State<SessionSetupScreen> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
// Plus besoin du _weaponController, on force la sélection
|
||||
int _shotsPerTarget = 5;
|
||||
int _distance = 25;
|
||||
List<Weapon> _availableWeapons = [];
|
||||
Weapon? _selectedWeapon;
|
||||
bool _isLoadingWeapons = true;
|
||||
|
||||
// AJOUT DE LA VARIABLE DATE : Initialisée par défaut à maintenant
|
||||
DateTime _selectedDate = DateTime.now();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -39,7 +42,6 @@ class _SessionSetupScreenState extends State<SessionSetupScreen> {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_availableWeapons = weapons;
|
||||
// Si la liste n'est pas vide et qu'aucune arme n'est sélectionnée, on sélectionne la première par défaut
|
||||
if (_availableWeapons.isNotEmpty && _selectedWeapon == null) {
|
||||
_selectedWeapon = _availableWeapons.first;
|
||||
_updateSettingsForWeapon(_selectedWeapon!);
|
||||
@@ -49,25 +51,47 @@ class _SessionSetupScreenState extends State<SessionSetupScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
// Petite méthode pour mettre à jour les curseurs selon l'arme sélectionnée
|
||||
void _updateSettingsForWeapon(Weapon weapon) {
|
||||
_shotsPerTarget = weapon.magazineCapacity;
|
||||
_distance = (weapon.type == WeaponType.handgun) ? 25 : 50;
|
||||
}
|
||||
|
||||
// Fonction pour ouvrir le calendrier si l'utilisateur clique sur le champ
|
||||
Future<void> _pickDate() async {
|
||||
final DateTime? picked = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: _selectedDate,
|
||||
firstDate: DateTime(2020),
|
||||
lastDate: DateTime(2100),
|
||||
locale: const Locale('fr', 'FR'), // Force le calendrier en français
|
||||
);
|
||||
if (picked != null && picked != _selectedDate) {
|
||||
setState(() {
|
||||
// On garde aussi l'heure actuelle lors du changement de date
|
||||
final now = DateTime.now();
|
||||
_selectedDate = DateTime(picked.year, picked.month, picked.day, now.hour, now.minute);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _startSession() {
|
||||
// Sécurité supplémentaire : impossible de démarrer sans arme
|
||||
if (_formKey.currentState!.validate() && _selectedWeapon != null) {
|
||||
final repository = context.read<SessionRepository>();
|
||||
final sessionProvider = context.read<SessionProvider>();
|
||||
|
||||
final sessionId = repository.generateId();
|
||||
|
||||
// PASSE-PARTOUT : On envoie la session au provider.
|
||||
// Note : Si ton `sessionProvider.startSession` ne prend pas encore la date en paramètre,
|
||||
// pas de panique, la compilation passera car Dart tolère les arguments nommés optionnels
|
||||
// s'ils sont déjà présents, ou tu pourras l'ajouter à ta méthode startSession.
|
||||
sessionProvider.startSession(
|
||||
_selectedWeapon!.displayName,
|
||||
_shotsPerTarget,
|
||||
sessionId,
|
||||
weaponId: _selectedWeapon!.id,
|
||||
distance: _distance,
|
||||
date: _selectedDate,
|
||||
);
|
||||
|
||||
Navigator.pushReplacement(
|
||||
@@ -79,6 +103,9 @@ class _SessionSetupScreenState extends State<SessionSetupScreen> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Petit formatage sympa en français (ex: "27 mai 2026")
|
||||
final String formattedDate = DateFormat('dd MMMM yyyy', 'fr_FR').format(_selectedDate);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Configuration de la session'),
|
||||
@@ -93,7 +120,7 @@ class _SessionSetupScreenState extends State<SessionSetupScreen> {
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const Text(
|
||||
'Choix de l\'arme',
|
||||
'Informations générales',
|
||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
@@ -128,7 +155,6 @@ class _SessionSetupScreenState extends State<SessionSetupScreen> {
|
||||
context,
|
||||
MaterialPageRoute(builder: (_) => const WeaponListScreen()),
|
||||
).then((_) {
|
||||
// Quand on revient de l'armurerie, on recharge la liste !
|
||||
_loadWeapons();
|
||||
});
|
||||
},
|
||||
@@ -139,28 +165,50 @@ class _SessionSetupScreenState extends State<SessionSetupScreen> {
|
||||
),
|
||||
)
|
||||
else
|
||||
DropdownButtonFormField<Weapon>(
|
||||
value: _selectedWeapon,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Sélectionner une arme',
|
||||
prefixIcon: const Icon(Icons.shield),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppConstants.borderRadius),
|
||||
Column(
|
||||
children: [
|
||||
DropdownButtonFormField<Weapon>(
|
||||
value: _selectedWeapon,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Sélectionner une arme',
|
||||
prefixIcon: const Icon(Icons.shield),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppConstants.borderRadius),
|
||||
),
|
||||
),
|
||||
items: _availableWeapons.map((w) => DropdownMenuItem(
|
||||
value: w,
|
||||
child: Text(w.displayName),
|
||||
)).toList(),
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
setState(() {
|
||||
_selectedWeapon = value;
|
||||
_updateSettingsForWeapon(value);
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
// On a retiré le choix "null / Autre", l'utilisateur DOIT choisir une arme existante
|
||||
items: _availableWeapons.map((w) => DropdownMenuItem(
|
||||
value: w,
|
||||
child: Text(w.displayName),
|
||||
)).toList(),
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
setState(() {
|
||||
_selectedWeapon = value;
|
||||
_updateSettingsForWeapon(value);
|
||||
});
|
||||
}
|
||||
},
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// NOUVEAU CHAMP : Sélecteur de date interactif
|
||||
InkWell(
|
||||
onTap: _availableWeapons.isEmpty ? null : _pickDate,
|
||||
borderRadius: BorderRadius.circular(AppConstants.borderRadius),
|
||||
child: IgnorePointer(
|
||||
child: TextFormField(
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Date de la session',
|
||||
prefixIcon: const Icon(Icons.calendar_today),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppConstants.borderRadius),
|
||||
),
|
||||
),
|
||||
controller: TextEditingController(text: formattedDate),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
@@ -252,7 +300,6 @@ class _SessionSetupScreenState extends State<SessionSetupScreen> {
|
||||
const SizedBox(height: 48),
|
||||
|
||||
ElevatedButton.icon(
|
||||
// On désactive le bouton si la liste est vide ou si aucune arme n'est sélectionnée
|
||||
onPressed: (_availableWeapons.isEmpty || _selectedWeapon == null)
|
||||
? null
|
||||
: _startSession,
|
||||
|
||||
Reference in New Issue
Block a user