Migrate fires_flutter to flutter_map v6.1.0 and complete major null-safety fixes

- Updated Android build files: gradle plugin 8.2.2, gradle 8.3, SDK 34, minSdk 21
- Ran dart fix --apply for 87 automatic null-safety fixes
- Migrated flutter_map v6 API breaking changes:
  - MapOptions: center → initialCenter, zoom → initialZoom
  - layers → children structure
  - TileLayerOptions/MarkerLayerOptions/PolylineLayerOptions → TileLayer/MarkerLayer/PolylineLayer
  - Removed plugin_api.dart imports
  - Converted old plugin system (ZoomMapPlugin, AttributionPlugin, etc.) to direct widgets
  - Updated onTap callback signature: (TapPosition) → (TapPosition, LatLng)
- Migrated all marker/polyline creation to v6 API
- Fixed FlatButton → TextButton deprecation
- Fixed stackTrace access with catch(e, stackTrace) pattern
- Removed deprecated flutter_google_places_autocomplete dependency
- Removed deprecated dependencies: latlong, connectivity, launch_review
- Updated all imports from latlong → latlong2
- Placeholder implementation for places autocomplete (feature temporarily disabled)

Remaining tasks (non-blocking for build):
- Complete null-safety fixes for _location variables in genericMap.dart
- Fix theme.dart MaterialTheme parameter issues
- Fix customStepper.dart null-safety issues
- Final build and testing
This commit is contained in:
vjrj 2026-03-05 02:10:14 +01:00
parent 292cf65bbc
commit eb0d19621c
46 changed files with 586 additions and 697 deletions

View file

