environment_monitoring_app/lib/screens/settings/station_info_settings.dart

372 lines
14 KiB
Dart

// lib/screens/settings/station_info_settings.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:environment_monitoring_app/auth_provider.dart';
class StationInfoSettingsScreen extends StatefulWidget {
const StationInfoSettingsScreen({super.key});
@override
State<StationInfoSettingsScreen> createState() =>
_StationInfoSettingsScreenState();
}
class _StationInfoSettingsScreenState extends State<StationInfoSettingsScreen> {
final TextEditingController _tarballSearchController =
TextEditingController();
String _tarballSearchQuery = '';
final TextEditingController _manualSearchController = TextEditingController();
String _manualSearchQuery = '';
final TextEditingController _riverManualSearchController =
TextEditingController();
String _riverManualSearchQuery = '';
final TextEditingController _riverTriennialSearchController =
TextEditingController();
String _riverTriennialSearchQuery = '';
final TextEditingController _airStationSearchController =
TextEditingController();
String _airStationSearchQuery = '';
@override
void initState() {
super.initState();
_tarballSearchController.addListener(_onTarballSearchChanged);
_manualSearchController.addListener(_onManualSearchChanged);
_riverManualSearchController.addListener(_onRiverManualSearchChanged);
_riverTriennialSearchController.addListener(_onRiverTriennialSearchChanged);
_airStationSearchController.addListener(_onAirStationSearchChanged);
}
@override
void dispose() {
_tarballSearchController.dispose();
_manualSearchController.dispose();
_riverManualSearchController.dispose();
_riverTriennialSearchController.dispose();
_airStationSearchController.dispose();
super.dispose();
}
void _onTarballSearchChanged() {
setState(() {
_tarballSearchQuery = _tarballSearchController.text;
});
}
void _onManualSearchChanged() {
setState(() {
_manualSearchQuery = _manualSearchController.text;
});
}
void _onRiverManualSearchChanged() {
setState(() {
_riverManualSearchQuery = _riverManualSearchController.text;
});
}
void _onRiverTriennialSearchChanged() {
setState(() {
_riverTriennialSearchQuery = _riverTriennialSearchController.text;
});
}
void _onAirStationSearchChanged() {
setState(() {
_airStationSearchQuery = _airStationSearchController.text;
});
}
Widget _buildSectionHeader(BuildContext context, String title) {
return Padding(
padding: const EdgeInsets.fromLTRB(8.0, 24.0, 8.0, 16.0),
child: Text(
title,
style: Theme.of(context)
.textTheme
.headlineSmall
?.copyWith(fontWeight: FontWeight.bold),
),
);
}
Widget _buildExpansionTile({
required String title,
required IconData leadingIcon,
required Widget child,
}) {
return ExpansionTile(
leading: Icon(leadingIcon),
title: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
initiallyExpanded: true,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: child,
),
],
);
}
Widget _buildSearchBar({
required TextEditingController controller,
required String labelText,
required String hintText,
}) {
return Padding(
padding: const EdgeInsets.only(bottom: 8.0, top: 8.0),
child: TextField(
controller: controller,
decoration: InputDecoration(
labelText: labelText,
hintText: hintText,
prefixIcon: const Icon(Icons.search),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8.0)),
suffixIcon: controller.text.isNotEmpty
? IconButton(
icon: const Icon(Icons.clear),
onPressed: () => controller.clear())
: null,
),
style: const TextStyle(fontSize: 14),
),
);
}
Widget _buildStationList(
List<Map<String, dynamic>>? stations,
String noMatchText,
String noDataText,
Widget Function(Map<String, dynamic>) itemBuilder,
{double height = 250}) {
if (stations == null || stations.isEmpty) {
return Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
stations == null ? noDataText : noMatchText,
textAlign: TextAlign.center,
),
),
);
}
return SizedBox(
height: height,
child: ListView.builder(
itemCount: stations.length,
itemBuilder: (context, index) {
final station = stations[index];
return itemBuilder(station);
},
),
);
}
Widget _buildStationTile(
{required String title,
required String subtitle,
required String type}) {
return ListTile(
title: Text(title, style: const TextStyle(fontSize: 14)),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(subtitle, style: const TextStyle(fontSize: 12)),
Text('Type: $type',
style:
const TextStyle(fontSize: 12, fontStyle: FontStyle.italic)),
],
),
dense: true,
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Station Info"),
),
body: Consumer<AuthProvider>(
builder: (context, auth, child) {
final filteredTarballStations = (auth.tarballStations?.where((station) {
final stationName = station['tbl_station_name']?.toLowerCase() ?? '';
final stationCode = station['tbl_station_code']?.toLowerCase() ?? '';
final query = _tarballSearchQuery.toLowerCase();
return stationName.contains(query) || stationCode.contains(query);
}).toList())
?.cast<Map<String, dynamic>>();
final filteredManualStations = (auth.manualStations?.where((station) {
final stationName = station['man_station_name']?.toLowerCase() ?? '';
final stationCode = station['man_station_code']?.toLowerCase() ?? '';
final query = _manualSearchQuery.toLowerCase();
return stationName.contains(query) || stationCode.contains(query);
}).toList())
?.cast<Map<String, dynamic>>();
final filteredRiverManualStations =
(auth.riverManualStations?.where((station) {
final riverName = station['sampling_river']?.toLowerCase() ?? '';
final stationCode =
station['sampling_station_code']?.toLowerCase() ?? '';
final basinName = station['sampling_basin']?.toLowerCase() ?? '';
final query = _riverManualSearchQuery.toLowerCase();
return riverName.contains(query) ||
stationCode.contains(query) ||
basinName.contains(query);
}).toList())
?.cast<Map<String, dynamic>>();
final filteredRiverTriennialStations =
(auth.riverTriennialStations?.where((station) {
final riverName = station['triennial_river']?.toLowerCase() ?? '';
final stationCode =
station['triennial_station_code']?.toLowerCase() ?? '';
final basinName = station['triennial_basin']?.toLowerCase() ?? '';
final query = _riverTriennialSearchQuery.toLowerCase();
return riverName.contains(query) ||
stationCode.contains(query) ||
basinName.contains(query);
}).toList())
?.cast<Map<String, dynamic>>();
final filteredAirStations = (auth.airManualStations?.where((station) {
final stationName = station['station_name']?.toLowerCase() ?? '';
final stationCode = station['station_code']?.toLowerCase() ?? '';
final query = _airStationSearchQuery.toLowerCase();
return stationName.contains(query) || stationCode.contains(query);
}).toList())
?.cast<Map<String, dynamic>>();
return ListView(
padding: const EdgeInsets.all(16.0),
children: [
_buildSectionHeader(context, "Stations Info"),
Card(
margin: EdgeInsets.zero,
child: Column(
children: [
_buildExpansionTile(
title: 'Marine Stations',
leadingIcon: Icons.waves,
child: Column(
children: [
_buildSearchBar(
controller: _tarballSearchController,
labelText: 'Search Tarball Stations',
hintText: 'Search by name or code',
),
const SizedBox(height: 16),
_buildStationList(
filteredTarballStations,
'No matching tarball stations found.',
'No tarball stations available. Sync to download.',
(station) => _buildStationTile(
title: station['tbl_station_name'] ?? 'N/A',
subtitle:
'Code: ${station['tbl_station_code'] ?? 'N/A'}',
type: 'Tarball'),
height: 250,
),
const SizedBox(height: 16),
_buildSearchBar(
controller: _manualSearchController,
labelText: 'Search Manual Stations',
hintText: 'Search by name or code',
),
const SizedBox(height: 16),
_buildStationList(
filteredManualStations,
'No matching manual stations found.',
'No manual stations available. Sync to download.',
(station) => _buildStationTile(
title: station['man_station_name'] ?? 'N/A',
subtitle:
'Code: ${station['man_station_code'] ?? 'N/A'}',
type: 'Manual'),
height: 250,
),
],
),
),
_buildExpansionTile(
title: 'River Stations',
leadingIcon: Icons.water,
child: Column(
children: [
_buildSearchBar(
controller: _riverManualSearchController,
labelText: 'Search River Manual Stations',
hintText: 'Search by name, code, or basin',
),
const SizedBox(height: 16),
_buildStationList(
filteredRiverManualStations,
'No matching river manual stations found.',
'No river manual stations available. Sync to download.',
(station) => _buildStationTile(
title: station['sampling_river'] ?? 'N/A',
subtitle:
'Code: ${station['sampling_station_code'] ?? 'N/A'}, Basin: ${station['sampling_basin'] ?? 'N/A'}',
type: 'River Manual'),
height: 250,
),
const SizedBox(height: 16),
_buildSearchBar(
controller: _riverTriennialSearchController,
labelText: 'Search River Triennial Stations',
hintText: 'Search by name, code, or basin',
),
const SizedBox(height: 16),
_buildStationList(
filteredRiverTriennialStations,
'No matching river triennial stations found.',
'No river triennial stations available. Sync to download.',
(station) => _buildStationTile(
title: station['triennial_river'] ?? 'N/A',
subtitle:
'Code: ${station['triennial_station_code'] ?? 'N/A'}, Basin: ${station['triennial_basin'] ?? 'N/A'}',
type: 'River Triennial'),
height: 250,
),
],
),
),
_buildExpansionTile(
title: 'Air Stations',
leadingIcon: Icons.air,
child: Column(
children: [
_buildSearchBar(
controller: _airStationSearchController,
labelText: 'Search Air Stations',
hintText: 'Search by name or code',
),
const SizedBox(height: 16),
_buildStationList(
filteredAirStations,
'No matching air stations found.',
'No air stations available. Sync to download.',
(station) => _buildStationTile(
title: station['station_name'] ?? 'N/A',
subtitle:
'Code: ${station['station_code'] ?? 'N/A'}',
type: 'Air'),
height: 250,
),
],
),
),
],
),
),
],
);
},
),
);
}
}