named router with constants to prevent silent bugs

This commit is contained in:
2026-05-09 20:42:42 +02:00
parent 5f39d5b1ad
commit 385c3da0a5
8 changed files with 64 additions and 59 deletions

View File

@@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flux/core/blocs/session/session_cubit.dart';
import 'package:flux/core/data/core_repository.dart';
import 'package:flux/core/layout/app_shell.dart';
import 'package:flux/core/routes/routes.dart';
import 'package:flux/core/widgets/image_upload/blocs/image_upload_cubit.dart';
import 'package:flux/core/widgets/image_upload/ui/image_upload_screen.dart';
import 'package:flux/core/widgets/set_password_screen.dart';
@@ -42,27 +43,6 @@ import 'package:flux/features/tickets/ui/ticket_list_screen.dart';
import 'package:get_it/get_it.dart';
import 'package:go_router/go_router.dart';
const String loginRoute = 'login';
const String setPasswordRoute = 'set-password';
const String onboardingRoute = 'onboarding';
const String homeRoute = 'home';
const String masterDataRoute = 'master-data';
const String productsRoute = 'products';
const String companySettingsRoute = 'company-settings';
const String staffRoute = 'staff';
const String storesRoute = 'stores';
const String providersRoute = 'providers';
const String settingsRoute = 'settings';
const String themeRoute = 'theme';
const String operationsRoute = 'operations';
const String customersRoute = 'customers';
const String ticketsRoute = 'tickets';
const String ticketFormRoute = 'ticket-form';
const String operationFormRoute = 'operation-form';
const String uploadSuccessRoute = 'upload-success';
const String customerFormRoute = 'customer-form';
const String uploadRoute = 'upload';
class AppRouter {
static GoRouter createRouter(SessionCubit sessionCubit) {
return GoRouter(
@@ -113,17 +93,17 @@ class AppRouter {
// --- ROTTE DI SERVIZIO (FUORI DALLA SHELL) ---
GoRoute(
path: '/login',
name: 'login',
name: Routes.login,
builder: (context, state) => const AuthScreen(),
),
GoRoute(
path: '/set-password',
name: 'set-password',
name: Routes.setPassword,
builder: (context, state) => const SetPasswordScreen(),
),
GoRoute(
path: '/onboarding',
name: 'onboarding',
name: Routes.onboarding,
builder: (context, state) => BlocProvider(
create: (context) => OnboardingCubit(
GetIt.I.get<SessionCubit>(),
@@ -140,19 +120,19 @@ class AppRouter {
// 1. DASHBOARD
GoRoute(
path: '/',
name: homeRoute,
name: Routes.home,
builder: (context, state) => const HomeScreen(),
),
// 2. HUB ANAGRAFICHE E SOTTO-ROTTE
GoRoute(
path: '/master-data',
name: masterDataRoute,
name: Routes.masterData,
builder: (context, state) => const MasterDataHubScreen(),
routes: [
GoRoute(
path: 'products', // Diventa /master-data/products
name: 'products',
name: Routes.products,
builder: (context, state) {
context.read<ProductsCubit>().refreshCubit();
@@ -161,7 +141,7 @@ class AppRouter {
),
GoRoute(
path: 'company-settings',
name: companySettingsRoute,
name: Routes.companySettings,
builder: (context, state) => BlocProvider(
create: (context) => CompanySettingsCubit(),
child: const CompanySettingsScreen(),
@@ -169,17 +149,17 @@ class AppRouter {
),
GoRoute(
path: 'staff',
name: staffRoute, // Diventa /master-data/staff
name: Routes.staff, // Diventa /master-data/staff
builder: (context, state) => const StaffScreen(),
),
GoRoute(
path: storesRoute,
path: Routes.stores,
name: 'stores', // Diventa /master-data/stores
builder: (context, state) => const StoresScreen(),
),
GoRoute(
path: 'providers',
name: providersRoute, // Diventa /master-data/providers
name: Routes.providers, // Diventa /master-data/providers
builder: (context, state) =>
const ProvidersMasterDataScreen(),
),
@@ -189,19 +169,19 @@ class AppRouter {
// 3. IMPOSTAZIONI
GoRoute(
path: '/settings',
name: settingsRoute,
name: Routes.settings,
builder: (context, state) => const SettingsView(),
routes: [
GoRoute(
path: 'theme',
name: themeRoute,
path: 'themeSettings',
name: Routes.themeSettings,
builder: (context, state) => const ThemeSettingsView(),
),
],
),
GoRoute(
path: '/operations',
name: operationsRoute,
name: Routes.operations,
builder: (context, state) => BlocProvider(
create: (context) => OperationListCubit(),
child: const OperationListScreen(),
@@ -209,13 +189,13 @@ class AppRouter {
),
GoRoute(
path: '/customers',
name: customersRoute,
name: Routes.customers,
builder: (context, state) =>
const CustomersContent(), // O come si chiama il tuo widget della lista!
),
GoRoute(
path: '/tickets',
name: ticketsRoute,
name: Routes.tickets,
builder: (context, state) => BlocProvider(
create: (context) => TicketListCubit(),
child: const TicketListScreen(),
@@ -228,7 +208,7 @@ class AppRouter {
GoRoute(
// Il path sarà es. /tickets/form/123 oppure /tickets/form/new
path: '/tickets/form/:id',
name: ticketFormRoute,
name: Routes.ticketForm,
builder: (context, state) {
// 1. Leggiamo l'ID dall'URL
final String pathId = state.pathParameters['id'] ?? 'new';
@@ -265,7 +245,7 @@ class AppRouter {
),
GoRoute(
path: '/upload-success',
name: uploadSuccessRoute,
name: Routes.uploadSuccess,
builder: (context, state) => const UploadSuccessScreen(),
),
GoRoute(
@@ -285,7 +265,7 @@ class AppRouter {
GoRoute(
path: '/operations/form/:id',
name: operationFormRoute,
name: Routes.operationForm,
builder: (context, state) {
final String pathId = state.pathParameters['id'] ?? 'new';
final OperationModel? operationFromExtra =
@@ -324,7 +304,7 @@ class AppRouter {
GoRoute(
path: '/upload/:type/:id',
name: uploadRoute,
name: Routes.upload,
builder: (context, state) {
final typeString = state.pathParameters['type']!;
final id = state.pathParameters['id']!;

View File

@@ -0,0 +1,22 @@
class Routes {
static const String login = 'login';
static const String setPassword = 'set-password';
static const String onboarding = 'onboarding';
static const String home = 'home';
static const String masterData = 'master-data';
static const String products = 'products';
static const String companySettings = 'company-settings';
static const String staff = 'staff';
static const String stores = 'stores';
static const String providers = 'providers';
static const String settings = 'settings';
static const String themeSettings = 'themeSettings';
static const String operations = 'operations';
static const String customers = 'customers';
static const String tickets = 'tickets';
static const String ticketForm = 'ticket-form';
static const String operationForm = 'operation-form';
static const String uploadSuccess = 'upload-success';
static const String customerForm = 'customer-form';
static const String upload = 'upload';
}

View File

@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flux/core/blocs/session/session_cubit.dart';
import 'package:flux/core/routes/app_router.dart';
import 'package:flux/core/routes/routes.dart';
import 'package:flux/core/theme/theme.dart';
import 'package:flux/features/customers/blocs/customers_cubit.dart';
import 'package:flux/features/customers/models/customer_model.dart';
@@ -111,7 +111,7 @@ class _CustomersContentState extends State<CustomersContent> {
return _CustomerTile(
customer: customer,
onTap: () => context.pushNamed(
customerFormRoute,
Routes.customerForm,
pathParameters: {'id': customer.id!},
extra: customer,
),

View File

@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flux/core/blocs/session/session_cubit.dart';
import 'package:flux/core/routes/app_router.dart';
import 'package:flux/core/routes/routes.dart';
import 'package:flux/core/theme/theme.dart';
import 'package:flux/core/utils/extensions.dart';
import 'package:flux/features/home/latest_store_operations/bloc/latest_store_operations_bloc.dart';
@@ -49,7 +49,7 @@ class _LatestOperationsCardContent extends StatelessWidget {
side: BorderSide(color: theme.dividerColor.withValues(alpha: 0.5)),
),
child: InkWell(
onTap: () => context.pushNamed(operationsRoute),
onTap: () => context.pushNamed(Routes.operations),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
@@ -136,7 +136,7 @@ class _LatestOperationsCardContent extends StatelessWidget {
final operation = state.operations[index];
return InkWell(
onTap: () => context.pushNamed(
operationFormRoute,
Routes.operationForm,
pathParameters: {'id': operation.id!},
extra: operation,
),

View File

@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flux/core/blocs/session/session_cubit.dart';
import 'package:flux/core/routes/app_router.dart';
import 'package:flux/core/routes/routes.dart';
import 'package:flux/core/theme/theme.dart';
import 'package:flux/core/utils/extensions.dart';
import 'package:flux/features/home/latest_store_operations/ui/latest_store_operations_card.dart';
@@ -83,8 +83,9 @@ class HomeScreen extends StatelessWidget {
icon: Icons.support_agent_outlined,
color: Colors.purple,
context: context,
onTap: () =>
context.pushNamed(ticketsRoute), // <-- Aggiunto!
onTap: () => context.pushNamed(
Routes.tickets,
), // <-- Aggiunto!
),
]),
),
@@ -188,7 +189,7 @@ class HomeScreen extends StatelessWidget {
onTap: () {
// Entriamo nel form! Nessun parametro extra = Nuovo Servizio
context.pushNamed(
operationFormRoute,
Routes.operationForm,
pathParameters: {'id': 'New'},
);
},
@@ -200,7 +201,10 @@ class HomeScreen extends StatelessWidget {
color: Colors.redAccent,
onTap: () {
// Andiamo alla lista! (Da lì poi aggiungeremo il tasto "+" per il form)
context.pushNamed(ticketFormRoute, pathParameters: {'id': 'New'});
context.pushNamed(
Routes.ticketForm,
pathParameters: {'id': 'New'},
);
},
),
const SizedBox(width: 12),

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flux/core/routes/app_router.dart';
import 'package:flux/core/routes/routes.dart';
import 'package:go_router/go_router.dart';
// Mantieni i tuoi import per il tema se usi le estensioni (es. context.accent)
// import 'package:flux/core/theme/theme.dart';
@@ -66,7 +66,7 @@ class MasterDataHubScreen extends StatelessWidget {
color: Colors.orange,
// Usiamo .push() perché avevamo detto che i clienti
// stanno FUORI dalla Shell (niente BottomBar)
onTap: () => context.pushNamed(customersRoute),
onTap: () => context.pushNamed(Routes.customers),
),
_buildHubCard(
context,

View File

@@ -2,7 +2,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flux/core/blocs/session/session_cubit.dart';
import 'package:flux/core/routes/app_router.dart';
import 'package:flux/core/routes/routes.dart';
import 'package:flux/core/theme/theme.dart';
import 'package:go_router/go_router.dart';
@@ -29,7 +29,7 @@ class SettingsView extends StatelessWidget {
icon: Icons.business,
subtitle: 'Configura i dati aziendali',
context: context,
onTap: () => context.pushNamed(companySettingsRoute),
onTap: () => context.pushNamed(Routes.companySettings),
),
]),
const SizedBox(height: 16),
@@ -39,7 +39,7 @@ class SettingsView extends StatelessWidget {
title: 'Tema (FLUX Dark)',
subtitle: 'Configurazione visiva',
context: context,
onTap: () => context.pushNamed(themeRoute),
onTap: () => context.pushNamed(Routes.themeSettings),
),
]),
const SizedBox(height: 24),

View File

@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flux/core/routes/app_router.dart';
import 'package:flux/core/routes/routes.dart';
import 'package:flux/features/tickets/blocs/ticket_list_cubit.dart';
import 'package:flux/features/tickets/blocs/ticket_list_state.dart';
import 'package:flux/features/tickets/models/ticket_model.dart';
@@ -149,7 +149,7 @@ class _TicketListScreenState extends State<TicketListScreen> {
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
context.pushNamed(ticketFormRoute, pathParameters: {'id': 'New'});
context.pushNamed(Routes.ticketForm, pathParameters: {'id': 'New'});
},
icon: const Icon(Icons.add),
label: const Text('Nuovo Ticket'),
@@ -286,7 +286,6 @@ class _TicketCard extends StatelessWidget {
pathParameters: {'id': ticket.id!},
extra:
ticket, // <-- LA MAGIA È QUI: Passa l'oggetto intero!
// Teniamo anche il parametro URL per coerenza di routing
);
},