theme work

This commit is contained in:
2026-04-05 10:06:26 +02:00
parent 0347a354ef
commit 28b2abdff3
16 changed files with 223 additions and 137 deletions

View File

@@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig" #include "Generated.xcconfig"

View File

@@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig" #include "Generated.xcconfig"

43
ios/Podfile Normal file
View File

@@ -0,0 +1,43 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end

12
lib/data/enums.dart Normal file
View File

@@ -0,0 +1,12 @@
import 'package:flutter/material.dart';
enum AppThemeMode {
light('light', 'Chiaro', ThemeMode.light),
dark('dark', 'Scuro', ThemeMode.dark),
system('system', 'Sistema', ThemeMode.system);
const AppThemeMode(this.value, this.label, this.themeMode);
final String value;
final String label;
final ThemeMode themeMode;
}

View File

@@ -14,42 +14,30 @@ void main() async {
await SharedPreferences.getInstance(), await SharedPreferences.getInstance(),
); );
getIt.registerSingleton<AppSettings>(AppSettings()); getIt.registerSingleton<AppSettings>(AppSettings());
runApp(const MainApp()); runApp(
BlocProvider(
create: (context) => ThemeBloc()..add(LoadThemeEvent()),
child: const FluxApp(),
),
);
} }
class MainApp extends StatelessWidget { class FluxApp extends StatelessWidget {
const MainApp({super.key}); const FluxApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
AppTheme appTheme = GetIt.I.get<AppSettings>().appTheme; return BlocBuilder<ThemeBloc, ThemeState>(
ThemeData themeData; builder: (context, state) {
switch (appTheme) { return MaterialApp(
case AppTheme.dark: title: 'FLUX Gestionale',
themeData = fluxDarkTheme; debugShowCheckedModeBanner: false,
break; theme: fluxLightTheme,
case AppTheme.light: darkTheme: fluxDarkTheme,
themeData = fluxLightTheme; themeMode: state.themeMode, // Applica il tema FLUX
break; home: const HomeScreen(),
default: );
themeData = ThemeMode.system == ThemeMode.dark },
? fluxDarkTheme
: fluxLightTheme;
break;
}
return MaterialApp(
title: 'FLUX Gestionale',
debugShowCheckedModeBanner: false,
theme: themeData, // Applica il tema FLUX
home: MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => ThemeBloc(initialAppTheme: appTheme),
),
],
child: const HomeScreen(),
),
); );
} }
} }

View File

@@ -1,17 +1,30 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:equatable/equatable.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_events.dart';
part 'theme_state.dart'; part 'theme_state.dart';
class ThemeBloc extends Bloc<ThemeEvent, ThemeState> { class ThemeBloc extends Bloc<ThemeEvent, ThemeState> {
final AppTheme initialAppTheme; static const String _savedThemeKey = "themeModeSetting";
ThemeBloc({required this.initialAppTheme}) final SharedPreferences _prefs = GetIt.I.get<SharedPreferences>();
: super( ThemeBloc() : super(ThemeState(themeMode: ThemeMode.system)) {
ThemeState(status: ThemeStatus.success, appTheme: initialAppTheme), on<LoadThemeEvent>((event, emit) {
) { String savedTheme = _prefs.getString(_savedThemeKey) ?? 'system';
emit(
state.copyWith(
themeMode: AppThemeMode.values
.firstWhere((test) => test.value == savedTheme)
.themeMode,
),
);
});
on<ChangeThemeEvent>((event, emit) async { on<ChangeThemeEvent>((event, emit) async {
emit(state.copyWith(appTheme: event.appTheme)); await _prefs.setString(_savedThemeKey, event.appThemeMode.value);
emit(state.copyWith(themeMode: event.appThemeMode.themeMode));
}); });
} }
} }

View File

@@ -7,11 +7,13 @@ abstract class ThemeEvent extends Equatable {
List<Object> get props => []; List<Object> get props => [];
} }
class ChangeThemeEvent extends ThemeEvent { final class LoadThemeEvent extends ThemeEvent {}
const ChangeThemeEvent({required this.appTheme});
final AppTheme appTheme; final class ChangeThemeEvent extends ThemeEvent {
final AppThemeMode appThemeMode;
const ChangeThemeEvent(this.appThemeMode);
@override @override
List<Object> get props => [appTheme]; List<Object> get props => [appThemeMode];
} }

View File

