app mobile allarmi prima

This commit is contained in:
2025-10-20 19:17:45 +02:00
commit 300912ee02
159 changed files with 11755 additions and 0 deletions

View File

@@ -0,0 +1,221 @@
import 'package:flutter/material.dart';
import '../models/sito.dart';
import '../services/api_service.dart';
import '../widgets/sito_card.dart';
import '../utils/constants.dart';
class SitiScreen extends StatefulWidget {
const SitiScreen({super.key});
@override
State<SitiScreen> createState() => _SitiScreenState();
}
class _SitiScreenState extends State<SitiScreen> {
final _apiService = ApiService();
List<Sito> _siti = [];
List<Sito> _sitiFiltered = [];
bool _isLoading = true;
String? _errorMessage;
String? _selectedTipo;
final Map<String, String> _tipiSito = {
'ponte': 'Ponte',
'galleria': 'Galleria',
'diga': 'Diga',
'frana': 'Frana',
'versante': 'Versante',
'edificio': 'Edificio',
};
@override
void initState() {
super.initState();
_loadSiti();
}
Future<void> _loadSiti({bool refresh = false}) async {
if (refresh) setState(() => _isLoading = true);
try {
final siti = await _apiService.getSiti();
setState(() {
_siti = siti;
_applyFilter();
_isLoading = false;
_errorMessage = null;
});
} catch (e) {
setState(() {
_errorMessage = 'Errore caricamento siti: $e';
_isLoading = false;
});
}
}
void _applyFilter() {
if (_selectedTipo == null) {
_sitiFiltered = _siti;
} else {
_sitiFiltered = _siti.where((s) => s.tipo == _selectedTipo).toList();
}
}
void _showFilterDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Filtra per Tipo'),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildFilterOption(null, 'Tutti i siti', Icons.all_inclusive),
const Divider(),
..._tipiSito.entries.map((entry) {
return _buildFilterOption(
entry.key,
entry.value,
_getIconForTipo(entry.key),
);
}),
],
),
),
),
);
}
Widget _buildFilterOption(String? tipo, String label, IconData icon) {
final isSelected = _selectedTipo == tipo;
return ListTile(
leading: Icon(
icon,
color: isSelected ? AppColors.primary : AppColors.textSecondary,
),
title: Text(
label,
style: TextStyle(
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
color: isSelected ? AppColors.primary : null,
),
),
trailing: isSelected
? const Icon(Icons.check, color: AppColors.primary)
: null,
onTap: () {
Navigator.pop(context);
setState(() {
_selectedTipo = tipo;
_applyFilter();
});
},
);
}
IconData _getIconForTipo(String tipo) {
switch (tipo) {
case 'ponte':
return Icons.architecture;
case 'galleria':
return Icons.south_west;
case 'diga':
return Icons.water_damage;
case 'frana':
return Icons.landscape;
case 'versante':
return Icons.terrain;
case 'edificio':
return Icons.business;
default:
return Icons.location_on;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Siti Monitorati'),
Text(
_selectedTipo != null
? _tipiSito[_selectedTipo]!
: '${_siti.length} siti totali',
style: const TextStyle(fontSize: 12),
),
],
),
actions: [
IconButton(
icon: const Icon(Icons.filter_list),
onPressed: _showFilterDialog,
tooltip: 'Filtra per tipo',
),
],
),
body: _isLoading
? const Center(child: CircularProgressIndicator())
: _errorMessage != null
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.error_outline,
size: 64,
color: AppColors.critical,
),
const SizedBox(height: 16),
Text(_errorMessage!),
const SizedBox(height: 16),
ElevatedButton.icon(
onPressed: () => _loadSiti(refresh: true),
icon: const Icon(Icons.refresh),
label: const Text('Riprova'),
),
],
),
)
: _sitiFiltered.isEmpty
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.location_off,
size: 64,
color: AppColors.textSecondary,
),
const SizedBox(height: 16),
Text(
_selectedTipo != null
? 'Nessun sito di tipo ${_tipiSito[_selectedTipo]}'
: 'Nessun sito disponibile',
style: AppTextStyles.h3,
),
],
),
)
: RefreshIndicator(
onRefresh: () => _loadSiti(refresh: true),
child: ListView.builder(
padding: const EdgeInsets.all(AppSizes.paddingM),
itemCount: _sitiFiltered.length,
itemBuilder: (context, index) {
return SitoCard(sito: _sitiFiltered[index]);
},
),
),
floatingActionButton: _isLoading
? null
: FloatingActionButton(
onPressed: () => _loadSiti(refresh: true),
child: const Icon(Icons.refresh),
),
);
}
}