Merge pull request #34 from dougborg/pr/02-lint-cleanup

Add flutter_lints and fix all analyzer warnings
This commit is contained in:
stopflock
2026-02-09 11:43:48 -06:00
committed by GitHub
77 changed files with 225 additions and 334 deletions
+1 -2
View File
@@ -1,7 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_map/flutter_map.dart' show LatLngBounds; import 'package:flutter_map/flutter_map.dart' show LatLngBounds;
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
@@ -23,7 +22,6 @@ import 'services/operator_profile_service.dart';
import 'services/deep_link_service.dart'; import 'services/deep_link_service.dart';
import 'widgets/node_provider_with_cache.dart'; import 'widgets/node_provider_with_cache.dart';
import 'services/profile_service.dart'; import 'services/profile_service.dart';
import 'widgets/proximity_warning_dialog.dart';
import 'widgets/reauth_messages_dialog.dart'; import 'widgets/reauth_messages_dialog.dart';
import 'dev_config.dart'; import 'dev_config.dart';
import 'state/auth_state.dart'; import 'state/auth_state.dart';
@@ -366,6 +364,7 @@ class AppState extends ChangeNotifier {
/// Show re-authentication dialog if needed /// Show re-authentication dialog if needed
Future<void> checkAndPromptReauthForMessages(BuildContext context) async { Future<void> checkAndPromptReauthForMessages(BuildContext context) async {
if (await needsReauthForMessages()) { if (await needsReauthForMessages()) {
if (!context.mounted) return;
_showReauthDialog(context); _showReauthDialog(context);
} }
} }
+1 -2
View File
@@ -1,5 +1,4 @@
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
@@ -147,7 +146,7 @@ class OneTimeMigrations {
debugPrint('[Migration] Stack trace: $stackTrace'); debugPrint('[Migration] Stack trace: $stackTrace');
// Nuclear option: clear everything and show non-dismissible error dialog // Nuclear option: clear everything and show non-dismissible error dialog
if (context != null) { if (context != null && context.mounted) {
NuclearResetDialog.show(context, error, stackTrace); NuclearResetDialog.show(context, error, stackTrace);
} else { } else {
// If no context available, just log and hope for the best // If no context available, just log and hope for the best
+1 -1
View File
@@ -9,7 +9,7 @@ class DirectionFov {
DirectionFov(this.centerDegrees, this.fovDegrees); DirectionFov(this.centerDegrees, this.fovDegrees);
@override @override
String toString() => 'DirectionFov(center: ${centerDegrees}°, fov: ${fovDegrees}°)'; String toString() => 'DirectionFov(center: $centerDegrees°, fov: $fovDegrees°)';
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
-1
View File
@@ -1,4 +1,3 @@
import 'package:uuid/uuid.dart';
import 'osm_node.dart'; import 'osm_node.dart';
/// Sentinel value for copyWith methods to distinguish between null and not provided /// Sentinel value for copyWith methods to distinguish between null and not provided
-1
View File
@@ -1,4 +1,3 @@
import 'package:uuid/uuid.dart';
import 'osm_node.dart'; import 'osm_node.dart';
/// A bundle of OSM tags that describe a particular surveillance operator. /// A bundle of OSM tags that describe a particular surveillance operator.
+6 -5
View File
@@ -1,4 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
/// A suspected surveillance location from the CSV data /// A suspected surveillance location from the CSV data
@@ -35,8 +36,8 @@ class SuspectedLocation {
bounds = coordinates.bounds; bounds = coordinates.bounds;
} catch (e) { } catch (e) {
// If GeoJSON parsing fails, use default coordinates // If GeoJSON parsing fails, use default coordinates
print('[SuspectedLocation] Failed to parse GeoJSON for ticket $ticketNo: $e'); debugPrint('[SuspectedLocation] Failed to parse GeoJSON for ticket $ticketNo: $e');
print('[SuspectedLocation] Location string: $locationString'); debugPrint('[SuspectedLocation] Location string: $locationString');
} }
} }
@@ -60,7 +61,7 @@ class SuspectedLocation {
// The geoJson IS the geometry object (not wrapped in a 'geometry' property) // The geoJson IS the geometry object (not wrapped in a 'geometry' property)
final coordinates = geoJson['coordinates'] as List?; final coordinates = geoJson['coordinates'] as List?;
if (coordinates == null || coordinates.isEmpty) { if (coordinates == null || coordinates.isEmpty) {
print('[SuspectedLocation] No coordinates found in GeoJSON'); debugPrint('[SuspectedLocation] No coordinates found in GeoJSON');
return (centroid: const LatLng(0, 0), bounds: <LatLng>[]); return (centroid: const LatLng(0, 0), bounds: <LatLng>[]);
} }
@@ -109,7 +110,7 @@ class SuspectedLocation {
} }
break; break;
default: default:
print('Unsupported geometry type: $type'); debugPrint('Unsupported geometry type: $type');
} }
if (points.isEmpty) { if (points.isEmpty) {
@@ -127,7 +128,7 @@ class SuspectedLocation {
return (centroid: centroid, bounds: points); return (centroid: centroid, bounds: points);
} catch (e) { } catch (e) {
print('Error extracting coordinates from GeoJSON: $e'); debugPrint('Error extracting coordinates from GeoJSON: $e');
return (centroid: const LatLng(0, 0), bounds: <LatLng>[]); return (centroid: const LatLng(0, 0), bounds: <LatLng>[]);
} }
} }
@@ -3,7 +3,6 @@ import 'settings/sections/max_nodes_section.dart';
import 'settings/sections/proximity_alerts_section.dart'; import 'settings/sections/proximity_alerts_section.dart';
import 'settings/sections/suspected_locations_section.dart'; import 'settings/sections/suspected_locations_section.dart';
import 'settings/sections/tile_provider_section.dart'; import 'settings/sections/tile_provider_section.dart';
import 'settings/sections/network_status_section.dart';
import '../services/localization_service.dart'; import '../services/localization_service.dart';
class AdvancedSettingsScreen extends StatelessWidget { class AdvancedSettingsScreen extends StatelessWidget {
@@ -9,7 +9,6 @@ import '../../widgets/add_node_sheet.dart';
import '../../widgets/edit_node_sheet.dart'; import '../../widgets/edit_node_sheet.dart';
import '../../widgets/navigation_sheet.dart'; import '../../widgets/navigation_sheet.dart';
import '../../widgets/measured_sheet.dart'; import '../../widgets/measured_sheet.dart';
import '../../state/settings_state.dart' show FollowMeMode;
/// Coordinates all bottom sheet operations including opening, closing, height tracking, /// Coordinates all bottom sheet operations including opening, closing, height tracking,
/// and sheet-related validation logic. /// and sheet-related validation logic.
@@ -118,9 +117,8 @@ class SheetCoordinator {
controller.closed.then((_) { controller.closed.then((_) {
_addSheetHeight = 0.0; _addSheetHeight = 0.0;
onStateChanged(); onStateChanged();
// Handle dismissal by canceling session if still active // Handle dismissal by canceling session if still active
final appState = context.read<AppState>();
if (appState.session != null) { if (appState.session != null) {
debugPrint('[SheetCoordinator] AddNodeSheet dismissed - canceling session'); debugPrint('[SheetCoordinator] AddNodeSheet dismissed - canceling session');
appState.cancelSession(); appState.cancelSession();
@@ -187,9 +185,8 @@ class SheetCoordinator {
_editSheetHeight = 0.0; _editSheetHeight = 0.0;
_transitioningToEdit = false; _transitioningToEdit = false;
onStateChanged(); onStateChanged();
// Handle dismissal by canceling session if still active // Handle dismissal by canceling session if still active
final appState = context.read<AppState>();
if (appState.editSession != null) { if (appState.editSession != null) {
debugPrint('[SheetCoordinator] EditNodeSheet dismissed - canceling edit session'); debugPrint('[SheetCoordinator] EditNodeSheet dismissed - canceling edit session');
appState.cancelEditSession(); appState.cancelEditSession();
@@ -266,7 +263,7 @@ class SheetCoordinator {
/// Restore the follow-me mode that was active before opening a node sheet /// Restore the follow-me mode that was active before opening a node sheet
void _restoreFollowMeMode(AppState appState) { void _restoreFollowMeMode(AppState appState) {
if (_followMeModeBeforeSheet != null) { if (_followMeModeBeforeSheet != null) {
debugPrint('[SheetCoordinator] Restoring follow-me mode: ${_followMeModeBeforeSheet}'); debugPrint('[SheetCoordinator] Restoring follow-me mode: $_followMeModeBeforeSheet');
appState.setFollowMeMode(_followMeModeBeforeSheet!); appState.setFollowMeMode(_followMeModeBeforeSheet!);
_followMeModeBeforeSheet = null; // Clear stored state _followMeModeBeforeSheet = null; // Clear stored state
} }
+6 -36
View File
@@ -1,8 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_animations/flutter_map_animations.dart'; import 'package:flutter_map_animations/flutter_map_animations.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:latlong2/latlong.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../app_state.dart'; import '../app_state.dart';
@@ -10,12 +8,9 @@ import '../dev_config.dart';
import '../widgets/map_view.dart'; import '../widgets/map_view.dart';
import '../services/localization_service.dart'; import '../services/localization_service.dart';
import '../widgets/add_node_sheet.dart';
import '../widgets/edit_node_sheet.dart';
import '../widgets/node_tag_sheet.dart'; import '../widgets/node_tag_sheet.dart';
import '../widgets/download_area_dialog.dart'; import '../widgets/download_area_dialog.dart';
import '../widgets/measured_sheet.dart'; import '../widgets/measured_sheet.dart';
import '../widgets/navigation_sheet.dart';
import '../widgets/search_bar.dart'; import '../widgets/search_bar.dart';
import '../widgets/suspected_location_sheet.dart'; import '../widgets/suspected_location_sheet.dart';
import '../widgets/welcome_dialog.dart'; import '../widgets/welcome_dialog.dart';
@@ -190,6 +185,7 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
// Run any needed migrations first // Run any needed migrations first
final versionsNeedingMigration = await ChangelogService().getVersionsNeedingMigration(); final versionsNeedingMigration = await ChangelogService().getVersionsNeedingMigration();
if (!mounted) return;
for (final version in versionsNeedingMigration) { for (final version in versionsNeedingMigration) {
await ChangelogService().runMigration(version, appState, context); await ChangelogService().runMigration(version, appState, context);
} }
@@ -214,6 +210,7 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
case PopupType.changelog: case PopupType.changelog:
final changelogContent = await ChangelogService().getChangelogContentForDisplay(); final changelogContent = await ChangelogService().getChangelogContentForDisplay();
if (!mounted) return;
if (changelogContent != null) { if (changelogContent != null) {
await showDialog( await showDialog(
context: context, context: context,
@@ -252,35 +249,6 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
); );
} }
void _zoomAndCenterForRoute(bool followMeEnabled, LatLng? userLocation, LatLng? routeStart) {
try {
LatLng centerLocation;
if (followMeEnabled && userLocation != null) {
// Center on user if follow-me is enabled
centerLocation = userLocation;
debugPrint('[HomeScreen] Centering on user location for route start');
} else if (routeStart != null) {
// Center on start pin if user is far away or no GPS
centerLocation = routeStart;
debugPrint('[HomeScreen] Centering on route start pin');
} else {
debugPrint('[HomeScreen] No valid location to center on');
return;
}
// Animate to zoom 14 and center location
_mapController.animateTo(
dest: centerLocation,
zoom: 14.0,
duration: const Duration(milliseconds: 800),
curve: Curves.easeInOut,
);
} catch (e) {
debugPrint('[HomeScreen] Could not zoom/center for route: $e');
}
}
void _onResumeRoute() { void _onResumeRoute() {
_navigationCoordinator.resumeRoute( _navigationCoordinator.resumeRoute(
context: context, context: context,
@@ -402,9 +370,11 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
); );
// Reset height and clear selection when sheet is dismissed // Reset height and clear selection when sheet is dismissed
final appState = context.read<AppState>();
controller.closed.then((_) { controller.closed.then((_) {
if (!mounted) return;
_sheetCoordinator.resetTagSheetHeight(() => setState(() {})); _sheetCoordinator.resetTagSheetHeight(() => setState(() {}));
context.read<AppState>().clearSuspectedLocationSelection(); appState.clearSuspectedLocationSelection();
}); });
} }
@@ -578,7 +548,7 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Theme.of(context).shadowColor.withOpacity(0.3), color: Theme.of(context).shadowColor.withValues(alpha: 0.3),
blurRadius: 10, blurRadius: 10,
offset: Offset(0, -2), offset: Offset(0, -2),
) )
+1 -1
View File
@@ -132,7 +132,7 @@ class _NavigationSettingsScreenState extends State<NavigationSettingsScreen> {
Text( Text(
value, value,
style: Theme.of(context).textTheme.bodySmall?.copyWith( style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).textTheme.bodySmall?.color?.withOpacity(0.6), color: Theme.of(context).textTheme.bodySmall?.color?.withValues(alpha: 0.6),
), ),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
-1
View File
@@ -105,7 +105,6 @@ class _OperatorProfileEditorState extends State<OperatorProfileEditor> {
return List.generate(_tags.length, (i) { return List.generate(_tags.length, (i) {
final keyController = TextEditingController(text: _tags[i].key); final keyController = TextEditingController(text: _tags[i].key);
final valueController = TextEditingController(text: _tags[i].value);
return Padding( return Padding(
padding: const EdgeInsets.only(bottom: 8.0), padding: const EdgeInsets.only(bottom: 8.0),
+4 -4
View File
@@ -275,7 +275,7 @@ class _OSMAccountScreenState extends State<OSMAccountScreen> {
Container( Container(
padding: const EdgeInsets.all(12.0), padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5), color: Theme.of(context).colorScheme.surfaceContainerHighest.withValues(alpha: 0.5),
borderRadius: BorderRadius.circular(8.0), borderRadius: BorderRadius.circular(8.0),
), ),
child: Row( child: Row(
@@ -308,7 +308,7 @@ class _OSMAccountScreenState extends State<OSMAccountScreen> {
label: Text(locService.t('auth.deleteAccount')), label: Text(locService.t('auth.deleteAccount')),
style: OutlinedButton.styleFrom( style: OutlinedButton.styleFrom(
foregroundColor: Theme.of(context).colorScheme.error, foregroundColor: Theme.of(context).colorScheme.error,
side: BorderSide(color: Theme.of(context).colorScheme.error.withOpacity(0.5)), side: BorderSide(color: Theme.of(context).colorScheme.error.withValues(alpha: 0.5)),
), ),
), ),
), ),
@@ -354,10 +354,10 @@ class _OSMAccountScreenState extends State<OSMAccountScreen> {
Container( Container(
padding: const EdgeInsets.all(12.0), padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.errorContainer.withOpacity(0.1), color: Theme.of(context).colorScheme.errorContainer.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8.0), borderRadius: BorderRadius.circular(8.0),
border: Border.all( border: Border.all(
color: Theme.of(context).colorScheme.error.withOpacity(0.3), color: Theme.of(context).colorScheme.error.withValues(alpha: 0.3),
), ),
), ),
child: Text( child: Text(
-1
View File
@@ -153,7 +153,6 @@ class _ProfileEditorState extends State<ProfileEditor> {
return List.generate(_tags.length, (i) { return List.generate(_tags.length, (i) {
final keyController = TextEditingController(text: _tags[i].key); final keyController = TextEditingController(text: _tags[i].key);
final valueController = TextEditingController(text: _tags[i].value);
return Padding( return Padding(
padding: const EdgeInsets.only(bottom: 8.0), padding: const EdgeInsets.only(bottom: 8.0),
+3 -3
View File
@@ -99,7 +99,7 @@ class _ReleaseNotesScreenState extends State<ReleaseNotesScreen> {
color: Theme.of(context).colorScheme.primaryContainer, color: Theme.of(context).colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
border: Border.all( border: Border.all(
color: Theme.of(context).colorScheme.primary.withOpacity(0.3), color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.3),
), ),
), ),
child: Row( child: Row(
@@ -142,8 +142,8 @@ class _ReleaseNotesScreenState extends State<ReleaseNotesScreen> {
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all( border: Border.all(
color: isCurrentVersion color: isCurrentVersion
? Theme.of(context).colorScheme.primary.withOpacity(0.3) ? Theme.of(context).colorScheme.primary.withValues(alpha: 0.3)
: Theme.of(context).dividerColor.withOpacity(0.3), : Theme.of(context).dividerColor.withValues(alpha: 0.3),
), ),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
@@ -23,14 +23,14 @@ class _LanguageSectionState extends State<LanguageSection> {
_loadLanguageNames(); _loadLanguageNames();
} }
_loadSelectedLanguage() async { Future<void> _loadSelectedLanguage() async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
setState(() { setState(() {
_selectedLanguage = prefs.getString('language_code'); _selectedLanguage = prefs.getString('language_code');
}); });
} }
_loadLanguageNames() async { Future<void> _loadLanguageNames() async {
final locService = LocalizationService.instance; final locService = LocalizationService.instance;
final Map<String, String> names = {}; final Map<String, String> names = {};
@@ -43,7 +43,7 @@ class _LanguageSectionState extends State<LanguageSection> {
}); });
} }
_setLanguage(String? languageCode) async { Future<void> _setLanguage(String? languageCode) async {
await LocalizationService.instance.setLanguage(languageCode); await LocalizationService.instance.setLanguage(languageCode);
setState(() { setState(() {
_selectedLanguage = languageCode; _selectedLanguage = languageCode;
@@ -118,7 +118,7 @@ class NodeProfilesSection extends StatelessWidget {
); );
// If user chose to create custom profile, open the profile editor // If user chose to create custom profile, open the profile editor
if (result == 'create') { if (result == 'create' && context.mounted) {
_createNewProfile(context); _createNewProfile(context);
} }
// If user chose import from website, ProfileAddChoiceDialog handles opening the URL // If user chose import from website, ProfileAddChoiceDialog handles opening the URL
@@ -87,9 +87,9 @@ class _OfflineAreasSectionState extends State<OfflineAreasSection> {
: "${(area.sizeBytes / 1024).toStringAsFixed(1)} ${locService.t('offlineAreas.kilobytes')}" : "${(area.sizeBytes / 1024).toStringAsFixed(1)} ${locService.t('offlineAreas.kilobytes')}"
: '--'; : '--';
String subtitle = '${locService.t('offlineAreas.provider')}: ${area.tileProviderDisplay}\n' + String subtitle = '${locService.t('offlineAreas.provider')}: ${area.tileProviderDisplay}\n'
'${locService.t('offlineAreas.maxZoom')}: Z${area.maxZoom}' + '\n' + '${locService.t('offlineAreas.maxZoom')}: Z${area.maxZoom}\n'
'${locService.t('offlineAreas.latitude')}: ${area.bounds.southWest.latitude.toStringAsFixed(3)}, ${area.bounds.southWest.longitude.toStringAsFixed(3)}\n' + '${locService.t('offlineAreas.latitude')}: ${area.bounds.southWest.latitude.toStringAsFixed(3)}, ${area.bounds.southWest.longitude.toStringAsFixed(3)}\n'
'${locService.t('offlineAreas.latitude')}: ${area.bounds.northEast.latitude.toStringAsFixed(3)}, ${area.bounds.northEast.longitude.toStringAsFixed(3)}'; '${locService.t('offlineAreas.latitude')}: ${area.bounds.northEast.latitude.toStringAsFixed(3)}, ${area.bounds.northEast.longitude.toStringAsFixed(3)}';
if (area.status == OfflineAreaStatus.downloading) { if (area.status == OfflineAreaStatus.downloading) {
@@ -207,7 +207,7 @@ class _OfflineAreasSectionState extends State<OfflineAreasSection> {
: null, : null,
), ),
); );
}).toList(), }),
], ],
); );
}, },
@@ -140,9 +140,9 @@ class _ProximityAlertsSectionState extends State<ProximityAlertsSection> {
Container( Container(
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.orange.withOpacity(0.1), color: Colors.orange.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.orange.withOpacity(0.3)), border: Border.all(color: Colors.orange.withValues(alpha: 0.3)),
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -233,7 +233,7 @@ class _ProximityAlertsSectionState extends State<ProximityAlertsSection> {
DistanceService.convertFromMeters(kProximityAlertDefaultDistance.toDouble(), appState.distanceUnit).round().toString(), DistanceService.convertFromMeters(kProximityAlertDefaultDistance.toDouble(), appState.distanceUnit).round().toString(),
]), ]),
style: Theme.of(context).textTheme.bodySmall?.copyWith( style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).textTheme.bodySmall?.color?.withOpacity(0.6), color: Theme.of(context).textTheme.bodySmall?.color?.withValues(alpha: 0.6),
), ),
), ),
], ],
@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../../../app_state.dart'; import '../../../app_state.dart';
import '../../../services/localization_service.dart'; import '../../../services/localization_service.dart';
import '../../../state/settings_state.dart';
class QueueSection extends StatelessWidget { class QueueSection extends StatelessWidget {
const QueueSection({super.key}); const QueueSection({super.key});
@@ -116,16 +115,16 @@ class QueueSection extends StatelessWidget {
(upload.error ? locService.t('queue.error') : "") + (upload.error ? locService.t('queue.error') : "") +
(upload.completing ? locService.t('queue.completing') : "")), (upload.completing ? locService.t('queue.completing') : "")),
subtitle: Text( subtitle: Text(
locService.t('queue.destination', params: [_getUploadModeDisplayName(upload.uploadMode)]) + '\n' + '${locService.t('queue.destination', params: [_getUploadModeDisplayName(upload.uploadMode)])}\n'
locService.t('queue.latitude', params: [upload.coord.latitude.toStringAsFixed(6)]) + '\n' + '${locService.t('queue.latitude', params: [upload.coord.latitude.toStringAsFixed(6)])}\n'
locService.t('queue.longitude', params: [upload.coord.longitude.toStringAsFixed(6)]) + '\n' + '${locService.t('queue.longitude', params: [upload.coord.longitude.toStringAsFixed(6)])}\n'
locService.t('queue.direction', params: [ '${locService.t('queue.direction', params: [
upload.direction is String upload.direction is String
? upload.direction.toString() ? upload.direction.toString()
: upload.direction.round().toString() : upload.direction.round().toString()
]) + '\n' + ])}\n'
locService.t('queue.attempts', params: [upload.attempts.toString()]) + '${locService.t('queue.attempts', params: [upload.attempts.toString()])}'
(upload.error ? "\n${locService.t('queue.uploadFailedRetry')}" : "") '${upload.error ? "\n${locService.t('queue.uploadFailedRetry')}" : ""}'
), ),
trailing: Row( trailing: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@@ -44,7 +44,7 @@ class TileProviderSection extends StatelessWidget {
), ),
) )
else else
...providers.map((provider) => _buildProviderTile(context, provider, appState)).toList(), ...providers.map((provider) => _buildProviderTile(context, provider, appState)),
], ],
); );
}, },
@@ -89,7 +89,7 @@ class TileProviderSection extends StatelessWidget {
leading: CircleAvatar( leading: CircleAvatar(
backgroundColor: isSelected backgroundColor: isSelected
? Theme.of(context).colorScheme.primary ? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.surfaceVariant, : Theme.of(context).colorScheme.surfaceContainerHighest,
child: Icon( child: Icon(
Icons.map, Icons.map,
color: isSelected color: isSelected
@@ -81,7 +81,7 @@ class UploadModeSection extends StatelessWidget {
fontSize: 12, fontSize: 12,
color: appState.pendingCount > 0 color: appState.pendingCount > 0
? Theme.of(context).disabledColor ? Theme.of(context).disabledColor
: Theme.of(context).colorScheme.onSurface.withOpacity(0.7) : Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.7)
) )
); );
case UploadMode.sandbox: case UploadMode.sandbox:
@@ -95,7 +95,6 @@ class UploadModeSection extends StatelessWidget {
), ),
); );
case UploadMode.simulate: case UploadMode.simulate:
default:
return Text( return Text(
locService.t('uploadMode.simulateDescription'), locService.t('uploadMode.simulateDescription'),
style: TextStyle( style: TextStyle(
+1 -1
View File
@@ -102,7 +102,7 @@ class SettingsScreen extends StatelessWidget {
child: Text( child: Text(
'Version: ${VersionService().version}', 'Version: ${VersionService().version}',
style: Theme.of(context).textTheme.bodySmall?.copyWith( style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).textTheme.bodySmall?.color?.withOpacity(0.6), color: Theme.of(context).textTheme.bodySmall?.color?.withValues(alpha: 0.6),
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
@@ -2,7 +2,6 @@ import 'dart:typed_data';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:collection/collection.dart';
import '../app_state.dart'; import '../app_state.dart';
import '../models/tile_provider.dart'; import '../models/tile_provider.dart';
+13 -14
View File
@@ -3,7 +3,6 @@ import 'package:provider/provider.dart';
import '../app_state.dart'; import '../app_state.dart';
import '../models/pending_upload.dart'; import '../models/pending_upload.dart';
import '../services/localization_service.dart'; import '../services/localization_service.dart';
import '../state/settings_state.dart';
class UploadQueueScreen extends StatelessWidget { class UploadQueueScreen extends StatelessWidget {
const UploadQueueScreen({super.key}); const UploadQueueScreen({super.key});
@@ -114,8 +113,8 @@ class UploadQueueScreen extends StatelessWidget {
margin: const EdgeInsets.only(bottom: 16), margin: const EdgeInsets.only(bottom: 16),
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.orange.withOpacity(0.1), color: Colors.orange.withValues(alpha: 0.1),
border: Border.all(color: Colors.orange.withOpacity(0.3)), border: Border.all(color: Colors.orange.withValues(alpha: 0.3)),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: Row( child: Row(
@@ -205,7 +204,7 @@ class UploadQueueScreen extends StatelessWidget {
icon: const Icon(Icons.clear_all), icon: const Icon(Icons.clear_all),
label: Text(locService.t('queue.clearUploadQueue')), label: Text(locService.t('queue.clearUploadQueue')),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: appState.pendingCount > 0 ? null : Theme.of(context).disabledColor.withOpacity(0.1), backgroundColor: appState.pendingCount > 0 ? null : Theme.of(context).disabledColor.withValues(alpha: 0.1),
), ),
), ),
), ),
@@ -224,13 +223,13 @@ class UploadQueueScreen extends StatelessWidget {
Icon( Icon(
Icons.check_circle_outline, Icons.check_circle_outline,
size: 64, size: 64,
color: Theme.of(context).textTheme.bodySmall?.color?.withOpacity(0.4), color: Theme.of(context).textTheme.bodySmall?.color?.withValues(alpha: 0.4),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Text( Text(
locService.t('queue.nothingInQueue'), locService.t('queue.nothingInQueue'),
style: Theme.of(context).textTheme.titleMedium?.copyWith( style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: Theme.of(context).textTheme.bodySmall?.color?.withOpacity(0.6), color: Theme.of(context).textTheme.bodySmall?.color?.withValues(alpha: 0.6),
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
@@ -272,16 +271,16 @@ class UploadQueueScreen extends StatelessWidget {
_getUploadStateText(upload, locService) _getUploadStateText(upload, locService)
), ),
subtitle: Text( subtitle: Text(
locService.t('queue.destination', params: [_getUploadModeDisplayName(upload.uploadMode)]) + '\n' + '${locService.t('queue.destination', params: [_getUploadModeDisplayName(upload.uploadMode)])}\n'
locService.t('queue.latitude', params: [upload.coord.latitude.toStringAsFixed(6)]) + '\n' + '${locService.t('queue.latitude', params: [upload.coord.latitude.toStringAsFixed(6)])}\n'
locService.t('queue.longitude', params: [upload.coord.longitude.toStringAsFixed(6)]) + '\n' + '${locService.t('queue.longitude', params: [upload.coord.longitude.toStringAsFixed(6)])}\n'
locService.t('queue.direction', params: [ '${locService.t('queue.direction', params: [
upload.direction is String upload.direction is String
? upload.direction.toString() ? upload.direction.toString()
: upload.direction.round().toString() : upload.direction.round().toString()
]) + '\n' + ])}\n'
locService.t('queue.attempts', params: [upload.attempts.toString()]) + '${locService.t('queue.attempts', params: [upload.attempts.toString()])}'
(upload.uploadState == UploadState.error ? "\n${locService.t('queue.uploadFailedRetry')}" : "") '${upload.uploadState == UploadState.error ? "\n${locService.t('queue.uploadFailedRetry')}" : ""}'
), ),
trailing: Row( trailing: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
+5 -6
View File
@@ -1,7 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
import 'dart:math' as math;
import 'package:flutter/foundation.dart';
import 'package:oauth2_client/oauth2_client.dart'; import 'package:oauth2_client/oauth2_client.dart';
import 'package:oauth2_client/oauth2_helper.dart'; import 'package:oauth2_client/oauth2_helper.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
@@ -30,7 +30,6 @@ class AuthService {
case UploadMode.sandbox: case UploadMode.sandbox:
return 'osm_token_sandbox'; return 'osm_token_sandbox';
case UploadMode.simulate: case UploadMode.simulate:
default:
return 'osm_token_simulate'; return 'osm_token_simulate';
} }
} }
@@ -97,10 +96,10 @@ class AuthService {
final tokenJson = jsonEncode(tokenMap); final tokenJson = jsonEncode(tokenMap);
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
await prefs.setString(_tokenKey, tokenJson); // Save token for current mode await prefs.setString(_tokenKey, tokenJson); // Save token for current mode
_displayName = await _fetchUsername(token!.accessToken!); _displayName = await _fetchUsername(token.accessToken!);
return _displayName; return _displayName;
} catch (e) { } catch (e) {
print('AuthService: OAuth login failed: $e'); debugPrint('AuthService: OAuth login failed: $e');
log('OAuth login failed: $e'); log('OAuth login failed: $e');
rethrow; rethrow;
} }
@@ -128,7 +127,7 @@ class AuthService {
_displayName = await _fetchUsername(accessToken); _displayName = await _fetchUsername(accessToken);
return _displayName; return _displayName;
} catch (e) { } catch (e) {
print('AuthService: Error restoring login with stored token: $e'); debugPrint('AuthService: Error restoring login with stored token: $e');
log('Error restoring login with stored token: $e'); log('Error restoring login with stored token: $e');
// Token might be expired or invalid, clear it // Token might be expired or invalid, clear it
await logout(); await logout();
@@ -194,7 +193,7 @@ class AuthService {
final displayName = userData['user']?['display_name']; final displayName = userData['user']?['display_name'];
return displayName; return displayName;
} catch (e) { } catch (e) {
print('AuthService: Error fetching username: $e'); debugPrint('AuthService: Error fetching username: $e');
log('Error fetching username: $e'); log('Error fetching username: $e');
return null; return null;
} }
+2 -3
View File
@@ -1,5 +1,4 @@
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
@@ -304,8 +303,8 @@ class ChangelogService {
final v2Parts = v2.split('.').map(int.parse).toList(); final v2Parts = v2.split('.').map(int.parse).toList();
// Ensure we have at least 3 parts (major.minor.patch) // Ensure we have at least 3 parts (major.minor.patch)
while (v1Parts.length < 3) v1Parts.add(0); while (v1Parts.length < 3) { v1Parts.add(0); }
while (v2Parts.length < 3) v2Parts.add(0); while (v2Parts.length < 3) { v2Parts.add(0); }
// Compare major version first // Compare major version first
if (v1Parts[0] < v2Parts[0]) return -1; if (v1Parts[0] < v2Parts[0]) return -1;
+1 -2
View File
@@ -1,7 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:app_links/app_links.dart'; import 'package:app_links/app_links.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import '../models/node_profile.dart'; import '../models/node_profile.dart';
import 'profile_import_service.dart'; import 'profile_import_service.dart';
@@ -87,7 +86,7 @@ class DeepLinkService {
} }
} }
/// Handle profile add deep link: deflockapp://profiles/add?p=<base64> /// Handle profile add deep link: `deflockapp://profiles/add?p=<base64>`
void _handleAddProfileLink(Uri uri) { void _handleAddProfileLink(Uri uri) {
final base64Data = uri.queryParameters['p']; final base64Data = uri.queryParameters['p'];
+5 -5
View File
@@ -1,12 +1,12 @@
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'dart:async'; import 'dart:async';
import 'dart:typed_data'; import 'dart:typed_data';
import 'dart:ui'; import 'dart:ui';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import '../app_state.dart'; import '../app_state.dart';
import '../models/tile_provider.dart' as models;
import 'map_data_provider.dart'; import 'map_data_provider.dart';
import 'offline_area_service.dart'; import 'offline_area_service.dart';
@@ -108,7 +108,7 @@ class DeflockTileImageProvider extends ImageProvider<DeflockTileImageProvider> {
// Re-throw the exception and let FlutterMap handle missing tiles gracefully // Re-throw the exception and let FlutterMap handle missing tiles gracefully
// This is better than trying to provide fallback images // This is better than trying to provide fallback images
throw e; rethrow;
} }
} }
-1
View File
@@ -1,6 +1,5 @@
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/flutter_map.dart';
import 'package:flutter/foundation.dart';
import '../models/node_profile.dart'; import '../models/node_profile.dart';
import '../models/osm_node.dart'; import '../models/osm_node.dart';
@@ -1,4 +1,3 @@
import 'dart:convert';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
@@ -81,7 +80,7 @@ Future<List<OsmNode>> _fetchFromOsmApi({
debugPrint('[fetchOsmApiNodes] Exception: $e'); debugPrint('[fetchOsmApiNodes] Exception: $e');
// Don't report status here - let the top level handle it // Don't report status here - let the top level handle it
throw e; // Re-throw to let caller handle rethrow; // Re-throw to let caller handle
} }
} }
@@ -1,5 +1,4 @@
import 'dart:io'; import 'dart:io';
import 'package:latlong2/latlong.dart';
import '../offline_area_service.dart'; import '../offline_area_service.dart';
import '../offline_areas/offline_area_models.dart'; import '../offline_areas/offline_area_models.dart';
import '../offline_areas/offline_tile_utils.dart'; import '../offline_areas/offline_tile_utils.dart';
+1 -1
View File
@@ -5,7 +5,7 @@ import 'dart:async';
/// Only tracks the latest user-initiated request - background requests are ignored. /// Only tracks the latest user-initiated request - background requests are ignored.
enum NetworkRequestStatus { enum NetworkRequestStatus {
idle, // No active requests idle, // No active requests
loading, // Request in progress loading, // Request in progress
splitting, // Request being split due to limits/timeouts splitting, // Request being split due to limits/timeouts
success, // Data loaded successfully success, // Data loaded successfully
timeout, // Request timed out timeout, // Request timed out
+5 -4
View File
@@ -1,3 +1,4 @@
import 'package:flutter/foundation.dart';
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
import '../models/osm_node.dart'; import '../models/osm_node.dart';
import 'package:flutter_map/flutter_map.dart' show LatLngBounds; import 'package:flutter_map/flutter_map.dart' show LatLngBounds;
@@ -85,7 +86,7 @@ class NodeCache {
/// Remove a node by ID from the cache (used for successful deletions) /// Remove a node by ID from the cache (used for successful deletions)
void removeNodeById(int nodeId) { void removeNodeById(int nodeId) {
if (_nodes.remove(nodeId) != null) { if (_nodes.remove(nodeId) != null) {
print('[NodeCache] Removed node $nodeId from cache (successful deletion)'); debugPrint('[NodeCache] Removed node $nodeId from cache (successful deletion)');
} }
} }
@@ -111,19 +112,19 @@ class NodeCache {
} }
if (nodesToRemove.isNotEmpty) { if (nodesToRemove.isNotEmpty) {
print('[NodeCache] Removed ${nodesToRemove.length} temp nodes at coordinate ${coord.latitude}, ${coord.longitude}'); debugPrint('[NodeCache] Removed ${nodesToRemove.length} temp nodes at coordinate ${coord.latitude}, ${coord.longitude}');
} }
} }
/// Remove a specific temporary node by its ID (for queue item-specific cleanup) /// Remove a specific temporary node by its ID (for queue item-specific cleanup)
void removeTempNodeById(int tempNodeId) { void removeTempNodeById(int tempNodeId) {
if (tempNodeId >= 0) { if (tempNodeId >= 0) {
print('[NodeCache] Warning: Attempted to remove non-temp node ID $tempNodeId'); debugPrint('[NodeCache] Warning: Attempted to remove non-temp node ID $tempNodeId');
return; return;
} }
if (_nodes.remove(tempNodeId) != null) { if (_nodes.remove(tempNodeId) != null) {
print('[NodeCache] Removed specific temp node $tempNodeId from cache'); debugPrint('[NodeCache] Removed specific temp node $tempNodeId from cache');
} }
} }
-2
View File
@@ -1,6 +1,4 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/flutter_map.dart';
-3
View File
@@ -1,17 +1,14 @@
import 'dart:io'; import 'dart:io';
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:latlong2/latlong.dart';
import 'package:flutter_map/flutter_map.dart' show LatLngBounds; import 'package:flutter_map/flutter_map.dart' show LatLngBounds;
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'offline_areas/offline_area_models.dart'; import 'offline_areas/offline_area_models.dart';
import 'offline_areas/offline_tile_utils.dart'; import 'offline_areas/offline_tile_utils.dart';
import 'offline_areas/offline_area_downloader.dart'; import 'offline_areas/offline_area_downloader.dart';
import '../models/osm_node.dart';
import '../app_state.dart'; import '../app_state.dart';
import 'map_data_provider.dart'; import 'map_data_provider.dart';
import 'package:deflockapp/dev_config.dart';
/// Service for managing download, storage, and retrieval of offline map areas and cameras. /// Service for managing download, storage, and retrieval of offline map areas and cameras.
class OfflineAreaService { class OfflineAreaService {
+5 -5
View File
@@ -36,9 +36,9 @@ class RoutingService {
debugPrint('[RoutingService] Calculating route from $start to $end'); debugPrint('[RoutingService] Calculating route from $start to $end');
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
final avoidance_distance = await prefs.getInt('navigation_avoidance_distance'); final avoidanceDistance = prefs.getInt('navigation_avoidance_distance') ?? 250;
final enabled_profiles = AppState.instance.enabledProfiles.map((p) { final enabledProfiles = AppState.instance.enabledProfiles.map((p) {
final full = p.toJson(); final full = p.toJson();
return { return {
'id': full['id'], 'id': full['id'],
@@ -47,7 +47,7 @@ class RoutingService {
}; };
}).toList(); }).toList();
final uri = Uri.parse('$_baseUrl'); final uri = Uri.parse(_baseUrl);
final params = { final params = {
'start': { 'start': {
'longitude': start.longitude, 'longitude': start.longitude,
@@ -57,8 +57,8 @@ class RoutingService {
'longitude': end.longitude, 'longitude': end.longitude,
'latitude': end.latitude 'latitude': end.latitude
}, },
'avoidance_distance': avoidance_distance, 'avoidance_distance': avoidanceDistance,
'enabled_profiles': enabled_profiles, 'enabled_profiles': enabledProfiles,
'show_exclusion_zone': false, // for debugging: if true, returns a GeoJSON Feature MultiPolygon showing what areas are avoided in calculating the route 'show_exclusion_zone': false, // for debugging: if true, returns a GeoJSON Feature MultiPolygon showing what areas are avoided in calculating the route
}; };
@@ -1,6 +1,5 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
import '../models/suspected_location.dart'; import '../models/suspected_location.dart';
import 'suspected_location_service.dart'; import 'suspected_location_service.dart';
@@ -143,7 +143,6 @@ class SuspectedLocationDatabase {
// Process entries in batches to avoid memory issues // Process entries in batches to avoid memory issues
const batchSize = 1000; const batchSize = 1000;
int totalInserted = 0;
int validCount = 0; int validCount = 0;
int errorCount = 0; int errorCount = 0;
@@ -188,7 +187,6 @@ class SuspectedLocationDatabase {
// Commit this batch // Commit this batch
await batch.commit(noResult: true); await batch.commit(noResult: true);
totalInserted += currentBatch.length;
// Log progress every few batches // Log progress every few batches
if ((i ~/ batchSize) % 5 == 0) { if ((i ~/ batchSize) % 5 == 0) {
+1 -3
View File
@@ -1,5 +1,3 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/flutter_map.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
@@ -208,7 +206,7 @@ class SuspectedLocationService {
validRows++; validRows++;
} }
} catch (e, stackTrace) { } catch (e) {
// Skip rows that can't be parsed // Skip rows that can't be parsed
debugPrint('[SuspectedLocationService] Error parsing row $rowIndex: $e'); debugPrint('[SuspectedLocationService] Error parsing row $rowIndex: $e');
continue; continue;
+1
View File
@@ -1,4 +1,5 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
+2 -8
View File
@@ -147,7 +147,7 @@ class Uploader {
final currentNodeXml = currentNodeResp.body; final currentNodeXml = currentNodeResp.body;
final versionMatch = RegExp(r'version="(\d+)"').firstMatch(currentNodeXml); final versionMatch = RegExp(r'version="(\d+)"').firstMatch(currentNodeXml);
if (versionMatch == null) { if (versionMatch == null) {
final errorMsg = 'Could not parse version from node XML: ${currentNodeXml.length > 200 ? currentNodeXml.substring(0, 200) + "..." : currentNodeXml}'; final errorMsg = 'Could not parse version from node XML: ${currentNodeXml.length > 200 ? '${currentNodeXml.substring(0, 200)}...' : currentNodeXml}';
debugPrint('[Uploader] $errorMsg'); debugPrint('[Uploader] $errorMsg');
return UploadResult.failure(errorMessage: errorMsg, changesetId: changesetId); return UploadResult.failure(errorMessage: errorMsg, changesetId: changesetId);
} }
@@ -184,7 +184,7 @@ class Uploader {
final currentNodeXml = currentNodeResp.body; final currentNodeXml = currentNodeResp.body;
final versionMatch = RegExp(r'version="(\d+)"').firstMatch(currentNodeXml); final versionMatch = RegExp(r'version="(\d+)"').firstMatch(currentNodeXml);
if (versionMatch == null) { if (versionMatch == null) {
final errorMsg = 'Could not parse version from node XML for deletion: ${currentNodeXml.length > 200 ? currentNodeXml.substring(0, 200) + "..." : currentNodeXml}'; final errorMsg = 'Could not parse version from node XML for deletion: ${currentNodeXml.length > 200 ? '${currentNodeXml.substring(0, 200)}...' : currentNodeXml}';
debugPrint('[Uploader] $errorMsg'); debugPrint('[Uploader] $errorMsg');
return UploadResult.failure(errorMessage: errorMsg, changesetId: changesetId); return UploadResult.failure(errorMessage: errorMsg, changesetId: changesetId);
} }
@@ -350,12 +350,6 @@ class Uploader {
headers: _headers, headers: _headers,
).timeout(kUploadHttpTimeout); ).timeout(kUploadHttpTimeout);
Future<http.Response> _post(String path, String body) => http.post(
Uri.https(_host, path),
headers: _headers,
body: body,
).timeout(kUploadHttpTimeout);
Future<http.Response> _put(String path, String body) => http.put( Future<http.Response> _put(String path, String body) => http.put(
Uri.https(_host, path), Uri.https(_host, path),
headers: _headers, headers: _headers,
+6 -6
View File
@@ -21,7 +21,7 @@ class AuthState extends ChangeNotifier {
_username = await _auth.restoreLogin(); _username = await _auth.restoreLogin();
} }
} catch (e) { } catch (e) {
print("AuthState: Error during auth initialization: $e"); debugPrint("AuthState: Error during auth initialization: $e");
} }
} }
@@ -29,7 +29,7 @@ class AuthState extends ChangeNotifier {
try { try {
_username = await _auth.login(); _username = await _auth.login();
} catch (e) { } catch (e) {
print("AuthState: Login error: $e"); debugPrint("AuthState: Login error: $e");
_username = null; _username = null;
} }
notifyListeners(); notifyListeners();
@@ -49,7 +49,7 @@ class AuthState extends ChangeNotifier {
_username = null; _username = null;
} }
} catch (e) { } catch (e) {
print("AuthState: Auth refresh error: $e"); debugPrint("AuthState: Auth refresh error: $e");
_username = null; _username = null;
} }
notifyListeners(); notifyListeners();
@@ -59,7 +59,7 @@ class AuthState extends ChangeNotifier {
try { try {
_username = await _auth.forceLogin(); _username = await _auth.forceLogin();
} catch (e) { } catch (e) {
print("AuthState: Forced login error: $e"); debugPrint("AuthState: Forced login error: $e");
_username = null; _username = null;
} }
notifyListeners(); notifyListeners();
@@ -69,7 +69,7 @@ class AuthState extends ChangeNotifier {
try { try {
return await _auth.isLoggedIn(); return await _auth.isLoggedIn();
} catch (e) { } catch (e) {
print("AuthState: Token validation error: $e"); debugPrint("AuthState: Token validation error: $e");
return false; return false;
} }
} }
@@ -92,7 +92,7 @@ class AuthState extends ChangeNotifier {
} }
} catch (e) { } catch (e) {
_username = null; _username = null;
print("AuthState: Mode change user restoration error: $e"); debugPrint("AuthState: Mode change user restoration error: $e");
} }
notifyListeners(); notifyListeners();
} }
-1
View File
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:latlong2/latlong.dart';
import '../models/search_result.dart'; import '../models/search_result.dart';
import '../services/search_service.dart'; import '../services/search_service.dart';
+8 -10
View File
@@ -29,23 +29,23 @@ class UploadQueueState extends ChangeNotifier {
// Initialize by loading queue from storage and repopulate cache with pending nodes // Initialize by loading queue from storage and repopulate cache with pending nodes
Future<void> init() async { Future<void> init() async {
await _loadQueue(); await _loadQueue();
print('[UploadQueue] Loaded ${_queue.length} items from storage'); debugPrint('[UploadQueue] Loaded ${_queue.length} items from storage');
_repopulateCacheFromQueue(); _repopulateCacheFromQueue();
} }
// Repopulate the cache with pending nodes from the queue on startup // Repopulate the cache with pending nodes from the queue on startup
void _repopulateCacheFromQueue() { void _repopulateCacheFromQueue() {
print('[UploadQueue] Repopulating cache from ${_queue.length} queue items'); debugPrint('[UploadQueue] Repopulating cache from ${_queue.length} queue items');
final nodesToAdd = <OsmNode>[]; final nodesToAdd = <OsmNode>[];
for (final upload in _queue) { for (final upload in _queue) {
// Skip completed uploads - they should already be in OSM and will be fetched normally // Skip completed uploads - they should already be in OSM and will be fetched normally
if (upload.isComplete) { if (upload.isComplete) {
print('[UploadQueue] Skipping completed upload at ${upload.coord}'); debugPrint('[UploadQueue] Skipping completed upload at ${upload.coord}');
continue; continue;
} }
print('[UploadQueue] Processing ${upload.operation} upload at ${upload.coord}'); debugPrint('[UploadQueue] Processing ${upload.operation} upload at ${upload.coord}');
if (upload.isDeletion) { if (upload.isDeletion) {
// For deletions: mark the original node as pending deletion if it exists in cache // For deletions: mark the original node as pending deletion if it exists in cache
@@ -73,9 +73,7 @@ class UploadQueueState extends ChangeNotifier {
tags['_temp_id'] = tempId.toString(); tags['_temp_id'] = tempId.toString();
// Store temp ID for future cleanup if not already set // Store temp ID for future cleanup if not already set
if (upload.tempNodeId == null) { upload.tempNodeId ??= tempId;
upload.tempNodeId = tempId;
}
if (upload.isEdit) { if (upload.isEdit) {
// For edits: also mark original with _pending_edit if it exists // For edits: also mark original with _pending_edit if it exists
@@ -112,7 +110,7 @@ class UploadQueueState extends ChangeNotifier {
if (nodesToAdd.isNotEmpty) { if (nodesToAdd.isNotEmpty) {
_nodeCache.addOrUpdate(nodesToAdd); _nodeCache.addOrUpdate(nodesToAdd);
print('[UploadQueue] Repopulated cache with ${nodesToAdd.length} pending nodes from queue'); debugPrint('[UploadQueue] Repopulated cache with ${nodesToAdd.length} pending nodes from queue');
// Save queue if we updated any temp IDs for backward compatibility // Save queue if we updated any temp IDs for backward compatibility
_saveQueue(); _saveQueue();
@@ -587,7 +585,7 @@ class UploadQueueState extends ChangeNotifier {
// Still have time, will retry after backoff delay // Still have time, will retry after backoff delay
final nextDelay = item.nextNodeSubmissionRetryDelay; final nextDelay = item.nextNodeSubmissionRetryDelay;
final timeLeft = item.timeUntilAutoClose; final timeLeft = item.timeUntilAutoClose;
debugPrint('[UploadQueue] Will retry node submission in ${nextDelay}, ${timeLeft?.inMinutes}m remaining'); debugPrint('[UploadQueue] Will retry node submission in $nextDelay, ${timeLeft?.inMinutes}m remaining');
// No state change needed - attempt count was already updated above // No state change needed - attempt count was already updated above
} }
} }
@@ -646,7 +644,7 @@ class UploadQueueState extends ChangeNotifier {
// Note: This will NEVER error out - will keep trying until 59-minute window expires // Note: This will NEVER error out - will keep trying until 59-minute window expires
final nextDelay = item.nextChangesetCloseRetryDelay; final nextDelay = item.nextChangesetCloseRetryDelay;
final timeLeft = item.timeUntilAutoClose; final timeLeft = item.timeUntilAutoClose;
debugPrint('[UploadQueue] Changeset close failed (attempt ${item.changesetCloseAttempts}), will retry in ${nextDelay}, ${timeLeft?.inMinutes}m remaining'); debugPrint('[UploadQueue] Changeset close failed (attempt ${item.changesetCloseAttempts}), will retry in $nextDelay, ${timeLeft?.inMinutes}m remaining');
debugPrint('[UploadQueue] Error: ${result.errorMessage}'); debugPrint('[UploadQueue] Error: ${result.errorMessage}');
// No additional state change needed - attempt count was already updated above // No additional state change needed - attempt count was already updated above
} }
+13 -29
View File
@@ -7,7 +7,6 @@ import 'package:flutter_map/flutter_map.dart';
import '../app_state.dart'; import '../app_state.dart';
import '../dev_config.dart'; import '../dev_config.dart';
import '../models/node_profile.dart'; import '../models/node_profile.dart';
import '../models/operator_profile.dart';
import '../services/localization_service.dart'; import '../services/localization_service.dart';
import '../services/map_data_provider.dart'; import '../services/map_data_provider.dart';
import '../services/node_data_manager.dart'; import '../services/node_data_manager.dart';
@@ -59,23 +58,6 @@ class _AddNodeSheetState extends State<AddNodeSheet> {
} }
} }
/// Listen for tutorial completion from AppState
void _onTutorialCompleted() {
_hideTutorial();
}
/// Also check periodically if tutorial was completed by another sheet
void _recheckTutorialStatus() async {
if (_showTutorial) {
final hasCompleted = await ChangelogService().hasCompletedPositioningTutorial();
if (hasCompleted && mounted) {
setState(() {
_showTutorial = false;
});
}
}
}
void _hideTutorial() { void _hideTutorial() {
if (mounted && _showTutorial) { if (mounted && _showTutorial) {
setState(() { setState(() {
@@ -107,7 +89,8 @@ class _AddNodeSheetState extends State<AddNodeSheet> {
void _checkSubmissionGuideAndProceed(BuildContext context, AppState appState, LocalizationService locService) async { void _checkSubmissionGuideAndProceed(BuildContext context, AppState appState, LocalizationService locService) async {
// Check if user has seen the submission guide // Check if user has seen the submission guide
final hasSeenGuide = await ChangelogService().hasSeenSubmissionGuide(); final hasSeenGuide = await ChangelogService().hasSeenSubmissionGuide();
if (!context.mounted) return;
if (!hasSeenGuide) { if (!hasSeenGuide) {
// Show submission guide dialog first // Show submission guide dialog first
final shouldProceed = await showDialog<bool>( final shouldProceed = await showDialog<bool>(
@@ -115,13 +98,14 @@ class _AddNodeSheetState extends State<AddNodeSheet> {
barrierDismissible: false, barrierDismissible: false,
builder: (context) => const SubmissionGuideDialog(), builder: (context) => const SubmissionGuideDialog(),
); );
if (!context.mounted) return;
// If user canceled the submission guide, don't proceed with submission // If user canceled the submission guide, don't proceed with submission
if (shouldProceed != true) { if (shouldProceed != true) {
return; return;
} }
} }
// Now proceed with proximity check // Now proceed with proximity check
_checkProximityOnly(context, appState, locService); _checkProximityOnly(context, appState, locService);
} }
@@ -402,11 +386,11 @@ class _AddNodeSheetState extends State<AddNodeSheet> {
final locService = LocalizationService.instance; final locService = LocalizationService.instance;
final appState = context.watch<AppState>(); final appState = context.watch<AppState>();
void _commit() { void commit() {
_checkProximityAndCommit(context, appState, locService); _checkProximityAndCommit(context, appState, locService);
} }
void _cancel() { void cancel() {
appState.cancelSession(); appState.cancelSession();
Navigator.pop(context); Navigator.pop(context);
} }
@@ -442,11 +426,11 @@ class _AddNodeSheetState extends State<AddNodeSheet> {
session.profile!.isSubmittable && session.profile!.isSubmittable &&
hasGoodCoverage; hasGoodCoverage;
void _navigateToLogin() { void navigateToLogin() {
Navigator.pushNamed(context, '/settings/osm-account'); Navigator.pushNamed(context, '/settings/osm-account');
} }
void _openRefineTags() async { void openRefineTags() async {
final result = await Navigator.push<RefineTagsResult?>( final result = await Navigator.push<RefineTagsResult?>(
context, context,
MaterialPageRoute( MaterialPageRoute(
@@ -578,7 +562,7 @@ class _AddNodeSheetState extends State<AddNodeSheet> {
child: SizedBox( child: SizedBox(
width: double.infinity, width: double.infinity,
child: OutlinedButton.icon( child: OutlinedButton.icon(
onPressed: session.profile != null ? _openRefineTags : null, // Disabled when no profile selected onPressed: session.profile != null ? openRefineTags : null, // Disabled when no profile selected
icon: const Icon(Icons.tune), icon: const Icon(Icons.tune),
label: Text(locService.t('addNode.refineTags')), label: Text(locService.t('addNode.refineTags')),
), ),
@@ -591,14 +575,14 @@ class _AddNodeSheetState extends State<AddNodeSheet> {
children: [ children: [
Expanded( Expanded(
child: OutlinedButton( child: OutlinedButton(
onPressed: _cancel, onPressed: cancel,
child: Text(locService.cancel), child: Text(locService.cancel),
), ),
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
Expanded( Expanded(
child: ElevatedButton( child: ElevatedButton(
onPressed: !appState.isLoggedIn ? _navigateToLogin : (allowSubmit ? _commit : null), onPressed: !appState.isLoggedIn ? navigateToLogin : (allowSubmit ? commit : null),
child: Text(!appState.isLoggedIn ? locService.t('actions.logIn') : locService.t('actions.submit')), child: Text(!appState.isLoggedIn ? locService.t('actions.logIn') : locService.t('actions.submit')),
), ),
), ),
@@ -185,6 +185,7 @@ class AdvancedEditOptionsSheet extends StatelessWidget {
} }
// No custom scheme or app launch failed - redirect to app store // No custom scheme or app launch failed - redirect to app store
if (!context.mounted) return;
await _redirectToAppStore(context, editor); await _redirectToAppStore(context, editor);
} }
+1 -1
View File
@@ -40,7 +40,7 @@ class CameraIcon extends StatelessWidget {
height: kNodeIconDiameter, height: kNodeIconDiameter,
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
color: _ringColor.withOpacity(kNodeDotOpacity), color: _ringColor.withValues(alpha: kNodeDotOpacity),
border: Border.all( border: Border.all(
color: _ringColor, color: _ringColor,
width: getNodeRingThickness(context), width: getNodeRingThickness(context),
-1
View File
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../services/changelog_service.dart';
import '../services/version_service.dart'; import '../services/version_service.dart';
class ChangelogDialog extends StatelessWidget { class ChangelogDialog extends StatelessWidget {
+3 -4
View File
@@ -1,7 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:math'; import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_animations/flutter_map_animations.dart'; import 'package:flutter_map_animations/flutter_map_animations.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@@ -97,8 +96,8 @@ class _CompassIndicatorState extends State<CompassIndicator> {
height: 52, height: 52,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDisabled color: isDisabled
? Colors.grey.withOpacity(0.8) ? Colors.grey.withValues(alpha: 0.8)
: Colors.white.withOpacity(0.95), : Colors.white.withValues(alpha: 0.95),
shape: BoxShape.circle, shape: BoxShape.circle,
border: Border.all( border: Border.all(
color: isDisabled color: isDisabled
@@ -108,7 +107,7 @@ class _CompassIndicatorState extends State<CompassIndicator> {
), ),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.25), color: Colors.black.withValues(alpha: 0.25),
blurRadius: 6, blurRadius: 6,
offset: const Offset(0, 3), offset: const Offset(0, 3),
), ),
+10 -10
View File
@@ -57,8 +57,7 @@ class _DownloadAreaDialogState extends State<DownloadAreaDialog> {
} }
final minZoom = 1; // Always start from zoom 1 to show area overview when zoomed out final minZoom = 1; // Always start from zoom 1 to show area overview when zoomed out
final maxZoom = _zoom.toInt();
// Calculate maximum possible zoom based on tile count limit and tile provider max zoom // Calculate maximum possible zoom based on tile count limit and tile provider max zoom
final maxPossibleZoom = _calculateMaxZoomForTileLimit(bounds, minZoom); final maxPossibleZoom = _calculateMaxZoomForTileLimit(bounds, minZoom);
@@ -124,7 +123,6 @@ class _DownloadAreaDialogState extends State<DownloadAreaDialog> {
final locService = LocalizationService.instance; final locService = LocalizationService.instance;
final appState = context.watch<AppState>(); final appState = context.watch<AppState>();
final bounds = widget.controller.camera.visibleBounds; final bounds = widget.controller.camera.visibleBounds;
final maxZoom = _zoom.toInt();
final isOfflineMode = appState.offlineMode; final isOfflineMode = appState.offlineMode;
// Use the calculated max possible zoom instead of fixed span // Use the calculated max possible zoom instead of fixed span
@@ -191,8 +189,8 @@ class _DownloadAreaDialogState extends State<DownloadAreaDialog> {
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
decoration: BoxDecoration( decoration: BoxDecoration(
color: _tileCount! > kMaxReasonableTileCount color: _tileCount! > kMaxReasonableTileCount
? Colors.orange.withOpacity(0.1) ? Colors.orange.withValues(alpha: 0.1)
: Colors.green.withOpacity(0.1), : Colors.green.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(4), borderRadius: BorderRadius.circular(4),
), ),
child: Column( child: Column(
@@ -200,7 +198,7 @@ class _DownloadAreaDialogState extends State<DownloadAreaDialog> {
children: [ children: [
Text( Text(
_tileCount! > kMaxReasonableTileCount _tileCount! > kMaxReasonableTileCount
? 'Above recommended limit (Z${_maxPossibleZoom})' ? 'Above recommended limit (Z$_maxPossibleZoom)'
: locService.t('download.maxRecommendedZoom', params: [_maxPossibleZoom.toString()]), : locService.t('download.maxRecommendedZoom', params: [_maxPossibleZoom.toString()]),
style: TextStyle( style: TextStyle(
fontSize: 12, fontSize: 12,
@@ -213,7 +211,7 @@ class _DownloadAreaDialogState extends State<DownloadAreaDialog> {
const SizedBox(height: 2), const SizedBox(height: 2),
Text( Text(
_tileCount! > kMaxReasonableTileCount _tileCount! > kMaxReasonableTileCount
? 'Current selection exceeds ${kMaxReasonableTileCount} recommended tile limit but is within ${kAbsoluteMaxTileCount} absolute limit' ? 'Current selection exceeds $kMaxReasonableTileCount recommended tile limit but is within $kAbsoluteMaxTileCount absolute limit'
: locService.t('download.withinTileLimit', params: [kMaxReasonableTileCount.toString()]), : locService.t('download.withinTileLimit', params: [kMaxReasonableTileCount.toString()]),
style: TextStyle( style: TextStyle(
fontSize: 11, fontSize: 11,
@@ -232,9 +230,9 @@ class _DownloadAreaDialogState extends State<DownloadAreaDialog> {
child: Container( child: Container(
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.orange.withOpacity(0.1), color: Colors.orange.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.orange.withOpacity(0.3)), border: Border.all(color: Colors.orange.withValues(alpha: 0.3)),
), ),
child: Row( child: Row(
children: [ children: [
@@ -266,8 +264,9 @@ class _DownloadAreaDialogState extends State<DownloadAreaDialog> {
try { try {
final id = DateTime.now().toIso8601String().replaceAll(':', '-'); final id = DateTime.now().toIso8601String().replaceAll(':', '-');
final appDocDir = await OfflineAreaService().getOfflineAreaDir(); final appDocDir = await OfflineAreaService().getOfflineAreaDir();
if (!context.mounted) return;
final dir = "${appDocDir.path}/$id"; final dir = "${appDocDir.path}/$id";
// Get current tile provider info // Get current tile provider info
final appState = context.read<AppState>(); final appState = context.read<AppState>();
final selectedProvider = appState.selectedTileProvider; final selectedProvider = appState.selectedTileProvider;
@@ -294,6 +293,7 @@ class _DownloadAreaDialogState extends State<DownloadAreaDialog> {
builder: (context) => const DownloadStartedDialog(), builder: (context) => const DownloadStartedDialog(),
); );
} catch (e) { } catch (e) {
if (!context.mounted) return;
Navigator.pop(context); Navigator.pop(context);
showDialog( showDialog(
context: context, context: context,
+14 -17
View File
@@ -7,14 +7,11 @@ import 'package:flutter_map/flutter_map.dart';
import '../app_state.dart'; import '../app_state.dart';
import '../dev_config.dart'; import '../dev_config.dart';
import '../models/node_profile.dart'; import '../models/node_profile.dart';
import '../models/operator_profile.dart';
import '../models/pending_upload.dart'; import '../models/pending_upload.dart';
import '../services/localization_service.dart'; import '../services/localization_service.dart';
import '../services/map_data_provider.dart'; import '../services/map_data_provider.dart';
import '../services/node_data_manager.dart'; import '../services/node_data_manager.dart';
import '../services/changelog_service.dart'; import '../services/changelog_service.dart';
import '../state/settings_state.dart';
import '../state/session_state.dart';
import 'refine_tags_sheet.dart'; import 'refine_tags_sheet.dart';
import 'advanced_edit_options_sheet.dart'; import 'advanced_edit_options_sheet.dart';
import 'proximity_warning_dialog.dart'; import 'proximity_warning_dialog.dart';
@@ -95,7 +92,8 @@ class _EditNodeSheetState extends State<EditNodeSheet> {
void _checkSubmissionGuideAndProceed(BuildContext context, AppState appState, LocalizationService locService) async { void _checkSubmissionGuideAndProceed(BuildContext context, AppState appState, LocalizationService locService) async {
// Check if user has seen the submission guide // Check if user has seen the submission guide
final hasSeenGuide = await ChangelogService().hasSeenSubmissionGuide(); final hasSeenGuide = await ChangelogService().hasSeenSubmissionGuide();
if (!context.mounted) return;
if (!hasSeenGuide) { if (!hasSeenGuide) {
// Show submission guide dialog first // Show submission guide dialog first
final shouldProceed = await showDialog<bool>( final shouldProceed = await showDialog<bool>(
@@ -103,13 +101,14 @@ class _EditNodeSheetState extends State<EditNodeSheet> {
barrierDismissible: false, barrierDismissible: false,
builder: (context) => const SubmissionGuideDialog(), builder: (context) => const SubmissionGuideDialog(),
); );
if (!context.mounted) return;
// If user canceled the submission guide, don't proceed with submission // If user canceled the submission guide, don't proceed with submission
if (shouldProceed != true) { if (shouldProceed != true) {
return; return;
} }
} }
// Now proceed with proximity check // Now proceed with proximity check
_checkProximityOnly(context, appState, locService); _checkProximityOnly(context, appState, locService);
} }
@@ -435,25 +434,23 @@ class _EditNodeSheetState extends State<EditNodeSheet> {
final locService = LocalizationService.instance; final locService = LocalizationService.instance;
final appState = context.watch<AppState>(); final appState = context.watch<AppState>();
void _commit() { void commit() {
// Check if there are any actual changes to submit // Check if there are any actual changes to submit
if (!_hasActualChanges(widget.session)) { if (!_hasActualChanges(widget.session)) {
_showNoChangesDialog(context, locService); _showNoChangesDialog(context, locService);
return; return;
} }
_checkProximityAndCommit(context, appState, locService); _checkProximityAndCommit(context, appState, locService);
} }
void _cancel() { void cancel() {
appState.cancelEditSession(); appState.cancelEditSession();
Navigator.pop(context); Navigator.pop(context);
} }
final session = widget.session; final session = widget.session;
final submittableProfiles = appState.enabledProfiles.where((p) => p.isSubmittable).toList(); final submittableProfiles = appState.enabledProfiles.where((p) => p.isSubmittable).toList();
final isSandboxMode = appState.uploadMode == UploadMode.sandbox;
// Check if we have good cache coverage around the node position // Check if we have good cache coverage around the node position
bool hasGoodCoverage = true; bool hasGoodCoverage = true;
final nodeCoord = session.originalNode.coord; final nodeCoord = session.originalNode.coord;
@@ -481,11 +478,11 @@ class _EditNodeSheetState extends State<EditNodeSheet> {
session.profile!.isSubmittable && session.profile!.isSubmittable &&
hasGoodCoverage; hasGoodCoverage;
void _navigateToLogin() { void navigateToLogin() {
Navigator.pushNamed(context, '/settings/osm-account'); Navigator.pushNamed(context, '/settings/osm-account');
} }
void _openRefineTags() async { void openRefineTags() async {
final result = await Navigator.push<RefineTagsResult?>( final result = await Navigator.push<RefineTagsResult?>(
context, context,
MaterialPageRoute( MaterialPageRoute(
@@ -694,7 +691,7 @@ class _EditNodeSheetState extends State<EditNodeSheet> {
child: SizedBox( child: SizedBox(
width: double.infinity, width: double.infinity,
child: OutlinedButton.icon( child: OutlinedButton.icon(
onPressed: session.profile != null ? _openRefineTags : null, // Disabled when no profile selected onPressed: session.profile != null ? openRefineTags : null, // Disabled when no profile selected
icon: const Icon(Icons.tune), icon: const Icon(Icons.tune),
label: Text(locService.t('editNode.refineTags')), label: Text(locService.t('editNode.refineTags')),
), ),
@@ -707,14 +704,14 @@ class _EditNodeSheetState extends State<EditNodeSheet> {
children: [ children: [
Expanded( Expanded(
child: OutlinedButton( child: OutlinedButton(
onPressed: _cancel, onPressed: cancel,
child: Text(locService.cancel), child: Text(locService.cancel),
), ),
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
Expanded( Expanded(
child: ElevatedButton( child: ElevatedButton(
onPressed: !appState.isLoggedIn ? _navigateToLogin : (allowSubmit ? _commit : null), onPressed: !appState.isLoggedIn ? navigateToLogin : (allowSubmit ? commit : null),
child: Text(!appState.isLoggedIn ? locService.t('actions.logIn') : locService.t('actions.saveEdit')), child: Text(!appState.isLoggedIn ? locService.t('actions.logIn') : locService.t('actions.saveEdit')),
), ),
), ),
+2 -30
View File
@@ -6,7 +6,6 @@ import 'package:latlong2/latlong.dart';
import '../../app_state.dart'; import '../../app_state.dart';
import '../../dev_config.dart'; import '../../dev_config.dart';
import '../../models/osm_node.dart'; import '../../models/osm_node.dart';
import '../../models/direction_fov.dart';
/// Helper class to build direction cone polygons for cameras /// Helper class to build direction cone polygons for cameras
class DirectionConesBuilder { class DirectionConesBuilder {
@@ -113,11 +112,6 @@ class DirectionConesBuilder {
node.coord.longitude.abs() <= 180; node.coord.longitude.abs() <= 180;
} }
static bool _isPendingUpload(OsmNode node) {
return node.tags.containsKey('_pending_upload') &&
node.tags['_pending_upload'] == 'true';
}
/// Build cone with variable FOV width - new method for range notation support /// Build cone with variable FOV width - new method for range notation support
static Polygon _buildConeWithFov( static Polygon _buildConeWithFov(
LatLng origin, LatLng origin,
@@ -141,28 +135,6 @@ class DirectionConesBuilder {
); );
} }
/// Legacy method for backward compatibility - uses dev_config FOV
static Polygon _buildCone(
LatLng origin,
double bearingDeg,
double zoom, {
required BuildContext context,
bool isPending = false,
bool isSession = false,
bool isActiveDirection = true,
}) {
return _buildConeInternal(
origin: origin,
bearingDeg: bearingDeg,
halfAngleDeg: kDirectionConeHalfAngle,
zoom: zoom,
context: context,
isPending: isPending,
isSession: isSession,
isActiveDirection: isActiveDirection,
);
}
/// Internal cone building method that handles the actual rendering /// Internal cone building method that handles the actual rendering
static Polygon _buildConeInternal({ static Polygon _buildConeInternal({
required LatLng origin, required LatLng origin,
@@ -231,7 +203,7 @@ class DirectionConesBuilder {
return Polygon( return Polygon(
points: points, points: points,
color: kDirectionConeColor.withOpacity(opacity), color: kDirectionConeColor.withValues(alpha: opacity),
borderColor: kDirectionConeColor, borderColor: kDirectionConeColor,
borderStrokeWidth: getDirectionConeBorderWidth(context), borderStrokeWidth: getDirectionConeBorderWidth(context),
); );
@@ -279,7 +251,7 @@ class DirectionConesBuilder {
return Polygon( return Polygon(
points: points, points: points,
color: kDirectionConeColor.withOpacity(opacity), color: kDirectionConeColor.withValues(alpha: opacity),
borderColor: kDirectionConeColor, borderColor: kDirectionConeColor,
borderStrokeWidth: getDirectionConeBorderWidth(context), borderStrokeWidth: getDirectionConeBorderWidth(context),
); );
+1 -1
View File
@@ -81,7 +81,7 @@ class _LayerSelectorDialogState extends State<_LayerSelectorDialog> {
Container( Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceVariant, color: Theme.of(context).colorScheme.surfaceContainerHighest,
borderRadius: const BorderRadius.vertical(top: Radius.circular(12)), borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
), ),
child: Row( child: Row(
-1
View File
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_animations/flutter_map_animations.dart';
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
import '../../models/osm_node.dart'; import '../../models/osm_node.dart';
+4 -6
View File
@@ -1,12 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_animations/flutter_map_animations.dart'; import 'package:flutter_map_animations/flutter_map_animations.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../../app_state.dart'; import '../../app_state.dart';
import '../../dev_config.dart'; import '../../dev_config.dart';
import '../../services/localization_service.dart'; import '../../services/localization_service.dart';
import '../camera_icon.dart';
import '../compass_indicator.dart'; import '../compass_indicator.dart';
import 'layer_selector_button.dart'; import 'layer_selector_button.dart';
@@ -64,8 +62,8 @@ class MapOverlays extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration( decoration: BoxDecoration(
color: uploadMode == UploadMode.sandbox color: uploadMode == UploadMode.sandbox
? Colors.orange.withOpacity(0.90) ? Colors.orange.withValues(alpha: 0.90)
: Colors.deepPurple.withOpacity(0.80), : Colors.deepPurple.withValues(alpha: 0.80),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
boxShadow: const [ boxShadow: const [
BoxShadow(color: Colors.black26, blurRadius: 5, offset: Offset(0,2)), BoxShadow(color: Colors.black26, blurRadius: 5, offset: Offset(0,2)),
@@ -98,7 +96,7 @@ class MapOverlays extends StatelessWidget {
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 2), padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 2),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.black.withOpacity(0.52), color: Colors.black.withValues(alpha: 0.52),
borderRadius: BorderRadius.circular(7), borderRadius: BorderRadius.circular(7),
), ),
child: Builder( child: Builder(
@@ -131,7 +129,7 @@ class MapOverlays extends StatelessWidget {
onTap: () => _showAttributionDialog(context, attribution!), onTap: () => _showAttributionDialog(context, attribution!),
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface.withOpacity(0.9), color: Theme.of(context).colorScheme.surface.withValues(alpha: 0.9),
borderRadius: BorderRadius.circular(4), borderRadius: BorderRadius.circular(4),
), ),
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4), padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
@@ -6,7 +6,6 @@ import 'package:latlong2/latlong.dart';
import '../../models/osm_node.dart'; import '../../models/osm_node.dart';
import '../../models/suspected_location.dart'; import '../../models/suspected_location.dart';
import '../../app_state.dart'; import '../../app_state.dart';
import '../../state/session_state.dart';
import '../../dev_config.dart'; import '../../dev_config.dart';
import '../camera_icon.dart'; import '../camera_icon.dart';
import '../provisional_pin.dart'; import '../provisional_pin.dart';
+2 -2
View File
@@ -20,8 +20,8 @@ class NodeMapMarker extends StatefulWidget {
required this.mapController, required this.mapController,
this.onNodeTap, this.onNodeTap,
this.enabled = true, this.enabled = true,
Key? key, super.key,
}) : super(key: key); });
@override @override
State<NodeMapMarker> createState() => _NodeMapMarkerState(); State<NodeMapMarker> createState() => _NodeMapMarkerState();
@@ -1,7 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_map_animations/flutter_map_animations.dart'; import 'package:flutter_map_animations/flutter_map_animations.dart';
import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
import '../../models/node_profile.dart'; import '../../models/node_profile.dart';
import '../../app_state.dart' show UploadMode; import '../../app_state.dart' show UploadMode;
+1 -2
View File
@@ -4,7 +4,6 @@ import 'package:latlong2/latlong.dart';
import '../../models/osm_node.dart'; import '../../models/osm_node.dart';
import '../../app_state.dart'; import '../../app_state.dart';
import '../../state/session_state.dart';
import '../../dev_config.dart'; import '../../dev_config.dart';
import 'direction_cones.dart'; import 'direction_cones.dart';
@@ -38,7 +37,7 @@ class OverlayLayerBuilder {
overlays.add( overlays.add(
Polygon( Polygon(
points: selectedLocation.bounds, points: selectedLocation.bounds,
color: Colors.orange.withOpacity(0.3), color: Colors.orange.withValues(alpha: 0.3),
borderColor: Colors.orange, borderColor: Colors.orange,
borderStrokeWidth: 2.0, borderStrokeWidth: 2.0,
), ),
@@ -20,8 +20,8 @@ class SuspectedLocationMapMarker extends StatefulWidget {
required this.mapController, required this.mapController,
this.onLocationTap, this.onLocationTap,
this.enabled = true, this.enabled = true,
Key? key, super.key,
}) : super(key: key); });
@override @override
State<SuspectedLocationMapMarker> createState() => _SuspectedLocationMapMarkerState(); State<SuspectedLocationMapMarker> createState() => _SuspectedLocationMapMarkerState();
+1 -2
View File
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
import '../../models/tile_provider.dart' as models; import '../../models/tile_provider.dart' as models;
import '../../services/deflock_tile_provider.dart'; import '../../services/deflock_tile_provider.dart';
@@ -93,7 +92,7 @@ class TileLayerManager {
return TileLayer( return TileLayer(
urlTemplate: urlTemplate, // Critical for cache key generation urlTemplate: urlTemplate, // Critical for cache key generation
userAgentPackageName: 'me.deflock.deflockapp', userAgentPackageName: 'me.deflock.deflockapp',
maxZoom: selectedTileType?.maxZoom?.toDouble() ?? 18.0, maxZoom: selectedTileType?.maxZoom.toDouble() ?? 18.0,
tileProvider: _tileProvider!, tileProvider: _tileProvider!,
); );
} }
+4 -5
View File
@@ -1,4 +1,3 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
@@ -182,9 +181,9 @@ class NavigationSheet extends StatelessWidget {
Container( Container(
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.amber.withOpacity(0.1), color: Colors.amber.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.amber.withOpacity(0.3)), border: Border.all(color: Colors.amber.withValues(alpha: 0.3)),
), ),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -212,9 +211,9 @@ class NavigationSheet extends StatelessWidget {
Container( Container(
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.orange.withOpacity(0.1), color: Colors.orange.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.orange.withOpacity(0.3)), border: Border.all(color: Colors.orange.withValues(alpha: 0.3)),
), ),
child: Row( child: Row(
children: [ children: [
@@ -1,12 +1,9 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:latlong2/latlong.dart';
import 'package:flutter_map/flutter_map.dart' show LatLngBounds; import 'package:flutter_map/flutter_map.dart' show LatLngBounds;
import '../services/map_data_provider.dart';
import '../services/node_data_manager.dart'; import '../services/node_data_manager.dart';
import '../services/node_spatial_cache.dart'; import '../services/node_spatial_cache.dart';
import '../services/network_status.dart';
import '../models/node_profile.dart'; import '../models/node_profile.dart';
import '../models/osm_node.dart'; import '../models/osm_node.dart';
import '../app_state.dart'; import '../app_state.dart';
+9 -9
View File
@@ -40,7 +40,7 @@ class NodeTagSheet extends StatelessWidget {
final isRealOSMNode = !node.tags.containsKey('_pending_upload') && final isRealOSMNode = !node.tags.containsKey('_pending_upload') &&
node.id > 0; // Real OSM nodes have positive IDs node.id > 0; // Real OSM nodes have positive IDs
void _openEditSheet() { void openEditSheet() {
// Check if node limit is active and warn user // Check if node limit is active and warn user
if (isNodeLimitActive) { if (isNodeLimitActive) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
@@ -64,7 +64,7 @@ class NodeTagSheet extends StatelessWidget {
} }
} }
void _deleteNode() async { void deleteNode() async {
final shouldDelete = await showDialog<bool>( final shouldDelete = await showDialog<bool>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
@@ -95,7 +95,7 @@ class NodeTagSheet extends StatelessWidget {
} }
} }
void _viewOnOSM() async { void viewOnOSM() async {
final url = 'https://www.openstreetmap.org/node/${node.id}'; final url = 'https://www.openstreetmap.org/node/${node.id}';
try { try {
final uri = Uri.parse(url); final uri = Uri.parse(url);
@@ -117,7 +117,7 @@ class NodeTagSheet extends StatelessWidget {
} }
} }
void _openAdvancedEdit() { void openAdvancedEdit() {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
@@ -177,7 +177,7 @@ class NodeTagSheet extends StatelessWidget {
}, },
text: e.value, text: e.value,
style: TextStyle( style: TextStyle(
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7), color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.7),
), ),
linkStyle: TextStyle( linkStyle: TextStyle(
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
@@ -201,7 +201,7 @@ class NodeTagSheet extends StatelessWidget {
children: [ children: [
if (isRealOSMNode) ...[ if (isRealOSMNode) ...[
TextButton.icon( TextButton.icon(
onPressed: () => _viewOnOSM(), onPressed: () => viewOnOSM(),
icon: const Icon(Icons.open_in_new, size: 16), icon: const Icon(Icons.open_in_new, size: 16),
label: Text(locService.t('actions.viewOnOSM')), label: Text(locService.t('actions.viewOnOSM')),
), ),
@@ -209,7 +209,7 @@ class NodeTagSheet extends StatelessWidget {
], ],
if (isEditable) ...[ if (isEditable) ...[
OutlinedButton.icon( OutlinedButton.icon(
onPressed: _openAdvancedEdit, onPressed: openAdvancedEdit,
icon: const Icon(Icons.open_in_new, size: 18), icon: const Icon(Icons.open_in_new, size: 18),
label: Text(locService.t('actions.advanced')), label: Text(locService.t('actions.advanced')),
style: OutlinedButton.styleFrom( style: OutlinedButton.styleFrom(
@@ -226,7 +226,7 @@ class NodeTagSheet extends StatelessWidget {
children: [ children: [
if (isEditable) ...[ if (isEditable) ...[
ElevatedButton.icon( ElevatedButton.icon(
onPressed: _openEditSheet, onPressed: openEditSheet,
icon: const Icon(Icons.edit, size: 18), icon: const Icon(Icons.edit, size: 18),
label: Text(locService.edit), label: Text(locService.edit),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
@@ -235,7 +235,7 @@ class NodeTagSheet extends StatelessWidget {
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
ElevatedButton.icon( ElevatedButton.icon(
onPressed: node.isConstrained ? null : _deleteNode, onPressed: node.isConstrained ? null : deleteNode,
icon: const Icon(Icons.delete, size: 18), icon: const Icon(Icons.delete, size: 18),
label: Text(locService.t('actions.delete')), label: Text(locService.t('actions.delete')),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
+6 -5
View File
@@ -8,15 +8,15 @@ class NuclearResetDialog extends StatelessWidget {
final String errorReport; final String errorReport;
const NuclearResetDialog({ const NuclearResetDialog({
Key? key, super.key,
required this.errorReport, required this.errorReport,
}) : super(key: key); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return WillPopScope( return PopScope(
// Prevent back button from closing dialog // Prevent back button from closing dialog
onWillPop: () async => false, canPop: false,
child: AlertDialog( child: AlertDialog(
title: const Row( title: const Row(
children: [ children: [
@@ -96,7 +96,8 @@ class NuclearResetDialog extends StatelessWidget {
// Clear all app data // Clear all app data
await NuclearResetService.clearEverything(); await NuclearResetService.clearEverything();
if (!context.mounted) return;
// Show non-dismissible dialog // Show non-dismissible dialog
await showDialog( await showDialog(
context: context, context: context,
@@ -30,7 +30,7 @@ class PositioningTutorialOverlay extends StatelessWidget {
), ),
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.black.withOpacity(0.3), // Semi-transparent overlay color: Colors.black.withValues(alpha: 0.3), // Semi-transparent overlay
), ),
child: Center( child: Center(
child: Padding( child: Padding(
@@ -73,7 +73,7 @@ class PositioningTutorialOverlay extends StatelessWidget {
Text( Text(
locService.t('positioningTutorial.hint'), locService.t('positioningTutorial.hint'),
style: TextStyle( style: TextStyle(
color: Colors.white.withOpacity(0.8), color: Colors.white.withValues(alpha: 0.8),
fontSize: 14, fontSize: 14,
fontStyle: FontStyle.italic, fontStyle: FontStyle.italic,
), ),
+2 -2
View File
@@ -43,7 +43,7 @@ class LocationPin extends StatelessWidget {
width: size * 0.4, width: size * 0.4,
height: size * 0.2, height: size * 0.2,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.black.withOpacity(0.3), color: Colors.black.withValues(alpha: 0.3),
borderRadius: BorderRadius.circular(size * 0.1), borderRadius: BorderRadius.circular(size * 0.1),
), ),
), ),
@@ -64,7 +64,7 @@ class LocationPin extends StatelessWidget {
color: Colors.white, color: Colors.white,
shape: BoxShape.circle, shape: BoxShape.circle,
border: Border.all( border: Border.all(
color: _pinColor.withOpacity(0.8), color: _pinColor.withValues(alpha: 0.8),
width: 1.5, width: 1.5,
), ),
), ),
+1 -1
View File
@@ -82,7 +82,7 @@ class _ProximityAlertBannerState extends State<ProximityAlertBanner>
color: Colors.red.shade600, color: Colors.red.shade600,
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.3), color: Colors.black.withValues(alpha: 0.3),
blurRadius: 10, blurRadius: 10,
offset: const Offset(0, 2), offset: const Offset(0, 2),
), ),
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:latlong2/latlong.dart';
import '../models/osm_node.dart'; import '../models/osm_node.dart';
import '../services/localization_service.dart'; import '../services/localization_service.dart';
+1 -1
View File
@@ -38,7 +38,7 @@ class ReauthMessagesDialog extends StatelessWidget {
Container( Container(
padding: const EdgeInsets.all(12.0), padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primaryContainer.withOpacity(0.3), color: Theme.of(context).colorScheme.primaryContainer.withValues(alpha: 0.3),
borderRadius: BorderRadius.circular(8.0), borderRadius: BorderRadius.circular(8.0),
), ),
child: Row( child: Row(
+3 -3
View File
@@ -107,7 +107,7 @@ class _LocationSearchBarState extends State<LocationSearchBar> {
borderRadius: const BorderRadius.vertical(bottom: Radius.circular(12)), borderRadius: const BorderRadius.vertical(bottom: Radius.circular(12)),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Theme.of(context).shadowColor.withOpacity(0.2), color: Theme.of(context).shadowColor.withValues(alpha: 0.2),
blurRadius: 8, blurRadius: 8,
offset: const Offset(0, 2), offset: const Offset(0, 2),
), ),
@@ -152,7 +152,7 @@ class _LocationSearchBarState extends State<LocationSearchBar> {
: null, : null,
dense: true, dense: true,
onTap: () => _onResultTap(result), onTap: () => _onResultTap(result),
)).toList(), )),
], ],
), ),
); );
@@ -184,7 +184,7 @@ class _LocationSearchBarState extends State<LocationSearchBar> {
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Theme.of(context).shadowColor.withOpacity(0.2), color: Theme.of(context).shadowColor.withValues(alpha: 0.2),
blurRadius: 8, blurRadius: 8,
offset: const Offset(0, 2), offset: const Offset(0, 2),
), ),
+4 -4
View File
@@ -81,9 +81,9 @@ class _SubmissionGuideDialogState extends State<SubmissionGuideDialog> {
Container( Container(
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.1), color: Colors.blue.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.blue.withOpacity(0.3)), border: Border.all(color: Colors.blue.withValues(alpha: 0.3)),
), ),
child: Text( child: Text(
locService.t('submissionGuide.bestPractices'), locService.t('submissionGuide.bestPractices'),
@@ -171,10 +171,10 @@ class _SubmissionGuideDialogState extends State<SubmissionGuideDialog> {
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary.withOpacity(0.1), color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(4), borderRadius: BorderRadius.circular(4),
border: Border.all( border: Border.all(
color: Theme.of(context).colorScheme.primary.withOpacity(0.3), color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.3),
), ),
), ),
child: Text( child: Text(
+2 -5
View File
@@ -1,9 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import '../models/suspected_location.dart'; import '../models/suspected_location.dart';
import '../app_state.dart';
import '../services/localization_service.dart'; import '../services/localization_service.dart';
import '../dev_config.dart'; import '../dev_config.dart';
@@ -17,7 +15,6 @@ class SuspectedLocationSheet extends StatelessWidget {
return AnimatedBuilder( return AnimatedBuilder(
animation: LocalizationService.instance, animation: LocalizationService.instance,
builder: (context, child) { builder: (context, child) {
final appState = context.watch<AppState>();
final locService = LocalizationService.instance; final locService = LocalizationService.instance;
// Get all fields except location and ticket_no // Get all fields except location and ticket_no
@@ -97,7 +94,7 @@ class SuspectedLocationSheet extends StatelessWidget {
: Text( : Text(
e.value, e.value,
style: TextStyle( style: TextStyle(
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7), color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.7),
), ),
softWrap: true, softWrap: true,
), ),
@@ -131,7 +128,7 @@ class SuspectedLocationSheet extends StatelessWidget {
child: Text( child: Text(
'${location.centroid.latitude.toStringAsFixed(6)}, ${location.centroid.longitude.toStringAsFixed(6)}', '${location.centroid.latitude.toStringAsFixed(6)}, ${location.centroid.longitude.toStringAsFixed(6)}',
style: TextStyle( style: TextStyle(
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7), color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.7),
), ),
softWrap: true, softWrap: true,
), ),
+4 -4
View File
@@ -88,9 +88,9 @@ class _WelcomeDialogState extends State<WelcomeDialog> {
Container( Container(
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.orange.withOpacity(0.1), color: Colors.orange.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.orange.withOpacity(0.3)), border: Border.all(color: Colors.orange.withValues(alpha: 0.3)),
), ),
child: Text( child: Text(
locService.t('welcome.firsthandKnowledge'), locService.t('welcome.firsthandKnowledge'),
@@ -171,10 +171,10 @@ class _WelcomeDialogState extends State<WelcomeDialog> {
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary.withOpacity(0.1), color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(4), borderRadius: BorderRadius.circular(4),
border: Border.all( border: Border.all(
color: Theme.of(context).colorScheme.primary.withOpacity(0.3), color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.3),
), ),
), ),
child: Text( child: Text(
+16
View File
@@ -222,6 +222,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.0.0" version: "6.0.0"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1"
url: "https://pub.dev"
source: hosted
version: "6.0.0"
flutter_local_notifications: flutter_local_notifications:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -496,6 +504,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.0.0" version: "5.0.0"
lints:
dependency: transitive
description:
name: lints
sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df"
url: "https://pub.dev"
source: hosted
version: "6.1.0"
lists: lists:
dependency: transitive dependency: transitive
description: description:
+3
View File
@@ -33,15 +33,18 @@ dependencies:
shared_preferences: ^2.2.2 shared_preferences: ^2.2.2
sqflite: ^2.4.1 sqflite: ^2.4.1
path: ^1.8.3 path: ^1.8.3
path_provider: ^2.1.0
uuid: ^4.0.0 uuid: ^4.0.0
package_info_plus: ^8.0.0 package_info_plus: ^8.0.0
csv: ^6.0.0 csv: ^6.0.0
collection: ^1.18.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
mocktail: ^1.0.4 mocktail: ^1.0.4
flutter_launcher_icons: ^0.14.4 flutter_launcher_icons: ^0.14.4
flutter_lints: ^6.0.0
flutter_native_splash: ^2.4.6 flutter_native_splash: ^2.4.6
flutter: flutter:
+8 -8
View File
@@ -31,16 +31,16 @@ void main() {
); );
// Test 0-3 range // Test 0-3 range
final url_0_3_a = tileType0_3.getTileUrl(1, 0, 0); final url03A = tileType0_3.getTileUrl(1, 0, 0);
final url_0_3_b = tileType0_3.getTileUrl(1, 3, 0); final url03B = tileType0_3.getTileUrl(1, 3, 0);
expect(url_0_3_a, contains('s0.example.com')); expect(url03A, contains('s0.example.com'));
expect(url_0_3_b, contains('s3.example.com')); expect(url03B, contains('s3.example.com'));
// Test 1-4 range // Test 1-4 range
final url_1_4_a = tileType1_4.getTileUrl(1, 0, 0); final url14A = tileType1_4.getTileUrl(1, 0, 0);
final url_1_4_b = tileType1_4.getTileUrl(1, 3, 0); final url14B = tileType1_4.getTileUrl(1, 3, 0);
expect(url_1_4_a, contains('s1.example.com')); expect(url14A, contains('s1.example.com'));
expect(url_1_4_b, contains('s4.example.com')); expect(url14B, contains('s4.example.com'));
// Test consistency // Test consistency
final url1 = tileType0_3.getTileUrl(1, 2, 3); final url1 = tileType0_3.getTileUrl(1, 2, 3);