diff --git a/lib/features/dashboard/widgets/kpi_row.dart b/lib/features/dashboard/widgets/kpi_row.dart index e776684..afc327b 100644 --- a/lib/features/dashboard/widgets/kpi_row.dart +++ b/lib/features/dashboard/widgets/kpi_row.dart @@ -11,8 +11,19 @@ class KpiRow extends StatelessWidget { Widget build(BuildContext context) { final autoconso = data.selfConsumptionRate.clamp(0.0, 100.0); final autonomy = data.autonomyRate.clamp(0.0, 100.0); - final toGrid = data.dayGridInjectionWh.clamp(0.0, double.infinity) / 1000; - final fromGrid = (-data.dayGridInjectionWh).clamp(0.0, double.infinity) / 1000; + // Deux compteurs DISTINCTS de la box, et non un seul et son opposé. + // + // Ces deux tuiles lisaient toutes les deux `dayGridInjectionWh` — l'export — la + // seconde en prenant son opposé. Or l'import a son propre compteur + // (`totalAcquisition`) : dès que l'export était positif, « Depuis réseau » affichait + // 0,0 kWh en permanence, quelle que soit l'électricité réellement achetée. + // + // `null` = la box publie une valeur inexploitable ; la tuile le dit au lieu + // d'imprimer un nombre absurde (constaté sur `.75` : 9,1e+33 kWh exportés, alors que + // `totalProduction` restait sain — les compteurs cumulés de l'experience-plugin + // énergie sont corrompus, pas ceux du Thing compteur). + final toGrid = data.gridExportKwhOrNull; + final fromGrid = data.gridImportKwhOrNull; return Column( children: [ @@ -40,7 +51,9 @@ class KpiRow extends StatelessWidget { children: [ Expanded( child: _ValueTile( - label: 'Vers réseau · aujourd\'hui', + // « aujourd'hui » était faux : `totalReturn` est cumulé depuis + // l'origine et n'est pas remis à zéro chaque nuit. + label: 'Vers réseau · cumulé', value: toGrid, icon: Icons.north_west_rounded, color: EtmTokens.ecoOf(context), @@ -49,7 +62,7 @@ class KpiRow extends StatelessWidget { const SizedBox(width: 10), Expanded( child: _ValueTile( - label: 'Depuis réseau · aujourd\'hui', + label: 'Depuis réseau · cumulé', value: fromGrid, icon: Icons.south_east_rounded, color: EtmTokens.importColor, @@ -161,7 +174,13 @@ class _RingTileState extends State<_RingTile> class _ValueTile extends StatelessWidget { final String label; - final double value; + + /// Valeur en kWh, ou `null` si la box en publie une **inexploitable**. + /// + /// Le cas n'est pas théorique : `.75` publie 9,1e+33 kWh exportés. Imprimer ce nombre + /// le présente comme une mesure ; l'effacer laisse croire à zéro. On affiche donc « — » + /// et on dit d'où vient le trou, ce qui est la seule lecture honnête des trois. + final double? value; final IconData icon; final Color color; @@ -198,16 +217,18 @@ class _ValueTile extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - '${value.toStringAsFixed(1)} kWh', + value == null ? '—' : '${value!.toStringAsFixed(1)} kWh', style: EtmTokens.mono( size: 16, weight: FontWeight.w600, - color: EtmTokens.inkOf(context), + color: value == null + ? EtmTokens.mutedOf(context) + : EtmTokens.inkOf(context), ), ), const SizedBox(height: 3), Text( - label, + value == null ? '$label · compteur box invalide' : label, style: EtmTokens.sans( size: 10.5, color: EtmTokens.mutedOf(context), diff --git a/lib/models/energy_data.dart b/lib/models/energy_data.dart index b4cfb30..87aa4ae 100644 --- a/lib/models/energy_data.dart +++ b/lib/models/energy_data.dart @@ -11,6 +11,19 @@ class EnergyData { final double dayProductionWh; final double daySelfConsumptionWh; final double dayGridInjectionWh; + /// Compteurs CUMULÉS de la box (kWh), tels qu'elle les publie — pas des totaux du jour. + /// + /// `Energy.GetPowerBalance` rend `totalReturn` (exporté) et `totalAcquisition` + /// (importé). Ils sont **cumulés depuis toujours**, pas remis à zéro chaque nuit : les + /// afficher sous un libellé « aujourd'hui » serait faux. + /// + /// `NaN` signifie **valeur non exploitable**, et c'est un état à part entière : le banc + /// `.75` publie des compteurs manifestement corrompus (7,5e+33 kWh importés) alors que + /// `totalProduction` reste sain. L'app ne les corrige pas — elle refuse de les + /// présenter comme une mesure (cf. [gridExportKwhOrNull]). + final double gridExportKwh; + final double gridImportKwh; + final double selfConsumptionRate; // % final double autonomyRate; // % final double dayGains; // € @@ -32,6 +45,8 @@ class EnergyData { this.dayProductionWh = 0, this.daySelfConsumptionWh = 0, this.dayGridInjectionWh = 0, + this.gridExportKwh = 0, + this.gridImportKwh = 0, this.selfConsumptionRate = 0, this.autonomyRate = 0, this.dayGains = 0, @@ -52,6 +67,8 @@ class EnergyData { double? dayProductionWh, double? daySelfConsumptionWh, double? dayGridInjectionWh, + double? gridExportKwh, + double? gridImportKwh, double? selfConsumptionRate, double? autonomyRate, double? dayGains, @@ -71,6 +88,8 @@ class EnergyData { dayProductionWh: dayProductionWh ?? this.dayProductionWh, daySelfConsumptionWh: daySelfConsumptionWh ?? this.daySelfConsumptionWh, dayGridInjectionWh: dayGridInjectionWh ?? this.dayGridInjectionWh, + gridExportKwh: gridExportKwh ?? this.gridExportKwh, + gridImportKwh: gridImportKwh ?? this.gridImportKwh, selfConsumptionRate: selfConsumptionRate ?? this.selfConsumptionRate, autonomyRate: autonomyRate ?? this.autonomyRate, dayGains: dayGains ?? this.dayGains, @@ -80,6 +99,26 @@ class EnergyData { gridLimitOk: gridLimitOk ?? this.gridLimitOk, ); } + + /// Plafond de plausibilité pour un compteur d'énergie domestique cumulé, en kWh. + /// + /// 10 GWh, soit environ **trois mille ans** de consommation d'une maison. Aucune + /// installation résidentielle ne l'atteindra ; tout ce qui le dépasse est un compteur + /// cassé, pas un record. Le seuil est volontairement absurde-haut : il ne s'agit pas de + /// juger une valeur, seulement d'attraper les corrompues. + static const double kPlausibleMaxKwh = 1e7; + + /// Vrai si [v] peut être présenté comme une mesure. + static bool isPlausibleKwh(double v) => + v.isFinite && v >= 0 && v <= kPlausibleMaxKwh; + + /// Export cumulé, ou `null` si la box publie une valeur inexploitable. + double? get gridExportKwhOrNull => + isPlausibleKwh(gridExportKwh) ? gridExportKwh : null; + + /// Import cumulé, ou `null` si la box publie une valeur inexploitable. + double? get gridImportKwhOrNull => + isPlausibleKwh(gridImportKwh) ? gridImportKwh : null; } enum ChargingMode { pv, minPv, boost } @@ -98,6 +137,7 @@ class HistoryPoint { this.gridWh = 0, this.batteryWh = 0, }); + } // ── Entrée Energy.GetPowerBalanceLogs ───────────────────────────────────────── @@ -123,4 +163,5 @@ class PowerBalanceEntry { this.totalReturnWh = 0, this.totalAcquisitionWh = 0, }); + } \ No newline at end of file diff --git a/lib/models/thing_category.dart b/lib/models/thing_category.dart index 9bad398..07f694b 100644 --- a/lib/models/thing_category.dart +++ b/lib/models/thing_category.dart @@ -102,7 +102,7 @@ const Map interfaceToCategoryMap = { // Infos visuelles par catégorie // ───────────────────────────────────────────────────────────────────────────── -const Map categoryInfoMap = { +Map categoryInfoMap = { ThingCategory.energy: ThingCategoryInfo( category: ThingCategory.energy, label: 'Compteurs & Prises', diff --git a/lib/screens/ac_screen.dart b/lib/screens/ac_screen.dart index 8ae6e33..10ebca5 100644 --- a/lib/screens/ac_screen.dart +++ b/lib/screens/ac_screen.dart @@ -103,12 +103,12 @@ class _ACScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: EtmTokens.bg, + backgroundColor: EtmTokens.bgOf(context), body: CustomScrollView( slivers: [ SliverAppBar( floating: true, - backgroundColor: EtmTokens.bg, + backgroundColor: EtmTokens.bgOf(context), elevation: 0, leading: const DrawerMenuButton(), leadingWidth: 64, @@ -118,8 +118,8 @@ class _ACScreenState extends State { Padding( padding: const EdgeInsets.only(right: 8), child: IconButton( - icon: const Icon(Icons.schedule_rounded, - color: EtmTokens.muted, size: 22), + icon: Icon(Icons.schedule_rounded, + color: EtmTokens.mutedOf(context), size: 22), onPressed: () {}, tooltip: 'Planning hebdomadaire', ), @@ -273,9 +273,9 @@ class _ActiveZoneCard extends StatelessWidget { ], ), - const Padding( + Padding( padding: EdgeInsets.symmetric(vertical: 14), - child: Divider(height: 1, color: EtmTokens.line), + child: Divider(height: 1, color: EtmTokens.lineOf(context)), ), // Températures @@ -285,16 +285,16 @@ class _ActiveZoneCard extends StatelessWidget { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('Actuelle', style: EtmTokens.sans(size: 11, color: EtmTokens.muted)), + Text('Actuelle', style: EtmTokens.sans(size: 11, color: EtmTokens.mutedOf(context))), Text('${zone.currentTemp.toStringAsFixed(1)}°C', style: EtmTokens.mono(size: 28, weight: FontWeight.w700)), ], ), - Icon(Icons.arrow_forward_rounded, color: EtmTokens.faint, size: 20), + Icon(Icons.arrow_forward_rounded, color: EtmTokens.faintOf(context), size: 20), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ - Text('Cible', style: EtmTokens.sans(size: 11, color: EtmTokens.muted)), + Text('Cible', style: EtmTokens.sans(size: 11, color: EtmTokens.mutedOf(context))), Row( children: [ _TempButton( @@ -338,10 +338,10 @@ class _ActiveZoneCard extends StatelessWidget { duration: const Duration(milliseconds: 180), padding: const EdgeInsets.symmetric(vertical: 8), decoration: BoxDecoration( - color: sel ? mcol.withValues(alpha: 0.12) : EtmTokens.bg, + color: sel ? mcol.withValues(alpha: 0.12) : EtmTokens.bgOf(context), borderRadius: BorderRadius.circular(10), border: Border.all( - color: sel ? mcol : EtmTokens.line, + color: sel ? mcol : EtmTokens.lineOf(context), width: sel ? 1.5 : 1, ), ), @@ -349,12 +349,12 @@ class _ActiveZoneCard extends StatelessWidget { children: [ Icon(_modeIcon(m), size: 18, - color: sel ? mcol : EtmTokens.faint), + color: sel ? mcol : EtmTokens.faintOf(context)), const SizedBox(height: 3), Text(_modeLabelShort(m), style: EtmTokens.sans( size: 10, - color: sel ? mcol : EtmTokens.muted, + color: sel ? mcol : EtmTokens.mutedOf(context), weight: sel ? FontWeight.w600 : FontWeight.w400)), ], ), @@ -393,10 +393,10 @@ class _InactiveZoneCard extends StatelessWidget { Container( width: 40, height: 40, decoration: BoxDecoration( - color: EtmTokens.bg, + color: EtmTokens.bgOf(context), borderRadius: BorderRadius.circular(12), ), - child: Icon(_modeIcon(zone.mode), color: EtmTokens.faint, size: 22), + child: Icon(_modeIcon(zone.mode), color: EtmTokens.faintOf(context), size: 22), ), const SizedBox(width: 14), Expanded( @@ -405,7 +405,7 @@ class _InactiveZoneCard extends StatelessWidget { children: [ Text(zone.name, style: EtmTokens.sans(size: 15, weight: FontWeight.w600)), - Text('Éteint', style: EtmTokens.sans(size: 12, color: EtmTokens.faint)), + Text('Éteint', style: EtmTokens.sans(size: 12, color: EtmTokens.faintOf(context))), ], ), ), @@ -457,7 +457,7 @@ class _PACCard extends StatelessWidget { Text('PAC Atlantic', style: EtmTokens.sans(size: 15, weight: FontWeight.w600)), Text('Source de chauffage · SG-Ready', - style: EtmTokens.sans(size: 11, color: EtmTokens.muted)), + style: EtmTokens.sans(size: 11, color: EtmTokens.mutedOf(context))), ], ), ), @@ -479,10 +479,10 @@ class _PACCard extends StatelessWidget { duration: const Duration(milliseconds: 180), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), decoration: BoxDecoration( - color: sgAuto ? EtmTokens.greenSoft : EtmTokens.bg, + color: sgAuto ? EtmTokens.greenSoft : EtmTokens.bgOf(context), borderRadius: BorderRadius.circular(99), border: Border.all( - color: sgAuto ? EtmTokens.green : EtmTokens.line, + color: sgAuto ? EtmTokens.green : EtmTokens.lineOf(context), ), ), child: Row( @@ -490,13 +490,13 @@ class _PACCard extends StatelessWidget { children: [ Icon(Icons.auto_awesome_rounded, size: 12, - color: sgAuto ? EtmTokens.green : EtmTokens.muted), + color: sgAuto ? EtmTokens.green : EtmTokens.mutedOf(context)), const SizedBox(width: 4), Text('Auto', style: EtmTokens.sans( size: 11, weight: FontWeight.w600, - color: sgAuto ? EtmTokens.green : EtmTokens.muted)), + color: sgAuto ? EtmTokens.green : EtmTokens.mutedOf(context))), ], ), ), @@ -512,7 +512,7 @@ class _PACCard extends StatelessWidget { final sel = !sgAuto && sgState == s; final (label, color) = switch (s) { SGReadyState.blocked => ('Bloqué', EtmTokens.danger), - SGReadyState.normal => ('Normal', EtmTokens.muted), + SGReadyState.normal => ('Normal', EtmTokens.mutedOf(context)), SGReadyState.recommended => ('Recommandé', EtmTokens.amber), SGReadyState.forced => ('Forcé', EtmTokens.green), }; @@ -526,13 +526,13 @@ class _PACCard extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 9), decoration: BoxDecoration( color: isBlocked - ? EtmTokens.bg + ? EtmTokens.bgOf(context) : sel ? color.withValues(alpha: 0.14) - : EtmTokens.bg, + : EtmTokens.bgOf(context), borderRadius: BorderRadius.circular(10), border: Border.all( - color: sel ? color : EtmTokens.line, + color: sel ? color : EtmTokens.lineOf(context), width: sel ? 1.5 : 1, ), ), @@ -543,8 +543,8 @@ class _PACCard extends StatelessWidget { size: 11, weight: sel ? FontWeight.w600 : FontWeight.w400, color: isBlocked - ? EtmTokens.faint - : sel ? color : EtmTokens.muted, + ? EtmTokens.faintOf(context) + : sel ? color : EtmTokens.mutedOf(context), ), ), ), @@ -618,7 +618,7 @@ class _DHWCard extends StatelessWidget { Text('Chauffe-eau', style: EtmTokens.sans(size: 15, weight: FontWeight.w600)), Text('Ballon thermodynamique', - style: EtmTokens.sans(size: 11, color: EtmTokens.muted)), + style: EtmTokens.sans(size: 11, color: EtmTokens.mutedOf(context))), ], ), ), @@ -626,9 +626,9 @@ class _DHWCard extends StatelessWidget { ], ), - const Padding( + Padding( padding: EdgeInsets.symmetric(vertical: 14), - child: Divider(height: 1, color: EtmTokens.line), + child: Divider(height: 1, color: EtmTokens.lineOf(context)), ), // Températures @@ -639,16 +639,16 @@ class _DHWCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Température eau', - style: EtmTokens.sans(size: 11, color: EtmTokens.muted)), + style: EtmTokens.sans(size: 11, color: EtmTokens.mutedOf(context))), Text('${currentTemp.toStringAsFixed(0)}°C', style: EtmTokens.mono(size: 28, weight: FontWeight.w700)), ], ), - Icon(Icons.arrow_forward_rounded, color: EtmTokens.faint, size: 20), + Icon(Icons.arrow_forward_rounded, color: EtmTokens.faintOf(context), size: 20), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ - Text('Cible', style: EtmTokens.sans(size: 11, color: EtmTokens.muted)), + Text('Cible', style: EtmTokens.sans(size: 11, color: EtmTokens.mutedOf(context))), Row( children: [ _TempButton( @@ -692,22 +692,22 @@ class _DHWCard extends StatelessWidget { duration: const Duration(milliseconds: 180), padding: const EdgeInsets.symmetric(vertical: 10), decoration: BoxDecoration( - color: sel ? mc.withValues(alpha: 0.12) : EtmTokens.bg, + color: sel ? mc.withValues(alpha: 0.12) : EtmTokens.bgOf(context), borderRadius: BorderRadius.circular(12), border: Border.all( - color: sel ? mc : EtmTokens.line, + color: sel ? mc : EtmTokens.lineOf(context), width: sel ? 1.5 : 1, ), ), child: Column( children: [ - Icon(mi, size: 20, color: sel ? mc : EtmTokens.faint), + Icon(mi, size: 20, color: sel ? mc : EtmTokens.faintOf(context)), const SizedBox(height: 4), Text(ml, style: EtmTokens.sans( size: 12, weight: sel ? FontWeight.w600 : FontWeight.w400, - color: sel ? mc : EtmTokens.muted)), + color: sel ? mc : EtmTokens.mutedOf(context))), ], ), ), @@ -771,16 +771,16 @@ class _ClimCard extends StatelessWidget { Text('Climatiseur', style: EtmTokens.sans(size: 15, weight: FontWeight.w600)), Text('Rafraîchissement anticipé', - style: EtmTokens.sans(size: 11, color: EtmTokens.muted)), + style: EtmTokens.sans(size: 11, color: EtmTokens.mutedOf(context))), ], ), ), ], ), - const Padding( + Padding( padding: EdgeInsets.symmetric(vertical: 14), - child: Divider(height: 1, color: EtmTokens.line), + child: Divider(height: 1, color: EtmTokens.lineOf(context)), ), // Températures @@ -790,16 +790,16 @@ class _ClimCard extends StatelessWidget { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('Actuelle', style: EtmTokens.sans(size: 11, color: EtmTokens.muted)), + Text('Actuelle', style: EtmTokens.sans(size: 11, color: EtmTokens.mutedOf(context))), Text('${currentTemp.toStringAsFixed(1)}°C', style: EtmTokens.mono(size: 28, weight: FontWeight.w700)), ], ), - const Icon(Icons.arrow_forward_rounded, color: EtmTokens.faint, size: 20), + Icon(Icons.arrow_forward_rounded, color: EtmTokens.faintOf(context), size: 20), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ - Text('Cible', style: EtmTokens.sans(size: 11, color: EtmTokens.muted)), + Text('Cible', style: EtmTokens.sans(size: 11, color: EtmTokens.mutedOf(context))), Row( children: [ _TempButton( @@ -849,14 +849,14 @@ class _ClimCard extends StatelessWidget { Text( 'Sur surplus solaire, pour traverser le pic tarifaire ' '17h–21h sans clim payante.', - style: EtmTokens.sans(size: 11, color: EtmTokens.muted), + style: EtmTokens.sans(size: 11, color: EtmTokens.mutedOf(context)), ), const SizedBox(height: 8), Row( children: [ Text('820 W', style: EtmTokens.mono(size: 12, color: EtmTokens.blue)), Text(' · 100% solaire', - style: EtmTokens.sans(size: 12, color: EtmTokens.muted)), + style: EtmTokens.sans(size: 12, color: EtmTokens.mutedOf(context))), ], ), ], @@ -879,7 +879,7 @@ class _Card extends StatelessWidget { Widget build(BuildContext context) { return Container( decoration: BoxDecoration( - color: EtmTokens.card, + color: EtmTokens.surfaceOf(context), borderRadius: BorderRadius.circular(EtmTokens.radiusLg), boxShadow: EtmTokens.cardShadow, ), @@ -991,7 +991,7 @@ class _SurplusInfo extends StatelessWidget { Expanded( child: RichText( text: TextSpan( - style: EtmTokens.sans(size: 11, color: EtmTokens.muted), + style: EtmTokens.sans(size: 11, color: EtmTokens.mutedOf(context)), children: [ TextSpan( text: '$label ', @@ -1010,7 +1010,7 @@ class _SurplusInfo extends StatelessWidget { Text('${powerKw.toStringAsFixed(1)} kW', style: EtmTokens.mono(size: 11, color: EtmTokens.green)), Text('$solarPct% solaire', - style: EtmTokens.sans(size: 10, color: EtmTokens.muted)), + style: EtmTokens.sans(size: 10, color: EtmTokens.mutedOf(context))), ], ), ], diff --git a/lib/screens/drawer/main_drawer.dart b/lib/screens/drawer/main_drawer.dart index 02dfb3b..9d2c36a 100644 --- a/lib/screens/drawer/main_drawer.dart +++ b/lib/screens/drawer/main_drawer.dart @@ -384,8 +384,8 @@ class _LinkItem extends StatelessWidget { return _DrawerTile( icon: icon, label: label, - trailing: const Icon(Icons.open_in_new_rounded, - size: 14, color: EtmTokens.faint), + trailing: Icon(Icons.open_in_new_rounded, + size: 14, color: EtmTokens.faintOf(context)), onTap: () async { context.read().closeDrawer(); final uri = Uri.parse(url); @@ -494,8 +494,8 @@ class _StyledExpansion extends StatelessWidget { child: ExpansionTile( leading: Icon(icon, size: 20, color: Colors.white.withValues(alpha: 0.92)), title: Text(label, style: EtmTokens.sans(size: 14, color: Colors.white.withValues(alpha: 0.92))), - iconColor: EtmTokens.faint, - collapsedIconColor: EtmTokens.faint, + iconColor: EtmTokens.faintOf(context), + collapsedIconColor: EtmTokens.faintOf(context), tilePadding: const EdgeInsets.symmetric(horizontal: 20), childrenPadding: EdgeInsets.zero, children: children, @@ -518,7 +518,7 @@ class _SubItem extends StatelessWidget { }, child: Padding( padding: const EdgeInsets.fromLTRB(56, 10, 16, 10), - child: Text(label, style: EtmTokens.sans(size: 13, color: EtmTokens.faint)), + child: Text(label, style: EtmTokens.sans(size: 13, color: EtmTokens.faintOf(context))), ), ); } diff --git a/lib/screens/energy_screen.dart b/lib/screens/energy_screen.dart index 381811c..67b904c 100644 --- a/lib/screens/energy_screen.dart +++ b/lib/screens/energy_screen.dart @@ -153,13 +153,13 @@ class _EnergyScreenState extends State { return Consumer( builder: (context, service, _) { return Scaffold( - backgroundColor: EtmTokens.bg, + backgroundColor: EtmTokens.bgOf(context), body: CustomScrollView( slivers: [ // AppBar SliverAppBar( floating: true, - backgroundColor: EtmTokens.bg, + backgroundColor: EtmTokens.bgOf(context), elevation: 0, leading: const DrawerMenuButton(), leadingWidth: 64, @@ -169,7 +169,7 @@ class _EnergyScreenState extends State { IconButton( icon: Icon( Icons.calendar_today_rounded, - color: _selectedDate != null ? EtmTokens.blue : EtmTokens.muted, + color: _selectedDate != null ? EtmTokens.blue : EtmTokens.mutedOf(context), size: 20, ), onPressed: _pickDate, @@ -177,8 +177,8 @@ class _EnergyScreenState extends State { Padding( padding: const EdgeInsets.only(right: 8), child: IconButton( - icon: const Icon(Icons.refresh_rounded, - color: EtmTokens.muted, size: 20), + icon: Icon(Icons.refresh_rounded, + color: EtmTokens.mutedOf(context), size: 20), onPressed: _fetch, ), ), @@ -302,7 +302,7 @@ class _EnergyScreenState extends State { } final xInterval = _xInterval(); - final labelStyle = EtmTokens.sans(size: 9, color: EtmTokens.muted); + final labelStyle = EtmTokens.sans(size: 9, color: EtmTokens.mutedOf(context)); final yInterval = yMax / 4; // 5 graduations : 0, 25%, 50%, 75%, 100% de yMax return LineChart( @@ -315,7 +315,7 @@ class _EnergyScreenState extends State { drawVerticalLine: false, horizontalInterval: yInterval, getDrawingHorizontalLine: (_) => - FlLine(color: EtmTokens.line, strokeWidth: 1), + FlLine(color: EtmTokens.lineOf(context), strokeWidth: 1), ), borderData: FlBorderData(show: false), titlesData: FlTitlesData( @@ -450,7 +450,7 @@ class _EnergyScreenState extends State { )); } - final labelStyle = EtmTokens.sans(size: 9, color: EtmTokens.muted); + final labelStyle = EtmTokens.sans(size: 9, color: EtmTokens.mutedOf(context)); return BarChart( BarChartData( @@ -468,7 +468,7 @@ class _EnergyScreenState extends State { gridData: FlGridData( show: true, drawVerticalLine: false, - getDrawingHorizontalLine: (_) => FlLine(color: EtmTokens.line, strokeWidth: 1), + getDrawingHorizontalLine: (_) => FlLine(color: EtmTokens.lineOf(context), strokeWidth: 1), ), borderData: FlBorderData(show: false), titlesData: FlTitlesData( @@ -606,7 +606,7 @@ class _KpiTile extends StatelessWidget { child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: EtmTokens.card, + color: EtmTokens.surfaceOf(context), borderRadius: BorderRadius.circular(EtmTokens.radius), boxShadow: EtmTokens.cardShadow, ), @@ -622,7 +622,7 @@ class _KpiTile extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(label, style: EtmTokens.sans(size: 11, color: EtmTokens.muted)), + Text(label, style: EtmTokens.sans(size: 11, color: EtmTokens.mutedOf(context))), const SizedBox(height: 2), Text(value, style: EtmTokens.mono(size: 15, weight: FontWeight.w700, color: color), @@ -656,7 +656,7 @@ class _PeriodSelector extends StatelessWidget { return Container( padding: const EdgeInsets.all(4), decoration: BoxDecoration( - color: EtmTokens.card, + color: EtmTokens.surfaceOf(context), borderRadius: BorderRadius.circular(EtmTokens.radius), boxShadow: EtmTokens.cardShadow, ), @@ -679,7 +679,7 @@ class _PeriodSelector extends StatelessWidget { style: EtmTokens.sans( size: 13, weight: selected ? FontWeight.w600 : FontWeight.w400, - color: selected ? Colors.white : EtmTokens.muted, + color: selected ? Colors.white : EtmTokens.mutedOf(context), ), ), ), @@ -758,7 +758,7 @@ class _EtmChartCard extends StatelessWidget { return Container( padding: const EdgeInsets.all(18), decoration: BoxDecoration( - color: EtmTokens.card, + color: EtmTokens.surfaceOf(context), borderRadius: BorderRadius.circular(EtmTokens.radiusLg), boxShadow: EtmTokens.cardShadow, ), @@ -774,7 +774,7 @@ class _EtmChartCard extends StatelessWidget { Text(title, style: EtmTokens.sans(size: 16, weight: FontWeight.w600)), Text(subtitle, - style: EtmTokens.sans(size: 11, color: EtmTokens.muted)), + style: EtmTokens.sans(size: 11, color: EtmTokens.mutedOf(context))), ], ), ], @@ -793,10 +793,10 @@ class _EtmChartCard extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.bar_chart_rounded, - color: EtmTokens.faint, size: 32), + color: EtmTokens.faintOf(context), size: 32), const SizedBox(height: 8), Text('Aucune donnée', - style: EtmTokens.sans(size: 13, color: EtmTokens.muted)), + style: EtmTokens.sans(size: 13, color: EtmTokens.mutedOf(context))), ], ), ) @@ -833,7 +833,7 @@ class _LegendDot extends StatelessWidget { decoration: BoxDecoration( color: color, borderRadius: BorderRadius.circular(2))), const SizedBox(width: 5), - Text(label, style: EtmTokens.sans(size: 11, color: EtmTokens.muted)), + Text(label, style: EtmTokens.sans(size: 11, color: EtmTokens.mutedOf(context))), ], ); } @@ -849,7 +849,7 @@ class _ForecastPlaceholder extends StatelessWidget { return Container( padding: const EdgeInsets.all(18), decoration: BoxDecoration( - color: EtmTokens.card, + color: EtmTokens.surfaceOf(context), borderRadius: BorderRadius.circular(EtmTokens.radiusLg), boxShadow: EtmTokens.cardShadow, ), @@ -862,19 +862,19 @@ class _ForecastPlaceholder extends StatelessWidget { Text('Météo & prévision', style: EtmTokens.sans(size: 16, weight: FontWeight.w600)), Text('aujourd\'hui', - style: EtmTokens.sans(size: 12, color: EtmTokens.muted)), + style: EtmTokens.sans(size: 12, color: EtmTokens.mutedOf(context))), ], ), const SizedBox(height: 14), Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: EtmTokens.bg, + color: EtmTokens.bgOf(context), borderRadius: BorderRadius.circular(12), ), child: Row( children: [ - const Icon(Icons.cloud_outlined, color: EtmTokens.faint, size: 30), + Icon(Icons.cloud_outlined, color: EtmTokens.faintOf(context), size: 30), const SizedBox(width: 14), Expanded( child: Column( @@ -882,12 +882,12 @@ class _ForecastPlaceholder extends StatelessWidget { children: [ Text('Prévisions non disponibles', style: EtmTokens.sans(size: 13, weight: FontWeight.w500, - color: EtmTokens.muted)), + color: EtmTokens.mutedOf(context))), const SizedBox(height: 4), Text( 'Configurez un provider dans Tarifs & Héos ' 'pour activer les prévisions PV et tarifaires.', - style: EtmTokens.sans(size: 11, color: EtmTokens.faint)), + style: EtmTokens.sans(size: 11, color: EtmTokens.faintOf(context))), ], ), ), diff --git a/lib/screens/favorites_screen.dart b/lib/screens/favorites_screen.dart index e613fb7..8d8f4f4 100644 --- a/lib/screens/favorites_screen.dart +++ b/lib/screens/favorites_screen.dart @@ -33,12 +33,12 @@ class _FavoritesScreenState extends State { final favs = service.favoriteWidgets; return Scaffold( - backgroundColor: EtmTokens.bg, + backgroundColor: EtmTokens.bgOf(context), body: CustomScrollView( slivers: [ SliverAppBar( floating: true, - backgroundColor: EtmTokens.bg, + backgroundColor: EtmTokens.bgOf(context), elevation: 0, leading: const DrawerMenuButton(), leadingWidth: 64, @@ -143,7 +143,7 @@ class _FavoriteCard extends StatelessWidget { Widget build(BuildContext context) { return Container( decoration: BoxDecoration( - color: EtmTokens.card, + color: EtmTokens.surfaceOf(context), borderRadius: BorderRadius.circular(EtmTokens.radiusLg), boxShadow: EtmTokens.cardShadow, ), @@ -154,20 +154,20 @@ class _FavoriteCard extends StatelessWidget { // En-tête : poignée + titre + supprimer Row( children: [ - const Icon(Icons.drag_handle_rounded, - color: EtmTokens.faint, size: 20), + Icon(Icons.drag_handle_rounded, + color: EtmTokens.faintOf(context), size: 20), const SizedBox(width: 8), Expanded( child: Text(fav.title, style: EtmTokens.sans( size: 14, weight: FontWeight.w600, - color: EtmTokens.muted)), + color: EtmTokens.mutedOf(context))), ), GestureDetector( onTap: onRemove, - child: const Icon(Icons.close_rounded, - color: EtmTokens.faint, size: 18), + child: Icon(Icons.close_rounded, + color: EtmTokens.faintOf(context), size: 18), ), ], ), @@ -263,11 +263,11 @@ class _BigMetric extends StatelessWidget { Padding( padding: const EdgeInsets.only(bottom: 5, left: 4), child: Text(unit, - style: EtmTokens.sans(size: 15, color: EtmTokens.muted)), + style: EtmTokens.sans(size: 15, color: EtmTokens.mutedOf(context))), ), ], ), - Text(label, style: EtmTokens.sans(size: 12, color: EtmTokens.muted)), + Text(label, style: EtmTokens.sans(size: 12, color: EtmTokens.mutedOf(context))), const SizedBox(height: 2), Text(sub, style: EtmTokens.sans( @@ -329,7 +329,7 @@ class _BatteryWidget extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('État de charge', - style: EtmTokens.sans(size: 13, color: EtmTokens.muted)), + style: EtmTokens.sans(size: 13, color: EtmTokens.mutedOf(context))), const SizedBox(height: 6), Text( isCharging @@ -343,7 +343,7 @@ class _BatteryWidget extends StatelessWidget { const SizedBox(height: 4), Text( '${data.batteryPower.abs().toStringAsFixed(0)} W', - style: EtmTokens.sans(size: 12, color: EtmTokens.muted), + style: EtmTokens.sans(size: 12, color: EtmTokens.mutedOf(context)), ), ], ), @@ -409,7 +409,7 @@ class _RateTile extends StatelessWidget { ), const SizedBox(height: 6), Text(label, - style: EtmTokens.sans(size: 11, color: EtmTokens.muted), + style: EtmTokens.sans(size: 11, color: EtmTokens.mutedOf(context)), textAlign: TextAlign.center), ], ); @@ -451,7 +451,7 @@ class _EVWidget extends StatelessWidget { style: EtmTokens.sans(size: 12, color: color)), Text( '${data.solarSourcePercent.toStringAsFixed(0)}% solaire', - style: EtmTokens.sans(size: 11, color: EtmTokens.muted)), + style: EtmTokens.sans(size: 11, color: EtmTokens.mutedOf(context))), ], ), ), @@ -543,7 +543,7 @@ class _MiniChart extends StatelessWidget { height: 64, child: Center( child: Text('Pas de données', - style: EtmTokens.sans(size: 12, color: EtmTokens.muted)), + style: EtmTokens.sans(size: 12, color: EtmTokens.mutedOf(context))), ), ); } @@ -603,7 +603,7 @@ class _EmptyState extends StatelessWidget { size: 18, weight: FontWeight.w600)), const SizedBox(height: 8), Text('Ajoutez des widgets pour un accès rapide', - style: EtmTokens.sans(size: 14, color: EtmTokens.muted)), + style: EtmTokens.sans(size: 14, color: EtmTokens.mutedOf(context))), const SizedBox(height: 24), GestureDetector( onTap: onAdd, @@ -652,8 +652,8 @@ class _AddSheet extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - decoration: const BoxDecoration( - color: EtmTokens.card, + decoration: BoxDecoration( + color: EtmTokens.surfaceOf(context), borderRadius: BorderRadius.vertical(top: Radius.circular(24)), ), child: DraggableScrollableSheet( @@ -668,7 +668,7 @@ class _AddSheet extends StatelessWidget { margin: const EdgeInsets.only(top: 12, bottom: 18), width: 40, height: 4, decoration: BoxDecoration( - color: EtmTokens.line, + color: EtmTokens.lineOf(context), borderRadius: BorderRadius.circular(2)), ), Padding( @@ -681,8 +681,8 @@ class _AddSheet extends StatelessWidget { size: 18, weight: FontWeight.w600)), GestureDetector( onTap: () => Navigator.pop(context), - child: const Icon(Icons.close_rounded, - color: EtmTokens.faint), + child: Icon(Icons.close_rounded, + color: EtmTokens.faintOf(context)), ), ], ), @@ -713,12 +713,12 @@ class _AddSheet extends StatelessWidget { child: Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( - color: added ? EtmTokens.bg : EtmTokens.card, + color: added ? EtmTokens.bgOf(context) : EtmTokens.surfaceOf(context), borderRadius: BorderRadius.circular(EtmTokens.radius), border: Border.all( color: added - ? EtmTokens.line + ? EtmTokens.lineOf(context) : color.withValues(alpha: 0.3), ), boxShadow: added ? null : EtmTokens.cardShadow, @@ -732,7 +732,7 @@ class _AddSheet extends StatelessWidget { borderRadius: BorderRadius.circular(12), ), child: Icon(icon, - color: added ? EtmTokens.faint : color, + color: added ? EtmTokens.faintOf(context) : color, size: 22), ), const SizedBox(width: 14), @@ -745,12 +745,12 @@ class _AddSheet extends StatelessWidget { size: 14, weight: FontWeight.w600, color: added - ? EtmTokens.faint + ? EtmTokens.faintOf(context) : EtmTokens.navy)), Text(subtitle, style: EtmTokens.sans( size: 11, - color: EtmTokens.muted)), + color: EtmTokens.mutedOf(context))), ], ), ), diff --git a/lib/screens/things_screen.dart b/lib/screens/things_screen.dart index 7053e03..506dc90 100644 --- a/lib/screens/things_screen.dart +++ b/lib/screens/things_screen.dart @@ -62,13 +62,13 @@ class _ThingsScreenState extends State { final offlineCount = things.where((t) => !_isOnline(t, _classFor(t, classes))).length; return Scaffold( - backgroundColor: EtmTokens.bg, + backgroundColor: EtmTokens.bgOf(context), body: CustomScrollView( slivers: [ // AppBar SliverAppBar( floating: true, - backgroundColor: EtmTokens.bg, + backgroundColor: EtmTokens.bgOf(context), elevation: 0, leading: const DrawerMenuButton(), leadingWidth: 64, @@ -94,8 +94,8 @@ class _ThingsScreenState extends State { Padding( padding: const EdgeInsets.only(right: 8), child: IconButton( - icon: const Icon(Icons.refresh_rounded, - color: EtmTokens.muted, size: 20), + icon: Icon(Icons.refresh_rounded, + color: EtmTokens.mutedOf(context), size: 20), onPressed: service.refresh, ), ), @@ -197,14 +197,14 @@ class _StatusBanner extends StatelessWidget { dotColor = EtmTokens.orange; text = '$offlineCount appareil(s) hors ligne'; } else { - dotColor = EtmTokens.faint; + dotColor = EtmTokens.faintOf(context); text = 'Aucun appareil connecté'; } return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( - color: EtmTokens.card, + color: EtmTokens.surfaceOf(context), borderRadius: BorderRadius.circular(EtmTokens.radius), boxShadow: EtmTokens.cardShadow, ), @@ -217,7 +217,7 @@ class _StatusBanner extends StatelessWidget { const SizedBox(width: 10), Expanded( child: Text(text, - style: EtmTokens.sans(size: 13, color: EtmTokens.muted)), + style: EtmTokens.sans(size: 13, color: EtmTokens.mutedOf(context))), ), ], ), @@ -334,7 +334,7 @@ class _CategoryCard extends StatelessWidget { // Container en dehors de Material pour que l'ombre soit visible return Container( decoration: BoxDecoration( - color: EtmTokens.card, + color: EtmTokens.surfaceOf(context), borderRadius: BorderRadius.circular(EtmTokens.radius), boxShadow: EtmTokens.cardShadow, ), @@ -379,7 +379,7 @@ class _CategoryCard extends StatelessWidget { ), Text( '$count appareil${count > 1 ? 's' : ''}', - style: EtmTokens.sans(size: 11, color: EtmTokens.muted), + style: EtmTokens.sans(size: 11, color: EtmTokens.mutedOf(context)), ), ], ), @@ -431,7 +431,7 @@ class _ThingRow extends StatelessWidget { decoration: BoxDecoration( border: isLast ? null - : Border(bottom: BorderSide(color: EtmTokens.line)), + : Border(bottom: BorderSide(color: EtmTokens.lineOf(context))), borderRadius: isLast ? const BorderRadius.vertical(bottom: Radius.circular(EtmTokens.radius)) : null, @@ -441,7 +441,7 @@ class _ThingRow extends StatelessWidget { Container( width: 7, height: 7, decoration: BoxDecoration( - color: isOnline ? color : EtmTokens.faint, + color: isOnline ? color : EtmTokens.faintOf(context), shape: BoxShape.circle, ), ), @@ -461,7 +461,7 @@ class _ThingRow extends StatelessWidget { style: EtmTokens.mono( size: 12, weight: FontWeight.w700, - color: isOnline ? color : EtmTokens.muted, + color: isOnline ? color : EtmTokens.mutedOf(context), ), maxLines: 1, overflow: TextOverflow.ellipsis, @@ -471,7 +471,7 @@ class _ThingRow extends StatelessWidget { isOnline ? 'OK' : 'Veille', style: EtmTokens.sans( size: 12, - color: isOnline ? color : EtmTokens.faint, + color: isOnline ? color : EtmTokens.faintOf(context), ), ), ], @@ -495,9 +495,9 @@ class _Placeholder extends StatelessWidget { child: Column( mainAxisSize: MainAxisSize.min, children: [ - Icon(icon, size: 56, color: EtmTokens.faint), + Icon(icon, size: 56, color: EtmTokens.faintOf(context)), const SizedBox(height: 14), - Text(label, style: EtmTokens.sans(size: 15, color: EtmTokens.muted)), + Text(label, style: EtmTokens.sans(size: 15, color: EtmTokens.mutedOf(context))), ], ), ), diff --git a/lib/services/nymea_service.dart b/lib/services/nymea_service.dart index ef7db4a..4eddfb0 100644 --- a/lib/services/nymea_service.dart +++ b/lib/services/nymea_service.dart @@ -883,6 +883,12 @@ class NymeaService extends ChangeNotifier { batteryPower: bat, dayProductionWh: totalProdKwh * 1000, dayGridInjectionWh: totalRetKwh * 1000, + // Compteurs CUMULÉS, transportés tels quels : ni corrigés, ni recalculés. Leur + // plausibilité se juge à l'affichage (EnergyData.isPlausibleKwh) — l'app ne + // maquille pas une donnée que la box publie fausse, elle refuse de la présenter + // comme une mesure. + gridExportKwh: totalRetKwh, + gridImportKwh: totalAcqKwh, daySelfConsumptionWh: selfConsoKwh * 1000, // n/a (seam → null) mappé à 0 pour le champ non-nullable (UX §2 item 4). selfConsumptionRate: ratios.autoconsommation ?? 0.0, diff --git a/lib/widgets/energy_flow_widget.dart b/lib/widgets/energy_flow_widget.dart index f09bf83..a343a6b 100644 --- a/lib/widgets/energy_flow_widget.dart +++ b/lib/widgets/energy_flow_widget.dart @@ -39,7 +39,7 @@ class _EnergyFlowWidgetState extends State final data = widget.data; return Container( decoration: BoxDecoration( - color: EtmTokens.card, + color: EtmTokens.surfaceOf(context), borderRadius: BorderRadius.circular(EtmTokens.radiusLg), boxShadow: EtmTokens.cardShadow, ), @@ -56,9 +56,9 @@ class _EnergyFlowWidgetState extends State Row( children: [ Text('${data.temperature.toStringAsFixed(0)}°C', - style: EtmTokens.sans(size: 14, color: EtmTokens.muted)), + style: EtmTokens.sans(size: 14, color: EtmTokens.mutedOf(context))), const SizedBox(width: 4), - const Icon(Icons.wb_cloudy_outlined, color: EtmTokens.faint, size: 20), + Icon(Icons.wb_cloudy_outlined, color: EtmTokens.faintOf(context), size: 20), ], ), ], @@ -357,7 +357,7 @@ class _Node extends StatelessWidget { color: Colors.white, shape: BoxShape.circle, border: Border.all( - color: borderColor ?? EtmTokens.line, + color: borderColor ?? EtmTokens.lineOf(context), width: 1.5, ), boxShadow: ringColor != null @@ -382,14 +382,14 @@ class _Node extends StatelessWidget { const SizedBox(height: 2), Text(value, style: EtmTokens.mono(size: r * 0.32, color: valueColor)), if (sub != null) - Text(sub!, style: EtmTokens.mono(size: r * 0.26, color: subColor ?? EtmTokens.muted)), + Text(sub!, style: EtmTokens.mono(size: r * 0.26, color: subColor ?? EtmTokens.mutedOf(context))), ], ), ), const SizedBox(height: 4), Text( label, - style: EtmTokens.sans(size: 11, color: EtmTokens.muted), + style: EtmTokens.sans(size: 11, color: EtmTokens.mutedOf(context)), textAlign: TextAlign.center, ), ], @@ -426,11 +426,11 @@ class _OptimalBanner extends StatelessWidget { Text('Mode optimal', style: EtmTokens.sans(size: 14, weight: FontWeight.w600, color: EtmTokens.greenDark)), Text('Héos optimise votre consommation', - style: EtmTokens.sans(size: 12, color: EtmTokens.muted)), + style: EtmTokens.sans(size: 12, color: EtmTokens.mutedOf(context))), ], ), ), - const Icon(Icons.chevron_right, color: EtmTokens.faint, size: 20), + Icon(Icons.chevron_right, color: EtmTokens.faintOf(context), size: 20), ], ), ); diff --git a/lib/widgets/ev_charging_card.dart b/lib/widgets/ev_charging_card.dart index 2a5fffc..ef0b324 100644 --- a/lib/widgets/ev_charging_card.dart +++ b/lib/widgets/ev_charging_card.dart @@ -128,7 +128,7 @@ class _EVChargingCardState extends State { return Container( decoration: BoxDecoration( - color: EtmTokens.card, + color: EtmTokens.surfaceOf(context), borderRadius: BorderRadius.circular(EtmTokens.radiusLg), boxShadow: EtmTokens.cardShadow, ), @@ -155,13 +155,13 @@ class _EVChargingCardState extends State { Text('Borne de recharge (EVSE)', style: EtmTokens.sans(size: 15, weight: FontWeight.w600)), Text(_statusLabel(mode), - style: EtmTokens.sans(size: 12, color: EtmTokens.muted)), + style: EtmTokens.sans(size: 12, color: EtmTokens.mutedOf(context))), ], ), ), GestureDetector( onTap: () => _showSettings(context), - child: const Icon(Icons.tune, color: EtmTokens.faint, size: 20), + child: Icon(Icons.tune, color: EtmTokens.faintOf(context), size: 20), ), ], ), @@ -197,7 +197,7 @@ class _EVChargingCardState extends State { style: EtmTokens.mono(size: 38, weight: FontWeight.w700), ), Text('W Puissance actuelle', - style: EtmTokens.sans(size: 12, color: EtmTokens.muted)), + style: EtmTokens.sans(size: 12, color: EtmTokens.mutedOf(context))), ], ), const Spacer(), @@ -206,7 +206,7 @@ class _EVChargingCardState extends State { children: [ Text('${data.solarSourcePercent.toStringAsFixed(0)}%', style: EtmTokens.mono(size: 22, color: EtmTokens.green)), - Text('solaire', style: EtmTokens.sans(size: 11, color: EtmTokens.muted)), + Text('solaire', style: EtmTokens.sans(size: 11, color: EtmTokens.mutedOf(context))), ], ), ], @@ -321,11 +321,11 @@ class _DeadlineToggleRow extends StatelessWidget { Row( children: [ Icon(Icons.flag_outlined, size: 16, - color: enabled ? EtmTokens.blue : EtmTokens.faint), + color: enabled ? EtmTokens.blue : EtmTokens.faintOf(context)), const SizedBox(width: 6), Text('Cible deadline', style: EtmTokens.sans(size: 13, - color: enabled ? EtmTokens.blue : EtmTokens.muted, + color: enabled ? EtmTokens.blue : EtmTokens.mutedOf(context), weight: enabled ? FontWeight.w600 : FontWeight.w400)), ], ), @@ -480,7 +480,7 @@ class _SocProgress extends StatelessWidget { ], ), const SizedBox(height: 2), - Text('Aujourd\'hui à 07:30', style: EtmTokens.sans(size: 11, color: EtmTokens.muted)), + Text('Aujourd\'hui à 07:30', style: EtmTokens.sans(size: 11, color: EtmTokens.mutedOf(context))), const SizedBox(height: 8), ClipRRect( borderRadius: BorderRadius.circular(99), @@ -621,12 +621,12 @@ class _TimePicker extends StatelessWidget { ), child: Row( children: [ - const Icon(Icons.access_time, size: 16, color: EtmTokens.faint), + Icon(Icons.access_time, size: 16, color: EtmTokens.faintOf(context)), const SizedBox(width: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(label, style: EtmTokens.sans(size: 11, color: EtmTokens.muted)), + Text(label, style: EtmTokens.sans(size: 11, color: EtmTokens.mutedOf(context))), Text( '${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}', style: EtmTokens.mono(size: 13),