environment_monitoring_app/lib/screens/marine/manual/tarball_sampling.dart

169 lines
6.4 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';
// START CHANGE: Import the new dedicated service
import 'package:environment_monitoring_app/services/marine_tarball_sampling_service.dart';
// END CHANGE
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;
// MODIFIED: The service instance is no longer created here.
// It will be fetched from the Provider right before it is used.
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 uses the new dedicated service for submission.
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;
// ADDED: Fetch the global service instance from Provider.
final tarballService = Provider.of<MarineTarballSamplingService>(context, listen: false);
// START CHANGE: Call the method on the new dedicated service
final result = await tarballService.submitTarballSample(
data: _data,
appSettings: appSettings,
);
// END CHANGE
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(); }
}