ok, design pulito e gorouter perfezionato (#11)
Reviewed-on: #11 Co-authored-by: Mark M2 Macbook <marco@catelli.it> Co-committed-by: Mark M2 Macbook <marco@catelli.it>
This commit is contained in:
@@ -1,157 +1,168 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flux/core/theme/theme.dart';
|
||||
import 'package:flux/features/customers/ui/customers_content.dart';
|
||||
import 'package:flux/features/master_data/products/ui/products_screen.dart';
|
||||
import 'package:flux/features/master_data/providers/ui/providers_master_data_screen.dart';
|
||||
import 'package:flux/features/master_data/staff/ui/staff_screen.dart';
|
||||
import 'package:flux/features/master_data/store/ui/stores_screen.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';
|
||||
|
||||
class MasterDataHubContent extends StatelessWidget {
|
||||
final Function(Widget) onOpenPage;
|
||||
|
||||
const MasterDataHubContent({super.key, required this.onOpenPage});
|
||||
class MasterDataHubScreen extends StatelessWidget {
|
||||
const MasterDataHubScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(23.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Anagrafiche",
|
||||
style: TextStyle(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: context.accent,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
"Gestisci i dati fondamentali del tuo business",
|
||||
style: TextStyle(color: context.secondaryText),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
final theme = Theme.of(context);
|
||||
|
||||
Expanded(
|
||||
child: GridView(
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: MediaQuery.of(context).size.width > 600 ? 3 : 2,
|
||||
mainAxisSpacing: 14,
|
||||
crossAxisSpacing: 14,
|
||||
// LA MAGIA: Fissiamo l'altezza della card a 200 pixel
|
||||
// indipendentemente da quanto sia stretta la colonna!
|
||||
mainAxisExtent: 200,
|
||||
return Scaffold(
|
||||
backgroundColor: theme.colorScheme.surface,
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Anagrafiche",
|
||||
style: theme.textTheme.headlineMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
letterSpacing: -0.5,
|
||||
// Se preferisci la tua estensione, usa: color: context.accent,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
children: [
|
||||
_buildHubCard(
|
||||
context,
|
||||
title: 'Prodotti',
|
||||
subtitle: 'Anagrafica di Marche e Modelli',
|
||||
icon: Icons.inventory_2_outlined,
|
||||
color: Colors.blue,
|
||||
onTap: () => onOpenPage(const ProductsScreen()),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
"Gestisci i dati fondamentali del tuo business",
|
||||
style: theme.textTheme.bodyLarge?.copyWith(
|
||||
// Se preferisci: color: context.secondaryText,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
_buildHubCard(
|
||||
context,
|
||||
title: 'Clienti',
|
||||
subtitle: 'Anagrafica dei clienti del tuo business',
|
||||
icon: Icons.people_outlined,
|
||||
color: Colors.orange,
|
||||
onTap: () => onOpenPage(const CustomersContent()),
|
||||
),
|
||||
_buildHubCard(
|
||||
context,
|
||||
title: 'Addetti',
|
||||
subtitle: 'Anagrafica del personale e dei collaboratori',
|
||||
icon: Icons.badge_outlined,
|
||||
color: Colors.teal,
|
||||
onTap: () => onOpenPage(const StaffScreen()),
|
||||
),
|
||||
_buildHubCard(
|
||||
context,
|
||||
title: 'Negozi',
|
||||
subtitle: 'Anagrafica punti vendita della tua azienda',
|
||||
icon: Icons.storefront_outlined,
|
||||
color: Colors.purple,
|
||||
onTap: () => onOpenPage(const StoresScreen()),
|
||||
),
|
||||
_buildHubCard(
|
||||
context,
|
||||
title:
|
||||
'Provider', // Accorciato per non andare a capo male su mobile
|
||||
subtitle: 'Anagrafica mandati e servizi',
|
||||
icon: Icons.handshake_rounded,
|
||||
color: Colors.indigo,
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
|
||||
Expanded(
|
||||
child: GridView(
|
||||
// MAGIA RESPONSIVA: invece di contare le colonne, diciamo "Ogni card
|
||||
// occupa massimo 350px". Su PC ne metterà 4, su Tablet 2, su Telefono 1 o 2.
|
||||
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 350,
|
||||
mainAxisSpacing: 16,
|
||||
crossAxisSpacing: 16,
|
||||
mainAxisExtent: 200, // Altezza fissa per impedire overflow
|
||||
),
|
||||
children: [
|
||||
_buildHubCard(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const ProvidersMasterDataScreen(),
|
||||
),
|
||||
);
|
||||
},
|
||||
title: 'Prodotti',
|
||||
subtitle: 'Anagrafica di Marche e Modelli',
|
||||
icon: Icons.inventory_2_outlined,
|
||||
color: Colors.blue,
|
||||
// Navighiamo dentro la Shell (la BottomBar rimane!)
|
||||
onTap: () => context.go('/master-data/products'),
|
||||
),
|
||||
_buildHubCard(
|
||||
context,
|
||||
title: 'Clienti',
|
||||
subtitle: 'Anagrafica dei clienti del tuo business',
|
||||
icon: Icons.people_outlined,
|
||||
color: Colors.orange,
|
||||
// Usiamo .push() perché avevamo detto che i clienti
|
||||
// stanno FUORI dalla Shell (niente BottomBar)
|
||||
onTap: () => context.push('/customers'),
|
||||
),
|
||||
_buildHubCard(
|
||||
context,
|
||||
title: 'Addetti',
|
||||
subtitle: 'Anagrafica del personale e collaboratori',
|
||||
icon: Icons.badge_outlined,
|
||||
color: Colors.teal,
|
||||
onTap: () => context.go('/master-data/staff'),
|
||||
),
|
||||
_buildHubCard(
|
||||
context,
|
||||
title: 'Negozi',
|
||||
subtitle: 'Anagrafica punti vendita della tua azienda',
|
||||
icon: Icons.storefront_outlined,
|
||||
color: Colors.purple,
|
||||
onTap: () => context.go('/master-data/stores'),
|
||||
),
|
||||
_buildHubCard(
|
||||
context,
|
||||
title: 'Provider',
|
||||
subtitle: 'Anagrafica mandati e servizi',
|
||||
icon: Icons.handshake_rounded,
|
||||
color: Colors.indigo,
|
||||
onTap: () => context.go('/master-data/providers'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHubCard(
|
||||
BuildContext context, {
|
||||
required String title,
|
||||
required String subtitle,
|
||||
required IconData icon,
|
||||
required Color color,
|
||||
required VoidCallback onTap,
|
||||
}) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return Card(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
elevation: 0, // Zero elevation, design più flat e moderno
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
// Stesso bordino elegante della Dashboard
|
||||
side: BorderSide(color: theme.dividerColor.withValues(alpha: 0.5)),
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withValues(alpha: 0.1), // Niente withOpacity! ❤️
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(icon, size: 36, color: color),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
title,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Expanded(
|
||||
child: Text(
|
||||
subtitle,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildHubCard(
|
||||
BuildContext context, {
|
||||
required String title,
|
||||
required String subtitle,
|
||||
required IconData icon,
|
||||
required Color color,
|
||||
required VoidCallback onTap,
|
||||
}) {
|
||||
return Card(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
elevation: 2,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
child: Padding(
|
||||
// Ridotto da 22 a 16 per dare più respiro orizzontale su mobile
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withValues(alpha: 0.1),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(icon, size: 40, color: color),
|
||||
),
|
||||
const SizedBox(height: 12), // Leggermente ridotto
|
||||
Text(
|
||||
title,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 4), // Leggermente ridotto
|
||||
Expanded(
|
||||
// Impedisce matematicamente l'overflow verticale
|
||||
child: Text(
|
||||
subtitle,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: 13, color: Colors.grey.shade500),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user