fix marine tarball telegram alert
This commit is contained in:
parent
e3b58bf74e
commit
475e645d25
@ -1,3 +1,4 @@
|
|||||||
|
//import 'dart' as dart;
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
/// This class holds all the data collected across the multi-step tarball sampling form.
|
/// This class holds all the data collected across the multi-step tarball sampling form.
|
||||||
@ -97,7 +98,7 @@ class TarballSamplingData {
|
|||||||
'optional_photo_remark_02': optionalRemark2 ?? '',
|
'optional_photo_remark_02': optionalRemark2 ?? '',
|
||||||
'optional_photo_remark_03': optionalRemark3 ?? '',
|
'optional_photo_remark_03': optionalRemark3 ?? '',
|
||||||
'optional_photo_remark_04': optionalRemark4 ?? '',
|
'optional_photo_remark_04': optionalRemark4 ?? '',
|
||||||
'distance_difference_remarks': distanceDifferenceRemarks ?? '',
|
'distance_remarks': distanceDifferenceRemarks ?? '',
|
||||||
|
|
||||||
// Human-readable names for the Telegram alert
|
// Human-readable names for the Telegram alert
|
||||||
'tbl_station_name': selectedStation?['tbl_station_name']?.toString() ?? '',
|
'tbl_station_name': selectedStation?['tbl_station_name']?.toString() ?? '',
|
||||||
|
|||||||
@ -17,6 +17,7 @@ class TarballSamplingStep1 extends StatefulWidget {
|
|||||||
|
|
||||||
class _TarballSamplingStep1State extends State<TarballSamplingStep1> {
|
class _TarballSamplingStep1State extends State<TarballSamplingStep1> {
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
// MODIFIED: A single data object is managed for the entire form.
|
||||||
final _data = TarballSamplingData();
|
final _data = TarballSamplingData();
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
|
|
||||||
@ -29,11 +30,10 @@ class _TarballSamplingStep1State extends State<TarballSamplingStep1> {
|
|||||||
final TextEditingController _currentLatController = TextEditingController();
|
final TextEditingController _currentLatController = TextEditingController();
|
||||||
final TextEditingController _currentLonController = TextEditingController();
|
final TextEditingController _currentLonController = TextEditingController();
|
||||||
|
|
||||||
// --- State for Dropdowns and Location ---
|
// --- State for Dropdowns ---
|
||||||
List<String> _statesList = [];
|
List<String> _statesList = [];
|
||||||
List<String> _categoriesForState = [];
|
List<String> _categoriesForState = [];
|
||||||
List<Map<String, dynamic>> _stationsForCategory = [];
|
List<Map<String, dynamic>> _stationsForCategory = [];
|
||||||
double? _distanceDifference;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -43,7 +43,6 @@ class _TarballSamplingStep1State extends State<TarballSamplingStep1> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
// Dispose all controllers to prevent memory leaks
|
|
||||||
_firstSamplerController.dispose();
|
_firstSamplerController.dispose();
|
||||||
_dateController.dispose();
|
_dateController.dispose();
|
||||||
_timeController.dispose();
|
_timeController.dispose();
|
||||||
@ -56,10 +55,6 @@ class _TarballSamplingStep1State extends State<TarballSamplingStep1> {
|
|||||||
|
|
||||||
void _initializeForm() {
|
void _initializeForm() {
|
||||||
final auth = Provider.of<AuthProvider>(context, listen: false);
|
final auth = Provider.of<AuthProvider>(context, listen: false);
|
||||||
|
|
||||||
// Set initial values for the data model and controllers
|
|
||||||
// This relies on the AuthProvider having been populated with data,
|
|
||||||
// which works offline if the data was fetched and cached previously.
|
|
||||||
_data.firstSampler = auth.profileData?['first_name'] ?? 'Current User';
|
_data.firstSampler = auth.profileData?['first_name'] ?? 'Current User';
|
||||||
_data.firstSamplerUserId = auth.profileData?['user_id'];
|
_data.firstSamplerUserId = auth.profileData?['user_id'];
|
||||||
_firstSamplerController.text = _data.firstSampler!;
|
_firstSamplerController.text = _data.firstSampler!;
|
||||||
@ -70,8 +65,6 @@ class _TarballSamplingStep1State extends State<TarballSamplingStep1> {
|
|||||||
_dateController.text = _data.samplingDate!;
|
_dateController.text = _data.samplingDate!;
|
||||||
_timeController.text = _data.samplingTime!;
|
_timeController.text = _data.samplingTime!;
|
||||||
|
|
||||||
// Populate the initial list of unique states from all available stations.
|
|
||||||
// This also relies on cached data in AuthProvider for offline use.
|
|
||||||
final allStations = auth.tarballStations ?? [];
|
final allStations = auth.tarballStations ?? [];
|
||||||
if (allStations.isNotEmpty) {
|
if (allStations.isNotEmpty) {
|
||||||
final states = allStations.map((s) => s['state_name'] as String?).whereType<String>().toSet().toList();
|
final states = allStations.map((s) => s['state_name'] as String?).whereType<String>().toSet().toList();
|
||||||
@ -80,12 +73,10 @@ class _TarballSamplingStep1State extends State<TarballSamplingStep1> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetches the device's location with an offline-first approach.
|
|
||||||
Future<void> _getCurrentLocation() async {
|
Future<void> _getCurrentLocation() async {
|
||||||
bool serviceEnabled;
|
bool serviceEnabled;
|
||||||
LocationPermission permission;
|
LocationPermission permission;
|
||||||
|
|
||||||
// Check if location services are enabled.
|
|
||||||
serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||||
if (!serviceEnabled) {
|
if (!serviceEnabled) {
|
||||||
_showSnackBar('Location services are disabled. Please enable them.');
|
_showSnackBar('Location services are disabled. Please enable them.');
|
||||||
@ -109,12 +100,7 @@ class _TarballSamplingStep1State extends State<TarballSamplingStep1> {
|
|||||||
setState(() => _isLoading = true);
|
setState(() => _isLoading = true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// --- OFFLINE-FIRST LOGIC ---
|
|
||||||
// 1. Try to get the last known position. This is fast and works offline.
|
|
||||||
Position? position = await Geolocator.getLastKnownPosition();
|
Position? position = await Geolocator.getLastKnownPosition();
|
||||||
|
|
||||||
// 2. If no last known position, get the current position using GPS.
|
|
||||||
// This can work offline but may take longer.
|
|
||||||
position ??= await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
|
position ??= await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
@ -144,21 +130,91 @@ class _TarballSamplingStep1State extends State<TarballSamplingStep1> {
|
|||||||
|
|
||||||
double distanceInMeters = Geolocator.distanceBetween(lat1, lon1, lat2, lon2);
|
double distanceInMeters = Geolocator.distanceBetween(lat1, lon1, lat2, lon2);
|
||||||
setState(() {
|
setState(() {
|
||||||
_distanceDifference = distanceInMeters / 1000;
|
_data.distanceDifference = distanceInMeters / 1000; // Convert to km
|
||||||
_data.distanceDifference = _distanceDifference;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- MODIFIED: This function now validates distance and shows a dialog if needed ---
|
||||||
void _goToNextStep() {
|
void _goToNextStep() {
|
||||||
if (_formKey.currentState!.validate()) {
|
if (_formKey.currentState!.validate()) {
|
||||||
_formKey.currentState!.save();
|
_formKey.currentState!.save();
|
||||||
|
final distanceInMeters = (_data.distanceDifference ?? 0) * 1000;
|
||||||
|
|
||||||
|
if (distanceInMeters > 700) {
|
||||||
|
_showDistanceRemarkDialog();
|
||||||
|
} else {
|
||||||
|
_data.distanceDifferenceRemarks = null; // Clear old remarks if within range
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(builder: (context) => TarballSamplingStep2(data: _data)),
|
MaterialPageRoute(builder: (context) => TarballSamplingStep2(data: _data)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- NEW: This function displays the mandatory remarks dialog ---
|
||||||
|
Future<void> _showDistanceRemarkDialog() async {
|
||||||
|
final remarkController = TextEditingController(text: _data.distanceDifferenceRemarks);
|
||||||
|
final dialogFormKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
|
return showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text('Distance Warning'),
|
||||||
|
content: SingleChildScrollView(
|
||||||
|
child: Form(
|
||||||
|
key: dialogFormKey,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text('Your current location is more than 700m away from the station.'),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
TextFormField(
|
||||||
|
controller: remarkController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Remarks *',
|
||||||
|
hintText: 'Please provide a reason...',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
validator: (value) {
|
||||||
|
if (value == null || value.trim().isEmpty) {
|
||||||
|
return 'Remarks are required to continue.';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
maxLines: 3,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
child: const Text('Cancel'),
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
),
|
||||||
|
FilledButton(
|
||||||
|
child: const Text('Confirm'),
|
||||||
|
onPressed: () {
|
||||||
|
if (dialogFormKey.currentState!.validate()) {
|
||||||
|
_data.distanceDifferenceRemarks = remarkController.text;
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(builder: (context) => TarballSamplingStep2(data: _data)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void _showSnackBar(String message) {
|
void _showSnackBar(String message) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
@ -168,14 +224,10 @@ class _TarballSamplingStep1State extends State<TarballSamplingStep1> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// For offline functionality, all data used in the dropdowns (tarballStations, allUsers)
|
|
||||||
// must be fetched and cached in the AuthProvider when the app is online.
|
|
||||||
final auth = Provider.of<AuthProvider>(context, listen: false);
|
final auth = Provider.of<AuthProvider>(context, listen: false);
|
||||||
final allStations = auth.tarballStations ?? [];
|
final allStations = auth.tarballStations ?? [];
|
||||||
|
|
||||||
final currentUser = auth.profileData;
|
|
||||||
final allUsers = auth.allUsers ?? [];
|
final allUsers = auth.allUsers ?? [];
|
||||||
final secondSamplersList = allUsers.where((user) => user['user_id'] != currentUser?['user_id']).toList();
|
final secondSamplersList = allUsers.where((user) => user['user_id'] != auth.profileData?['user_id']).toList();
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: const Text("Tarball Sampling (1/3)")),
|
appBar: AppBar(title: const Text("Tarball Sampling (1/3)")),
|
||||||
@ -188,12 +240,10 @@ class _TarballSamplingStep1State extends State<TarballSamplingStep1> {
|
|||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
TextFormField(controller: _firstSamplerController, readOnly: true, decoration: const InputDecoration(labelText: '1st Sampler')),
|
TextFormField(controller: _firstSamplerController, readOnly: true, decoration: const InputDecoration(labelText: '1st Sampler')),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
DropdownSearch<Map<String, dynamic>>(
|
DropdownSearch<Map<String, dynamic>>(
|
||||||
// --- CORRECTED: Added a ValueKey to prevent the "Duplicate GlobalKey" error ---
|
|
||||||
// This ensures the widget rebuilds cleanly when its item list changes.
|
|
||||||
key: ValueKey(allUsers.length),
|
key: ValueKey(allUsers.length),
|
||||||
items: secondSamplersList,
|
items: secondSamplersList,
|
||||||
|
selectedItem: _data.secondSampler, // Bind to the data model
|
||||||
itemAsString: (sampler) => "${sampler['first_name']} ${sampler['last_name']}",
|
itemAsString: (sampler) => "${sampler['first_name']} ${sampler['last_name']}",
|
||||||
onChanged: (sampler) => setState(() => _data.secondSampler = sampler),
|
onChanged: (sampler) => setState(() => _data.secondSampler = sampler),
|
||||||
popupProps: const PopupProps.menu(
|
popupProps: const PopupProps.menu(
|
||||||
@ -204,7 +254,6 @@ class _TarballSamplingStep1State extends State<TarballSamplingStep1> {
|
|||||||
dropdownSearchDecoration: InputDecoration(labelText: '2nd Sampler (Optional)'),
|
dropdownSearchDecoration: InputDecoration(labelText: '2nd Sampler (Optional)'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
@ -216,6 +265,7 @@ class _TarballSamplingStep1State extends State<TarballSamplingStep1> {
|
|||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
DropdownSearch<String>(
|
DropdownSearch<String>(
|
||||||
items: _statesList,
|
items: _statesList,
|
||||||
|
selectedItem: _data.selectedStateName, // Bind to the data model
|
||||||
popupProps: const PopupProps.menu(showSearchBox: true, searchFieldProps: TextFieldProps(decoration: InputDecoration(hintText: "Search State..."))),
|
popupProps: const PopupProps.menu(showSearchBox: true, searchFieldProps: TextFieldProps(decoration: InputDecoration(hintText: "Search State..."))),
|
||||||
dropdownDecoratorProps: const DropDownDecoratorProps(dropdownSearchDecoration: InputDecoration(labelText: "Select State *")),
|
dropdownDecoratorProps: const DropDownDecoratorProps(dropdownSearchDecoration: InputDecoration(labelText: "Select State *")),
|
||||||
onChanged: (state) {
|
onChanged: (state) {
|
||||||
@ -225,7 +275,7 @@ class _TarballSamplingStep1State extends State<TarballSamplingStep1> {
|
|||||||
_data.selectedStation = null;
|
_data.selectedStation = null;
|
||||||
_stationLatController.clear();
|
_stationLatController.clear();
|
||||||
_stationLonController.clear();
|
_stationLonController.clear();
|
||||||
_distanceDifference = null;
|
_data.distanceDifference = null;
|
||||||
|
|
||||||
if (state != null) {
|
if (state != null) {
|
||||||
_categoriesForState = allStations.where((s) => s['state_name'] == state).map((s) => s['category_name'] as String?).whereType<String>().toSet().toList();
|
_categoriesForState = allStations.where((s) => s['state_name'] == state).map((s) => s['category_name'] as String?).whereType<String>().toSet().toList();
|
||||||
@ -241,6 +291,7 @@ class _TarballSamplingStep1State extends State<TarballSamplingStep1> {
|
|||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
DropdownSearch<String>(
|
DropdownSearch<String>(
|
||||||
items: _categoriesForState,
|
items: _categoriesForState,
|
||||||
|
selectedItem: _data.selectedCategoryName, // Bind to the data model
|
||||||
enabled: _data.selectedStateName != null,
|
enabled: _data.selectedStateName != null,
|
||||||
popupProps: const PopupProps.menu(showSearchBox: true, searchFieldProps: TextFieldProps(decoration: InputDecoration(hintText: "Search Category..."))),
|
popupProps: const PopupProps.menu(showSearchBox: true, searchFieldProps: TextFieldProps(decoration: InputDecoration(hintText: "Search Category..."))),
|
||||||
dropdownDecoratorProps: const DropDownDecoratorProps(dropdownSearchDecoration: InputDecoration(labelText: "Select Category *")),
|
dropdownDecoratorProps: const DropDownDecoratorProps(dropdownSearchDecoration: InputDecoration(labelText: "Select Category *")),
|
||||||
@ -250,7 +301,7 @@ class _TarballSamplingStep1State extends State<TarballSamplingStep1> {
|
|||||||
_data.selectedStation = null;
|
_data.selectedStation = null;
|
||||||
_stationLatController.clear();
|
_stationLatController.clear();
|
||||||
_stationLonController.clear();
|
_stationLonController.clear();
|
||||||
_distanceDifference = null;
|
_data.distanceDifference = null;
|
||||||
|
|
||||||
if (category != null) {
|
if (category != null) {
|
||||||
_stationsForCategory = allStations.where((s) => s['state_name'] == _data.selectedStateName && s['category_name'] == category).toList();
|
_stationsForCategory = allStations.where((s) => s['state_name'] == _data.selectedStateName && s['category_name'] == category).toList();
|
||||||
@ -264,6 +315,7 @@ class _TarballSamplingStep1State extends State<TarballSamplingStep1> {
|
|||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
DropdownSearch<Map<String, dynamic>>(
|
DropdownSearch<Map<String, dynamic>>(
|
||||||
items: _stationsForCategory,
|
items: _stationsForCategory,
|
||||||
|
selectedItem: _data.selectedStation, // Bind to the data model
|
||||||
enabled: _data.selectedCategoryName != null,
|
enabled: _data.selectedCategoryName != null,
|
||||||
itemAsString: (station) => "${station['tbl_station_code']} - ${station['tbl_station_name']}",
|
itemAsString: (station) => "${station['tbl_station_code']} - ${station['tbl_station_name']}",
|
||||||
popupProps: const PopupProps.menu(showSearchBox: true, searchFieldProps: TextFieldProps(decoration: InputDecoration(hintText: "Search Station..."))),
|
popupProps: const PopupProps.menu(showSearchBox: true, searchFieldProps: TextFieldProps(decoration: InputDecoration(hintText: "Search Station..."))),
|
||||||
@ -289,14 +341,32 @@ class _TarballSamplingStep1State extends State<TarballSamplingStep1> {
|
|||||||
TextFormField(controller: _currentLatController, readOnly: true, decoration: const InputDecoration(labelText: 'Current Latitude')),
|
TextFormField(controller: _currentLatController, readOnly: true, decoration: const InputDecoration(labelText: 'Current Latitude')),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
TextFormField(controller: _currentLonController, readOnly: true, decoration: const InputDecoration(labelText: 'Current Longitude')),
|
TextFormField(controller: _currentLonController, readOnly: true, decoration: const InputDecoration(labelText: 'Current Longitude')),
|
||||||
if (_distanceDifference != null)
|
if (_data.distanceDifference != null)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 16.0),
|
padding: const EdgeInsets.only(top: 16.0),
|
||||||
child: Text('Distance from Station: ${_distanceDifference!.toStringAsFixed(2)} km',
|
// --- MODIFIED: This UI now better reflects the warning/ok status ---
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ((_data.distanceDifference ?? 0) * 1000) > 700 ? Colors.red.withOpacity(0.1) : Colors.green.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
border: Border.all(color: ((_data.distanceDifference ?? 0) * 1000) > 700 ? Colors.red : Colors.green),
|
||||||
|
),
|
||||||
|
child: RichText(
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
text: TextSpan(
|
||||||
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
|
children: <TextSpan>[
|
||||||
|
const TextSpan(text: 'Distance from Station: '),
|
||||||
|
TextSpan(
|
||||||
|
text: '${(_data.distanceDifference! * 1000).toStringAsFixed(0)} meters',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: _distanceDifference! > 1.0 ? Colors.red : Colors.green,
|
color: ((_data.distanceDifference ?? 0) * 1000) > 700 ? Colors.red : Colors.green),
|
||||||
)
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
|||||||
@ -114,9 +114,11 @@ class _TarballSamplingStep3SummaryState extends State<TarballSamplingStep3Summar
|
|||||||
_buildDetailRow("Current Longitude:", widget.data.currentLongitude),
|
_buildDetailRow("Current Longitude:", widget.data.currentLongitude),
|
||||||
_buildDetailRow("Distance Difference:",
|
_buildDetailRow("Distance Difference:",
|
||||||
widget.data.distanceDifference != null
|
widget.data.distanceDifference != null
|
||||||
? "${widget.data.distanceDifference!.toStringAsFixed(2)} km"
|
? "${(widget.data.distanceDifference! * 1000).toStringAsFixed(0)} meters"
|
||||||
: "N/A"
|
: "N/A"
|
||||||
),
|
),
|
||||||
|
// NECESSARY CHANGE: Add this line to display the remarks.
|
||||||
|
_buildDetailRow("Distance Remarks:", widget.data.distanceDifferenceRemarks),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -199,7 +201,11 @@ class _TarballSamplingStep3SummaryState extends State<TarballSamplingStep3Summar
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDetailRow(String label, String? value) {
|
Widget _buildDetailRow(String label, String? value) {
|
||||||
return Padding(
|
// This function now correctly handles null or empty remarks by displaying 'N/A'
|
||||||
|
final bool isValueAvailable = value != null && value.isNotEmpty;
|
||||||
|
return Visibility(
|
||||||
|
visible: isValueAvailable, // Only show the row if there is a value to display
|
||||||
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 6.0),
|
padding: const EdgeInsets.symmetric(vertical: 6.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@ -215,12 +221,13 @@ class _TarballSamplingStep3SummaryState extends State<TarballSamplingStep3Summar
|
|||||||
Expanded(
|
Expanded(
|
||||||
flex: 3,
|
flex: 3,
|
||||||
child: Text(
|
child: Text(
|
||||||
value != null && value.isNotEmpty ? value : 'N/A',
|
value ?? 'N/A', // Display the value or 'N/A' if null
|
||||||
style: Theme.of(context).textTheme.bodyLarge,
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user