refactor
This commit is contained in:
27
lib/core/theme/bloc/theme_bloc.dart
Normal file
27
lib/core/theme/bloc/theme_bloc.dart
Normal file
@@ -0,0 +1,27 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flux/data/enums.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
part 'theme_events.dart';
|
||||
part 'theme_state.dart';
|
||||
|
||||
class ThemeBloc extends Bloc<ThemeEvent, ThemeState> {
|
||||
final SharedPreferences _prefs = GetIt.I.get<SharedPreferences>();
|
||||
ThemeBloc() : super(ThemeState(currentTheme: AppThemeMode.system)) {
|
||||
on<LoadThemeEvent>((event, emit) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
currentTheme: AppThemeMode.fromValue(
|
||||
_prefs.getString(PrefKeys.theme.value),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
on<ChangeThemeEvent>((event, emit) async {
|
||||
await _prefs.setString(PrefKeys.theme.value, event.appThemeMode.value);
|
||||
emit(state.copyWith(currentTheme: event.appThemeMode));
|
||||
});
|
||||
}
|
||||
}
|
||||
19
lib/core/theme/bloc/theme_events.dart
Normal file
19
lib/core/theme/bloc/theme_events.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
part of 'theme_bloc.dart';
|
||||
|
||||
abstract class ThemeEvent extends Equatable {
|
||||
const ThemeEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
final class LoadThemeEvent extends ThemeEvent {}
|
||||
|
||||
final class ChangeThemeEvent extends ThemeEvent {
|
||||
final AppThemeMode appThemeMode;
|
||||
|
||||
const ChangeThemeEvent(this.appThemeMode);
|
||||
|
||||
@override
|
||||
List<Object> get props => [appThemeMode];
|
||||
}
|
||||
14
lib/core/theme/bloc/theme_state.dart
Normal file
14
lib/core/theme/bloc/theme_state.dart
Normal file
@@ -0,0 +1,14 @@
|
||||
part of 'theme_bloc.dart';
|
||||
|
||||
class ThemeState extends Equatable {
|
||||
const ThemeState({required this.currentTheme});
|
||||
|
||||
final AppThemeMode currentTheme;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [currentTheme];
|
||||
|
||||
ThemeState copyWith({AppThemeMode? currentTheme}) {
|
||||
return ThemeState(currentTheme: currentTheme ?? this.currentTheme);
|
||||
}
|
||||
}
|
||||
266
lib/core/theme/theme.dart
Normal file
266
lib/core/theme/theme.dart
Normal file
@@ -0,0 +1,266 @@
|
||||
// lib/theme.dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
class FluxColors {
|
||||
// === Palette comune (dal logo) ===
|
||||
static const Color primaryBlue = Color(
|
||||
0xFF007BFF,
|
||||
); // Blu Elettrico (Affidabilità)
|
||||
static const Color accentTurquoise = Color(
|
||||
0xFF17A2B8,
|
||||
); // Turchese (Flusso/Tech)
|
||||
|
||||
// === Palette Tech Dark (esistente) ===
|
||||
static const Color darkBackground = Color(0xFF0A0E17);
|
||||
static const Color darkSurface = Color(0xFF161B22);
|
||||
static const Color darkTextPrimary = Colors.white;
|
||||
static const Color darkTextSecondary = Color(0xFF8B949E);
|
||||
|
||||
// === Palette Tech Light (NUOVA) ===
|
||||
static const Color lightBackground = Color(
|
||||
0xFFF0F2F5,
|
||||
); // Grigio chiarissimo "pulito"
|
||||
static const Color lightSurface = Colors.white; // Pannelli bianchi puri
|
||||
static const Color lightTextPrimary = Color(
|
||||
0xFF1C1E21,
|
||||
); // Quasi nero per contrasto
|
||||
static const Color lightTextSecondary = Color(0xFF606770); // Grigio medio
|
||||
}
|
||||
|
||||
// --- Configurazione Tipografica Comune ---
|
||||
TextTheme _buildFluxTextTheme(
|
||||
TextTheme base,
|
||||
Color primaryColor,
|
||||
Color secondaryColor,
|
||||
) {
|
||||
return GoogleFonts.poppinsTextTheme(base).copyWith(
|
||||
headlineMedium: GoogleFonts.poppins(
|
||||
color: primaryColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: 0.5,
|
||||
),
|
||||
titleLarge: GoogleFonts.poppins(
|
||||
color: primaryColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
titleSmall: GoogleFonts.poppins(
|
||||
color:
|
||||
FluxColors.accentTurquoise, // Sempre turchese per i titoli di sezione
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: 1.2,
|
||||
),
|
||||
bodyMedium: GoogleFonts.poppins(color: secondaryColor),
|
||||
);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// === TEMA SCURO (esistente, ottimizzato) ===
|
||||
// ==========================================
|
||||
ThemeData fluxDarkTheme = ThemeData(
|
||||
useMaterial3: true,
|
||||
brightness: Brightness.dark,
|
||||
primaryColor: FluxColors.primaryBlue,
|
||||
scaffoldBackgroundColor: FluxColors.darkBackground,
|
||||
colorScheme: const ColorScheme.dark(
|
||||
primary: FluxColors.primaryBlue,
|
||||
secondary: FluxColors.accentTurquoise,
|
||||
surface: FluxColors.darkSurface,
|
||||
onPrimary: Colors.white,
|
||||
onSurface: FluxColors.darkTextPrimary,
|
||||
),
|
||||
hoverColor: FluxColors.accentTurquoise.withValues(
|
||||
alpha: 0.08,
|
||||
), // <--- AGGIUNGI QUESTO
|
||||
splashColor: FluxColors.accentTurquoise.withValues(
|
||||
alpha: 0.12,
|
||||
), // <--- AGGIUNGI QUESTO
|
||||
highlightColor: Colors.transparent,
|
||||
|
||||
textTheme: _buildFluxTextTheme(
|
||||
ThemeData.dark().textTheme,
|
||||
FluxColors.darkTextPrimary,
|
||||
FluxColors.darkTextSecondary,
|
||||
),
|
||||
|
||||
appBarTheme: const AppBarTheme(
|
||||
backgroundColor: FluxColors.darkBackground,
|
||||
elevation: 0,
|
||||
centerTitle: false,
|
||||
iconTheme: IconThemeData(color: FluxColors.darkTextPrimary),
|
||||
titleTextStyle: TextStyle(
|
||||
color: FluxColors.darkTextPrimary,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
|
||||
cardTheme: const CardThemeData(
|
||||
color: FluxColors.darkSurface,
|
||||
elevation: 2,
|
||||
margin: EdgeInsets.all(8),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
),
|
||||
|
||||
listTileTheme: ListTileThemeData(
|
||||
// Definiamo la forma arrotondata (fondamentale per l'effetto moderno)
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
// Colore dell'icona (Turchese Flux)
|
||||
iconColor: FluxColors.accentTurquoise,
|
||||
// Colore del testo
|
||||
titleTextStyle: GoogleFonts.poppins(
|
||||
color: FluxColors.darkTextPrimary,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
subtitleTextStyle: GoogleFonts.poppins(
|
||||
color: FluxColors.darkTextSecondary,
|
||||
fontSize: 13,
|
||||
),
|
||||
// Per far apparire la "manina" su Web/Desktop
|
||||
mouseCursor: WidgetStateProperty.all(SystemMouseCursors.click),
|
||||
|
||||
// NOTA: Se overlayColor non esiste, Flutter userà i colori
|
||||
// di default del tema (splashColor e hoverColor) definiti nel ThemeData generale.
|
||||
),
|
||||
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: FluxColors.primaryBlue,
|
||||
foregroundColor: Colors.white,
|
||||
elevation: 3,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
),
|
||||
),
|
||||
|
||||
// Colore delle icone non attive nella BottomNav
|
||||
unselectedWidgetColor: FluxColors.darkTextSecondary,
|
||||
);
|
||||
|
||||
// ==========================================
|
||||
// === TEMA CHIARO (NUOVO) ===
|
||||
// ==========================================
|
||||
ThemeData fluxLightTheme = ThemeData(
|
||||
useMaterial3: true,
|
||||
brightness: Brightness.light,
|
||||
primaryColor: FluxColors.primaryBlue,
|
||||
// Sfondo chiarissimo per staccare dalle card bianche
|
||||
scaffoldBackgroundColor: FluxColors.lightBackground,
|
||||
hoverColor: FluxColors.primaryBlue.withValues(alpha: 0.05),
|
||||
splashColor: FluxColors.primaryBlue.withValues(alpha: 0.1),
|
||||
highlightColor: Colors.transparent,
|
||||
|
||||
colorScheme: const ColorScheme.light(
|
||||
primary: FluxColors.primaryBlue,
|
||||
secondary: FluxColors.accentTurquoise,
|
||||
surface: FluxColors.lightSurface,
|
||||
onPrimary: Colors.white,
|
||||
onSurface: FluxColors.lightTextPrimary,
|
||||
),
|
||||
|
||||
// Applica la stessa tipografia ma con colori scuri
|
||||
textTheme: _buildFluxTextTheme(
|
||||
ThemeData.light().textTheme,
|
||||
FluxColors.lightTextPrimary,
|
||||
FluxColors.lightTextSecondary,
|
||||
),
|
||||
|
||||
appBarTheme: const AppBarTheme(
|
||||
backgroundColor: FluxColors.lightSurface, // AppBar bianca
|
||||
elevation: 1, // Leggera ombra per staccare dallo sfondo
|
||||
centerTitle: false,
|
||||
iconTheme: IconThemeData(color: FluxColors.lightTextPrimary),
|
||||
titleTextStyle: TextStyle(
|
||||
color: FluxColors.lightTextPrimary,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
|
||||
cardTheme: const CardThemeData(
|
||||
color: FluxColors.lightSurface, // Card bianca pura
|
||||
elevation: 4, // Ombra più marcata su sfondo chiaro
|
||||
margin: EdgeInsets.all(8),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
),
|
||||
|
||||
listTileTheme: ListTileThemeData(
|
||||
// Definiamo la forma arrotondata (fondamentale per l'effetto moderno)
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
// Colore dell'icona (Turchese Flux)
|
||||
iconColor: FluxColors.accentTurquoise,
|
||||
// Colore del testo
|
||||
titleTextStyle: GoogleFonts.poppins(
|
||||
color: FluxColors.darkTextPrimary,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
subtitleTextStyle: GoogleFonts.poppins(
|
||||
color: FluxColors.darkTextSecondary,
|
||||
fontSize: 13,
|
||||
),
|
||||
// Per far apparire la "manina" su Web/Desktop
|
||||
mouseCursor: WidgetStateProperty.all(SystemMouseCursors.click),
|
||||
|
||||
// NOTA: Se overlayColor non esiste, Flutter userà i colori
|
||||
// di default del tema (splashColor e hoverColor) definiti nel ThemeData generale.
|
||||
),
|
||||
|
||||
// Il pulsante principale rimane Blu Elettrico, molto visibile
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: FluxColors.primaryBlue,
|
||||
foregroundColor: Colors.white,
|
||||
elevation: 4,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
),
|
||||
),
|
||||
|
||||
// TabBar (usata nelle Anagrafiche) ottimizzata per il chiaro
|
||||
tabBarTheme: const TabBarThemeData(
|
||||
labelColor: FluxColors.primaryBlue,
|
||||
unselectedLabelColor: FluxColors.lightTextSecondary,
|
||||
indicatorColor: FluxColors.accentTurquoise,
|
||||
indicatorSize: TabBarIndicatorSize.label,
|
||||
),
|
||||
|
||||
// BottomNavigationBar (usata nella HomeScreen)
|
||||
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
|
||||
backgroundColor: FluxColors.lightSurface,
|
||||
selectedItemColor: FluxColors.primaryBlue,
|
||||
unselectedItemColor: FluxColors.lightTextSecondary,
|
||||
elevation: 8,
|
||||
),
|
||||
|
||||
// Colore delle icone generiche
|
||||
iconTheme: const IconThemeData(color: FluxColors.lightTextSecondary),
|
||||
);
|
||||
|
||||
extension FluxThemeContext on BuildContext {
|
||||
// --- Colori del Brand ---
|
||||
Color get primary => Theme.of(this).colorScheme.primary; // Blu Flux
|
||||
Color get accent => Theme.of(this).colorScheme.secondary; // Turchese Flux
|
||||
|
||||
// --- Superfici ---
|
||||
Color get surface => Theme.of(this).colorScheme.surface;
|
||||
Color get background =>
|
||||
Theme.of(this).colorScheme.surfaceContainerHighest; // O background
|
||||
|
||||
// --- Testi (La parte mancante) ---
|
||||
// Mappiamo primaryText sul colore del titolo e secondaryText su quello del corpo
|
||||
Color get primaryText =>
|
||||
Theme.of(this).textTheme.titleLarge?.color ?? Colors.white;
|
||||
Color get secondaryText =>
|
||||
Theme.of(this).textTheme.bodyMedium?.color ?? Colors.grey;
|
||||
|
||||
// Opzionale: un colore ancora più tenue per suggerimenti o icone disabilitate
|
||||
Color get hintText =>
|
||||
Theme.of(this).textTheme.bodySmall?.color ??
|
||||
Colors.grey.withValues(alpha: 0.5);
|
||||
}
|
||||
Reference in New Issue
Block a user