import 'package:flutter/material.dart'; import '../models/energy_data.dart'; import '../theme/app_theme.dart'; class EnergyFlowWidget extends StatelessWidget { final EnergyData data; const EnergyFlowWidget({super.key, required this.data}); @override Widget build(BuildContext context) { return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Expanded( child: Text( 'Données en temps réel', style: TextStyle( fontSize: 17, fontWeight: FontWeight.bold, color: AppTheme.textDark, ), overflow: TextOverflow.ellipsis, ), ), Row( children: [ Text( '${data.temperature.toStringAsFixed(0)}°C', style: const TextStyle( fontSize: 15, fontWeight: FontWeight.w600, color: AppTheme.textDark, ), ), const SizedBox(width: 4), const Icon(Icons.cloud, color: Colors.blueGrey, size: 22), ], ), ], ), const SizedBox(height: 20), // Energy nodes Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _EnergyNode( icon: Icons.wb_sunny_rounded, color: AppTheme.solarYellow, power: data.pvPower, label: 'Solaire', ), _EnergyNode( icon: Icons.home_rounded, color: AppTheme.homeBlue, power: data.homePower, label: 'Maison', ), _EnergyNode( icon: Icons.battery_charging_full_rounded, color: AppTheme.batteryGreen, power: data.batteryPower.abs(), label: 'Batterie', badge: '${data.batterySOC.toStringAsFixed(0)}%', ), _EnergyNode( icon: Icons.electrical_services_rounded, color: AppTheme.gridGray, power: data.gridPower.abs(), label: data.gridPower >= 0 ? 'Réseau ↓' : 'Réseau ↑', ), ], ), const SizedBox(height: 16), // Flow diagram SizedBox( height: 80, child: CustomPaint( painter: _FlowPainter(data: data), size: Size.infinite, ), ), ], ), ), ); } } class _EnergyNode extends StatelessWidget { final IconData icon; final Color color; final double power; final String label; final String? badge; const _EnergyNode({ required this.icon, required this.color, required this.power, required this.label, this.badge, }); @override Widget build(BuildContext context) { return Column( children: [ Stack( clipBehavior: Clip.none, children: [ Container( width: 52, height: 52, decoration: BoxDecoration( color: color, shape: BoxShape.circle, ), child: Icon(icon, color: Colors.white, size: 24), ), if (badge != null) Positioned( bottom: -4, left: 0, right: 0, child: Center( child: Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 1), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), border: Border.all(color: color, width: 1.5), ), child: Text( badge!, style: TextStyle( fontSize: 10, fontWeight: FontWeight.bold, color: color, ), ), ), ), ), ], ), const SizedBox(height: 10), Text( _formatPower(power), style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: AppTheme.textDark, ), ), Text( _powerUnit(power), style: const TextStyle( fontSize: 12, color: AppTheme.textLight, ), ), Text( label, style: const TextStyle( fontSize: 11, color: AppTheme.textLight, ), ), ], ); } String _formatPower(double w) { if (w >= 1000) return (w / 1000).toStringAsFixed(1); return w.toStringAsFixed(0); } String _powerUnit(double w) => w >= 1000 ? 'kW' : 'W'; } class _FlowPainter extends CustomPainter { final EnergyData data; _FlowPainter({required this.data}); @override void paint(Canvas canvas, Size size) { // Draw animated flow lines between nodes final centerY = size.height * 0.4; final nodePositions = [ size.width * 0.125, // PV size.width * 0.375, // Home size.width * 0.625, // Battery size.width * 0.875, // Grid ]; final linePaint = Paint() ..strokeWidth = 2 ..style = PaintingStyle.stroke; // PV → Home (si PV produit) if (data.pvPower > 0) { _drawArrowLine(canvas, Offset(nodePositions[0], centerY), Offset(nodePositions[1], centerY), AppTheme.solarYellow, linePaint); } // Batterie en charge : source = PV si disponible, sinon Réseau → Batterie if (data.batteryPower > 0) { if (data.pvPower > 0) { // PV → Battery (solaire charge la batterie) _drawArrowLine(canvas, Offset(nodePositions[0], centerY), Offset(nodePositions[2], centerY), AppTheme.batteryGreen, linePaint); } else { // Grid → Battery (réseau charge la batterie) _drawArrowLine(canvas, Offset(nodePositions[3], centerY), Offset(nodePositions[2], centerY), AppTheme.batteryGreen, linePaint); } } // Batterie en décharge → Home if (data.batteryPower < 0) { _drawArrowLine(canvas, Offset(nodePositions[2], centerY), Offset(nodePositions[1], centerY), AppTheme.batteryGreen, linePaint); } // Réseau → Home (si import) if (data.gridPower > 0) { _drawArrowLine(canvas, Offset(nodePositions[3], centerY), Offset(nodePositions[1], centerY), Colors.orange, linePaint); } else if (data.gridPower < 0) { // PV → Réseau (export / injection réseau) _drawArrowLine(canvas, Offset(nodePositions[0], centerY), Offset(nodePositions[3], centerY), AppTheme.solarYellow, linePaint); } // Draw node dots final dotPaint = Paint()..style = PaintingStyle.fill; for (int i = 0; i < nodePositions.length; i++) { final colors = [ AppTheme.solarYellow, AppTheme.homeBlue, AppTheme.batteryGreen, AppTheme.gridGray, ]; dotPaint.color = colors[i]; canvas.drawCircle(Offset(nodePositions[i], centerY), 6, dotPaint); } } void _drawArrowLine(Canvas canvas, Offset start, Offset end, Color color, Paint paint) { paint.color = color.withValues(alpha:0.7); final path = Path() ..moveTo(start.dx, start.dy) ..lineTo(end.dx, end.dy); canvas.drawPath(path, paint); // Arrow head final arrowPaint = Paint() ..color = color ..style = PaintingStyle.fill; final dx = end.dx - start.dx; final arrowSign = dx > 0 ? 1 : -1; final arrowTip = Offset(end.dx - arrowSign * 8, end.dy); final arrowPath = Path() ..moveTo(arrowTip.dx + arrowSign * 8, arrowTip.dy) ..lineTo(arrowTip.dx, arrowTip.dy - 5) ..lineTo(arrowTip.dx, arrowTip.dy + 5) ..close(); canvas.drawPath(arrowPath, arrowPaint); } @override bool shouldRepaint(_FlowPainter oldDelegate) => true; }