@@ -1,30 +1,14 @@
part of 'theme_bloc.dart'; 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 { class ThemeState extends Equatable {
const ThemeState({required this.status, required this.appTheme}); const ThemeState({required this.themeMode});
final ThemeStatus status; final ThemeMode themeMode;
final AppTheme appTheme;
@override @override
List<Object?> get props => [status, appTheme]; List<Object?> get props => [themeMode];
ThemeState copyWith({ThemeStatus? status, AppTheme? appTheme}) { ThemeState copyWith({ThemeMode? themeMode}) {
return ThemeState( return ThemeState(themeMode: themeMode ?? this.themeMode);
status: status ?? this.status,
appTheme: appTheme ?? this.appTheme,
);
} }
} }

View File

@@ -16,7 +16,7 @@ class AnagraficheMainView extends StatelessWidget {
isScrollable: true, isScrollable: true,
indicatorColor: FluxColors.accentTurquoise, indicatorColor: FluxColors.accentTurquoise,
labelColor: FluxColors.accentTurquoise, labelColor: FluxColors.accentTurquoise,
unselectedLabelColor: FluxColors.textSecondary, unselectedLabelColor: Theme.of(context).textTheme.bodyMedium?.color,
tabs: [ tabs: [
Tab(icon: Icon(Icons.storefront), text: 'Negozi'), Tab(icon: Icon(Icons.storefront), text: 'Negozi'),
Tab(icon: Icon(Icons.support_agent), text: 'Gestori'), Tab(icon: Icon(Icons.support_agent), text: 'Gestori'),

View File

@@ -92,26 +92,28 @@ class _RecentActivityPreview extends StatelessWidget {
'Attività Recenti', 'Attività Recenti',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
), ),
const Divider(color: FluxColors.textSecondary), Divider(color: Theme.of(context).textTheme.bodyMedium?.color),
// Sostituire con BlocBuilder // Sostituire con BlocBuilder
_activityTile('Nuova Linea', 'Mario Rossi', '10 min fa'), _activityTile('Nuova Linea', 'Mario Rossi', '10 min fa', context),
_activityTile('Assistenza Tech', 'iPhone 13', '45 min fa'), _activityTile('Assistenza Tech', 'iPhone 13', '45 min fa', context),
], ],
), ),
), ),
); );
} }
Widget _activityTile(String title, String subtitle, String time) { Widget _activityTile(
String title,
String subtitle,
String time,
BuildContext context,
) {
return ListTile( return ListTile(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
leading: const Icon(Icons.history, color: FluxColors.accentTurquoise), leading: const Icon(Icons.history, color: FluxColors.accentTurquoise),
title: Text(title, style: const TextStyle(color: FluxColors.textPrimary)), title: Text(title, style: Theme.of(context).textTheme.titleLarge),
subtitle: Text(subtitle), subtitle: Text(subtitle),
trailing: Text( trailing: Text(time, style: Theme.of(context).textTheme.bodyMedium),
time,
style: const TextStyle(color: FluxColors.textSecondary, fontSize: 12),
),
); );
} }
} }

View File

@@ -1,7 +1,5 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flux/theme/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/anagrafiche/anagrafiche_main_view.dart';
import 'package:flux/ui/dashboard/dashboard_view.dart'; import 'package:flux/ui/dashboard/dashboard_view.dart';
import 'package:flux/ui/settings/settings_view.dart'; import 'package:flux/ui/settings/settings_view.dart';
@@ -31,46 +29,39 @@ class _HomeScreenState extends State<HomeScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<ThemeBloc, ThemeState>( final surfaceColor = Theme.of(context).colorScheme.surface;
builder: (context, state) { return Scaffold(
return Scaffold( body: Center(child: _widgetOptions.elementAt(_selectedIndex)),
body: Center(child: _widgetOptions.elementAt(_selectedIndex)), bottomNavigationBar: BottomNavigationBar(
bottomNavigationBar: BottomNavigationBar( items: const <BottomNavigationBarItem>[
items: const <BottomNavigationBarItem>[ BottomNavigationBarItem(
BottomNavigationBarItem( icon: Icon(Icons.dashboard_outlined),
icon: Icon(Icons.dashboard_outlined), activeIcon: Icon(Icons.dashboard),
activeIcon: Icon(Icons.dashboard), label: 'Dashboard',
label: 'Dashboard',
),
BottomNavigationBarItem(
icon: Icon(Icons.history_edu_outlined),
activeIcon: Icon(Icons.history_edu),
label: 'Operazioni',
),
BottomNavigationBarItem(
icon: Icon(Icons.people_alt_outlined),
activeIcon: Icon(Icons.people_alt),
label: 'Anagrafiche',
),
BottomNavigationBarItem(
icon: Icon(Icons.settings_outlined),
activeIcon: Icon(Icons.settings),
label: 'Impostazioni',
),
],
currentIndex: _selectedIndex,
selectedItemColor: FluxColors.accentTurquoise,
unselectedItemColor: state.themeMode == ThemeMode.dark
? FluxColors.darkTextSecondary
: FluxColors.lightTextSecondary,
backgroundColor: state.themeMode == ThemeMode.dark
? FluxColors.darkSurface
: FluxColors.lightSurface,
type: BottomNavigationBarType.fixed,
onTap: _onItemTapped,
), ),
); BottomNavigationBarItem(
}, icon: Icon(Icons.history_edu_outlined),
activeIcon: Icon(Icons.history_edu),
label: 'Operazioni',
),
BottomNavigationBarItem(
icon: Icon(Icons.people_alt_outlined),
activeIcon: Icon(Icons.people_alt),
label: 'Anagrafiche',
),
BottomNavigationBarItem(
icon: Icon(Icons.settings_outlined),
activeIcon: Icon(Icons.settings),
label: 'Impostazioni',
),
],
currentIndex: _selectedIndex,
selectedItemColor: FluxColors.accentTurquoise,
unselectedItemColor: Theme.of(context).textTheme.bodyMedium?.color,
backgroundColor: surfaceColor,
type: BottomNavigationBarType.fixed,
onTap: _onItemTapped,
),
); );
} }
} }

