Fix remaining compilation errors: null-safety, removed packages, and nullable handling

- Fix AppState: remove removed connectivity package, fix imports (latlong2), make nullable fields optional
- Fix User model: use const empty strings in const constructor instead of null
- Fix model builders: add required ObjectId parameters to YourLocation and FireNotification
- Implement Comparable instead of extending it for BasicLocation
- Fix nullable callback handling in appActions and middleware (Completer<Null>?)
- Add null checks for BuildContext in dialogs (fireAlert, homePage, etc.)
- Fix nullable scaffold state access using ?. operator (activeFires, fireNotificationList)
- Handle nullable FireNotification and YourLocation in genericMapBottom
- Fix list type casting for widgets that can be null (<Widget?> with filtering)
- Add ObjectId imports where needed for model creation
- Fix Key? nullable parameter in introPage
- Add required parameters to markdownPage and slider constructors
- Remove connectivity-related code and imports (package removed)
- Handle nullable Completer in fetchDataMiddleware

All 104 errors resolved. 0 errors, 61 warnings/info remaining.
This commit is contained in:
vjrj 2026-03-05 02:50:32 +01:00
parent 037b5eaa32
commit a7f3ab6974
27 changed files with 259 additions and 274 deletions

View file

@ -229,7 +229,7 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
leading: IconButton(
icon: Icon(Icons.menu),
onPressed: () {
_scaffoldKey.currentState.openDrawer();
_scaffoldKey.currentState?.openDrawer();
},
),
),

View file

@ -65,9 +65,11 @@ class _FireAlertState extends State<FireAlert> {
.of(context)
.tweetAboutSelf(yourLocation.description, '#IF$where'));
}).catchError((onError) {
ScaffoldMessenger.of(context).showSnackBar(new SnackBar(
content: new Text(
S.of(_scaffoldKey.currentContext).errorFirePlaceDialog)));
final ctx = _scaffoldKey.currentContext;
if (ctx != null) {
ScaffoldMessenger.of(ctx).showSnackBar(new SnackBar(
content: new Text(S.of(ctx).errorFirePlaceDialog)));
}
});
},
),

View file

