premier app version beta

This commit is contained in:
2026-01-18 13:38:09 +01:00
commit 031d4a4e17
164 changed files with 13698 additions and 0 deletions

View File

@@ -0,0 +1,273 @@
import 'package:flutter/material.dart';
import '../../../core/constants/app_constants.dart';
import '../../../core/theme/app_theme.dart';
import '../../../services/grouping_analyzer_service.dart';
class GroupingStats extends StatelessWidget {
final GroupingResult groupingResult;
final double targetCenterX;
final double targetCenterY;
const GroupingStats({
super.key,
required this.groupingResult,
required this.targetCenterX,
required this.targetCenterY,
});
@override
Widget build(BuildContext context) {
final offsetX = groupingResult.centerX - targetCenterX;
final offsetY = groupingResult.centerY - targetCenterY;
final offsetDescription = _getOffsetDescription(offsetX, offsetY);
return Card(
child: Padding(
padding: const EdgeInsets.all(AppConstants.defaultPadding),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(Icons.center_focus_strong, color: AppTheme.groupingCenterColor),
const SizedBox(width: 8),
Text(
'Groupement',
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
const Spacer(),
_buildQualityBadge(context),
],
),
const Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildStat(
context,
'Diametre',
'${(groupingResult.diameter * 100).toStringAsFixed(1)}%',
icon: Icons.straighten,
),
_buildStat(
context,
'Dispersion',
'${(groupingResult.standardDeviation * 100).toStringAsFixed(1)}%',
icon: Icons.scatter_plot,
),
_buildStat(
context,
'Decalage',
offsetDescription,
icon: Icons.compare_arrows,
),
],
),
const SizedBox(height: 12),
_buildOffsetIndicator(context, offsetX, offsetY),
],
),
),
);
}
Widget _buildQualityBadge(BuildContext context) {
final rating = groupingResult.qualityRating;
final color = _getQualityColor(rating);
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
decoration: BoxDecoration(
color: color.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
...List.generate(5, (index) {
return Icon(
index < rating ? Icons.star : Icons.star_border,
size: 16,
color: color,
);
}),
const SizedBox(width: 4),
Text(
groupingResult.qualityDescription,
style: TextStyle(
color: color,
fontWeight: FontWeight.bold,
fontSize: 12,
),
),
],
),
);
}
Widget _buildStat(
BuildContext context,
String label,
String value, {
required IconData icon,
}) {
return Column(
children: [
Icon(icon, size: 20, color: Colors.grey),
const SizedBox(height: 4),
Text(
value,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
Text(
label,
style: Theme.of(context).textTheme.bodySmall,
),
],
);
}
Widget _buildOffsetIndicator(BuildContext context, double offsetX, double offsetY) {
return Container(
height: 80,
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(8),
),
child: Stack(
children: [
// Grid lines
Center(
child: Container(
width: 1,
color: Colors.grey[300],
),
),
Center(
child: Container(
height: 1,
color: Colors.grey[300],
),
),
// Center point (target)
const Center(
child: Icon(Icons.add, size: 16, color: Colors.grey),
),
// Grouping center
LayoutBuilder(
builder: (context, constraints) {
// Scale offset for visualization (max 40 pixels from center)
final maxOffset = 40.0;
final scaledX = (offsetX * 200).clamp(-maxOffset, maxOffset);
final scaledY = (offsetY * 200).clamp(-maxOffset, maxOffset);
return Center(
child: Transform.translate(
offset: Offset(scaledX, scaledY),
child: Container(
width: 12,
height: 12,
decoration: BoxDecoration(
color: AppTheme.groupingCenterColor,
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2),
),
),
),
);
},
),
// Labels
Positioned(
top: 4,
left: 0,
right: 0,
child: Text(
'Haut',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 10, color: Colors.grey[600]),
),
),
Positioned(
bottom: 4,
left: 0,
right: 0,
child: Text(
'Bas',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 10, color: Colors.grey[600]),
),
),
Positioned(
left: 4,
top: 0,
bottom: 0,
child: Center(
child: Text(
'G',
style: TextStyle(fontSize: 10, color: Colors.grey[600]),
),
),
),
Positioned(
right: 4,
top: 0,
bottom: 0,
child: Center(
child: Text(
'D',
style: TextStyle(fontSize: 10, color: Colors.grey[600]),
),
),
),
],
),
);
}
Color _getQualityColor(int rating) {
switch (rating) {
case 5:
return AppTheme.successColor;
case 4:
return Colors.lightGreen;
case 3:
return AppTheme.warningColor;
case 2:
return Colors.orange;
default:
return AppTheme.errorColor;
}
}
String _getOffsetDescription(double offsetX, double offsetY) {
if (offsetX.abs() < 0.02 && offsetY.abs() < 0.02) {
return 'Centre';
}
String vertical = '';
String horizontal = '';
if (offsetY < -0.02) {
vertical = 'H';
} else if (offsetY > 0.02) {
vertical = 'B';
}
if (offsetX < -0.02) {
horizontal = 'G';
} else if (offsetX > 0.02) {
horizontal = 'D';
}
if (vertical.isNotEmpty && horizontal.isNotEmpty) {
return '$vertical-$horizontal';
}
return vertical.isNotEmpty ? vertical : horizontal;
}
}