This commit is contained in:
2026-04-04 19:25:55 +02:00
parent c91415b8b3
commit 0347a354ef
13 changed files with 377 additions and 113 deletions

View File

@@ -1,8 +1,19 @@
import 'package:flutter/material.dart';
import 'package:flux/theme.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flux/theme/theme.dart';
import 'package:flux/theme/theme_bloc.dart';
import 'package:flux/ui/home_screen.dart';
import 'package:flux/ui/settings/settings.dart';
import 'package:get_it/get_it.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final GetIt getIt = GetIt.instance;
getIt.registerSingleton<SharedPreferences>(
await SharedPreferences.getInstance(),
);
getIt.registerSingleton<AppSettings>(AppSettings());
runApp(const MainApp());
}
@@ -11,11 +22,34 @@ class MainApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
AppTheme appTheme = GetIt.I.get<AppSettings>().appTheme;
ThemeData themeData;
switch (appTheme) {
case AppTheme.dark:
themeData = fluxDarkTheme;
break;
case AppTheme.light:
themeData = fluxLightTheme;
break;
default:
themeData = ThemeMode.system == ThemeMode.dark
? fluxDarkTheme
: fluxLightTheme;
break;
}
return MaterialApp(
title: 'FLUX Gestionale',
debugShowCheckedModeBanner: false,
theme: fluxDarkTheme, // Applica il tema FLUX
home: const HomeScreen(),
theme: themeData, // Applica il tema FLUX
home: MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => ThemeBloc(initialAppTheme: appTheme),
),
],
child: const HomeScreen(),
),
);
}
}

View File

@@ -1,73 +0,0 @@
// lib/theme.dart
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
class FluxColors {
// Palette Tech Dark da loghi generati
static const Color background = Color(0xFF0A0E17); // Nero profondo/Blu scuro
static const Color surface = Color(0xFF161B22); // Pannelli scuri
static const Color primaryBlue = Color(0xFF007BFF); // Blu Elettrico del logo
static const Color accentTurquoise = Color(0xFF17A2B8); // Turchese del flusso
static const Color textPrimary = Colors.white;
static const Color textSecondary = Color(0xFF8B949E); // Grigio fumo
}
ThemeData fluxDarkTheme = ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
primaryColor: FluxColors.primaryBlue,
scaffoldBackgroundColor: FluxColors.background,
colorScheme: const ColorScheme.dark(
primary: FluxColors.primaryBlue,
secondary: FluxColors.accentTurquoise,
surface: FluxColors.surface,
background: FluxColors.background,
onPrimary: Colors.white,
onSurface: FluxColors.textPrimary,
),
// Font moderno sans-serif (es. Montserrat o Poppins coerente con descrizione)
textTheme: GoogleFonts.poppinsTextTheme(ThemeData.dark().textTheme).copyWith(
headlineMedium: GoogleFonts.poppins(
color: FluxColors.textPrimary,
fontWeight: FontWeight.w600,
letterSpacing: 0.5,
),
titleLarge: GoogleFonts.poppins(
color: FluxColors.textPrimary,
fontWeight: FontWeight.w500,
),
bodyMedium: GoogleFonts.poppins(color: FluxColors.textSecondary),
),
appBarTheme: const AppBarTheme(
backgroundColor: FluxColors.background,
elevation: 0,
centerTitle: false,
titleTextStyle: TextStyle(
color: FluxColors.textPrimary,
fontSize: 20,
fontWeight: FontWeight.w600,
),
),
cardTheme: const CardThemeData(
color: FluxColors.surface,
elevation: 2,
margin: EdgeInsets.all(8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12)),
),
),
// Stile per i pulsanti (es. "Nuova Operazione")
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: FluxColors.primaryBlue,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
textStyle: GoogleFonts.poppins(fontWeight: FontWeight.w500),
),
),
);

189
lib/theme/theme.dart Normal file
View File

@@ -0,0 +1,189 @@
// 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,
),
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)),
),
),
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,
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)),
),
),
// 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),
);

17
lib/theme/theme_bloc.dart Normal file
View File

@@ -0,0 +1,17 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:equatable/equatable.dart';
part 'theme_events.dart';
part 'theme_state.dart';
class ThemeBloc extends Bloc<ThemeEvent, ThemeState> {
final AppTheme initialAppTheme;
ThemeBloc({required this.initialAppTheme})
: super(
ThemeState(status: ThemeStatus.success, appTheme: initialAppTheme),
) {
on<ChangeThemeEvent>((event, emit) async {
emit(state.copyWith(appTheme: event.appTheme));
});
}
}

