import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; /// Décrit un onglet du menu principal (bottom nav). class AppScreen { final String id; final String label; final IconData icon; bool visible; AppScreen({ required this.id, required this.label, required this.icon, this.visible = true, }); AppScreen copyWith({bool? visible}) => AppScreen( id: id, label: label, icon: icon, visible: visible ?? this.visible, ); Map toJson() => {'id': id, 'visible': visible}; } /// Gère les préférences d'apparence et les écrans visibles. class AppSettingsProvider extends ChangeNotifier { static const _themeKey = 'app_theme_mode'; static const _accentKey = 'app_accent_color'; static const _textScaleKey = 'app_text_scale'; static const _screensKey = 'app_screens_config'; static const _densityKey = 'app_ui_density'; ThemeMode _themeMode = ThemeMode.system; int _accentIndex = 0; // 0=ETM blue, 1=vert, 2=orange, 3=violet double _textScale = 1.0; VisualDensity _density = VisualDensity.standard; // Ordre et visibilité des écrans principaux List _screens = [ AppScreen(id: 'dashboard', label: 'Dashboard', icon: Icons.home_rounded), AppScreen(id: 'energy', label: 'Énergie', icon: Icons.bar_chart_rounded), AppScreen(id: 'things', label: 'Things', icon: Icons.device_hub_rounded), AppScreen(id: 'favorites', label: 'Favoris', icon: Icons.star_rounded), AppScreen(id: 'groups', label: 'Groupes', icon: Icons.group_work_rounded, visible: false), AppScreen(id: 'scenes', label: 'Scènes', icon: Icons.auto_awesome_rounded, visible: false), AppScreen(id: 'media', label: 'Médias', icon: Icons.music_note_rounded, visible: false), AppScreen(id: 'garages', label: 'Garages', icon: Icons.garage_rounded, visible: false), AppScreen(id: 'ac', label: 'AC / Climatisation', icon: Icons.ac_unit_rounded), ]; ThemeMode get themeMode => _themeMode; int get accentIndex => _accentIndex; double get textScale => _textScale; VisualDensity get density => _density; List get screens => _screens; List get visibleScreens => _screens.where((s) => s.visible).toList(); static const List accentColors = [ Color(0xFF2E75B6), // ETM blue Color(0xFF4CAF50), // vert Color(0xFFFF7043), // orange Color(0xFF7B1FA2), // violet ]; Color get accentColor => accentColors[_accentIndex.clamp(0, 3)]; // ── Chargement depuis SharedPreferences ───────────────────────────────────── Future load() async { final prefs = await SharedPreferences.getInstance(); final themeVal = prefs.getInt(_themeKey) ?? 0; _themeMode = ThemeMode.values[themeVal.clamp(0, 2)]; _accentIndex = (prefs.getInt(_accentKey) ?? 0).clamp(0, 3); _textScale = (prefs.getDouble(_textScaleKey) ?? 1.0).clamp(0.8, 1.4); final densityVal = prefs.getInt(_densityKey) ?? 1; _density = _densityFromIndex(densityVal); // Restaure l'ordre et la visibilité des écrans final raw = prefs.getString(_screensKey); if (raw != null) { try { final List saved = jsonDecode(raw) as List; final byId = {for (final s in _screens) s.id: s}; final restored = []; for (final item in saved) { final id = item['id'] as String?; final visible = item['visible'] as bool? ?? true; if (id != null && byId.containsKey(id)) { restored.add(byId[id]!.copyWith(visible: visible)); byId.remove(id); } } // Ajoute les écrans nouveaux non présents dans la config sauvegardée restored.addAll(byId.values); _screens = restored; } catch (_) {} } notifyListeners(); } // ── Sauvegarde ─────────────────────────────────────────────────────────────── Future _save() async { final prefs = await SharedPreferences.getInstance(); await prefs.setInt(_themeKey, _themeMode.index); await prefs.setInt(_accentKey, _accentIndex); await prefs.setDouble(_textScaleKey, _textScale); await prefs.setInt(_densityKey, _densityIndex(_density)); await prefs.setString( _screensKey, jsonEncode(_screens.map((s) => s.toJson()).toList())); } // ── Setters ────────────────────────────────────────────────────────────────── void setThemeMode(ThemeMode mode) { _themeMode = mode; _save(); notifyListeners(); } void setAccentIndex(int idx) { _accentIndex = idx.clamp(0, 3); _save(); notifyListeners(); } void setTextScale(double v) { _textScale = v.clamp(0.8, 1.4); _save(); notifyListeners(); } void setDensity(VisualDensity d) { _density = d; _save(); notifyListeners(); } void setScreenVisible(String id, bool visible) { final idx = _screens.indexWhere((s) => s.id == id); if (idx >= 0) { _screens[idx] = _screens[idx].copyWith(visible: visible); _save(); notifyListeners(); } } void reorderScreens(int oldIdx, int newIdx) { final item = _screens.removeAt(oldIdx); _screens.insert(newIdx, item); _save(); notifyListeners(); } void resetAppearance() { _themeMode = ThemeMode.system; _accentIndex = 0; _textScale = 1.0; _density = VisualDensity.standard; _save(); notifyListeners(); } // ── Helpers ────────────────────────────────────────────────────────────────── VisualDensity _densityFromIndex(int i) => switch (i) { 0 => VisualDensity.compact, 2 => const VisualDensity(horizontal: 2, vertical: 2), _ => VisualDensity.standard, }; int _densityIndex(VisualDensity d) { if (d == VisualDensity.compact) return 0; if (d.horizontal > 0) return 2; return 1; } }