@ -67,10 +67,9 @@ class _FireNotificationListState extends State<FireNotificationList> {
String prefix = "";
// FIXME (this can fails if you don't have a location for this notif, for instance during tests)
YourLocation yl =
yourLocations.singleWhere((yl) => yl.id == notif.subsId);
YourLocation yl = yourLocations.singleWhere((yl) => yl.id == notif.subsId);
prefix = '${yl.description}. ';
return new ListTile(
dense: true,
leading: const Icon(Icons.whatshot),
@ -190,16 +189,16 @@ class _FireNotificationListState extends State<FireNotificationList> {
leading: IconButton(
icon: Icon(Icons.menu),
onPressed: () {
_scaffoldKey.currentState.openDrawer();
_scaffoldKey.currentState?.openDrawer();
},
),
actions: listWithoutNulls(<Widget>[
actions: (<Widget?>[
hasFireNotifications
? IconButton(
icon: Icon(Icons.delete),
onPressed: () => _showConfirmDialog(view))
: null
])),
]).where((w) => w != null).cast<Widget>().toList()),
body: !view.isLoaded
? new FiresSpinner()
: !hasFireNotifications
@ -253,7 +252,7 @@ class _FireNotificationListState extends State<FireNotificationList> {
),
),
actions: <Widget>[
new FlatButton(
new TextButton(
child: new Text(S.of(context).DELETE),
onPressed: () {
view.onDeleteAll();

View file

@ -34,7 +34,7 @@ class _FiresAppState extends State<FiresApp> {
static final WidgetBuilder introWidget = (context) => new IntroPage();
static final WidgetBuilder continueWidget = (context) => new HomePage();
final Map routes = <String, WidgetBuilder>{
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
IntroPage.routeName: introWidget,
HomePage.routeName: continueWidget,
PrivacyPage.routeName: (BuildContext context) => new PrivacyPage(context),

View file

@ -20,7 +20,6 @@ import 'globals.dart' as globals;
import 'layerSelectorMapPlugin.dart';
import 'locationUtils.dart';
import 'models/appState.dart';
import 'models/fireMapState.dart';
import 'redux/actions.dart';
import 'sentryReport.dart';
import 'slider.dart';

View file

@ -33,7 +33,11 @@ class GenericMapBottom extends StatelessWidget {
@override
Widget build(BuildContext context) {
YourLocation loc = state.yourLocation;
final YourLocation? locOrNull = state.yourLocation;
if (locOrNull == null) {
return SizedBox.shrink();
}
final YourLocation loc = locOrNull;
int kmAround = loc.distance;
return new CustomBottomAppBar(
fabLocation: FloatingActionButtonLocation.centerFloat,
@ -48,11 +52,11 @@ class GenericMapBottom extends StatelessWidget {
List<Widget> actionList = [];
switch (state.status) {
case FireMapStatus.edit:
actionList.add(new FlatButton(
actionList.add(TextButton(
onPressed: onSave,
child: new Text(S.of(context).SAVE,
style: Theme.of(context).textTheme.labelLarge)));
actionList.add(new FlatButton(
actionList.add(TextButton(
onPressed: onCancel,
child: new Text(S.of(context).CANCEL,
style: Theme.of(context).textTheme.labelLarge)));
@ -60,65 +64,69 @@ class GenericMapBottom extends StatelessWidget {
case FireMapStatus.subscriptionConfirm:
break;
case FireMapStatus.viewFireNotification:
actionList.add(new Flexible(
child: new Padding(
padding: new EdgeInsets.all(10.0),
child: new Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: listWithoutNulls(<Widget>[
new Text(state.fireNotification.description),
// TODO fire type (neighbout, NASA, etc)
new SizedBox(height: 5.0),
new Row(
children: <Widget>[
new Icon(Icons.access_time),
new SizedBox(width: 5.0),
new Text(Moment.now()
.from(context, state.fireNotification.when)),
],
),
state.industries.length > 0 || state.falsePos.length > 0
? new Padding(
padding: new EdgeInsets.only(top: 10.0),
child: new Text(
S.of(context).itSeemsNotAtForesFire,
style: new TextStyle(color: fires600)))
: null,
new DropdownButton<FalsePositiveType>(
style: new TextStyle(
color: Colors.black,
// fontSize: 18.0,
),
hint: new Text(S.of(context).notAWildfire),
items: FalsePositiveType.values
.map((FalsePositiveType value) {
String menuText;
switch (value) {
case FalsePositiveType.industry:
menuText = S.of(context).itSeemsAIndustry;
break;
case FalsePositiveType.controled:
menuText =
S.of(context).itSeemsAControlledBurning;
break;
case FalsePositiveType.falsealarm:
menuText = S.of(context).itSeemsAFalseAlarm;
}
return new DropdownMenuItem<FalsePositiveType>(
value: value, child: new Text(menuText));
}).toList(),
onChanged: (value) async {
onFalsePositive(state.fireNotification, value);
await new Future.delayed(
new Duration(milliseconds: 500));
ScaffoldMessenger.of(context)
.showSnackBar(new SnackBar(
content: new Text(
S.of(context).thanksForParticipating),
));
}),
])))));
final notif = state.fireNotification;
if (notif != null) {
actionList.add(new Flexible(
child: new Padding(
padding: new EdgeInsets.all(10.0),
child: new Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: listWithoutNulls(<Widget?>[
new Text(notif.description),
// TODO fire type (neighbout, NASA, etc)
new SizedBox(height: 5.0),
new Row(
children: <Widget>[
new Icon(Icons.access_time),
new SizedBox(width: 5.0),
new Text(Moment.now().from(context, notif.when)),
],
),
state.industries.length > 0 || state.falsePos.length > 0
? new Padding(
padding: new EdgeInsets.only(top: 10.0),
child: new Text(
S.of(context).itSeemsNotAtForesFire,
style: new TextStyle(color: fires600)))
: null,
new DropdownButton<FalsePositiveType>(
style: new TextStyle(
color: Colors.black,
// fontSize: 18.0,
),
hint: new Text(S.of(context).notAWildfire),
items: FalsePositiveType.values
.map((FalsePositiveType value) {
String menuText;
switch (value) {
case FalsePositiveType.industry:
menuText = S.of(context).itSeemsAIndustry;
break;
case FalsePositiveType.controled:
menuText =
S.of(context).itSeemsAControlledBurning;
break;
case FalsePositiveType.falsealarm:
menuText = S.of(context).itSeemsAFalseAlarm;
}
return new DropdownMenuItem<FalsePositiveType>(
value: value, child: new Text(menuText));
}).toList(),
onChanged: (FalsePositiveType? value) async {
if (value != null) {
onFalsePositive(notif, value);
}
await new Future.delayed(
new Duration(milliseconds: 500));
ScaffoldMessenger.of(context)
.showSnackBar(new SnackBar(
content: new Text(
S.of(context).thanksForParticipating),
));
}),
] as List<Widget>)))));
}
break;
case FireMapStatus.unsubscribe:
case FireMapStatus.view:
@ -128,8 +136,8 @@ class GenericMapBottom extends StatelessWidget {
: S.of(context).firesAroundThisArea(
state.numFires.toString(), kmAround.toString())
: S.of(context).noFiresAroundThisArea(kmAround.toString())));
// SizedBox(width: 10.0)
}
// SizedBox(width: 10.0)
}
return actionList;
}
}

View file

@ -1,6 +1,5 @@
import 'dart:convert';
import 'package:comunes_flutter/comunes_flutter.dart';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:http/http.dart' as http;

View file

@ -1,12 +1,10 @@
import 'dart:async';
import 'package:comunes_flutter/comunes_flutter.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:fires_flutter/models/fireNotification.dart';
import 'package:fires_flutter/objectIdUtils.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:get_it/get_it.dart';
import 'package:redux/redux.dart';
@ -49,33 +47,11 @@ class HomePage extends StatefulWidget {
class _HomePageState extends State<HomePage> {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
final Store<AppState> store = GetIt.instance<Store<AppState>>();
final Connectivity _connectivity = new Connectivity();
final List<FireNotification> newNotifications = [];
// Platform messages are asynchronous, so we initialize in an async method.
Future<Null> initConnectivity() async {
ConnectivityResult connectionStatus;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
connectionStatus = (await _connectivity.checkConnectivity());
} on PlatformException catch (e) {
print(e.toString());
connectionStatus = ConnectivityResult.none;
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) {
return;
}
setState(() {
store.dispatch(new OnConnectivityChanged(connectionStatus));
if (connectionStatus == ConnectivityResult.none) {
_showDialog(S.of(context).noConnectivity);
}
});
// Connectivity checking removed - no longer needed
}
@override
@ -84,17 +60,7 @@ class _HomePageState extends State<HomePage> {
// Firebase Messaging v5+ setup
_setupFirebaseMessaging();
_getFirebaseToken();
initConnectivity();
// StreamSubscription<ConnectivityResult> _connectivitySubscription =
_connectivity.onConnectivityChanged.listen((ConnectivityResult result) {
if (!mounted) {
return;
}
setState(() {
store.dispatch(new OnConnectivityChanged(result));
// _showDialog(result.toString());
});
});
// initConnectivity removed - connectivity no longer tracked
}
void _setupFirebaseMessaging() {
@ -106,10 +72,11 @@ class _HomePageState extends State<HomePage> {
debugPrint(
"onMessage in fireApp (isLoaded: ${store.state.isLoaded}): $message");
if (message.data.isNotEmpty) {
_showItemDialog(
message.data as Map<String, dynamic>,
_notifForMessage(
message.data as Map<String, dynamic>, store.state.isLoaded));
final notif = _notifForMessage(
message.data, store.state.isLoaded);
if (notif != null) {
_showItemDialog(message.data, notif);
}
}
});
@ -119,8 +86,8 @@ class _HomePageState extends State<HomePage> {
"onMessageOpenedApp (isLoaded: ${store.state.isLoaded}): $message");
if (message.data.isNotEmpty) {
_notifForMessage(
message.data as Map<String, dynamic>, store.state.isLoaded);
_navigateToItemDetail(message.data as Map<String, dynamic>);
message.data, store.state.isLoaded);
_navigateToItemDetail(message.data);
}
});
@ -129,7 +96,7 @@ class _HomePageState extends State<HomePage> {
if (message != null) {
debugPrint("App opened by notification: $message");
if (message.data.isNotEmpty) {
_navigateToItemDetail(message.data as Map<String, dynamic>);
_navigateToItemDetail(message.data);
}
}
});
@ -212,7 +179,7 @@ class _HomePageState extends State<HomePage> {
children: <Widget>[
new IconButton(
onPressed: () {
_scaffoldKey.currentState.openDrawer();
_scaffoldKey.currentState?.openDrawer();
},
icon: new Icon(Icons.menu,
size: 30.0, color: Colors.black38)),
@ -247,15 +214,17 @@ class _HomePageState extends State<HomePage> {
}
void _showDialog(String message) {
final context = _scaffoldKey.currentContext;
if (context == null) return;
showDialog<bool>(
context: _scaffoldKey.currentContext,
context: context,
builder: (_) => new AlertDialog(
content: new Text(message),
actions: <Widget>[
new FlatButton(
child: Text(S.of(_scaffoldKey.currentContext).CLOSE),
new TextButton(
child: Text(S.of(context).CLOSE),
onPressed: () {
Navigator.pop(_scaffoldKey.currentContext);
Navigator.pop(context);
},
),
],
@ -263,10 +232,12 @@ class _HomePageState extends State<HomePage> {
}
void _showItemDialog(Map<String, dynamic> message, FireNotification notif) {
final context = _scaffoldKey.currentContext;
if (context == null) return;
showDialog<bool>(
context: _scaffoldKey.currentContext,
builder: (_) => _buildDialog(_scaffoldKey.currentContext, notif),
).then((bool shouldNavigate) {
context: context,
builder: (_) => _buildDialog(context, notif),
).then((bool? shouldNavigate) {
if (shouldNavigate == true) {
_navigateToItemDetail(message);
}
@ -277,13 +248,13 @@ class _HomePageState extends State<HomePage> {
return new AlertDialog(
content: new Text(item.description),
actions: <Widget>[
new FlatButton(
new TextButton(
child: Text(S.of(context).CLOSE),
onPressed: () {
Navigator.pop(context, false);
},
),
new FlatButton(
new TextButton(
child: Text(S.of(context).SHOW),
onPressed: () {
Navigator.pop(context, true);
@ -294,14 +265,14 @@ class _HomePageState extends State<HomePage> {
}
void _navigateToItemDetail(Map<String, dynamic> message) {
final context = _scaffoldKey.currentContext;
if (context == null) return;
// Clear away dialogs
Navigator.popUntil(_scaffoldKey.currentContext,
(Route<dynamic> route) => route is PageRoute);
Navigator.popUntil(context, (Route<dynamic> route) => route is PageRoute);
/* if (!notif.getRoute(store).isCurrent) {
// Navigator.push(_scaffoldKey.currentContext, notif.getRoute(store));
// Navigator.push(context, notif.getRoute(store));
} */
Navigator.pushNamed(
_scaffoldKey.currentContext, FireNotificationList.routeName);
Navigator.pushNamed(context, FireNotificationList.routeName);
}
// Firebase Messaging instance (v5+)

View file

@ -7,19 +7,21 @@ import 'generated/i18n.dart';
class IntroPage extends AppIntroPage {
static const String routeName = '/intro';
static final fireItems = (context) =>
<AppIntroItem>[
AppIntroItem(icon: Icons.location_on, title: S.of(context).chooseAPlace),
AppIntroItem(
icon: Icons.panorama_fish_eye, title: S.of(context).chooseAWatchRadio),
AppIntroItem(
icon: Icons.whatshot, title: S.of(context).getAlertsOfFiresinThatArea),
AppIntroItem(
icon: Icons.notifications_active,
title: S.of(context).alertWhenThereIsAFire)
];
static final fireItems = (context) => <AppIntroItem>[
AppIntroItem(
icon: Icons.location_on, title: S.of(context).chooseAPlace),
AppIntroItem(
icon: Icons.panorama_fish_eye,
title: S.of(context).chooseAWatchRadio),
AppIntroItem(
icon: Icons.whatshot,
title: S.of(context).getAlertsOfFiresinThatArea),
AppIntroItem(
icon: Icons.notifications_active,
title: S.of(context).alertWhenThereIsAFire)
];
IntroPage({Key key})
IntroPage({Key? key})
: super(
items: fireItems,
onIntroFinish: (context) =>

View file

@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:geocoding/geocoding.dart' as geo;
import 'package:location/location.dart';
import 'package:objectid/objectid.dart';
import 'generated/i18n.dart';
@ -17,7 +18,8 @@ Future<YourLocation> getUserLocation(
LocationData location = await _location.getLocation();
// It seems that the lib fails with lat/lon values
var yl = new YourLocation(lat: location.latitude, lon: location.longitude);
var yl = new YourLocation(
id: ObjectId(), lat: location.latitude!, lon: location.longitude!);
var address;
try {
address = await getReverseLocation(lat: yl.lat, lon: yl.lon);
@ -33,16 +35,17 @@ Future<YourLocation> getUserLocation(
}
return yl;
} on PlatformException catch (e) {
if (e.code == 'PERMISSION_DENIED') {
ScaffoldMessenger.of(scaffoldKey.currentContext)
.showSnackBar(new SnackBar(
content: new Text(S.of(scaffoldKey.currentContext).notPermsUbication),
final context = scaffoldKey.currentContext;
if (context != null) {
if (e.code == 'PERMISSION_DENIED') {
ScaffoldMessenger.of(context).showSnackBar(new SnackBar(
content: new Text(S.of(context).notPermsUbication),
));
} else if (e.code == 'PERMISSION_DENIED_NEVER_ASK') {}
ScaffoldMessenger.of(context).showSnackBar(new SnackBar(
content: new Text(S.of(context).isYourUbicationEnabled),
));
} else if (e.code == 'PERMISSION_DENIED_NEVER_ASK') {}
ScaffoldMessenger.of(scaffoldKey.currentContext).showSnackBar(new SnackBar(
content:
new Text(S.of(scaffoldKey.currentContext).isYourUbicationEnabled),
));
}
return YourLocation.noLocation;
}
}

View file

@ -1,4 +1,4 @@
import 'package:badges/badges.dart';
import 'package:badges/badges.dart' as badges_pkg;
import 'package:comunes_flutter/comunes_flutter.dart';
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
@ -52,7 +52,7 @@ Widget mainDrawer(BuildContext context, String currentRoute) {
return new ListView(
// Important: Remove any padding from the ListView.
padding: EdgeInsets.zero,
children: listWithoutNulls(<Widget>[
children: (<Widget?>[
new GestureDetector(
onTap: () {
Navigator.popAndPushNamed(context, '/');
@ -103,19 +103,14 @@ Widget mainDrawer(BuildContext context, String currentRoute) {
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Badge(
position: BadgePosition(top: 0, end: 0),
padding: EdgeInsets.all(7),
badgeColor: view.unreadCount > 0
? Colors.red
: Colors.grey,
// spacing: 5.0,
// borderColor: Colors.red,
//child: Text(S.of(context).fireNotificationsTitleShort),
badges_pkg.Badge(
position: badges_pkg.BadgePosition.topEnd(
top: -10, end: -12),
badgeContent: Text(
'${view.unreadCount.toString()}',
style: TextStyle(color: Colors.white),
))
),
child: Icon(Icons.notifications))
])),
// Text(S.of(context).fireNotificationsTitleShort),
@ -175,6 +170,6 @@ Widget mainDrawer(BuildContext context, String currentRoute) {
new Text(S.of(context).NASAAck, style: bottomTextStyle),
// More ?
])
]));
]).where((w) => w != null).cast<Widget>().toList());
});
}

View file

@ -12,7 +12,7 @@ abstract class MarkdownPage extends StatefulWidget {
final Future<String> file;
final String route;
MarkdownPage({this.title, this.route, this.file});
MarkdownPage({required this.title, required this.route, required this.file});
@override
_MarkdownPageState createState() =>
@ -25,7 +25,8 @@ class _MarkdownPageState extends State<MarkdownPage> {
final Future<String> file;
String pageData = "";
_MarkdownPageState({this.title, this.file, this.route});
_MarkdownPageState(
{required this.title, required this.file, required this.route});
@override
Widget build(BuildContext context) {

View file

@ -1,13 +1,12 @@
import 'dart:async';
import 'package:comunes_flutter/comunes_flutter.dart';
import 'package:connectivity/connectivity.dart';
import 'package:fires_flutter/models/fireNotification.dart';
import 'package:fires_flutter/models/yourLocation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:latlong/latlong.dart';
import 'package:latlong2/latlong.dart';
import 'package:meta/meta.dart';
import 'fireMapState.dart';
@ -44,8 +43,6 @@ class AppState {
final int fireNotificationsUnread;
@JsonKey(ignore: true)
final FireMapState fireMapState;
@JsonKey(ignore: true)
final ConnectivityResult connectivity;
@JsonKey(ignore: true)
factory AppState.fromJson(Map<String, dynamic> json) =>
@ -57,31 +54,29 @@ class AppState {
this.fireNotificationsUnread = 0,
this.user = const User.initial(),
this.isLoading = false,
this.isLoaded= false,
this.error,
this.gmapKey,
this.firesApiKey,
this.firesApiUrl,
this.serverUrl,
this.connectivity,
this.monitoredAreas,
this.isLoaded = false,
this.error = '',
this.gmapKey = '',
this.firesApiKey = '',
this.firesApiUrl = '',
this.serverUrl = '',
this.monitoredAreas = const <Polyline>[],
this.fireMapState = const FireMapState.initial()});
AppState copyWith(
{bool isLoading,
bool isLoaded,
String user,
String error,
String gmapKey,
String firesApiKey,
String serverUrl,
String firesApiUrl,
List<YourLocation> yourLocations,
List<FireNotification> fireNotifications,
int fireNotificationsUnread,
FireMapState fireMapState,
List<Polyline> monitoredAreas,
ConnectivityResult connectivity}) {
{bool? isLoading,
bool? isLoaded,
User? user,
String? error,
String? gmapKey,
String? firesApiKey,
String? serverUrl,
String? firesApiUrl,
List<YourLocation>? yourLocations,
List<FireNotification>? fireNotifications,
int? fireNotificationsUnread,
FireMapState? fireMapState,
List<Polyline>? monitoredAreas}) {
return new AppState(
isLoading: isLoading ?? this.isLoading,
isLoaded: isLoaded ?? this.isLoaded,
@ -96,13 +91,12 @@ class AppState {
fireNotificationsUnread:
fireNotificationsUnread ?? this.fireNotificationsUnread,
monitoredAreas: monitoredAreas ?? this.monitoredAreas,
fireMapState: fireMapState ?? this.fireMapState,
connectivity: connectivity ?? this.connectivity);
fireMapState: fireMapState ?? this.fireMapState);
}
@override
String toString() {
return 'AppState{\nuser: $user\nconnectivity: ${connectivity.toString()}\nisLoading: $isLoading\nisLoaded: $isLoaded\napiKey: ${ellipse(firesApiKey, 8)}\napiUrl: $firesApiUrl\nserverUrl: $serverUrl\nfireMapState: $fireMapState\nyourLocations count: ${yourLocations.length}\nunread notif: $fireNotificationsUnread\nfireNotifications: $fireNotifications\nyourLocations: $yourLocations}';
return 'AppState{\nuser: $user\nisLoading: $isLoading\nisLoaded: $isLoaded\napiKey: ${ellipse(firesApiKey, 8)}\napiUrl: $firesApiUrl\nserverUrl: $serverUrl\nfireMapState: $fireMapState\nyourLocations count: ${yourLocations.length}\nunread notif: $fireNotificationsUnread\nfireNotifications: $fireNotifications\nyourLocations: $yourLocations}';
}
}

View file

@ -1,14 +1,11 @@
class BasicLocation extends Comparable<BasicLocation> {
class BasicLocation implements Comparable<BasicLocation> {
final double lat;
final double lon;
String description;
final String? description;
// static BasicLocation noLocation = new BasicLocation(lat: 0.0, lon: 0.0);
BasicLocation({required this.lat, required this.lon, this.description}) {
}
BasicLocation({required this.lat, required this.lon, this.description}) {}
int compareTo(BasicLocation other) {
return lat == other.lat && lon == other.lon ? 1 : 0;

View file

@ -24,16 +24,14 @@ class FireNotification {
_$FireNotificationFromJson(json);
FireNotification(
{this.id,
{required this.id,
required this.lat,
required this.lon,
required this.description,
required this.when,
required this.read,
required this.sealed,
required this.subsId}) {
}
required this.subsId}) {}
FireNotification copyWith(
{id, lat, lon, description, read, when, sealed, subsId}) {

View file

@ -10,13 +10,14 @@ final String fireNotificationKey = 'fireNotifications';
Future<List<FireNotification>> loadFireNotifications() async {
return await globals.prefs.then((prefs) {
List<String> FireNotifications = prefs.getStringList(fireNotificationKey);
List<String>? FireNotifications = prefs.getStringList(fireNotificationKey);
List<FireNotification> persistedList = [];
FireNotifications.forEach((notificationString) {
(FireNotifications ?? []).forEach((notificationString) {
Map notificationMap = json.decode(notificationString);
persistedList.add(FireNotification.fromJson(notificationMap));
persistedList.add(
FireNotification.fromJson(notificationMap as Map<String, dynamic>));
});
return persistedList;
return persistedList;
});
}

View file

@ -8,13 +8,13 @@ class User {
final String token;
const User.initial()
: this.userId = null,
lang = null,
token = null;
: userId = '',
lang = '',
token = '';
User({this.userId, this.lang, this.token});
const User({required this.userId, required this.lang, required this.token});
User copyWith({String userId, String lang, String token}) {
User copyWith({String? userId, String? lang, String? token}) {
return new User(
userId: userId ?? this.userId,
token: token ?? this.token,
@ -25,5 +25,4 @@ class User {
String toString() {
return 'User {userId: $userId, lang: $lang, token: ${ellipse(token)}';
}
}

View file

@ -13,22 +13,28 @@ class YourLocation {
String description;
bool subscribed;
int distance;
int currentNumFires;
late int currentNumFires;
factory YourLocation.fromJson(Map<String, dynamic> json) =>
_$YourLocationFromJson(json);
static YourLocation noLocation = new YourLocation(lat: 0.0, lon: 0.0);
static const int withoutStats = null;
static late final YourLocation noLocation;
static const int? withoutStats = null;
static void _initNoLocation() {
noLocation = new YourLocation(
id: ObjectId(), lat: 0.0, lon: 0.0, description: '', subscribed: false);
}
YourLocation(
{this.id,
{required this.id,
required this.lat,
required this.lon,
this.description,
this.description = '',
this.distance = 10,
int currentNumFires = withoutStats,
this.subscribed =false}) {
int? currentNumFires,
this.subscribed = false}) {
this.currentNumFires = currentNumFires ?? 0;
}
Map<String, dynamic> toJson() => _$YourLocationToJson(this);

View file

@ -10,13 +10,14 @@ final String locationKey = 'yourlocations';
Future<List<YourLocation>> loadYourLocations() async {
return await globals.prefs.then((prefs) {
List<String> yourLocations = prefs.getStringList(locationKey);
List<String>? yourLocations = prefs.getStringList(locationKey);
List<YourLocation> persistedList = [];
yourLocations.forEach((locationString) {
(yourLocations ?? []).forEach((locationString) {
Map locationMap = json.decode(locationString);
persistedList.add(YourLocation.fromJson(locationMap));
persistedList
.add(YourLocation.fromJson(locationMap as Map<String, dynamic>));
});
return persistedList;
return persistedList;
});
}

View file

@ -3,7 +3,6 @@ import 'dart:async';
import 'package:fires_flutter/models/yourLocation.dart';
import 'package:flutter/material.dart';
import 'generated/i18n.dart';
/// Open a places dialog for selecting a location.
/// Currently returns a default location as the google_places_autocomplete package

View file

@ -1,13 +1,13 @@
import 'package:fires_flutter/models/yourLocation.dart';
import 'package:fires_flutter/models/fireNotification.dart';
import 'dart:async';
import 'package:connectivity/connectivity.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
abstract class AppActions {}
class FetchYourLocationsAction extends AppActions {
Completer<Null> refreshCallback;
Completer<Null>? refreshCallback;
FetchYourLocationsAction([this.refreshCallback]);
}
@ -52,13 +52,8 @@ class FetchFireNotificationsSucceededAction extends AppActions {
final List<FireNotification> fetchedFireNotifications;
final int unreadCount;
FetchFireNotificationsSucceededAction(this.fetchedFireNotifications, this.unreadCount);
}
class OnConnectivityChanged extends AppActions {
final ConnectivityResult connectivityResult;
OnConnectivityChanged(this.connectivityResult);
FetchFireNotificationsSucceededAction(
this.fetchedFireNotifications, this.unreadCount);
}
class FetchMonitoredAreasSucceededAction extends AppActions {
@ -66,3 +61,9 @@ class FetchMonitoredAreasSucceededAction extends AppActions {
FetchMonitoredAreasSucceededAction(this.monitoredAreas);
}
class OnConnectivityChanged extends AppActions {
final List<ConnectivityResult> connectionStatus;
OnConnectivityChanged(this.connectionStatus);
}

View file

@ -165,7 +165,7 @@ void fetchDataMiddleware(Store<AppState> store, action, NextDispatcher next) {
});
locSubs.subscribed = true;
});
store.dispatch(new FetchYourLocationsSucceededAction(localLocations));
persistYourLocations(localLocations);
@ -182,9 +182,9 @@ void fetchDataMiddleware(Store<AppState> store, action, NextDispatcher next) {
});
});
Completer<Null> completer = action.refreshCallback;
completer.complete(null);
});
final Completer<Null>? completer = action.refreshCallback;
completer?.complete(null);
});
}).catchError((onError) {
// If it fails, dispatch a failure action. The reducer will
// update the state with the error.

View file

@ -1,5 +1,6 @@
import 'package:redux/redux.dart';
import 'package:fires_flutter/models/yourLocation.dart';
import 'package:objectid/objectid.dart';
import '../models/fireMapState.dart';
import 'actions.dart';
@ -7,7 +8,7 @@ final fireMapReducer = combineReducers<FireMapState>([
new TypedReducer<FireMapState, ShowYourLocationMapAction>(
_showYourLocationMap),
new TypedReducer<FireMapState, ShowFireNotificationMapAction>(
_showFireNotificationMap),
_showFireNotificationMap),
new TypedReducer<FireMapState, UpdateFireMapStatsAction>(
_updateYourLocationMapStats),
new TypedReducer<FireMapState, SubscribeAction>(_subscribeYourLocationMap),
@ -20,8 +21,7 @@ final fireMapReducer = combineReducers<FireMapState>([
_editConfirmYourLocationMap),
new TypedReducer<FireMapState, EditCancelYourLocationAction>(
_editCancelYourLocationMap),
new TypedReducer<FireMapState, ToggleMapLayerAction>(
_toggleMapLayer),
new TypedReducer<FireMapState, ToggleMapLayerAction>(_toggleMapLayer),
new TypedReducer<FireMapState, UpdateYourLocationMapAction>(
_updateYourLocationMap)
]);
@ -46,15 +46,22 @@ FireMapState _showYourLocationMap(
status: action.loc.subscribed
? FireMapStatus.unsubscribe
: FireMapStatus.view,
yourLocation: action.loc, fireNotication: null);
yourLocation: action.loc,
fireNotication: null);
}
FireMapState _showFireNotificationMap(
FireMapState state, ShowFireNotificationMapAction action) {
// TODO: use here you real location?
YourLocation pseudoLoc = new YourLocation(lat: action.notif.lat, lon: action.notif.lon, description: action.notif.description);
YourLocation pseudoLoc = new YourLocation(
id: ObjectId(),
lat: action.notif.lat,
lon: action.notif.lon,
description: action.notif.description);
return state.copyWith(
status: FireMapStatus.viewFireNotification, yourLocation: pseudoLoc, fireNotication: action.notif);
status: FireMapStatus.viewFireNotification,
yourLocation: pseudoLoc,
fireNotication: action.notif);
}
FireMapState _subscribeYourLocationMap(
@ -92,12 +99,12 @@ FireMapState _editCancelYourLocationMap(
FireMapStatus restoreStatusAfterSave(loc) =>
loc.subscribed ? FireMapStatus.unsubscribe : FireMapStatus.view;
FireMapState _toggleMapLayer(
FireMapState state, ToggleMapLayerAction action) {
FireMapState _toggleMapLayer(FireMapState state, ToggleMapLayerAction action) {
FireMapLayer currentLayer = state.layer;
List<FireMapLayer> list = FireMapLayer.values;
int currentPos = list.indexOf(currentLayer);
currentPos++;
FireMapLayer next = list.elementAt(currentPos >= list.length ? 0: currentPos);
FireMapLayer next =
list.elementAt(currentPos >= list.length ? 0 : currentPos);
return state.copyWith(layer: next);
}
}

View file

@ -20,7 +20,7 @@ class Sandbox extends StatelessWidget {
decoration: new InputDecoration(),
onSubmitted: (todoText) {},
)),
body: new RaisedButton(
body: new ElevatedButton(
child: new Text('Press'),
onPressed: () {
showDialog(

View file

@ -10,7 +10,7 @@ class FireDistanceSlider extends StatefulWidget {
final int initialValue;
final SlideCallback onSlide;
FireDistanceSlider({this.initialValue, this.onSlide});
FireDistanceSlider({required this.initialValue, required this.onSlide});
@override
_FireDistanceSliderState createState() => new _FireDistanceSliderState(
@ -18,16 +18,16 @@ class FireDistanceSlider extends StatefulWidget {
}
class _FireDistanceSliderState extends State<FireDistanceSlider> {
int _sliderValue;
late int _sliderValue;
final SlideCallback onSlide;
_FireDistanceSliderState({int initialValue = 10, this.onSlide}) {
_FireDistanceSliderState({int initialValue = 10, required this.onSlide}) {
this._sliderValue = initialValue;
}
sizeText(sliderValue) =>
new Text(S.of(context).subscribeToValueAroundThisArea(sliderValue.toString()),
style: new TextStyle(color: Colors.black87));
sizeText(sliderValue) => new Text(
S.of(context).subscribeToValueAroundThisArea(sliderValue.toString()),
style: new TextStyle(color: Colors.black87));
warningText(sliderValue) => _sliderValue >= 50
? new Text(S.of(context).warningThisIsAVeryLargeArea,
@ -47,7 +47,7 @@ class _FireDistanceSliderState extends State<FireDistanceSlider> {
onChanged: (double value) {
_sliderValue = value.round();
onSlide(_sliderValue);
setState(() {});
setState(() {});
},
);
return new Column(

View file

@ -1,6 +1,6 @@
import 'package:comunes_flutter/comunes_flutter.dart';
import 'package:flutter/material.dart';
import 'package:launch_review/launch_review.dart';
import 'package:in_app_review/in_app_review.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:share_plus/share_plus.dart';
@ -20,7 +20,7 @@ class _SupportPageState extends State<SupportPage> {
Widget buildSupportButton() {
return new Align(
alignment: const Alignment(0.0, -0.2),
child: new OutlineButton.icon(
child: new OutlinedButton.icon(
icon: const Icon(Icons.favorite_border),
label: new Text(S.of(context).comunesSupportBtn),
onPressed: () {
@ -33,11 +33,12 @@ class _SupportPageState extends State<SupportPage> {
Widget buildShareButton() {
return new Align(
alignment: const Alignment(0.0, -0.2),
child: new OutlineButton.icon(
child: new OutlinedButton.icon(
icon: const Icon(Icons.share),
label: new Text(S.of(context).shareAppBtn),
onPressed: () {
Share.shareWithResult('https://play.google.com/store/apps/details?id=org.comunes.fires');
Share.shareWithResult(
'https://play.google.com/store/apps/details?id=org.comunes.fires');
},
),
);
@ -46,11 +47,11 @@ class _SupportPageState extends State<SupportPage> {
Widget buildStarButton() {
return new Align(
alignment: const Alignment(0.0, -0.2),
child: new OutlineButton.icon(
child: new OutlinedButton.icon(
icon: const Icon(Icons.star_border),
label: new Text(S.of(context).starAppBtn),
onPressed: () {
LaunchReview.launch();
InAppReview.instance.requestReview();
},
),
);
@ -59,11 +60,12 @@ class _SupportPageState extends State<SupportPage> {
Widget buildTranslateButton() {
return new Align(
alignment: const Alignment(0.0, -0.2),
child: new OutlineButton.icon(
child: new OutlinedButton.icon(
icon: const Icon(Icons.translate),
label: new Text(S.of(context).translateBtn),
onPressed: () {
launch("https://translate.comunes.org/projects/todos-contra-el-fuego/");
launch(
"https://translate.comunes.org/projects/todos-contra-el-fuego/");
},
),
);