163 lines
6.1 KiB
Dart
163 lines
6.1 KiB
Dart
// lib/screens/marine/manual/tarball_sampling.dart
|
|
|
|
import 'dart:io';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:dropdown_search/dropdown_search.dart';
|
|
import 'package:image_picker/image_picker.dart';
|
|
import 'package:intl/intl.dart';
|
|
import 'package:geolocator/geolocator.dart';
|
|
import 'package:path_provider/path_provider.dart';
|
|
import 'package:path/path.dart' as path;
|
|
import 'package:image/image.dart' as img;
|
|
|
|
import 'package:environment_monitoring_app/auth_provider.dart';
|
|
import 'package:environment_monitoring_app/services/marine_api_service.dart';
|
|
import 'package:environment_monitoring_app/models/tarball_data.dart';
|
|
|
|
class MarineTarballSampling extends StatefulWidget {
|
|
const MarineTarballSampling({super.key});
|
|
|
|
@override
|
|
State<MarineTarballSampling> createState() => _MarineTarballSamplingState();
|
|
}
|
|
|
|
class _MarineTarballSamplingState extends State<MarineTarballSampling> {
|
|
final _formKey1 = GlobalKey<FormState>();
|
|
final _formKey2 = GlobalKey<FormState>();
|
|
int _currentStep = 1;
|
|
|
|
final MarineApiService _marineApiService = MarineApiService();
|
|
bool _isLoading = false;
|
|
|
|
final TarballSamplingData _data = TarballSamplingData();
|
|
|
|
List<String> _statesList = [];
|
|
List<String> _categoriesForState = [];
|
|
List<Map<String, dynamic>> _stationsForCategory = [];
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_initializeForm();
|
|
}
|
|
|
|
void _initializeForm() {
|
|
final auth = Provider.of<AuthProvider>(context, listen: false);
|
|
_data.firstSampler = auth.profileData?['first_name'] ?? 'Current User';
|
|
final now = DateTime.now();
|
|
_data.samplingDate = DateFormat('yyyy-MM-dd').format(now);
|
|
_data.samplingTime = DateFormat('HH:mm:ss').format(now);
|
|
|
|
final allStations = auth.tarballStations ?? [];
|
|
if (allStations.isNotEmpty) {
|
|
final states = allStations.map((s) => s['state_name'] as String?).whereType<String>().toSet().toList();
|
|
states.sort();
|
|
_statesList = states;
|
|
}
|
|
}
|
|
|
|
Future<File?> _pickAndProcessImage(ImageSource source) async {
|
|
final picker = ImagePicker();
|
|
final XFile? photo = await picker.pickImage(source: source, imageQuality: 85, maxWidth: 1024);
|
|
if (photo == null) return null;
|
|
|
|
final bytes = await photo.readAsBytes();
|
|
img.Image? originalImage = img.decodeImage(bytes);
|
|
if (originalImage == null) return null;
|
|
|
|
final String timestamp = DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.now());
|
|
|
|
// Shadow for visibility
|
|
img.drawString(originalImage, timestamp, font: img.arial24, x: 11, y: 11, color: img.ColorRgb8(0, 0, 0));
|
|
// Foreground text
|
|
img.drawString(originalImage, timestamp, font: img.arial24, x: 10, y: 10, color: img.ColorRgb8(255, 255, 255));
|
|
|
|
final tempDir = await getTemporaryDirectory();
|
|
final filePath = path.join(tempDir.path, '${DateTime.now().millisecondsSinceEpoch}.jpg');
|
|
return File(filePath)..writeAsBytesSync(img.encodeJpg(originalImage));
|
|
}
|
|
|
|
void _setImage(Function(File?) setImageCallback, ImageSource source) async {
|
|
final file = await _pickAndProcessImage(source);
|
|
if (file != null) setState(() => setImageCallback(file));
|
|
}
|
|
|
|
Future<void> _getCurrentLocation() async { /* ... Location logic ... */ }
|
|
void _calculateDistance() { /* ... Distance logic ... */ }
|
|
|
|
// MODIFIED: This method now fetches appSettings and passes it to the API service.
|
|
Future<void> _submitForm() async {
|
|
setState(() => _isLoading = true);
|
|
|
|
// Get the appSettings list from AuthProvider.
|
|
final authProvider = Provider.of<AuthProvider>(context, listen: false);
|
|
final appSettings = authProvider.appSettings;
|
|
|
|
// Pass the appSettings list to the submit method.
|
|
final result = await _marineApiService.submitTarballSample(
|
|
formData: _data.toFormData(),
|
|
imageFiles: _data.toImageFiles(),
|
|
appSettings: appSettings,
|
|
);
|
|
|
|
if (!mounted) return;
|
|
setState(() => _isLoading = false);
|
|
|
|
if (result['success'] == true) {
|
|
_showSnackBar("Data submitted successfully!");
|
|
Navigator.of(context).pop();
|
|
} else {
|
|
_showSnackBar("Submission failed: ${result['message']}");
|
|
}
|
|
}
|
|
|
|
void _showSnackBar(String message) {
|
|
if (mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(title: const Text("Marine Tarball Sampling")),
|
|
body: Stepper(
|
|
currentStep: _currentStep - 1,
|
|
onStepContinue: () {
|
|
if (_currentStep == 1 && _formKey1.currentState!.validate()) {
|
|
_formKey1.currentState!.save();
|
|
setState(() => _currentStep++);
|
|
} else if (_currentStep == 2 && _formKey2.currentState!.validate()) {
|
|
_formKey2.currentState!.save();
|
|
setState(() => _currentStep++);
|
|
}
|
|
},
|
|
onStepCancel: () {
|
|
if (_currentStep > 1) setState(() => _currentStep--);
|
|
},
|
|
controlsBuilder: (context, details) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(top: 16.0),
|
|
child: _isLoading
|
|
? const Center(child: CircularProgressIndicator())
|
|
: Row(
|
|
children: [
|
|
if (_currentStep < 3) ElevatedButton(onPressed: details.onStepContinue, child: const Text('Next')),
|
|
if (_currentStep == 3) ElevatedButton(onPressed: _submitForm, child: const Text('Submit')),
|
|
if (_currentStep > 1) TextButton(onPressed: details.onStepCancel, child: const Text('Back')),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
steps: [
|
|
Step(title: const Text('Sampling Info'), content: _buildForm1(), isActive: _currentStep >= 1),
|
|
Step(title: const Text('On-Site Info'), content: _buildForm2(), isActive: _currentStep >= 2),
|
|
Step(title: const Text('Summary'), content: _buildForm3(), isActive: _currentStep >= 3),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildForm1() { /* ... UI for Step 1 ... */ return Container(); }
|
|
Widget _buildForm2() { /* ... UI for Step 2 ... */ return Container(); }
|
|
Widget _buildForm3() { /* ... UI for Step 3 ... */ return Container(); }
|
|
} |