add in river flowrate menu under parameter sampling for river station
This commit is contained in:
parent
c7b97ecd1a
commit
e90f4972cc
@ -61,6 +61,15 @@ class RiverInSituSamplingData {
|
|||||||
double? tss;
|
double? tss;
|
||||||
double? batteryVoltage;
|
double? batteryVoltage;
|
||||||
|
|
||||||
|
// ADDED: New properties for Flowrate
|
||||||
|
String? flowrateMethod; // 'Surface Drifter', 'Flowmeter', 'NA'
|
||||||
|
double? flowrateSurfaceDrifterHeight;
|
||||||
|
double? flowrateSurfaceDrifterDistance;
|
||||||
|
String? flowrateSurfaceDrifterTimeFirst;
|
||||||
|
String? flowrateSurfaceDrifterTimeLast;
|
||||||
|
double? flowrateValue;
|
||||||
|
|
||||||
|
|
||||||
// --- Post-Submission Status ---
|
// --- Post-Submission Status ---
|
||||||
String? submissionStatus;
|
String? submissionStatus;
|
||||||
String? submissionMessage;
|
String? submissionMessage;
|
||||||
@ -71,15 +80,11 @@ class RiverInSituSamplingData {
|
|||||||
this.samplingTime,
|
this.samplingTime,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// ADDED: Factory constructor to create an instance from a map (JSON).
|
|
||||||
/// This is the required fix for the "fromJson isn't defined" error.
|
|
||||||
factory RiverInSituSamplingData.fromJson(Map<String, dynamic> json) {
|
factory RiverInSituSamplingData.fromJson(Map<String, dynamic> json) {
|
||||||
// Helper function to safely create a File object from a path string
|
|
||||||
File? fileFromJson(dynamic path) {
|
File? fileFromJson(dynamic path) {
|
||||||
return (path is String && path.isNotEmpty) ? File(path) : null;
|
return (path is String && path.isNotEmpty) ? File(path) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to safely parse numbers
|
|
||||||
double? doubleFromJson(dynamic value) {
|
double? doubleFromJson(dynamic value) {
|
||||||
if (value is num) return value.toDouble();
|
if (value is num) return value.toDouble();
|
||||||
if (value is String) return double.tryParse(value);
|
if (value is String) return double.tryParse(value);
|
||||||
@ -130,7 +135,14 @@ class RiverInSituSamplingData {
|
|||||||
..optionalImage1 = fileFromJson(json['r_man_optional_photo_01'])
|
..optionalImage1 = fileFromJson(json['r_man_optional_photo_01'])
|
||||||
..optionalImage2 = fileFromJson(json['r_man_optional_photo_02'])
|
..optionalImage2 = fileFromJson(json['r_man_optional_photo_02'])
|
||||||
..optionalImage3 = fileFromJson(json['r_man_optional_photo_03'])
|
..optionalImage3 = fileFromJson(json['r_man_optional_photo_03'])
|
||||||
..optionalImage4 = fileFromJson(json['r_man_optional_photo_04']);
|
..optionalImage4 = fileFromJson(json['r_man_optional_photo_04'])
|
||||||
|
// ADDED: Flowrate fields from JSON
|
||||||
|
..flowrateMethod = json['r_man_flowrate_method']
|
||||||
|
..flowrateSurfaceDrifterHeight = doubleFromJson(json['r_man_flowrate_sd_height'])
|
||||||
|
..flowrateSurfaceDrifterDistance = doubleFromJson(json['r_man_flowrate_sd_distance'])
|
||||||
|
..flowrateSurfaceDrifterTimeFirst = json['r_man_flowrate_sd_time_first']
|
||||||
|
..flowrateSurfaceDrifterTimeLast = json['r_man_flowrate_sd_time_last']
|
||||||
|
..flowrateValue = doubleFromJson(json['r_man_flowrate_value']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -183,6 +195,15 @@ class RiverInSituSamplingData {
|
|||||||
add('r_man_tss', tss);
|
add('r_man_tss', tss);
|
||||||
add('r_man_battery_volt', batteryVoltage);
|
add('r_man_battery_volt', batteryVoltage);
|
||||||
|
|
||||||
|
// ADDED: Flowrate fields to API form data
|
||||||
|
add('r_man_flowrate_method', flowrateMethod);
|
||||||
|
add('r_man_flowrate_sd_height', flowrateSurfaceDrifterHeight);
|
||||||
|
add('r_man_flowrate_sd_distance', flowrateSurfaceDrifterDistance);
|
||||||
|
add('r_man_flowrate_sd_time_first', flowrateSurfaceDrifterTimeFirst);
|
||||||
|
add('r_man_flowrate_sd_time_last', flowrateSurfaceDrifterTimeLast);
|
||||||
|
add('r_man_flowrate_value', flowrateValue);
|
||||||
|
|
||||||
|
|
||||||
// Additional data for display or logging
|
// Additional data for display or logging
|
||||||
add('first_sampler_name', firstSamplerName);
|
add('first_sampler_name', firstSamplerName);
|
||||||
add('r_man_station_code', selectedStation?['sampling_station_code']);
|
add('r_man_station_code', selectedStation?['sampling_station_code']);
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
|
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
|
||||||
import 'package:usb_serial/usb_serial.dart';
|
import 'package:usb_serial/usb_serial.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
import '../../../../models/river_in_situ_sampling_data.dart';
|
import '../../../../models/river_in_situ_sampling_data.dart';
|
||||||
import '../../../../services/river_in_situ_sampling_service.dart';
|
import '../../../../services/river_in_situ_sampling_service.dart';
|
||||||
@ -13,7 +14,6 @@ import '../../../../serial/serial_manager.dart';
|
|||||||
import '../../../../bluetooth/widgets/bluetooth_device_list_dialog.dart';
|
import '../../../../bluetooth/widgets/bluetooth_device_list_dialog.dart';
|
||||||
import '../../../../serial/widget/serial_port_list_dialog.dart';
|
import '../../../../serial/widget/serial_port_list_dialog.dart';
|
||||||
|
|
||||||
// UPDATED: Class name changed from RiverInSituStep2DataCapture to RiverInSituStep3DataCapture
|
|
||||||
class RiverInSituStep3DataCapture extends StatefulWidget {
|
class RiverInSituStep3DataCapture extends StatefulWidget {
|
||||||
final RiverInSituSamplingData data;
|
final RiverInSituSamplingData data;
|
||||||
final VoidCallback onNext;
|
final VoidCallback onNext;
|
||||||
@ -25,11 +25,9 @@ class RiverInSituStep3DataCapture extends StatefulWidget {
|
|||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
// UPDATED: State class reference
|
|
||||||
State<RiverInSituStep3DataCapture> createState() => _RiverInSituStep3DataCaptureState();
|
State<RiverInSituStep3DataCapture> createState() => _RiverInSituStep3DataCaptureState();
|
||||||
}
|
}
|
||||||
|
|
||||||
// UPDATED: State class name
|
|
||||||
class _RiverInSituStep3DataCaptureState extends State<RiverInSituStep3DataCapture> {
|
class _RiverInSituStep3DataCaptureState extends State<RiverInSituStep3DataCapture> {
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
@ -38,6 +36,7 @@ class _RiverInSituStep3DataCaptureState extends State<RiverInSituStep3DataCaptur
|
|||||||
|
|
||||||
final List<Map<String, dynamic>> _parameters = [];
|
final List<Map<String, dynamic>> _parameters = [];
|
||||||
|
|
||||||
|
// Sonde parameter controllers
|
||||||
final _sondeIdController = TextEditingController();
|
final _sondeIdController = TextEditingController();
|
||||||
final _dateController = TextEditingController();
|
final _dateController = TextEditingController();
|
||||||
final _timeController = TextEditingController();
|
final _timeController = TextEditingController();
|
||||||
@ -52,16 +51,26 @@ class _RiverInSituStep3DataCaptureState extends State<RiverInSituStep3DataCaptur
|
|||||||
final _tssController = TextEditingController();
|
final _tssController = TextEditingController();
|
||||||
final _batteryController = TextEditingController();
|
final _batteryController = TextEditingController();
|
||||||
|
|
||||||
|
// ADDED: Flowrate controllers and state
|
||||||
|
String? _selectedFlowrateMethod;
|
||||||
|
final _flowrateValueController = TextEditingController();
|
||||||
|
final _sdHeightController = TextEditingController();
|
||||||
|
final _sdDistanceController = TextEditingController();
|
||||||
|
final _sdTimeFirstController = TextEditingController();
|
||||||
|
final _sdTimeLastController = TextEditingController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_initializeControllers();
|
_initializeControllers();
|
||||||
|
_initializeFlowrateControllers();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_dataSubscription?.cancel();
|
_dataSubscription?.cancel();
|
||||||
_disposeControllers();
|
_disposeControllers();
|
||||||
|
_disposeFlowrateControllers();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,27 +82,16 @@ class _RiverInSituStep3DataCaptureState extends State<RiverInSituStep3DataCaptur
|
|||||||
_dateController.text = widget.data.dataCaptureDate ?? '';
|
_dateController.text = widget.data.dataCaptureDate ?? '';
|
||||||
_timeController.text = widget.data.dataCaptureTime ?? '';
|
_timeController.text = widget.data.dataCaptureTime ?? '';
|
||||||
|
|
||||||
widget.data.oxygenConcentration ??= -999.0;
|
_oxyConcController.text = widget.data.oxygenConcentration?.toString() ?? '-999.0';
|
||||||
widget.data.oxygenSaturation ??= -999.0;
|
_oxySatController.text = widget.data.oxygenSaturation?.toString() ?? '-999.0';
|
||||||
widget.data.ph ??= -999.0;
|
_phController.text = widget.data.ph?.toString() ?? '-999.0';
|
||||||
widget.data.salinity ??= -999.0;
|
_salinityController.text = widget.data.salinity?.toString() ?? '-999.0';
|
||||||
widget.data.electricalConductivity ??= -999.0;
|
_ecController.text = widget.data.electricalConductivity?.toString() ?? '-999.0';
|
||||||
widget.data.temperature ??= -999.0;
|
_tempController.text = widget.data.temperature?.toString() ?? '-999.0';
|
||||||
widget.data.tds ??= -999.0;
|
_tdsController.text = widget.data.tds?.toString() ?? '-999.0';
|
||||||
widget.data.turbidity ??= -999.0;
|
_turbidityController.text = widget.data.turbidity?.toString() ?? '-999.0';
|
||||||
widget.data.tss ??= -999.0;
|
_tssController.text = widget.data.tss?.toString() ?? '-999.0';
|
||||||
widget.data.batteryVoltage ??= -999.0;
|
_batteryController.text = widget.data.batteryVoltage?.toString() ?? '-999.0';
|
||||||
|
|
||||||
_oxyConcController.text = widget.data.oxygenConcentration!.toString();
|
|
||||||
_oxySatController.text = widget.data.oxygenSaturation!.toString();
|
|
||||||
_phController.text = widget.data.ph!.toString();
|
|
||||||
_salinityController.text = widget.data.salinity!.toString();
|
|
||||||
_ecController.text = widget.data.electricalConductivity!.toString();
|
|
||||||
_tempController.text = widget.data.temperature!.toString();
|
|
||||||
_tdsController.text = widget.data.tds!.toString();
|
|
||||||
_turbidityController.text = widget.data.turbidity!.toString();
|
|
||||||
_tssController.text = widget.data.tss!.toString();
|
|
||||||
_batteryController.text = widget.data.batteryVoltage!.toString();
|
|
||||||
|
|
||||||
if (_parameters.isEmpty) {
|
if (_parameters.isEmpty) {
|
||||||
_parameters.addAll([
|
_parameters.addAll([
|
||||||
@ -127,6 +125,85 @@ class _RiverInSituStep3DataCaptureState extends State<RiverInSituStep3DataCaptur
|
|||||||
_batteryController.dispose();
|
_batteryController.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- START: Flowrate Logic ---
|
||||||
|
void _initializeFlowrateControllers() {
|
||||||
|
_selectedFlowrateMethod = widget.data.flowrateMethod;
|
||||||
|
_flowrateValueController.text = widget.data.flowrateValue?.toString() ?? '';
|
||||||
|
_sdHeightController.text = widget.data.flowrateSurfaceDrifterHeight?.toString() ?? '';
|
||||||
|
_sdDistanceController.text = widget.data.flowrateSurfaceDrifterDistance?.toString() ?? '';
|
||||||
|
_sdTimeFirstController.text = widget.data.flowrateSurfaceDrifterTimeFirst ?? '';
|
||||||
|
_sdTimeLastController.text = widget.data.flowrateSurfaceDrifterTimeLast ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
void _disposeFlowrateControllers() {
|
||||||
|
_flowrateValueController.dispose();
|
||||||
|
_sdHeightController.dispose();
|
||||||
|
_sdDistanceController.dispose();
|
||||||
|
_sdTimeFirstController.dispose();
|
||||||
|
_sdTimeLastController.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onFlowrateMethodChanged(String? value) {
|
||||||
|
setState(() {
|
||||||
|
_selectedFlowrateMethod = value;
|
||||||
|
if (value == 'NA') {
|
||||||
|
_flowrateValueController.text = 'NA';
|
||||||
|
} else if (value == 'Flowmeter') {
|
||||||
|
_flowrateValueController.clear();
|
||||||
|
} else {
|
||||||
|
// Clear calculated value when switching back to Surface Drifter
|
||||||
|
_flowrateValueController.clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _calculateFlowrate() {
|
||||||
|
final distance = double.tryParse(_sdDistanceController.text);
|
||||||
|
final timeFirstStr = _sdTimeFirstController.text;
|
||||||
|
final timeLastStr = _sdTimeLastController.text;
|
||||||
|
|
||||||
|
if (distance == null || timeFirstStr.isEmpty || timeLastStr.isEmpty) {
|
||||||
|
_showSnackBar("Please fill in Distance, Time First, and Time Last.", isError: true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final timeFormat = DateFormat("HH:mm:ss");
|
||||||
|
final timeFirst = timeFormat.parse(timeFirstStr);
|
||||||
|
final timeLast = timeFormat.parse(timeLastStr);
|
||||||
|
|
||||||
|
final differenceInSeconds = timeLast.difference(timeFirst).inSeconds;
|
||||||
|
|
||||||
|
if (differenceInSeconds <= 0) {
|
||||||
|
_showSnackBar("Time Last Deploy must be after Time First Deploy.", isError: true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final flowrate = distance / differenceInSeconds;
|
||||||
|
setState(() {
|
||||||
|
_flowrateValueController.text = flowrate.toStringAsFixed(4);
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
_showSnackBar("Invalid time format. Please use HH:mm:ss.", isError: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _selectTime(BuildContext context, TextEditingController controller) async {
|
||||||
|
final TimeOfDay? picked = await showTimePicker(
|
||||||
|
context: context,
|
||||||
|
initialTime: TimeOfDay.now(),
|
||||||
|
);
|
||||||
|
if (picked != null) {
|
||||||
|
final now = DateTime.now();
|
||||||
|
final dt = DateTime(now.year, now.month, now.day, picked.hour, picked.minute);
|
||||||
|
setState(() {
|
||||||
|
controller.text = DateFormat('HH:mm:ss').format(dt);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// --- END: Flowrate Logic ---
|
||||||
|
|
||||||
|
|
||||||
Future<void> _handleConnectionAttempt(String type) async {
|
Future<void> _handleConnectionAttempt(String type) async {
|
||||||
final service = context.read<RiverInSituSamplingService>();
|
final service = context.read<RiverInSituSamplingService>();
|
||||||
final bool hasPermissions = await service.requestDevicePermissions();
|
final bool hasPermissions = await service.requestDevicePermissions();
|
||||||
@ -268,6 +345,21 @@ class _RiverInSituStep3DataCaptureState extends State<RiverInSituStep3DataCaptur
|
|||||||
widget.data.turbidity = double.tryParse(_turbidityController.text) ?? defaultValue;
|
widget.data.turbidity = double.tryParse(_turbidityController.text) ?? defaultValue;
|
||||||
widget.data.tss = double.tryParse(_tssController.text) ?? defaultValue;
|
widget.data.tss = double.tryParse(_tssController.text) ?? defaultValue;
|
||||||
widget.data.batteryVoltage = double.tryParse(_batteryController.text) ?? defaultValue;
|
widget.data.batteryVoltage = double.tryParse(_batteryController.text) ?? defaultValue;
|
||||||
|
|
||||||
|
// Save flowrate data
|
||||||
|
widget.data.flowrateMethod = _selectedFlowrateMethod;
|
||||||
|
if (_selectedFlowrateMethod == 'Surface Drifter') {
|
||||||
|
widget.data.flowrateSurfaceDrifterHeight = double.tryParse(_sdHeightController.text);
|
||||||
|
widget.data.flowrateSurfaceDrifterDistance = double.tryParse(_sdDistanceController.text);
|
||||||
|
widget.data.flowrateSurfaceDrifterTimeFirst = _sdTimeFirstController.text;
|
||||||
|
widget.data.flowrateSurfaceDrifterTimeLast = _sdTimeLastController.text;
|
||||||
|
widget.data.flowrateValue = double.tryParse(_flowrateValueController.text);
|
||||||
|
} else if (_selectedFlowrateMethod == 'Flowmeter') {
|
||||||
|
widget.data.flowrateValue = double.tryParse(_flowrateValueController.text);
|
||||||
|
} else { // NA
|
||||||
|
widget.data.flowrateValue = null;
|
||||||
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_showSnackBar("Could not save parameters due to a data format error.", isError: true);
|
_showSnackBar("Could not save parameters due to a data format error.", isError: true);
|
||||||
return;
|
return;
|
||||||
@ -353,7 +445,6 @@ class _RiverInSituStep3DataCaptureState extends State<RiverInSituStep3DataCaptur
|
|||||||
valueListenable: service.sondeId,
|
valueListenable: service.sondeId,
|
||||||
builder: (context, sondeId, child) {
|
builder: (context, sondeId, child) {
|
||||||
final newSondeId = sondeId ?? '';
|
final newSondeId = sondeId ?? '';
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (mounted && _sondeIdController.text != newSondeId) {
|
if (mounted && _sondeIdController.text != newSondeId) {
|
||||||
_sondeIdController.text = newSondeId;
|
_sondeIdController.text = newSondeId;
|
||||||
@ -395,6 +486,11 @@ class _RiverInSituStep3DataCaptureState extends State<RiverInSituStep3DataCaptur
|
|||||||
}).toList(),
|
}).toList(),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
const Divider(height: 32),
|
||||||
|
// --- START: Flowrate Section ---
|
||||||
|
_buildFlowrateSection(),
|
||||||
|
// --- END: Flowrate Section ---
|
||||||
|
|
||||||
const SizedBox(height: 32),
|
const SizedBox(height: 32),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: _goToNextStep,
|
onPressed: _goToNextStep,
|
||||||
@ -476,4 +572,116 @@ class _RiverInSituStep3DataCaptureState extends State<RiverInSituStep3DataCaptur
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ADDED: Widget for the entire Flowrate section
|
||||||
|
Widget _buildFlowrateSection() {
|
||||||
|
return Card(
|
||||||
|
margin: const EdgeInsets.symmetric(vertical: 4.0),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text("Flowrate", style: Theme.of(context).textTheme.titleLarge),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: [
|
||||||
|
_buildFlowrateRadioButton("Surface Drifter"),
|
||||||
|
_buildFlowrateRadioButton("Flowmeter"),
|
||||||
|
_buildFlowrateRadioButton("NA"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (_selectedFlowrateMethod == 'Surface Drifter')
|
||||||
|
_buildSurfaceDrifterFields(),
|
||||||
|
if (_selectedFlowrateMethod == 'Flowmeter')
|
||||||
|
_buildFlowmeterField(),
|
||||||
|
if (_selectedFlowrateMethod == 'NA')
|
||||||
|
_buildNAField(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildFlowrateRadioButton(String title) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Radio<String>(
|
||||||
|
value: title,
|
||||||
|
groupValue: _selectedFlowrateMethod,
|
||||||
|
onChanged: _onFlowrateMethodChanged,
|
||||||
|
),
|
||||||
|
Text(title),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSurfaceDrifterFields() {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 16.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
TextFormField(
|
||||||
|
controller: _sdHeightController,
|
||||||
|
decoration: const InputDecoration(labelText: 'Height (m)'),
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
TextFormField(
|
||||||
|
controller: _sdDistanceController,
|
||||||
|
decoration: const InputDecoration(labelText: 'Distance (m)'),
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
TextFormField(
|
||||||
|
controller: _sdTimeFirstController,
|
||||||
|
decoration: const InputDecoration(labelText: 'Time First Deploy (HH:mm:ss)', suffixIcon: Icon(Icons.timer)),
|
||||||
|
readOnly: true,
|
||||||
|
onTap: () => _selectTime(context, _sdTimeFirstController),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
TextFormField(
|
||||||
|
controller: _sdTimeLastController,
|
||||||
|
decoration: const InputDecoration(labelText: 'Time Last Deploy (HH:mm:ss)', suffixIcon: Icon(Icons.timer)),
|
||||||
|
readOnly: true,
|
||||||
|
onTap: () => _selectTime(context, _sdTimeLastController),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _calculateFlowrate,
|
||||||
|
child: const Text('Get Flowrate'),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
TextFormField(
|
||||||
|
controller: _flowrateValueController,
|
||||||
|
decoration: const InputDecoration(labelText: 'Flowrate (m/s)'),
|
||||||
|
readOnly: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildFlowmeterField() {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 16.0),
|
||||||
|
child: TextFormField(
|
||||||
|
controller: _flowrateValueController,
|
||||||
|
decoration: const InputDecoration(labelText: 'Flowrate (m/s)'),
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildNAField() {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 16.0),
|
||||||
|
child: TextFormField(
|
||||||
|
controller: _flowrateValueController,
|
||||||
|
decoration: const InputDecoration(labelText: 'Flowrate (m/s)'),
|
||||||
|
readOnly: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -46,7 +46,6 @@ class RiverInSituStep5Summary extends StatelessWidget {
|
|||||||
"${data.selectedStation?['sampling_station_code']} | ${data.selectedStation?['sampling_river']} | ${data.selectedStation?['sampling_basin']}"
|
"${data.selectedStation?['sampling_station_code']} | ${data.selectedStation?['sampling_river']} | ${data.selectedStation?['sampling_basin']}"
|
||||||
),
|
),
|
||||||
_buildDetailRow("Station Location:", "${data.stationLatitude}, ${data.stationLongitude}"),
|
_buildDetailRow("Station Location:", "${data.stationLatitude}, ${data.stationLongitude}"),
|
||||||
// REMOVED: Weather and remarks moved to the next section.
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -60,26 +59,25 @@ class RiverInSituStep5Summary extends StatelessWidget {
|
|||||||
_buildDetailRow("Distance Remarks:", data.distanceDifferenceRemarks),
|
_buildDetailRow("Distance Remarks:", data.distanceDifferenceRemarks),
|
||||||
const Divider(height: 20),
|
const Divider(height: 20),
|
||||||
|
|
||||||
// ADDED: Display for Weather and Remarks.
|
|
||||||
_buildDetailRow("Weather:", data.weather),
|
_buildDetailRow("Weather:", data.weather),
|
||||||
_buildDetailRow("Event Remarks:", data.eventRemarks),
|
_buildDetailRow("Event Remarks:", data.eventRemarks),
|
||||||
_buildDetailRow("Lab Remarks:", data.labRemarks),
|
_buildDetailRow("Lab Remarks:", data.labRemarks),
|
||||||
const Divider(height: 20),
|
const Divider(height: 20),
|
||||||
|
|
||||||
// UPDATED: Image cards reflect new names and data properties.
|
|
||||||
_buildImageCard("Background Station", data.backgroundStationImage),
|
_buildImageCard("Background Station", data.backgroundStationImage),
|
||||||
_buildImageCard("Upstream River", data.upstreamRiverImage),
|
_buildImageCard("Upstream River", data.upstreamRiverImage),
|
||||||
_buildImageCard("Downstream River", data.downstreamRiverImage),
|
_buildImageCard("Downstream River", data.downstreamRiverImage),
|
||||||
_buildImageCard("Sample Turbidity", data.sampleTurbidityImage),
|
|
||||||
|
|
||||||
// REMOVED: pH paper image card.
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
_buildSectionCard(
|
_buildSectionCard(
|
||||||
context,
|
context,
|
||||||
"Optional Photos & Remarks",
|
"Additional Photos & Remarks",
|
||||||
[
|
[
|
||||||
|
_buildImageCard("Sample Turbidity", data.sampleTurbidityImage),
|
||||||
|
const Divider(height: 24),
|
||||||
|
Text("Optional Photos", style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold)),
|
||||||
|
const SizedBox(height: 8),
|
||||||
_buildImageCard("Optional Photo 1", data.optionalImage1, remark: data.optionalRemark1),
|
_buildImageCard("Optional Photo 1", data.optionalImage1, remark: data.optionalRemark1),
|
||||||
_buildImageCard("Optional Photo 2", data.optionalImage2, remark: data.optionalRemark2),
|
_buildImageCard("Optional Photo 2", data.optionalImage2, remark: data.optionalRemark2),
|
||||||
_buildImageCard("Optional Photo 3", data.optionalImage3, remark: data.optionalRemark3),
|
_buildImageCard("Optional Photo 3", data.optionalImage3, remark: data.optionalRemark3),
|
||||||
@ -104,6 +102,10 @@ class RiverInSituStep5Summary extends StatelessWidget {
|
|||||||
_buildParameterListItem(context, icon: Icons.opacity, label: "Turbidity", unit: "NTU", value: data.turbidity?.toStringAsFixed(2)),
|
_buildParameterListItem(context, icon: Icons.opacity, label: "Turbidity", unit: "NTU", value: data.turbidity?.toStringAsFixed(2)),
|
||||||
_buildParameterListItem(context, icon: Icons.filter_alt_outlined, label: "TSS", unit: "mg/L", value: data.tss?.toStringAsFixed(2)),
|
_buildParameterListItem(context, icon: Icons.filter_alt_outlined, label: "TSS", unit: "mg/L", value: data.tss?.toStringAsFixed(2)),
|
||||||
_buildParameterListItem(context, icon: Icons.battery_charging_full, label: "Battery", unit: "V", value: data.batteryVoltage?.toStringAsFixed(2)),
|
_buildParameterListItem(context, icon: Icons.battery_charging_full, label: "Battery", unit: "V", value: data.batteryVoltage?.toStringAsFixed(2)),
|
||||||
|
|
||||||
|
// ADDED: Display for Flowrate
|
||||||
|
const Divider(height: 20),
|
||||||
|
_buildFlowrateSummary(context),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -227,4 +229,24 @@ class RiverInSituStep5Summary extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ADDED: Widget to build the flowrate summary section
|
||||||
|
Widget _buildFlowrateSummary(BuildContext context) {
|
||||||
|
final method = data.flowrateMethod ?? 'N/A';
|
||||||
|
final value = data.flowrateValue != null ? '${data.flowrateValue!.toStringAsFixed(4)} m/s' : 'NA';
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_buildDetailRow("Flowrate Method:", method),
|
||||||
|
if (method == 'Surface Drifter') ...[
|
||||||
|
_buildDetailRow(" Height:", data.flowrateSurfaceDrifterHeight?.toString()),
|
||||||
|
_buildDetailRow(" Distance:", data.flowrateSurfaceDrifterDistance?.toString()),
|
||||||
|
_buildDetailRow(" Time First:", data.flowrateSurfaceDrifterTimeFirst),
|
||||||
|
_buildDetailRow(" Time Last:", data.flowrateSurfaceDrifterTimeLast),
|
||||||
|
],
|
||||||
|
_buildDetailRow("Flowrate Value:", value),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user