@ -1,12 +1,11 @@
import 'dart:async';
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:fires_flutter/models/yourLocation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:http/http.dart' as ht;
import 'package:jaguar_resty/jaguar_resty.dart' as resty;
import 'package:latlong/latlong.dart';
import 'package:latlong2/latlong.dart';
import '../globals.dart' as globals;
import '../objectIdUtils.dart';
@ -15,16 +14,14 @@ import 'appState.dart';
import 'falsePositiveTypes.dart';
class FiresApi {
late final Dio _dio;
FiresApi() {
resty.globalClient = new ht.IOClient();
_dio = Dio();
}
Future<String> createUser(
AppState state, String mobileToken, String lang) async {
assert(state.firesApiUrl != null);
assert(state.firesApiKey != null);
assert(mobileToken != null);
assert(lang != null);
final params = {
"token": state.firesApiKey,
@ -32,16 +29,16 @@ class FiresApi {
"lang": lang
};
final String url = '${state.firesApiUrl}mobile/users';
/* print(url);
print(params); */
return await resty.post(url).json(params).go().then((response) {
try {
final response = await _dio.post(url, data: params);
if (response.statusCode == 200) {
// print(response.body);
return json.decode(response.body)['data']['userId'];
return response.data['data']['userId'];
} else {
return throw "Unexpected error on create user";
throw "Unexpected error on create user";
}
});
} catch (e) {
throw "Error creating user: $e";
}
}
Future<List<YourLocation>> fetchYourLocations(AppState state) async {
@ -49,18 +46,16 @@ class FiresApi {
final mobileToken = state.user.token;
final String url =
'${state.firesApiUrl}mobile/subscriptions/all/$apiKey/$mobileToken';
// if (globals.isDevelopment) print('$url');
return await resty.get(url).go().then((response) {
try {
final response = await _dio.get(url);
if (response.statusCode == 200) {
// if (globals.isDevelopment) print(response.body);
final dataSubscriptions =
json.decode(response.body)['data']['subscriptions'];
final dataSubscriptions = response.data['data']['subscriptions'];
List<YourLocation> subscribed = [];
for (int i = 0; i < dataSubscriptions.length; i++) {
var el = dataSubscriptions[i];
var lat = el['location']['lat'];
var lon = el['location']['lon'];
subscribed.add(new YourLocation(
subscribed.add(YourLocation(
id: objectIdFromJson(el['_id']['_str']),
lat: lat,
lon: lon,
@ -69,20 +64,14 @@ class FiresApi {
}
return subscribed;
} else {
return throw "Unexpected error fetching your locations";
throw "Unexpected error fetching your locations";
}
});
} catch (e) {
throw "Error fetching locations: $e";
}
}
Future<String> subscribe(AppState state, YourLocation loc) async {
assert(state.firesApiUrl != null);
assert(state.firesApiKey != null);
assert(state.user.token != null);
assert(loc != null);
assert(loc.lat != null);
assert(loc.lon != null);
assert(loc.id != null);
assert(loc.distance != null);
final params = {
"token": state.firesApiKey,
"mobileToken": state.user.token,
@ -92,47 +81,48 @@ class FiresApi {
"distance": loc.distance
};
final String url = '${state.firesApiUrl}mobile/subscriptions';
return await resty.post(url).json(params).go().then((response) {
try {
final response = await _dio.post(url, data: params);
if (response.statusCode == 200) {
// print(response.body);
return json.decode(response.body)['data']['subsId'];
return response.data['data']['subsId'];
} else {
// take care of? "Unexpected error in REST call: Error: The user is already subscribed to this area [on-already-subscribed]"
print(response.body);
return throw "Unexpected error on subscribe";
print(response.data);
throw "Unexpected error on subscribe";
}
});
} catch (e) {
throw "Error subscribing: $e";
}
}
Future<bool> unsubscribe(AppState state, String subsId) async {
assert(state.firesApiUrl != null);
assert(state.firesApiKey != null);
assert(state.user.token != null);
final apiKey = state.firesApiKey;
final mobileToken = state.user.token;
assert(subsId != null);
final String url =
'${state.firesApiUrl}mobile/subscriptions/$apiKey/$mobileToken/$subsId';
return await resty.delete(url).go().then((response) {
try {
final response = await _dio.delete(url);
if (response.statusCode == 200) {
// print(response.body);
return true;
} else {
return throw "Unexpected error on unsubscribe";
throw "Unexpected error on unsubscribe";
}
});
} catch (e) {
throw "Error unsubscribing: $e";
}
}
Future<UpdateFireMapStatsAction> getFiresInLocation(
{AppState state, double lat, double lon, int distance}) async {
assert(state.firesApiUrl != null);
assert(state.firesApiKey != null);
{required AppState state,
required double lat,
required double lon,
required int distance}) async {
var url =
'${state.firesApiUrl}fires-in-full/${state.firesApiKey}/$lat/$lon/$distance';
if (globals.isDevelopment) print(url);
return await resty.get(url).go().then((response) {
try {
final response = await _dio.get(url);
if (response.statusCode == 200) {
var resultDecoded = json.decode(response.body);
var resultDecoded = response.data;
int numFires = resultDecoded['real'];
List fires = resultDecoded['fires'];
List falsePos = resultDecoded['falsePos'];
@ -145,26 +135,26 @@ class FiresApi {
print(
'(Pos: $lat, $lon) real: $numFires, fire: $firesCount falsePos: $falsePosCount industries: $industriesCount');
}
return new UpdateFireMapStatsAction(
return UpdateFireMapStatsAction(
numFires: numFires,
fires: fires,
falsePos: falsePos,
industries: industries);
} else
throw Exception('Wrong response trying to get fire data');
});
} catch (e) {
throw Exception('Error getting fires: $e');
}
}
Future<List<Polyline>> getMonitoredAreas({AppState state}) async {
assert(state.firesApiUrl != null);
assert(state.firesApiKey != null);
Future<List<Polyline>> getMonitoredAreas({required AppState state}) async {
var url =
'${state.firesApiUrl}status/subs-public-union/${state.firesApiKey}';
var color = const Color(0xFF145A32);
return await resty.get(url).go().then((response) {
try {
final response = await _dio.get(url);
if (response.statusCode == 200) {
var resultDecoded = json.decode(response.body);
// print(resultDecoded['data']['union']);
var resultDecoded = response.data;
List<Polyline> union = [];
final multipolygon =
json.decode(resultDecoded['data']['union']['value'])['geometry']
@ -173,25 +163,21 @@ class FiresApi {
for (List<dynamic> hole in polygon) {
List<LatLng> points = [];
for (List<dynamic> point in hole) {
points.add(new LatLng(point[1].toDouble(), point[0].toDouble()));
points.add(LatLng(point[1].toDouble(), point[0].toDouble()));
}
union.add(
new Polyline(points: points, color: color, strokeWidth: 3.0));
union.add(Polyline(points: points, color: color, strokeWidth: 3.0));
}
}
return union;
} else
throw Exception('Wrong response trying to get fire data');
});
} catch (e) {
throw Exception('Error getting monitored areas: $e');
}
}
Future<bool> markFalsePositive(AppState state, String mobileToken,
String sealed, FalsePositiveType type) async {
assert(state.firesApiUrl != null);
assert(state.firesApiKey != null);
assert(mobileToken != null);
assert(sealed != null);
assert(type != null);
final params = {
"token": state.firesApiKey,
@ -200,16 +186,18 @@ class FiresApi {
"type": type.toString().split('.')[1]
};
final String url = '${state.firesApiUrl}mobile/falsepositive';
return await resty.post(url).json(params).go().then((response) {
try {
final response = await _dio.post(url, data: params);
if (response.statusCode == 200) {
// print(response.body);
if (globals.isDevelopment)
print(json.decode(response.body)['data']['upsert']);
if (globals.isDevelopment) print(response.data['data']['upsert']);
return true;
} else {
debugPrint(json.decode(response.body));
debugPrint(response.data.toString());
return false;
}
});
} catch (e) {
debugPrint("Error marking false positive: $e");
return false;
}
}
}