repair river station name and code from local db

This commit is contained in:
ALim Aidrus 2025-08-06 12:33:21 +08:00
parent 641c073d76
commit d2b3ca2bb0
2 changed files with 38 additions and 91 deletions

View File

@ -38,8 +38,7 @@ class _RiverInSituStep1SamplingInfoState extends State<RiverInSituStep1SamplingI
late final TextEditingController _currentLonController;
List<String> _statesList = [];
List<String> _categoriesForState = [];
List<Map<String, dynamic>> _stationsForCategory = [];
List<Map<String, dynamic>> _stationsForState = [];
final List<String> _samplingTypes = ['Schedule', 'Ad-Hoc', 'Complaint'];
@override
@ -94,20 +93,8 @@ class _RiverInSituStep1SamplingInfoState extends State<RiverInSituStep1SamplingI
states.sort();
if (widget.data.selectedStateName != null) {
final categories = allStations
_stationsForState = allStations
.where((s) => s['state_name'] == widget.data.selectedStateName)
.map((s) => s['category_name'] as String?)
.whereType<String>()
.toSet()
.toList();
categories.sort();
_categoriesForState = categories;
}
if (widget.data.selectedCategoryName != null) {
_stationsForCategory = allStations
.where((s) =>
s['state_name'] == widget.data.selectedStateName &&
s['category_name'] == widget.data.selectedCategoryName)
.toList();
}
@ -266,8 +253,6 @@ class _RiverInSituStep1SamplingInfoState extends State<RiverInSituStep1SamplingI
return Form(
key: _formKey,
child: ListView(
// REMOVED: physics property to allow scrolling.
// shrinkWrap: true, // This can be kept or removed; it's less critical in PageView.
padding: const EdgeInsets.all(24.0),
children: [
Text("Sampling Information", style: Theme.of(context).textTheme.headlineSmall),
@ -298,6 +283,21 @@ class _RiverInSituStep1SamplingInfoState extends State<RiverInSituStep1SamplingI
decoration: const InputDecoration(labelText: 'Sampling Type *'),
validator: (value) => value == null ? 'Please select a type' : null,
),
const SizedBox(height: 16),
TextFormField(
controller: _barcodeController,
decoration: InputDecoration(
labelText: 'Sample ID Code *',
suffixIcon: IconButton(
icon: const Icon(Icons.qr_code_scanner),
onPressed: _scanBarcode,
),
),
validator: (val) => val == null || val.isEmpty ? "Sample ID is required" : null,
onSaved: (val) => widget.data.sampleIdCode = val,
onChanged: (val) => widget.data.sampleIdCode = val,
),
const SizedBox(height: 24),
DropdownSearch<String>(
@ -308,64 +308,44 @@ class _RiverInSituStep1SamplingInfoState extends State<RiverInSituStep1SamplingI
onChanged: (state) {
setState(() {
widget.data.selectedStateName = state;
widget.data.selectedCategoryName = null;
widget.data.selectedStation = null;
_stationLatController.clear();
_stationLonController.clear();
widget.data.distanceDifferenceInKm = null;
final categories = state != null
_stationsForState = state != null
? allStations
.where((s) => s['state_name'] == state)
.map((s) => s['category_name'] as String?)
.whereType<String>()
.toSet()
.toList()
: <String>[];
categories.sort();
_categoriesForState = categories;
_stationsForCategory = [];
: [];
});
},
validator: (val) => val == null ? "State is required" : null,
),
const SizedBox(height: 16),
DropdownSearch<String>(
items: _categoriesForState,
selectedItem: widget.data.selectedCategoryName,
enabled: widget.data.selectedStateName != null,
popupProps: const PopupProps.menu(showSearchBox: true, searchFieldProps: TextFieldProps(decoration: InputDecoration(hintText: "Search Category..."))),
dropdownDecoratorProps: const DropDownDecoratorProps(dropdownSearchDecoration: InputDecoration(labelText: "Select Category *")),
onChanged: (category) {
setState(() {
widget.data.selectedCategoryName = category;
widget.data.selectedStation = null;
_stationLatController.clear();
_stationLonController.clear();
widget.data.distanceDifferenceInKm = null;
_stationsForCategory = category != null ? allStations.where((s) => s['state_name'] == widget.data.selectedStateName && s['category_name'] == category).toList() : [];
});
},
validator: (val) => widget.data.selectedStateName != null && val == null ? "Category is required" : null,
),
const SizedBox(height: 16),
DropdownSearch<Map<String, dynamic>>(
items: _stationsForCategory,
items: _stationsForState,
selectedItem: widget.data.selectedStation,
enabled: widget.data.selectedCategoryName != null,
itemAsString: (station) => "${station['r_man_station_code']} - ${station['r_man_station_name']}",
popupProps: const PopupProps.menu(showSearchBox: true, searchFieldProps: TextFieldProps(decoration: InputDecoration(hintText: "Search Station..."))),
dropdownDecoratorProps: const DropDownDecoratorProps(dropdownSearchDecoration: InputDecoration(labelText: "Select Station *")),
enabled: widget.data.selectedStateName != null,
itemAsString: (station) =>
"${station['sampling_station_code']} | ${station['sampling_river']} | ${station['sampling_basin']}",
popupProps: const PopupProps.menu(
showSearchBox: true,
searchFieldProps: TextFieldProps(
decoration: InputDecoration(hintText: "Search Station..."))),
dropdownDecoratorProps: const DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
labelText: "Select Station *")),
onChanged: (station) => setState(() {
widget.data.selectedStation = station;
widget.data.stationLatitude = station?['r_man_latitude']?.toString();
widget.data.stationLongitude = station?['r_man_longitude']?.toString();
widget.data.stationLatitude = station?['sampling_lat']?.toString();
widget.data.stationLongitude = station?['sampling_long']?.toString();
_stationLatController.text = widget.data.stationLatitude ?? '';
_stationLonController.text = widget.data.stationLongitude ?? '';
_calculateDistance();
}),
validator: (val) => widget.data.selectedCategoryName != null && val == null ? "Station is required" : null,
validator: (val) => widget.data.selectedStateName != null && val == null ? "Station is required" : null,
),
const SizedBox(height: 16),
TextFormField(controller: _stationLatController, readOnly: true, decoration: const InputDecoration(labelText: 'Station Latitude')),
@ -412,21 +392,6 @@ class _RiverInSituStep1SamplingInfoState extends State<RiverInSituStep1SamplingI
icon: _isLoadingLocation ? const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2)) : const Icon(Icons.location_searching),
label: const Text("Get Current Location"),
),
const SizedBox(height: 16),
TextFormField(
controller: _barcodeController,
decoration: InputDecoration(
labelText: 'Sample ID Code *',
suffixIcon: IconButton(
icon: const Icon(Icons.qr_code_scanner),
onPressed: _scanBarcode,
),
),
validator: (val) => val == null || val.isEmpty ? "Sample ID is required" : null,
onSaved: (val) => widget.data.sampleIdCode = val,
onChanged: (val) => widget.data.sampleIdCode = val,
),
const SizedBox(height: 32),
ElevatedButton(
onPressed: _goToNextStep,

View File

@ -1,5 +1,3 @@
// lib/screens/river/manual/widgets/river_in_situ_step_2_site_info.dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
@ -33,9 +31,8 @@ class _RiverInSituStep2SiteInfoState extends State<RiverInSituStep2SiteInfo> {
late final TextEditingController _optionalRemark3Controller;
late final TextEditingController _optionalRemark4Controller;
final List<String> _weatherOptions = ['Clear', 'Rainy', 'Cloudy'];
final List<String> _waterLevelOptions = ['Normal', 'High', 'Low'];
final List<String> _riverConditionOptions = ['Calm', 'Moderate Flow', 'Fast Flow'];
final List<String> _weatherOptions = ['Clear', 'Rainy', 'Cloudy', 'Windy', 'Sunny', 'Drizzle'];
// MODIFIED: Removed _waterLevelOptions and _riverConditionOptions
@override
void initState() {
@ -113,7 +110,6 @@ class _RiverInSituStep2SiteInfoState extends State<RiverInSituStep2SiteInfo> {
return Form(
key: _formKey,
child: ListView(
// REMOVED: physics property to allow scrolling.
padding: const EdgeInsets.all(24.0),
children: [
Text("On-Site Information", style: Theme.of(context).textTheme.headlineSmall),
@ -126,22 +122,8 @@ class _RiverInSituStep2SiteInfoState extends State<RiverInSituStep2SiteInfo> {
validator: (value) => value == null ? 'Weather is required' : null,
),
const SizedBox(height: 16),
DropdownButtonFormField<String>(
value: widget.data.waterLevel,
items: _waterLevelOptions.map((item) => DropdownMenuItem(value: item, child: Text(item))).toList(),
onChanged: (value) => setState(() => widget.data.waterLevel = value),
decoration: const InputDecoration(labelText: 'Water Level *'),
validator: (value) => value == null ? 'Water level is required' : null,
),
const SizedBox(height: 16),
DropdownButtonFormField<String>(
value: widget.data.riverCondition,
items: _riverConditionOptions.map((item) => DropdownMenuItem(value: item, child: Text(item))).toList(),
onChanged: (value) => setState(() => widget.data.riverCondition = value),
decoration: const InputDecoration(labelText: 'River Condition *'),
validator: (value) => value == null ? 'River condition is required' : null,
),
const SizedBox(height: 24),
// MODIFIED: The DropdownButtonFormField for 'Water Level' has been removed.
// MODIFIED: The DropdownButtonFormField for 'River Condition' has been removed.
Text("Required Photos *", style: Theme.of(context).textTheme.titleLarge),
const Text("All photos must be taken in landscape (horizontal) orientation.", style: TextStyle(color: Colors.grey)),