View File

@@ -0,0 +1,17 @@
part of 'theme_bloc.dart';
abstract class ThemeEvent extends Equatable {
const ThemeEvent();
@override
List<Object> get props => [];
}
class ChangeThemeEvent extends ThemeEvent {
const ChangeThemeEvent({required this.appTheme});
final AppTheme appTheme;
@override
List<Object> get props => [appTheme];
}

View File

@@ -0,0 +1,30 @@
part of 'theme_bloc.dart';
enum ThemeStatus { initial, success }
enum AppTheme {
dark(name: 'dark'),
light(name: 'light'),
system(name: 'system');
final String name;
const AppTheme({required this.name});
}
class ThemeState extends Equatable {
const ThemeState({required this.status, required this.appTheme});
final ThemeStatus status;
final AppTheme appTheme;
@override
List<Object?> get props => [status, appTheme];
ThemeState copyWith({ThemeStatus? status, AppTheme? appTheme}) {
return ThemeState(
status: status ?? this.status,
appTheme: appTheme ?? this.appTheme,
);
}
}

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flux/theme.dart';
import 'package:flux/theme/theme.dart';
class AnagraficheMainView extends StatelessWidget {
const AnagraficheMainView({super.key});

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flux/theme.dart';
import 'package:flux/theme/theme.dart';
class DashboardView extends StatelessWidget {
const DashboardView({super.key});

View File

@@ -1,5 +1,7 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flux/theme.dart';
import 'package:flux/theme/theme.dart';
import 'package:flux/theme/theme_bloc.dart';
import 'package:flux/ui/anagrafiche/anagrafiche_main_view.dart';
import 'package:flux/ui/dashboard/dashboard_view.dart';
import 'package:flux/ui/settings/settings_view.dart';
@@ -29,6 +31,8 @@ class _HomeScreenState extends State<HomeScreen> {
@override
Widget build(BuildContext context) {
return BlocBuilder<ThemeBloc, ThemeState>(
builder: (context, state) {
return Scaffold(
body: Center(child: _widgetOptions.elementAt(_selectedIndex)),
bottomNavigationBar: BottomNavigationBar(
@@ -56,11 +60,17 @@ class _HomeScreenState extends State<HomeScreen> {
],
currentIndex: _selectedIndex,
selectedItemColor: FluxColors.accentTurquoise,
unselectedItemColor: FluxColors.textSecondary,
backgroundColor: FluxColors.surface,
unselectedItemColor: state.themeMode == ThemeMode.dark
? FluxColors.darkTextSecondary
: FluxColors.lightTextSecondary,
backgroundColor: state.themeMode == ThemeMode.dark
? FluxColors.darkSurface
: FluxColors.lightSurface,
type: BottomNavigationBarType.fixed,
onTap: _onItemTapped,
),
);
},
);
}
}

View File

@@ -0,0 +1,39 @@
import 'package:flux/theme/theme_bloc.dart';
import 'package:get_it/get_it.dart';
import 'package:shared_preferences/shared_preferences.dart';
class AppSettings {
late AppTheme _appTheme;
late SharedPreferences _prefs;
// Singleton
static final AppSettings _instance = AppSettings._internal();
factory AppSettings() {
return _instance;
}
AppSettings._internal() {
_prefs = GetIt.I.get<SharedPreferences>();
String theme = _prefs.getString('theme') ?? 'light';
switch (theme) {
case 'dark':
_appTheme = AppTheme.dark;
break;
case 'light':
_appTheme = AppTheme.light;
break;
default:
_appTheme = AppTheme.system;
break;
}
}
AppTheme get appTheme => _appTheme;
void setAppTheme(AppTheme theme) {
_appTheme = theme;
_prefs.setString('theme', theme.name);
}
}

View File

@@ -1,6 +1,6 @@
// lib/ui/impostazioni/impostazioni_view.dart
import 'package:flutter/material.dart';
import 'package:flux/theme.dart';
import 'package:flux/theme/theme.dart';
class SettingsView extends StatelessWidget {
const SettingsView({super.key});

View File

@@ -489,7 +489,7 @@ packages:
source: hosted
version: "0.28.0"
shared_preferences:
dependency: transitive
dependency: "direct main"
description:
name: shared_preferences
sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf

View File

@@ -14,6 +14,7 @@ dependencies:
get_it: ^9.2.1
google_fonts: ^8.0.2
intl: ^0.20.2
shared_preferences: ^2.5.5
supabase_flutter: ^2.12.2
dev_dependencies: