372 lines
14 KiB
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,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
} |