View File

@@ -1,9 +1,8 @@
import 'package:flux/theme/theme_bloc.dart';
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
class AppSettings { class AppSettings {
late AppTheme _appTheme; late String _themeModeSetting;
late SharedPreferences _prefs; late SharedPreferences _prefs;
// Singleton // Singleton
@@ -16,24 +15,13 @@ class AppSettings {
AppSettings._internal() { AppSettings._internal() {
_prefs = GetIt.I.get<SharedPreferences>(); _prefs = GetIt.I.get<SharedPreferences>();
String theme = _prefs.getString('theme') ?? 'light'; _themeModeSetting = _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; String get themeModeSetting => _themeModeSetting;
void setAppTheme(AppTheme theme) { void setThemeModeSetting(String value) {
_appTheme = theme; _themeModeSetting = value;
_prefs.setString('theme', theme.name); _prefs.setString('theme', value);
} }
} }

View File

@@ -17,16 +17,28 @@ class SettingsView extends StatelessWidget {
Icons.person, Icons.person,
'Profilo Utente', 'Profilo Utente',
'Configura i tuoi dati', 'Configura i tuoi dati',
context,
),
_settingsTile(
Icons.store,
'Mio Negozio',
'Piacenza Centro',
context,
), ),
_settingsTile(Icons.store, 'Mio Negozio', 'Piacenza Centro'),
]), ]),
const SizedBox(height: 16), const SizedBox(height: 16),
_settingsSection('Applicazione', [ _settingsSection('Applicazione', [
_settingsTile(Icons.sync, 'Sincronizzazione', 'Ultima: 5 min fa'), _settingsTile(
Icons.sync,
'Sincronizzazione',
'Ultima: 5 min fa',
context,
),
_settingsTile( _settingsTile(
Icons.dark_mode, Icons.dark_mode,
'Tema (FLUX Dark Active)', 'Tema (FLUX Dark Active)',
'Configurazione visiva', 'Configurazione visiva',
context,
), ),
]), ]),
const SizedBox(height: 24), const SizedBox(height: 24),
@@ -58,14 +70,19 @@ class SettingsView extends StatelessWidget {
); );
} }
Widget _settingsTile(IconData icon, String title, String subtitle) { Widget _settingsTile(
IconData icon,
String title,
String subtitle,
BuildContext context,
) {
return ListTile( return ListTile(
leading: Icon(icon, color: FluxColors.primaryBlue), leading: Icon(icon, color: FluxColors.primaryBlue),
title: Text(title, style: const TextStyle(color: FluxColors.textPrimary)), title: Text(title, style: Theme.of(context).textTheme.titleLarge),
subtitle: Text(subtitle), subtitle: Text(subtitle),
trailing: const Icon( trailing: Icon(
Icons.chevron_right, Icons.chevron_right,
color: FluxColors.textSecondary, color: Theme.of(context).textTheme.bodyMedium?.color,
), ),
); );
} }

View File

@@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig"

View File

@@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig"

42
macos/Podfile Normal file
View File

@@ -0,0 +1,42 @@
platform :osx, '10.15'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_macos_podfile_setup
target 'Runner' do
use_frameworks!
flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_macos_build_settings(target)
end
end