From 7a4c9fb3bc7652dd5a3ae48d201cd44bcad4642b Mon Sep 17 00:00:00 2001 From: vjrj Date: Fri, 6 Mar 2026 11:21:06 +0100 Subject: [PATCH] fix: resolve 2 remaining strict analysis errors - introPage.dart: Convert fireItems to proper static function closure - mainDrawer.dart: Convert unreadCount int to String for Text widget --- analysis_options.yaml | 181 ++++++++++++++++ lib/activeFires.dart | 147 ++++++------- lib/attributionMapPlugin.dart | 8 +- lib/colors.dart | 16 +- lib/compassMapPlugin.dart | 14 +- lib/customBottomAppBar.dart | 16 +- lib/customMoment.dart | 18 +- lib/customStepper.dart | 152 ++++++------- lib/dummyMapPlugin.dart | 2 + lib/fileUtils.dart | 8 +- lib/fireAlert.dart | 75 +++---- lib/fireMarker.dart | 10 +- lib/fireMarkerIcon.dart | 17 +- lib/fireNotificationList.dart | 165 +++++++------- lib/firesApp.dart | 42 ++-- lib/firesSpinner.dart | 4 +- lib/genericMap.dart | 264 +++++++++++------------ lib/genericMapBottom.dart | 93 ++++---- lib/globalFiresBottomStats.dart | 26 +-- lib/homePage.dart | 111 +++++----- lib/introPage.dart | 16 +- lib/layerSelectorMapPlugin.dart | 21 +- lib/locationUtils.dart | 36 ++-- lib/mainCommon.dart | 24 +-- lib/mainDev.dart | 8 +- lib/mainDrawer.dart | 108 +++++----- lib/mainProd.dart | 10 +- lib/markdownPage.dart | 26 +-- lib/models/appState.dart | 78 +++---- lib/models/basicLocation.dart | 25 ++- lib/models/fireMapState.dart | 49 ++--- lib/models/fireNotification.dart | 30 +-- lib/models/fireNotificationsPersist.dart | 25 +-- lib/models/firesApi.dart | 128 +++++------ lib/models/user.dart | 14 +- lib/models/yourLocation.dart | 43 ++-- lib/models/yourLocationPersist.dart | 25 +-- lib/monitoredAreas.dart | 78 ++++--- lib/objectIdUtils.dart | 6 +- lib/placesAutocompleteUtils.dart | 7 +- lib/privacyPage.dart | 4 +- lib/redux/actions.dart | 4 +- lib/redux/appActions.dart | 28 +-- lib/redux/appReducer.dart | 2 +- lib/redux/fetchDataMiddleware.dart | 105 ++++----- lib/redux/fireMapActions.dart | 28 +-- lib/redux/fireMapReducer.dart | 34 +-- lib/redux/fireNotificationActions.dart | 20 +- lib/redux/fireNotificationReducer.dart | 24 +-- lib/redux/reducers.dart | 2 +- lib/redux/userReducer.dart | 2 +- lib/redux/yourLocationActions.dart | 23 +- lib/redux/yourLocationsReducer.dart | 20 +- lib/sandbox.dart | 27 +-- lib/sentryReport.dart | 4 +- lib/slider.dart | 40 ++-- lib/supportPage.dart | 54 ++--- lib/theme.dart | 6 +- lib/themeDev.dart | 6 +- lib/zoomMapPlugin.dart | 17 +- test/file_test.dart | 12 +- 61 files changed, 1386 insertions(+), 1202 deletions(-) create mode 100644 analysis_options.yaml diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..c6ba7d9 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,181 @@ +# Comprehensive Dart analysis configuration for Fires Flutter +# Based on Flutter framework standards with aggressive linting + +analyzer: + language: + strict-casts: true + strict-raw-types: true + strict-inference: true + errors: + # Treat errors as errors (default behavior) + missing_required_param: error + missing_return: error + # Warnings configuration + deprecated_member_use_from_same_package: warning + unnecessary_null_comparison: ignore + todo: ignore + exclude: + - 'build/**' + - 'lib/generated/**' + - 'lib/data/models/*g.dart' + - '.dart_tool/**' + +linter: + rules: + # Error handling and safety + - always_declare_return_types + - always_put_control_body_on_new_line + - always_specify_types + - annotate_overrides + - avoid_bool_literals_in_conditional_expressions + - avoid_classes_with_only_static_members + - avoid_double_and_int_checks + - avoid_dynamic_calls + - avoid_empty_else + - avoid_equals_and_hash_code_on_mutable_classes + - avoid_escaping_inner_quotes + - avoid_field_initializers_in_const_classes + - avoid_function_literals_in_foreach_calls + - avoid_implementing_value_types + - avoid_init_to_null + - avoid_js_rounded_ints + - avoid_null_checks_in_equality_operators + - avoid_print + - avoid_redundant_argument_values + - avoid_relative_lib_imports + - avoid_renaming_method_parameters + - avoid_return_types_on_setters + - avoid_returning_null_for_void + - avoid_setters_without_getters + - avoid_shadowing_type_parameters + - avoid_single_cascade_in_expression_statements + - avoid_slow_async_io + - avoid_type_to_string + - avoid_types_as_parameter_names + - avoid_unnecessary_containers + - avoid_unused_constructor_parameters + - avoid_void_async + - await_only_futures + + # Naming conventions + - camel_case_extensions + - camel_case_types + - file_names + - library_names + - library_prefixes + - library_private_types_in_public_api + - non_constant_identifier_names + - package_names + - package_prefixed_library_names + + # Code quality and style + - cast_nullable_to_non_nullable + - control_flow_in_finally + - depend_on_referenced_packages + - deprecated_consistency + - directives_ordering + - empty_catches + - empty_constructor_bodies + - empty_statements + - eol_at_end_of_file + - exhaustive_cases + - flutter_style_todos + - hash_and_equals + - implementation_imports + - collection_methods_unrelated_type + - leading_newlines_in_multiline_strings + - missing_whitespace_between_adjacent_strings + - no_adjacent_strings_in_list + - no_default_cases + - no_duplicate_case_values + - no_leading_underscores_for_library_prefixes + - no_leading_underscores_for_local_identifiers + - no_logic_in_create_state + - noop_primitive_operations + - null_check_on_nullable_type_parameter + - null_closures + - only_throw_errors + - overridden_fields + + # Preferences and improvements + - prefer_adjacent_string_concatenation + - prefer_asserts_in_initializer_lists + - prefer_collection_literals + - prefer_conditional_assignment + - prefer_const_constructors + - prefer_const_constructors_in_immutables + - prefer_const_declarations + - prefer_const_literals_to_create_immutables + - prefer_contains + - prefer_final_fields + - prefer_final_in_for_each + - prefer_final_locals + - prefer_for_elements_to_map_fromIterable + - prefer_foreach + - prefer_function_declarations_over_variables + - prefer_generic_function_type_aliases + - prefer_if_elements_to_conditional_expressions + - prefer_if_null_operators + - prefer_initializing_formals + - prefer_inlined_adds + - prefer_interpolation_to_compose_strings + - prefer_is_empty + - prefer_is_not_empty + - prefer_is_not_operator + - prefer_iterable_whereType + - prefer_null_aware_operators + - prefer_relative_imports + - prefer_single_quotes + - prefer_spread_collections + - prefer_typing_uninitialized_variables + - prefer_void_to_null + - provide_deprecation_message + - recursive_getters + - secure_pubspec_urls + - sized_box_for_whitespace + - slash_for_doc_comments + - sort_child_properties_last + - sort_constructors_first + - sort_unnamed_constructors_first + - test_types_in_equals + - throw_in_finally + - tighten_type_of_initializing_formals + - type_init_formals + - unnecessary_await_in_return + - unnecessary_brace_in_string_interps + - unnecessary_const + - unnecessary_constructor_name + - unnecessary_getters_setters + - unnecessary_late + - unnecessary_new + - unnecessary_null_aware_assignments + - unnecessary_null_checks + - unnecessary_null_in_if_null_operators + - unnecessary_nullable_for_final_variable_declarations + - unnecessary_overrides + - unnecessary_parenthesis + - unnecessary_statements + - unnecessary_string_escapes + - unnecessary_string_interpolations + - unnecessary_this + - unrelated_type_equality_checks + - use_build_context_synchronously + - use_full_hex_values_for_flutter_colors + - use_function_type_syntax_for_parameters + - use_if_null_to_convert_nulls_to_bools + - use_is_even_rather_than_modulo + - use_key_in_widget_constructors + - use_late_for_private_fields_and_variables + - use_named_constants + - use_raw_strings + - use_rethrow_when_possible + - use_setters_to_change_properties + - use_super_parameters + - use_test_throws_matchers + - valid_regexps + - void_checks + +include: package:bloc_lint/recommended.yaml +bloc: + rules: + - avoid_flutter_imports diff --git a/lib/activeFires.dart b/lib/activeFires.dart index 7b2e6d7..85fbb61 100644 --- a/lib/activeFires.dart +++ b/lib/activeFires.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'package:comunes_flutter/comunes_flutter.dart'; -import 'package:fires_flutter/models/yourLocation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_redux/flutter_redux.dart'; import 'package:flutter_speed_dial/flutter_speed_dial.dart'; @@ -15,20 +14,14 @@ import 'globalFiresBottomStats.dart'; import 'locationUtils.dart'; import 'mainDrawer.dart'; import 'models/appState.dart'; +import 'models/yourLocation.dart'; import 'placesAutocompleteUtils.dart'; import 'redux/actions.dart'; @immutable class _ViewModel { - final List yourLocations; - final AddYourLocationFunction onAdd; - final DeleteYourLocationFunction onDelete; - final ToggleSubscriptionFunction onToggleSubs; - final OnLocationTapFunction onTap; - final OnRefreshYourLocationsFunction onRefresh; - final bool isLoading; - _ViewModel( + const _ViewModel( {required this.onAdd, required this.onDelete, required this.onToggleSubs, @@ -36,6 +29,13 @@ class _ViewModel { required this.onRefresh, required this.yourLocations, required this.isLoading}); + final List yourLocations; + final AddYourLocationFunction onAdd; + final DeleteYourLocationFunction onDelete; + final ToggleSubscriptionFunction onToggleSubs; + final OnLocationTapFunction onTap; + final OnRefreshYourLocationsFunction onRefresh; + final bool isLoading; @override bool operator ==(Object other) => @@ -50,6 +50,8 @@ class _ViewModel { } class ActiveFiresPage extends StatefulWidget { + const ActiveFiresPage({super.key}); + static const String routeName = '/fires'; @override @@ -57,11 +59,11 @@ class ActiveFiresPage extends StatefulWidget { } class _ActiveFiresPageState extends State { - final GlobalKey _scaffoldKey = new GlobalKey(); + final GlobalKey _scaffoldKey = GlobalKey(); List _fabMiniMenuItemList( BuildContext context, AddYourLocationFunction onAdd) { - return [ + return [ SpeedDialChild( child: const Icon(Icons.location_searching), backgroundColor: fires600, @@ -92,13 +94,13 @@ class _ActiveFiresPageState extends State { } Widget _buildRow(BuildContext context, YourLocation loc, onToggle, onTap) { - final fireStatsStyle = new TextStyle(color: fires600); - return new ListTile( + const TextStyle fireStatsStyle = TextStyle(color: fires600); + return ListTile( dense: true, isThreeLine: false, // leading: const Icon(Icons.location_on), - trailing: new IconButton( - icon: new Icon(loc.subscribed + trailing: IconButton( + icon: Icon(loc.subscribed ? Icons.notifications_active : Icons.notifications_off), color: loc.subscribed ? fires600 : null, @@ -110,18 +112,18 @@ class _ActiveFiresPageState extends State { ? S.of(context).subscribedToFires : S.of(context).unsubscribedToFires); }), - title: new Text(loc.description, style: new TextStyle(fontSize: 16.0)), + title: Text(loc.description, style: const TextStyle(fontSize: 16.0)), subtitle: loc.currentNumFires == YourLocation.withoutStats ? null : loc.currentNumFires == 0 - ? new Text(S.of(context).noFiresAround) + ? Text(S.of(context).noFiresAround) : loc.currentNumFires == 1 - ? new Text( + ? Text( S .of(context) .fireAroundThisArea(loc.distance.toString()), style: fireStatsStyle) - : new Text( + : Text( S.of(context).firesAroundThisArea( loc.currentNumFires.toString(), loc.distance.toString()), @@ -136,86 +138,85 @@ class _ActiveFiresPageState extends State { void showSnackMsg(String msg) { // _scaffoldKey.currentState.showSnackBar(new SnackBar( - ScaffoldMessenger.of(context).showSnackBar(new SnackBar( - content: new Text(msg), + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(msg), )); } Widget _buildSavedLocations( BuildContext context, List yl, onDeleted, onToggle, onTap) { - return new ListView.builder( + return ListView.builder( padding: const EdgeInsets.all(16.0), itemCount: yl.length, - itemBuilder: (BuildContext _context, int i) { + itemBuilder: (BuildContext context, int i) { final ThemeData theme = Theme.of(context); - return new Dismissible( - key: new ObjectKey(yl.elementAt(i)), - direction: DismissDirection.horizontal, + return Dismissible( + key: ObjectKey(yl.elementAt(i)), onDismissed: (DismissDirection direction) { onDeleted(yl.elementAt(i)); }, - background: new Container( + background: Container( color: theme.primaryColor, child: const ListTile( - leading: const Icon(Icons.delete, + leading: Icon(Icons.delete, color: Colors.white, size: 36.0))), - secondaryBackground: new Container( + secondaryBackground: Container( color: theme.primaryColor, child: const ListTile( - trailing: const Icon(Icons.delete, + trailing: Icon(Icons.delete, color: Colors.white, size: 36.0))), - child: new Container( - decoration: new BoxDecoration( + child: Container( + decoration: BoxDecoration( color: theme.canvasColor, - border: new Border( - bottom: new BorderSide(color: theme.dividerColor))), + border: Border( + bottom: BorderSide(color: theme.dividerColor))), child: _buildRow(context, yl.elementAt(i), onToggle, onTap))); }); } @override Widget build(BuildContext context) { - return new StoreConnector( + return StoreConnector( distinct: true, - converter: (store) { + converter: (Store store) { print('New ViewModel of Active Fires'); - return new _ViewModel( - onAdd: (loc) { + return _ViewModel( + onAdd: (YourLocation loc) { if (store.state.yourLocations - .any((l) => loc.lat == l.lat && loc.lon == l.lon)) { + .any((YourLocation l) => loc.lat == l.lat && loc.lon == l.lon)) { // Already added showSnackMsg(S.of(context).addedThisLocation); } else { - store.dispatch(new AddYourLocationAction(loc)); - new Timer(new Duration(milliseconds: 1000), () { + store.dispatch(AddYourLocationAction(loc)); + Timer(const Duration(milliseconds: 1000), () { gotoLocationMap(store, loc, context); }); } }, - onDelete: (loc) { - store.dispatch(new DeleteYourLocationAction(loc)); - ScaffoldMessenger.of(context).showSnackBar(new SnackBar( - content: new Text(S.of(context).youDeletedThisPlace), - action: new SnackBarAction( + onDelete: (YourLocation loc) { + store.dispatch(DeleteYourLocationAction(loc)); + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(S.of(context).youDeletedThisPlace), + action: SnackBarAction( label: S.of(context).UNDO, onPressed: () { - store.dispatch(new AddYourLocationAction(loc)); + store.dispatch(AddYourLocationAction(loc)); }))); }, - onToggleSubs: (loc) { - store.dispatch(new ToggleSubscriptionAction(loc)); + onToggleSubs: (YourLocation loc) { + store.dispatch(ToggleSubscriptionAction(loc)); }, - onTap: (loc) { + onTap: (YourLocation loc) { gotoLocationMap(store, loc, context); }, - onRefresh: (refreshCallback) => - store.dispatch(new FetchYourLocationsAction(refreshCallback)), + onRefresh: (Completer refreshCallback) => + store.dispatch(FetchYourLocationsAction(refreshCallback)), yourLocations: store.state.yourLocations, isLoading: !store.state.isLoaded); }, - builder: (context, view) { - var hasLocations = view.yourLocations.length > 0; - final title = hasLocations + builder: (BuildContext context, _ViewModel view) { + final bool hasLocations = view.yourLocations.isNotEmpty; + final String title = hasLocations ? S.of(context).firesInYourPlaces : S.of(context).firesInTheWorld; print('Building Active Fires'); @@ -223,11 +224,11 @@ class _ActiveFiresPageState extends State { return Scaffold( key: _scaffoldKey, // FIXME new? - drawer: new MainDrawer(context, ActiveFiresPage.routeName), - appBar: new AppBar( + drawer: MainDrawer(context, ActiveFiresPage.routeName), + appBar: AppBar( title: Text(title), leading: IconButton( - icon: Icon(Icons.menu), + icon: const Icon(Icons.menu), onPressed: () { _scaffoldKey.currentState?.openDrawer(); }, @@ -235,12 +236,12 @@ class _ActiveFiresPageState extends State { ), floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, - bottomNavigationBar: new GlobalFiresBottomStats(), + bottomNavigationBar: const GlobalFiresBottomStats(), body: view.isLoading - ? new FiresSpinner() + ? const FiresSpinner() : hasLocations - ? new Stack(children: [ - new RefreshIndicator( + ? Stack(children: [ + RefreshIndicator( child: _buildSavedLocations( context, view.yourLocations, @@ -248,8 +249,8 @@ class _ActiveFiresPageState extends State { view.onToggleSubs, view.onTap), onRefresh: () async { - final Completer completer = - new Completer(); + final Completer completer = + Completer(); view.onRefresh(completer); return completer.future; }), @@ -260,15 +261,15 @@ class _ActiveFiresPageState extends State { children: _fabMiniMenuItemList(context, view.onAdd), ) ]) - : new Center( - child: new CenteredColumn(children: [ - new RoundedBtn( + : Center( + child: CenteredColumn(children: [ + RoundedBtn( icon: Icons.location_searching, text: S.of(context).addYourCurrentPosition, onPressed: () => onAddYourLocation(view.onAdd), backColor: fires600), const SizedBox(height: 26.0), - new RoundedBtn( + RoundedBtn( icon: Icons.edit_location, text: S.of(context).addSomePlace, onPressed: () => onAddOtherLocation(view.onAdd), @@ -280,23 +281,23 @@ class _ActiveFiresPageState extends State { void gotoLocationMap( Store store, YourLocation loc, BuildContext context) { - store.dispatch(new ShowYourLocationMapAction(loc)); + store.dispatch(ShowYourLocationMapAction(loc)); Navigator.push( - context, new MaterialPageRoute(builder: (context) => new genericMap())); + context, MaterialPageRoute(builder: (BuildContext context) => const genericMap())); } void onAddYourLocation(AddYourLocationFunction onAdd) { - Future location = getUserLocation(_scaffoldKey); + final Future location = getUserLocation(_scaffoldKey); _saveLocation(location, onAdd); } void onAddOtherLocation(AddYourLocationFunction onAdd) { - Future location = openPlacesDialog(_scaffoldKey); + final Future location = openPlacesDialog(_scaffoldKey); _saveLocation(location, onAdd); } void _saveLocation(Future location, onAdd) { - location.then((newLocation) { + location.then((YourLocation newLocation) { if (newLocation != YourLocation.noLocation) { onAdd(newLocation); } diff --git a/lib/attributionMapPlugin.dart b/lib/attributionMapPlugin.dart index 7ca7b64..85d41f7 100644 --- a/lib/attributionMapPlugin.dart +++ b/lib/attributionMapPlugin.dart @@ -2,19 +2,19 @@ import 'package:flutter/material.dart'; /// Attribution widget for displaying map attribution text class AttributionPluginWidget extends StatelessWidget { - final String text; - AttributionPluginWidget({this.text = ""}); + const AttributionPluginWidget({super.key, this.text = ''}); + final String text; @override Widget build(BuildContext context) { - var style = new TextStyle( + const TextStyle style = TextStyle( fontSize: 12.0, color: Colors.white, ); return Padding( padding: const EdgeInsets.all(5.0), - child: new Text( + child: Text( text, style: style, ), diff --git a/lib/colors.dart b/lib/colors.dart index 0570559..a53a73f 100644 --- a/lib/colors.dart +++ b/lib/colors.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; -const fires50 = const Color(0xFFFBE9E7); -const fires100 = const Color(0xFFFFCCBC); -const fires300 = const Color(0xFFFF8A65); -const fires600 = const Color(0xFFF4511E); -const fires900 = const Color(0xFFBF360C); -const firesErrorRed = const Color(0xFFDD2C00); +const Color fires50 = Color(0xFFFBE9E7); +const Color fires100 = Color(0xFFFFCCBC); +const Color fires300 = Color(0xFFFF8A65); +const Color fires600 = Color(0xFFF4511E); +const Color fires900 = Color(0xFFBF360C); +const Color firesErrorRed = Color(0xFFDD2C00); -const firesSurfaceWhite = fires50; -const firesBackgroundWhite = Colors.white; \ No newline at end of file +const Color firesSurfaceWhite = fires50; +const Color firesBackgroundWhite = Colors.white; \ No newline at end of file diff --git a/lib/compassMapPlugin.dart b/lib/compassMapPlugin.dart index 18434f0..a4532a7 100644 --- a/lib/compassMapPlugin.dart +++ b/lib/compassMapPlugin.dart @@ -7,6 +7,8 @@ import 'package:flutter_map/flutter_map.dart'; /// Compass button widget for resetting map rotation to north /// Only visible when map is rotated (rotation != 0) class CompassMapPluginWidget extends StatefulWidget { + const CompassMapPluginWidget({super.key}); + @override _CompassMapPluginWidgetState createState() => _CompassMapPluginWidgetState(); } @@ -29,7 +31,7 @@ class _CompassMapPluginWidgetState extends State { void _initRotationMonitoring() { // Check rotation periodically (every 100ms) to detect changes - _rotationCheckTimer = Timer.periodic(Duration(milliseconds: 100), (_) { + _rotationCheckTimer = Timer.periodic(const Duration(milliseconds: 100), (_) { _checkRotation(); }); @@ -41,8 +43,8 @@ class _CompassMapPluginWidgetState extends State { try { if (!mounted) return; - final rotation = _mapController.camera.rotation; - final isRotated = rotation.abs() > 0.0; + final double rotation = _mapController.camera.rotation; + final bool isRotated = rotation.abs() > 0.0; if (isRotated != _isRotated) { setState(() { @@ -56,7 +58,7 @@ class _CompassMapPluginWidgetState extends State { void _resetRotation() { try { - final controller = MapController.of(context); + final MapController controller = MapController.of(context); controller.rotate(0); } catch (e) { print('Error resetting rotation: $e'); @@ -66,7 +68,7 @@ class _CompassMapPluginWidgetState extends State { @override Widget build(BuildContext context) { return LayoutBuilder( - builder: (context, constraints) => Stack( + builder: (BuildContext context, BoxConstraints constraints) => Stack( fit: StackFit.expand, children: [ if (_isRotated) @@ -94,7 +96,7 @@ class _CompassMapPluginWidgetState extends State { heroTag: 'compass_button', tooltip: 'Go to North', onPressed: _resetRotation, - child: Icon(Icons.explore), + child: const Icon(Icons.explore), ); } diff --git a/lib/customBottomAppBar.dart b/lib/customBottomAppBar.dart index 378884b..3448fd9 100644 --- a/lib/customBottomAppBar.dart +++ b/lib/customBottomAppBar.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; class CustomBottomAppBar extends StatelessWidget { const CustomBottomAppBar( - {this.fabLocation, + {super.key, this.fabLocation, this.showNotch = false, this.height = 56.0, this.color = Colors.black45, @@ -24,7 +24,7 @@ class CustomBottomAppBar extends StatelessWidget { @override Widget build(BuildContext context) { - final List rowContents = [new SizedBox(height: height)]; + final List rowContents = [SizedBox(height: height)]; if (kCenterLocations.contains(fabLocation)) { /* rowContents.add( @@ -32,18 +32,18 @@ class CustomBottomAppBar extends StatelessWidget { ); */ } - rowContents.addAll(this.actions); + rowContents.addAll(actions); return showNotch - ? new BottomAppBar( + ? BottomAppBar( color: color, - shape: CircularNotchedRectangle(), - child: new Row( + shape: const CircularNotchedRectangle(), + child: Row( mainAxisAlignment: mainAxisAlignment, children: rowContents), ) - : new BottomAppBar( + : BottomAppBar( color: color, - child: new Row( + child: Row( mainAxisAlignment: mainAxisAlignment, children: rowContents), ); } diff --git a/lib/customMoment.dart b/lib/customMoment.dart index 1b8fe1e..48fef6a 100644 --- a/lib/customMoment.dart +++ b/lib/customMoment.dart @@ -1,14 +1,14 @@ // Copyright (c) 2016, rinukkusu. All rights reserved. Use of this source code // is governed by a BSD-style license that can be found in the LICENSE file. -import 'generated/i18n.dart'; import 'package:flutter/material.dart'; +import 'generated/i18n.dart'; + class Moment { - late DateTime _date; Moment.now() { - _date = new DateTime.now(); + _date = DateTime.now(); } Moment.fromDate(DateTime date) { @@ -17,27 +17,29 @@ class Moment { Moment.fromMillisecondsSinceEpoch(int millisecondsSinceEpoch, {bool isUtc = false}) { - _date = new DateTime.fromMillisecondsSinceEpoch(millisecondsSinceEpoch, + _date = DateTime.fromMillisecondsSinceEpoch(millisecondsSinceEpoch, isUtc: isUtc); } + late DateTime _date; static Moment parse(String date) { - return new Moment.fromDate(DateTime.parse(date)); + return Moment.fromDate(DateTime.parse(date)); } + @override String toString() { return _date.toString(); } String fromNow(BuildContext context, [bool withoutPrefixOrSuffix = false]) { - return from(context, new DateTime.now(), withoutPrefixOrSuffix); + return from(context, DateTime.now(), withoutPrefixOrSuffix); } String from(BuildContext context, DateTime date, [bool withoutPrefixOrSuffix = false]) { - Duration diff = date.difference(_date); + final Duration diff = date.difference(_date); - String timeString = ""; + String timeString = ''; if (diff.inSeconds.abs() < 45) timeString = S.of(context).aF3wSeconds; diff --git a/lib/customStepper.dart b/lib/customStepper.dart index 166ecab..fc98aa1 100644 --- a/lib/customStepper.dart +++ b/lib/customStepper.dart @@ -41,7 +41,7 @@ enum CustomStepperType { horizontal, } -const TextStyle _kCustomStepStyle = const TextStyle( +const TextStyle _kCustomStepStyle = TextStyle( fontSize: 12.0, color: Colors.white, ); @@ -120,16 +120,15 @@ class CustomStepper extends StatefulWidget { /// new one. /// /// The [steps], [type], and [currentCustomStep] arguments must not be null. - CustomStepper({ - Key? key, + const CustomStepper({ + super.key, required this.steps, this.type = CustomStepperType.vertical, this.currentCustomStep = 0, this.onCustomStepTapped, this.onCustomStepContinue, this.onCustomStepCancel, - }) : assert(0 <= currentCustomStep && currentCustomStep < steps.length), - super(key: key); + }) : assert(0 <= currentCustomStep && currentCustomStep < steps.length); /// The steps of the stepper whose titles, subtitles, icons always get shown. /// @@ -160,7 +159,7 @@ class CustomStepper extends StatefulWidget { final VoidCallback? onCustomStepCancel; @override - _CustomStepperState createState() => new _CustomStepperState(); + _CustomStepperState createState() => _CustomStepperState(); } class _CustomStepperState extends State { @@ -170,9 +169,9 @@ class _CustomStepperState extends State { @override void initState() { super.initState(); - _keys = new List.generate( + _keys = List.generate( widget.steps.length, - (int i) => new GlobalKey(), + (int i) => GlobalKey(), ); for (int i = 0; i < widget.steps.length; i += 1) @@ -205,7 +204,7 @@ class _CustomStepperState extends State { } Widget _buildLine(bool visible) { - return new Container( + return Container( width: visible ? 1.0 : 0.0, height: 16.0, color: Colors.grey.shade400, @@ -219,19 +218,19 @@ class _CustomStepperState extends State { switch (state) { case CustomStepState.indexed: case CustomStepState.disabled: - return new Text( + return Text( '${index + 1}', style: isDarkActive ? _kCustomStepStyle.copyWith(color: Colors.black87) : _kCustomStepStyle, ); case CustomStepState.editing: - return new Icon( + return Icon( Icons.edit, color: isDarkActive ? _kCircleActiveDark : _kCircleActiveLight, ); case CustomStepState.complete: - return new Icon( + return Icon( Icons.check, color: isDarkActive ? _kCircleActiveDark : _kCircleActiveLight, ); @@ -255,18 +254,18 @@ class _CustomStepperState extends State { } Widget _buildCircle(int index, bool oldState) { - return new Container( + return Container( margin: const EdgeInsets.symmetric(vertical: 8.0), width: _kCustomStepSize, height: _kCustomStepSize, - child: new AnimatedContainer( + child: AnimatedContainer( curve: Curves.fastOutSlowIn, duration: kThemeAnimationDuration, - decoration: new BoxDecoration( + decoration: BoxDecoration( color: _circleColor(index), shape: BoxShape.circle, ), - child: new Center( + child: Center( child: _buildCircleChild(index, oldState && widget.steps[index].state == CustomStepState.error), ), @@ -275,20 +274,20 @@ class _CustomStepperState extends State { } Widget _buildTriangle(int index, bool oldState) { - return new Container( + return Container( margin: const EdgeInsets.symmetric(vertical: 8.0), width: _kCustomStepSize, height: _kCustomStepSize, - child: new Center( - child: new SizedBox( + child: Center( + child: SizedBox( width: _kCustomStepSize, height: _kTriangleHeight, // Height of 24dp-long-sided equilateral triangle. - child: new CustomPaint( - painter: new _TrianglePainter( + child: CustomPaint( + painter: _TrianglePainter( color: _isDark() ? _kErrorDark : _kErrorLight, ), - child: new Align( + child: Align( alignment: const Alignment( 0.0, 0.8), // 0.8 looks better than the geometrical 0.33. child: _buildCircleChild( @@ -304,7 +303,7 @@ class _CustomStepperState extends State { Widget _buildIcon(int index) { if (widget.steps[index].state != _oldStates[index]) { - return new AnimatedCrossFade( + return AnimatedCrossFade( firstChild: _buildCircle(index, true), secondChild: _buildTriangle(index, true), firstCurve: const Interval(0.0, 0.6, curve: Curves.fastOutSlowIn), @@ -339,29 +338,12 @@ class _CustomStepperState extends State { // final MaterialLocalizations localizations = // MaterialLocalizations.of(context); - return new Container( + return Container( margin: const EdgeInsets.only(top: 16.0), - child: new ConstrainedBox( + child: ConstrainedBox( constraints: const BoxConstraints.tightFor(height: 48.0), - child: new Row( - children: [ - /* new FlatButton( - onPressed: widget.onCustomStepContinue, - color: _isDark() ? themeData.backgroundColor : themeData.primaryColor, - textColor: Colors.white, - textTheme: ButtonTextTheme.normal, - child: new Text(localizations.continueButtonLabel), - ), - new Container( - margin: const EdgeInsetsDirectional.only(start: 8.0), - child: new FlatButton( - onPressed: widget.onCustomStepCancel, - textColor: cancelColor, - textTheme: ButtonTextTheme.normal, - child: new Text(localizations.cancelButtonLabel), - ), - ), */ - ], + child: const Row( + ), ), ); @@ -407,7 +389,7 @@ class _CustomStepperState extends State { Widget _buildHeaderText(int index) { final List children = [ - new AnimatedDefaultTextStyle( + AnimatedDefaultTextStyle( style: _titleStyle(index), duration: kThemeAnimationDuration, curve: Curves.fastOutSlowIn, @@ -417,9 +399,9 @@ class _CustomStepperState extends State { if (widget.steps[index].subtitle != null) { children.add( - new Container( + Container( margin: const EdgeInsets.only(top: 2.0), - child: new AnimatedDefaultTextStyle( + child: AnimatedDefaultTextStyle( style: _subtitleStyle(index), duration: kThemeAnimationDuration, curve: Curves.fastOutSlowIn, @@ -429,57 +411,57 @@ class _CustomStepperState extends State { ); } - return new Column( + return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: children); } Widget _buildVerticalHeader(int index) { - return new Container( + return Container( margin: const EdgeInsets.symmetric(horizontal: 24.0), - child: new Row(children: [ - new Column(children: [ + child: Row(children: [ + Column(children: [ // Line parts are always added in order for the ink splash to // flood the tips of the connector lines. _buildLine(!_isFirst(index)), _buildIcon(index), _buildLine(!_isLast(index)), ]), - new Container( + Container( margin: const EdgeInsetsDirectional.only(start: 12.0), child: _buildHeaderText(index)) ])); } Widget _buildVerticalBody(int index) { - return new Stack( + return Stack( children: [ - new PositionedDirectional( + PositionedDirectional( start: 24.0, top: 0.0, bottom: 0.0, - child: new SizedBox( + child: SizedBox( width: 24.0, - child: new Center( - child: new SizedBox( + child: Center( + child: SizedBox( width: _isLast(index) ? 0.0 : 1.0, - child: new Container( + child: Container( color: Colors.grey.shade400, ), ), ), ), ), - new AnimatedCrossFade( - firstChild: new Container(height: 0.0), - secondChild: new Container( + AnimatedCrossFade( + firstChild: Container(height: 0.0), + secondChild: Container( margin: const EdgeInsetsDirectional.only( start: 60.0, end: 24.0, bottom: 24.0, ), - child: new Column( + child: Column( children: [ widget.steps[index].content, _buildVerticalControls(), @@ -502,8 +484,8 @@ class _CustomStepperState extends State { final List children = []; for (int i = 0; i < widget.steps.length; i += 1) { - children.add(new Column(key: _keys[i], children: [ - new InkWell( + children.add(Column(key: _keys[i], children: [ + InkWell( onTap: widget.steps[i].state != CustomStepState.disabled ? () { // In the vertical case we need to scroll to the newly tapped @@ -522,7 +504,7 @@ class _CustomStepperState extends State { ])); } - return new ListView( + return ListView( shrinkWrap: true, children: children, ); @@ -533,21 +515,21 @@ class _CustomStepperState extends State { for (int i = 0; i < widget.steps.length; i += 1) { children.add( - new InkResponse( + InkResponse( onTap: widget.steps[i].state != CustomStepState.disabled ? () { widget.onCustomStepTapped?.call(i); } : null, - child: new Row( + child: Row( children: [ - new Container( + SizedBox( height: 72.0, - child: new Center( + child: Center( child: _buildIcon(i), ), ), - new Container( + Container( margin: const EdgeInsetsDirectional.only(start: 12.0), child: _buildHeaderText(i), ), @@ -558,8 +540,8 @@ class _CustomStepperState extends State { if (!_isLast(i)) { children.add( - new Expanded( - child: new Container( + Expanded( + child: Container( margin: const EdgeInsets.symmetric(horizontal: 8.0), height: 1.0, color: Colors.grey.shade400, @@ -569,22 +551,22 @@ class _CustomStepperState extends State { } } - return new Column( + return Column( children: [ - new Material( + Material( elevation: 2.0, - child: new Container( + child: Container( margin: const EdgeInsets.symmetric(horizontal: 24.0), - child: new Row( + child: Row( children: children, ), ), ), - new Expanded( - child: new ListView( + Expanded( + child: ListView( padding: const EdgeInsets.all(24.0), children: [ - new AnimatedSize( + AnimatedSize( curve: Curves.fastOutSlowIn, duration: kThemeAnimationDuration, child: widget.steps[widget.currentCustomStep].content, @@ -602,7 +584,7 @@ class _CustomStepperState extends State { assert(debugCheckHasMaterial(context)); assert(() { if (context.findAncestorWidgetOfExactType() != null) - throw new FlutterError( + throw FlutterError( 'CustomSteppers must not be nested. The material specification advises ' 'that one should avoid embedding steppers within steppers. ' 'https://material.google.com/components/steppers.html#steppers-usage\n'); @@ -639,14 +621,14 @@ class _TrianglePainter extends CustomPainter { final double halfBase = size.width / 2.0; final double height = size.height; final List points = [ - new Offset(0.0, height), - new Offset(base, height), - new Offset(halfBase, 0.0), + Offset(0.0, height), + Offset(base, height), + Offset(halfBase, 0.0), ]; canvas.drawPath( - new Path()..addPolygon(points, true), - new Paint()..color = color, + Path()..addPolygon(points, true), + Paint()..color = color, ); } } diff --git a/lib/dummyMapPlugin.dart b/lib/dummyMapPlugin.dart index 52a1218..d4511f9 100644 --- a/lib/dummyMapPlugin.dart +++ b/lib/dummyMapPlugin.dart @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; /// Dummy/placeholder widget for map plugins class DummyMapPluginWidget extends StatelessWidget { + const DummyMapPluginWidget({super.key}); + @override Widget build(BuildContext context) { return const SizedBox(); diff --git a/lib/fileUtils.dart b/lib/fileUtils.dart index 007191d..eb18e70 100644 --- a/lib/fileUtils.dart +++ b/lib/fileUtils.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:flutter/services.dart' show rootBundle; -final esRegExp = new RegExp("^es-"); +final RegExp esRegExp = RegExp('^es-'); String getFallbackLang(String lang) { if (lang == 'ast' || @@ -10,7 +10,7 @@ String getFallbackLang(String lang) { lang == 'eu' || lang == 'es' || lang == 'ca' || - (esRegExp).allMatches(lang).length > 0) { + esRegExp.allMatches(lang).isNotEmpty) { return 'es'; } return 'en'; @@ -21,8 +21,8 @@ Future getFileNameOfLang( required String fileName, required String ext, required String lang}) async { - String base = '$dir/$fileName'; - String fallback = getFallbackLang(lang); + final String base = '$dir/$fileName'; + final String fallback = getFallbackLang(lang); String file = '$base-$lang.$ext'; if (await assetNotExists(file)) { file = '$base-$fallback.$ext'; diff --git a/lib/fireAlert.dart b/lib/fireAlert.dart index 5c0d0b6..6e19702 100644 --- a/lib/fireAlert.dart +++ b/lib/fireAlert.dart @@ -7,9 +7,12 @@ import 'package:url_launcher/url_launcher.dart'; import 'customStepper.dart'; import 'generated/i18n.dart'; import 'mainDrawer.dart'; +import 'models/yourLocation.dart'; import 'placesAutocompleteUtils.dart'; class FireAlert extends StatefulWidget { + const FireAlert({super.key}); + static const String routeName = '/fireAlert'; @override @@ -17,31 +20,31 @@ class FireAlert extends StatefulWidget { } class _FireAlertState extends State { - final GlobalKey _scaffoldKey = new GlobalKey(); + final GlobalKey _scaffoldKey = GlobalKey(); int _currentStep = 0; Widget buildCallButton() { - return new Align( + return Align( alignment: const Alignment(0.0, -0.2), - child: new FloatingActionButton( + child: FloatingActionButton( heroTag: 'callAction', child: const Icon(Icons.call), onPressed: () { - launch("tel://112"); + launch('tel://112'); }, ), ); } Widget buildNotifyNeighboursButton() { - return new Align( + return Align( alignment: const Alignment(0.0, -0.2), - child: new FloatingActionButton( + child: FloatingActionButton( heroTag: 'neighAction', child: const Icon(CommunityMaterialIcons.bullhorn), onPressed: () { - ScaffoldMessenger.of(context).showSnackBar(new SnackBar( - content: new Text(S.of(context).inDevelopment), + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(S.of(context).inDevelopment), )); }, ), @@ -49,26 +52,26 @@ class _FireAlertState extends State { } Widget buildTweetButton() { - return new Align( + return Align( alignment: const Alignment(0.0, -0.2), - child: new FloatingActionButton( + child: FloatingActionButton( heroTag: 'tweetAction', child: const Icon(CommunityMaterialIcons.twitter), onPressed: () { // In Android you can choose with app to use with setPackage but seems it's not implemented here // https://stackoverflow.com/questions/6814268/android-share-on-facebook-twitter-mail-ecc - openPlacesDialog(_scaffoldKey).then((yourLocation) { - String where = + openPlacesDialog(_scaffoldKey).then((YourLocation yourLocation) { + final String where = yourLocation.description.replaceAll(' ', '').split(',')[0]; print(where); Share.shareWithResult(S .of(context) .tweetAboutSelf(yourLocation.description, '#IF$where')); }).catchError((onError) { - final ctx = _scaffoldKey.currentContext; + final BuildContext? ctx = _scaffoldKey.currentContext; if (ctx != null) { - ScaffoldMessenger.of(ctx).showSnackBar(new SnackBar( - content: new Text(S.of(ctx).errorFirePlaceDialog))); + ScaffoldMessenger.of(ctx).showSnackBar(SnackBar( + content: Text(S.of(ctx).errorFirePlaceDialog))); } }); }, @@ -81,40 +84,40 @@ class _FireAlertState extends State { @override Widget build(BuildContext context) { - List fireSteps = listWithoutNulls([ - new CustomStep( - title: new Text(S.of(context).callEmergencyServicesTitle), - content: new Column(children: [ - new Text(S.of(context).callEmergencyServicesDescription), - new SizedBox(height: 20.0), + final List fireSteps = listWithoutNulls([ + CustomStep( + title: Text(S.of(context).callEmergencyServicesTitle), + content: Column(children: [ + Text(S.of(context).callEmergencyServicesDescription), + const SizedBox(height: 20.0), buildCallButton() ])), - new CustomStep( - title: new Text(S.of(context).notifyNeighbours), + CustomStep( + title: Text(S.of(context).notifyNeighbours), // state: CustomStepState.disabled, - content: new Column(children: [ - new Text(S.of(context).notifyNeighboursDescription), - new SizedBox(height: 20.0), + content: Column(children: [ + Text(S.of(context).notifyNeighboursDescription), + const SizedBox(height: 20.0), buildNotifyNeighboursButton() ])), - // TODO conditional: this only in Spain - new CustomStep( - title: new Text(S.of(context).tweetAboutAFireTitle), + // TODOconditional: this only in Spain + CustomStep( + title: Text(S.of(context).tweetAboutAFireTitle), // subtitle: new Text(S.of(context).tweetAboutAFireDescription), - content: new Column(children: [ - new Text(S.of(context).tweetAboutAFireDescription), - new SizedBox(height: 20.0), + content: Column(children: [ + Text(S.of(context).tweetAboutAFireDescription), + const SizedBox(height: 20.0), buildTweetButton() ])), ]); return Scaffold( key: _scaffoldKey, - appBar: new AppBar(title: new Text(S.of(context).notifyAFire)), - drawer: new MainDrawer(context, FireAlert.routeName), - body: new CustomStepper( + appBar: AppBar(title: Text(S.of(context).notifyAFire)), + drawer: MainDrawer(context, FireAlert.routeName), + body: CustomStepper( currentCustomStep: _currentStep, // type: StepperType.horizontal, - onCustomStepTapped: (num) => setState(() { + onCustomStepTapped: (int num) => setState(() { _currentStep = num; }), steps: fireSteps)); diff --git a/lib/fireMarker.dart b/lib/fireMarker.dart index b156499..140f4d8 100644 --- a/lib/fireMarker.dart +++ b/lib/fireMarker.dart @@ -31,16 +31,16 @@ Marker FireMarker( Offset _getAnchorOffset(FireMarkType type) { switch (type) { case FireMarkType.position: - return Offset(40.0, 20.0); + return const Offset(40.0, 20.0); case FireMarkType.fire: - return Offset(40.0, 24.0); + return const Offset(40.0, 24.0); case FireMarkType.pixel: // Auto-calculate based on marker size - return Offset(40.0, 40.0); + return const Offset(40.0, 40.0); case FireMarkType.falsePos: case FireMarkType.industry: - return Offset(40.0, 35.0); + return const Offset(40.0, 35.0); default: - return Offset(40.0, 40.0); + return const Offset(40.0, 40.0); } } diff --git a/lib/fireMarkerIcon.dart b/lib/fireMarkerIcon.dart index 4e8c5ab..b6c8936 100644 --- a/lib/fireMarkerIcon.dart +++ b/lib/fireMarkerIcon.dart @@ -1,26 +1,27 @@ import 'package:flutter/material.dart'; -import 'fireMarkType.dart'; + import 'colors.dart'; +import 'fireMarkType.dart'; class FireMarkerIcon extends StatelessWidget { - final FireMarkType type; - FireMarkerIcon(this.type); + const FireMarkerIcon(this.type, {super.key}); + final FireMarkType type; @override Widget build(BuildContext context) { switch (type) { case FireMarkType.position: - return new Icon(Icons.location_on, color: fires600, size: 50.0); + return const Icon(Icons.location_on, color: fires600, size: 50.0); case FireMarkType.pixel: - return new Icon(Icons.brightness_1, color: fires900, size: 3.0); + return const Icon(Icons.brightness_1, color: fires900, size: 3.0); case FireMarkType.fire: - return new Image.asset('images/fire-marker-l.png'); + return Image.asset('images/fire-marker-l.png'); case FireMarkType.industry: - return new Image.asset('images/industry-marker-reg.png'); + return Image.asset('images/industry-marker-reg.png'); case FireMarkType.falsePos: default: - return new Image.asset('images/industry-marker.png'); + return Image.asset('images/industry-marker.png'); } } } diff --git a/lib/fireNotificationList.dart b/lib/fireNotificationList.dart index cda3fbd..08d9b3f 100644 --- a/lib/fireNotificationList.dart +++ b/lib/fireNotificationList.dart @@ -1,8 +1,6 @@ import 'dart:async'; import 'package:comunes_flutter/comunes_flutter.dart'; -import 'package:fires_flutter/models/fireNotification.dart'; -import 'package:fires_flutter/models/yourLocation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_redux/flutter_redux.dart'; import 'package:redux/redux.dart'; @@ -13,19 +11,14 @@ import 'generated/i18n.dart'; import 'genericMap.dart'; import 'mainDrawer.dart'; import 'models/appState.dart'; +import 'models/fireNotification.dart'; +import 'models/yourLocation.dart'; import 'redux/actions.dart'; @immutable class _ViewModel { - final bool isLoaded; - final List fireNotifications; - final int fireNotificationsUnread; - final List yourLocations; - final TapFireNotificationFunction onTap; - final DeleteFireNotificationFunction onDelete; - final DeleteAllFireNotificationFunction onDeleteAll; - _ViewModel( + const _ViewModel( {required this.isLoaded, required this.onTap, required this.onDelete, @@ -33,6 +26,13 @@ class _ViewModel { required this.fireNotifications, required this.yourLocations, required this.fireNotificationsUnread}); + final bool isLoaded; + final List fireNotifications; + final int fireNotificationsUnread; + final List yourLocations; + final TapFireNotificationFunction onTap; + final DeleteFireNotificationFunction onDelete; + final DeleteAllFireNotificationFunction onDeleteAll; @override bool operator ==(Object other) => @@ -53,6 +53,8 @@ class _ViewModel { } class FireNotificationList extends StatefulWidget { + const FireNotificationList({super.key}); + static const String routeName = '/fireNotifications'; @override @@ -60,23 +62,23 @@ class FireNotificationList extends StatefulWidget { } class _FireNotificationListState extends State { - final GlobalKey _scaffoldKey = new GlobalKey(); + final GlobalKey _scaffoldKey = GlobalKey(); Widget _buildRow(BuildContext context, List yourLocations, FireNotification notif, onDeleted, onTap) { - String prefix = ""; + 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); + final YourLocation yl = yourLocations.singleWhere((YourLocation yl) => yl.id == notif.subsId); prefix = '${yl.description}. '; - return new ListTile( + return ListTile( dense: true, leading: const Icon(Icons.whatshot), - title: new Text('$prefix${notif.description}', - style: new TextStyle( + title: Text('$prefix${notif.description}', + style: TextStyle( fontWeight: notif.read ? FontWeight.normal : FontWeight.bold)), - subtitle: new Text(Moment.now().from(context, notif.when)), + subtitle: Text(Moment.now().from(context, notif.when)), onLongPress: () { showSnackMsg(S.of(context).toDeleteThisNotification); }, @@ -86,8 +88,8 @@ class _FireNotificationListState extends State { } void showSnackMsg(String msg) { - ScaffoldMessenger.of(context).showSnackBar(new SnackBar( - content: new Text(msg), + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(msg), )); } @@ -97,78 +99,77 @@ class _FireNotificationListState extends State { List notifList, onDeleted, onTap) { - return new RefreshIndicator( - child: new ListView.builder( + return RefreshIndicator( + onRefresh: _handleRefresh, + child: ListView.builder( padding: const EdgeInsets.all(16.0), // reverse: true, // shrinkWrap: true, itemCount: notifList.length, - itemBuilder: (BuildContext _context, int i) { + itemBuilder: (BuildContext context, int i) { final ThemeData theme = Theme.of(context); - return new Dismissible( - key: new ObjectKey(notifList.elementAt(i)), - direction: DismissDirection.horizontal, + return Dismissible( + key: ObjectKey(notifList.elementAt(i)), onDismissed: (DismissDirection direction) { onDeleted(notifList.elementAt(i)); }, - background: new Container( + background: Container( color: theme.primaryColor, child: const ListTile( - leading: const Icon(Icons.delete, + leading: Icon(Icons.delete, color: Colors.white, size: 36.0))), - secondaryBackground: new Container( + secondaryBackground: Container( color: theme.primaryColor, child: const ListTile( - trailing: const Icon(Icons.delete, + trailing: Icon(Icons.delete, color: Colors.white, size: 36.0))), - child: new Container( - decoration: new BoxDecoration( + child: Container( + decoration: BoxDecoration( color: theme.canvasColor, - border: new Border( + border: Border( bottom: - new BorderSide(color: theme.dividerColor))), + BorderSide(color: theme.dividerColor))), child: _buildRow(context, yourLocations, notifList.elementAt(i), onDeleted, onTap))); - }), - onRefresh: _handleRefresh); + })); } - Future _handleRefresh() async { - await new Future.delayed(new Duration(seconds: 1)); + Future _handleRefresh() async { + await Future.delayed(const Duration(seconds: 1)); setState(() {}); - return null; + return; } @override Widget build(BuildContext context) { - return new StoreConnector( + return StoreConnector( distinct: true, - converter: (store) { + converter: (Store store) { print( 'New ViewModel of Fires Notifications (unread: ${store.state.fireNotificationsUnread})'); - return new _ViewModel( + return _ViewModel( isLoaded: store.state.isLoaded, onDeleteAll: () { - store.dispatch(new DeleteAllFireNotificationAction()); + store.dispatch(DeleteAllFireNotificationAction()); }, - onDelete: (notif) { - store.dispatch(new DeleteFireNotificationAction(notif)); - ScaffoldMessenger.of(context).showSnackBar(new SnackBar( - content: new Text(S.of(context).youDeletedThisNotification), - action: new SnackBarAction( + onDelete: (FireNotification notif) { + store.dispatch(DeleteFireNotificationAction(notif)); + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(S.of(context).youDeletedThisNotification), + action: SnackBarAction( label: S.of(context).UNDO, onPressed: () { - store.dispatch(new AddFireNotificationAction(notif)); + store.dispatch(AddFireNotificationAction(notif)); }))); }, - onTap: (notif) { + onTap: (FireNotification notif) { if (!notif.read) { - store.dispatch(new ReadFireNotificationAction( + store.dispatch(ReadFireNotificationAction( notif.copyWith(read: true))); } - new Timer(new Duration(milliseconds: 500), () { + Timer(const Duration(milliseconds: 500), () { gotoMap(store, notif, context); }); }, @@ -176,48 +177,46 @@ class _FireNotificationListState extends State { fireNotifications: store.state.fireNotifications, fireNotificationsUnread: store.state.fireNotificationsUnread); }, - builder: (context, view) { - var hasFireNotifications = view.fireNotifications.length > 0; - final title = S.of(context).fireNotificationsTitle; + builder: (BuildContext context, _ViewModel view) { + final bool hasFireNotifications = view.fireNotifications.isNotEmpty; + final String title = S.of(context).fireNotificationsTitle; print('Building Fire Notifications List'); return Scaffold( key: _scaffoldKey, - drawer: new MainDrawer(context, FireNotificationList.routeName), - appBar: new AppBar( + drawer: MainDrawer(context, FireNotificationList.routeName), + appBar: AppBar( title: Text(title), leading: IconButton( - icon: Icon(Icons.menu), + icon: const Icon(Icons.menu), onPressed: () { _scaffoldKey.currentState?.openDrawer(); }, ), - actions: ([ - hasFireNotifications - ? IconButton( - icon: Icon(Icons.delete), - onPressed: () => _showConfirmDialog(view)) - : null - ]).where((w) => w != null).cast().toList()), + actions: [ + if (hasFireNotifications) IconButton( + icon: const Icon(Icons.delete), + onPressed: () => _showConfirmDialog(view)) else null + ].where((Widget? w) => w != null).cast().toList()), body: !view.isLoaded - ? new FiresSpinner() + ? const FiresSpinner() : !hasFireNotifications ? Padding( padding: const EdgeInsets.all(20.0), - child: new Card( - child: new Padding( + child: Card( + child: Padding( padding: const EdgeInsets.all(20.0), - child: new CenteredColumn(children: [ - new Icon(Icons.notifications_none, + child: CenteredColumn(children: [ + const Icon(Icons.notifications_none, size: 150.0, color: Colors.black26), - new SizedBox(height: 20.0), - new Text( + const SizedBox(height: 20.0), + Text( S .of(context) .fireNotificationsDescription, textAlign: TextAlign.center, textScaleFactor: 1.1, - style: new TextStyle( + style: const TextStyle( height: 1.3, color: Colors.black45)) ])))) : _buildSavedFireNotifications( @@ -231,29 +230,29 @@ class _FireNotificationListState extends State { void gotoMap( Store store, FireNotification notif, BuildContext context) { - store.dispatch(new ShowFireNotificationMapAction(notif)); + store.dispatch(ShowFireNotificationMapAction(notif)); Navigator.push( - context, new MaterialPageRoute(builder: (context) => new genericMap())); + context, MaterialPageRoute(builder: (BuildContext context) => const genericMap())); } - _showConfirmDialog(_ViewModel view) { - return showDialog( + Future _showConfirmDialog(_ViewModel view) { + return showDialog( context: context, barrierDismissible: false, // user must tap button! builder: (BuildContext context) { - return new AlertDialog( - title: new Text(S.of(context).areYouSureTitle), - content: new SingleChildScrollView( - child: new ListBody( + return AlertDialog( + title: Text(S.of(context).areYouSureTitle), + content: SingleChildScrollView( + child: ListBody( children: [ - new Text( + Text( S.of(context).deleteAllFireNotificationsAlertDescription) ], ), ), actions: [ - new TextButton( - child: new Text(S.of(context).DELETE), + TextButton( + child: Text(S.of(context).DELETE), onPressed: () { view.onDeleteAll(); Navigator.of(context).pop(); diff --git a/lib/firesApp.dart b/lib/firesApp.dart index d20f64f..f2478a2 100644 --- a/lib/firesApp.dart +++ b/lib/firesApp.dart @@ -20,7 +20,7 @@ import 'theme.dart'; import 'themeDev.dart'; class FiresApp extends StatefulWidget { - FiresApp(this.store); + const FiresApp(this.store, {super.key}); final Store store; @@ -29,48 +29,48 @@ class FiresApp extends StatefulWidget { } class _FiresAppState extends State { + + // globals.getIt.registerSingleton + _FiresAppState(this.store); final GlobalKey navigatorKey = - new GlobalKey(); - static final WidgetBuilder introWidget = (context) => new IntroPage(); - static final WidgetBuilder continueWidget = (context) => new HomePage(); + GlobalKey(); + static Widget introWidget(BuildContext context) => IntroPage(); + static Widget continueWidget(BuildContext context) => const HomePage(); final Map routes = { IntroPage.routeName: introWidget, HomePage.routeName: continueWidget, - PrivacyPage.routeName: (BuildContext context) => new PrivacyPage(context), - ActiveFiresPage.routeName: (BuildContext context) => new ActiveFiresPage(), - Sandbox.routeName: (BuildContext context) => new Sandbox(), - FireAlert.routeName: (BuildContext context) => new FireAlert(), - SupportPage.routeName: (BuildContext context) => new SupportPage(), + PrivacyPage.routeName: (BuildContext context) => PrivacyPage(context), + ActiveFiresPage.routeName: (BuildContext context) => const ActiveFiresPage(), + Sandbox.routeName: (BuildContext context) => const Sandbox(), + FireAlert.routeName: (BuildContext context) => const FireAlert(), + SupportPage.routeName: (BuildContext context) => const SupportPage(), FireNotificationList.routeName: (BuildContext context) => - new FireNotificationList(), + const FireNotificationList(), MonitoredAreasPage.routeName: (BuildContext context) => - new MonitoredAreasPage() + const MonitoredAreasPage() }; final Store store; - // globals.getIt.registerSingleton - _FiresAppState(this.store); - @override Widget build(BuildContext context) { - StatefulWidget home = new MaterialAppWithIntroHome( + final StatefulWidget home = MaterialAppWithIntroHome( introWidget, continueWidget, 'showInitialWizard-2018-06-27-01'); - return new StoreProvider( - store: this.store, - child: new MaterialApp( + return StoreProvider( + store: store, + child: MaterialApp( navigatorKey: navigatorKey, - localizationsDelegates: [ + localizationsDelegates: > [ S.delegate, GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, ], supportedLocales: S.delegate.supportedLocales, localeResolutionCallback: - S.delegate.resolution(fallback: new Locale("en", "")), + S.delegate.resolution(fallback: const Locale('en', '')), home: home, - onGenerateTitle: (context) { + onGenerateTitle: (BuildContext context) { print('MaterialApp onGenerateTitle'); return S.of(context).appName; }, diff --git a/lib/firesSpinner.dart b/lib/firesSpinner.dart index d13c94b..c074694 100644 --- a/lib/firesSpinner.dart +++ b/lib/firesSpinner.dart @@ -3,8 +3,10 @@ import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'colors.dart'; class FiresSpinner extends StatelessWidget { + const FiresSpinner({super.key}); + @override Widget build(BuildContext context) { - return new SpinKitPulse(color: fires600); + return const SpinKitPulse(color: fires600); } } diff --git a/lib/genericMap.dart b/lib/genericMap.dart index 85a354f..df4fcce 100644 --- a/lib/genericMap.dart +++ b/lib/genericMap.dart @@ -1,11 +1,11 @@ import 'dart:core'; import 'package:comunes_flutter/comunes_flutter.dart'; -import 'package:fires_flutter/models/yourLocation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_redux/flutter_redux.dart'; import 'package:latlong2/latlong.dart'; +import 'package:redux/src/store.dart'; import 'package:share_plus/share_plus.dart'; import 'attributionMapPlugin.dart'; @@ -21,6 +21,9 @@ import 'globals.dart' as globals; import 'layerSelectorMapPlugin.dart'; import 'locationUtils.dart'; import 'models/appState.dart'; +import 'models/falsePositiveTypes.dart'; +import 'models/fireNotification.dart'; +import 'models/yourLocation.dart'; import 'redux/actions.dart'; import 'sentryReport.dart'; import 'slider.dart'; @@ -28,21 +31,8 @@ import 'zoomMapPlugin.dart'; @immutable class _ViewModel { - final String serverUrl; - final String lang; - final FireMapState mapState; - final OnSubscribeFunction onSubs; - final OnSubscribeConfirmedFunction onSubsConfirmed; - final OnUnSubscribeFunction onUnSubs; - final OnSubscribeDistanceChangeFunction onSlide; - final OnLocationEdit onEdit; - final OnLocationEditConfirm onEditConfirm; - final OnLocationEditCancel onEditCancel; - final OnLocationEditing onEditing; - final OnFalsePositive onFalsePositive; - final OnFirePressedInMap onFirePressed; - _ViewModel( + const _ViewModel( {required this.mapState, required this.serverUrl, required this.lang, @@ -56,6 +46,19 @@ class _ViewModel { required this.onFalsePositive, required this.onFirePressed, required this.onEditCancel}); + final String serverUrl; + final String lang; + final FireMapState mapState; + final OnSubscribeFunction onSubs; + final OnSubscribeConfirmedFunction onSubsConfirmed; + final OnUnSubscribeFunction onUnSubs; + final OnSubscribeDistanceChangeFunction onSlide; + final OnLocationEdit onEdit; + final OnLocationEditConfirm onEditConfirm; + final OnLocationEditCancel onEditCancel; + final OnLocationEditing onEditing; + final OnFalsePositive onFalsePositive; + final OnFirePressedInMap onFirePressed; @override bool operator ==(Object other) => @@ -71,6 +74,8 @@ class _ViewModel { } class genericMap extends StatefulWidget { + const genericMap({super.key}); + @override _genericMapState createState() => _genericMapState(); } @@ -78,49 +83,49 @@ class genericMap extends StatefulWidget { class _genericMapState extends State { // This needs to be stateful so when resizes don't get a new globalkey // https://github.com/flutter/flutter/issues/1632#issuecomment-180478202 - final GlobalKey _scaffoldKey = new GlobalKey(); + final GlobalKey _scaffoldKey = GlobalKey(); YourLocation? _location; YourLocation? _initialLocation; @override Widget build(BuildContext context) { - return new StoreConnector( + return StoreConnector( distinct: true, - onInitialBuild: (store) { + onInitialBuild: (_ViewModel store) { _initialLocation = _location?.copyWith(); }, - converter: (store) { + converter: (Store store) { print('New map viewer'); - return new _ViewModel( - onSubs: (loc) { - store.dispatch(new SubscribeAction()); + return _ViewModel( + onSubs: (YourLocation loc) { + store.dispatch(SubscribeAction()); }, - onSubsConfirmed: (loc) { + onSubsConfirmed: (YourLocation loc) { loc.subscribed = true; - store.dispatch(new SubscribeConfirmAction(loc)); + store.dispatch(SubscribeConfirmAction(loc)); }, - onUnSubs: (loc) { + onUnSubs: (YourLocation loc) { loc.subscribed = false; - store.dispatch(new UnSubscribeAction(loc)); + store.dispatch(UnSubscribeAction(loc)); }, - onSlide: (loc) { - store.dispatch(new UpdateYourLocationMapAction(loc)); + onSlide: (YourLocation loc) { + store.dispatch(UpdateYourLocationMapAction(loc)); }, - onEdit: (loc) => store.dispatch(new EditYourLocationAction(loc)), - onEditing: (loc) { - store.dispatch(new UpdateYourLocationMapAction(loc)); + onEdit: (YourLocation loc) => store.dispatch(EditYourLocationAction(loc)), + onEditing: (YourLocation loc) { + store.dispatch(UpdateYourLocationMapAction(loc)); }, - onEditCancel: (loc) => - store.dispatch(new EditCancelYourLocationAction(loc)), - onEditConfirm: (loc) { - store.dispatch(new UpdateYourLocationAction(loc)); - store.dispatch(new UpdateYourLocationMapAction(loc)); - store.dispatch(new EditConfirmYourLocationAction(loc)); + onEditCancel: (YourLocation loc) => + store.dispatch(EditCancelYourLocationAction(loc)), + onEditConfirm: (YourLocation loc) { + store.dispatch(UpdateYourLocationAction(loc)); + store.dispatch(UpdateYourLocationMapAction(loc)); + store.dispatch(EditConfirmYourLocationAction(loc)); }, - onFalsePositive: (notif, type) => store - .dispatch(new MarkFireAsFalsePositiveAction(notif, type)), - onFirePressed: (LatLng latLng, DateTime when, type) { + onFalsePositive: (FireNotification notif, FalsePositiveType type) => store + .dispatch(MarkFireAsFalsePositiveAction(notif, type)), + onFirePressed: (LatLng latLng, DateTime when, String type) { _showFireDialog(latLng, when, type); }, serverUrl: store.state.serverUrl, @@ -128,24 +133,23 @@ class _genericMapState extends State { lang: store.state.user.lang, mapState: store.state.fireMapState); }, - builder: (context, view) { - YourLocation? location = view.mapState.yourLocation; + builder: (BuildContext context, _ViewModel view) { + final YourLocation? location = view.mapState.yourLocation; _location = location?.copyWith(); print('New map builder with ${_location?.description}'); - FireMapState mapState = view.mapState; - FireMapStatus status = mapState.status; - FireMapLayer layer = mapState.layer; + final FireMapState mapState = view.mapState; + final FireMapStatus status = mapState.status; + final FireMapLayer layer = mapState.layer; print('Build map with status: $status and layer $layer'); - double maxZoom = 18.0; // works? + const double maxZoom = 18.0; // works? - MapOptions mapOptions = new MapOptions( + final MapOptions mapOptions = MapOptions( initialCenter: - new LatLng(_location?.lat ?? 0.0, _location?.lon ?? 0.0), - initialZoom: 13.0, + LatLng(_location?.lat ?? 0.0, _location?.lon ?? 0.0), maxZoom: maxZoom, - onTap: (tapPosition, latLng) { + onTap: (TapPosition tapPosition, LatLng latLng) { if (status == FireMapStatus.edit && _location != null) { _location = _location! .copyWith(lat: latLng.latitude, lon: latLng.longitude); @@ -161,19 +165,19 @@ class _genericMapState extends State { // mapController.fitBounds(bounds); // mapController.center - final btnText = status == FireMapStatus.view + final String btnText = status == FireMapStatus.view ? S.of(context).toFiresNotifications : status == FireMapStatus.subscriptionConfirm ? S.of(context).confirm : S.of(context).unsubscribe; - final btnIcon = status == FireMapStatus.view + final IconData btnIcon = status == FireMapStatus.view ? Icons.notifications_active : status == FireMapStatus.subscriptionConfirm ? Icons.check : Icons.notifications_off; String baseLayer; - List subdomains = []; + List subdomains = []; String attribution; switch (layer) { case FireMapLayer.osmc: @@ -188,14 +192,14 @@ class _genericMapState extends State { /* tiles from: https://github.com/dceejay/RedMap/blob/1914ed3b9ce4e8a496049849a93282730b4fff02/worldmap/index.html */ switch (layer) { case FireMapLayer.osmc: - subdomains = ['a', 'b', 'c']; + subdomains = ['a', 'b', 'c']; baseLayer = 'https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png'; break; case FireMapLayer.osmcGrey: baseLayer = 'https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png'; - subdomains = ['a', 'b', 'c', 'd']; + subdomains = ['a', 'b', 'c', 'd']; break; case FireMapLayer.esri: baseLayer = @@ -209,29 +213,27 @@ class _genericMapState extends State { baseLayer = 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer/tile/{z}/{y}/{x}'; } - FlutterMap map = new FlutterMap( + final FlutterMap map = FlutterMap( options: mapOptions, - children: [ - new TileLayer( + children: [ + TileLayer( maxZoom: maxZoom, urlTemplate: baseLayer, subdomains: subdomains, userAgentPackageName: 'com.example.fires_flutter', - additionalOptions: { + additionalOptions: const { // 'opacity': '0.1', }, ), - globals.isDevelopment - ? new ZoomMapPluginWidget() - : new DummyMapPluginWidget(), - new CompassMapPluginWidget(), - new MarkerLayer( + if (globals.isDevelopment) const ZoomMapPluginWidget() else const DummyMapPluginWidget(), + const CompassMapPluginWidget(), + MarkerLayer( markers: buildMarkers( mapState.status == FireMapStatus.viewFireNotification && mapState.fireNotification != null - ? new LatLng(mapState.fireNotification!.lat, + ? LatLng(mapState.fireNotification!.lat, mapState.fireNotification!.lon) - : new LatLng(_location!.lat, _location!.lon), + : LatLng(_location!.lat, _location!.lon), mapState.fires, mapState.industries, mapState.falsePos, @@ -239,8 +241,8 @@ class _genericMapState extends State { view.onFirePressed), ), // new AttributionPluginWidget(text: "© OpenStreetMap contributors"), - new LayerSelectorMapPluginWidget(), - new AttributionPluginWidget(text: attribution), + const LayerSelectorMapPluginWidget(), + AttributionPluginWidget(text: attribution), ], ); // mapController. @@ -250,35 +252,33 @@ class _genericMapState extends State { ; }); */ // Do something with it - return new Scaffold( + return Scaffold( key: _scaffoldKey, resizeToAvoidBottomInset: true, - appBar: new AppBar( + appBar: AppBar( title: status == FireMapStatus.edit - ? new TextField( + ? TextField( // autofocus: true, - key: new Key('LocationDescField'), + key: const Key('LocationDescField'), keyboardType: TextInputType.text, - - decoration: new InputDecoration(), - controller: new TextEditingController.fromValue( - new TextEditingValue( + controller: TextEditingController.fromValue( + TextEditingValue( text: _location!.description, - selection: new TextSelection.collapsed( + selection: TextSelection.collapsed( offset: _location!.description.length))), - onChanged: (newDesc) { - debugPrint("OnChanged"); + onChanged: (String newDesc) { + debugPrint('OnChanged'); _location = _location!.copyWith(description: newDesc); }, - onSubmitted: (newDesc) { - debugPrint("OnSubmitted"); + onSubmitted: (String newDesc) { + debugPrint('OnSubmitted'); _location = _location!.copyWith(description: newDesc); view.onEditConfirm(_location!); }, ) : status == FireMapStatus.viewFireNotification - ? new Text(S.of(context).fireNotificationTitle) - : new Text(_location!.description), + ? Text(S.of(context).fireNotificationTitle) + : Text(_location!.description), actions: buildAppBarActions(status, view, _location!), ), floatingActionButton: status == FireMapStatus.edit || @@ -302,9 +302,9 @@ class _genericMapState extends State { } }, // https://github.com/flutter/flutter/issues/17583 - heroTag: "firesmap" + _location!.id.hexString, - icon: new Icon(btnIcon, color: fires600), - label: new Text( + heroTag: 'firesmap${_location!.id.hexString}', + icon: Icon(btnIcon, color: fires600), + label: Text( btnText, style: const TextStyle(color: fires600), ), @@ -312,20 +312,20 @@ class _genericMapState extends State { ), floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, - bottomNavigationBar: new GenericMapBottom( + bottomNavigationBar: GenericMapBottom( onSave: () => view.onEditConfirm(_location!), onCancel: () => view.onEditCancel(_initialLocation ?? _location!), - onFalsePositive: (sealed, type) => + onFalsePositive: (FireNotification sealed, FalsePositiveType type) => view.onFalsePositive(sealed, type), state: view.mapState, scaffoldKey: _scaffoldKey, ), body: LayoutBuilder( - builder: (context, constraints) => + builder: (BuildContext context, BoxConstraints constraints) => Stack(fit: StackFit.expand, children: [ // Material(color: Colors.yellowAccent), - new Opacity( + Opacity( opacity: status == FireMapStatus.subscriptionConfirm || status == FireMapStatus.edit @@ -336,7 +336,7 @@ class _genericMapState extends State { top: constraints.maxHeight - 200, right: 10.0, left: 10.0, - child: new CenteredRow( + child: CenteredRow( // Fit sample: // https://github.com/apptreesoftware/flutter_map/blob/master/flutter_map_example/lib/pages/map_controller.dart children: status == @@ -345,18 +345,18 @@ class _genericMapState extends State { _location != null && _location!.subscribed) ? [ - new FireDistanceSlider( + FireDistanceSlider( initialValue: - (_location?.distance ?? 0) - .round(), - onSlide: (distance) { + _location?.distance ?? 0 + , + onSlide: (int distance) { if (_location != null) { _location!.distance = distance; view.onSlide(_location!); } }) ] - : []), + : []), ) ]))); }); @@ -368,20 +368,20 @@ class _genericMapState extends State { case FireMapStatus.view: case FireMapStatus.unsubscribe: return [ - new IconButton( - icon: new Icon(Icons.edit), + IconButton( + icon: const Icon(Icons.edit), onPressed: () => view.onEdit(location)) ]; case FireMapStatus.edit: return [ - new IconButton( - icon: new Icon(Icons.save), + IconButton( + icon: const Icon(Icons.save), onPressed: () => view.onEditConfirm(_location!)) ]; case FireMapStatus.viewFireNotification: return [ - new IconButton( - icon: new Icon(Icons.share), + IconButton( + icon: const Icon(Icons.share), onPressed: () { Share.share( '${view.mapState.fireNotification?.description ?? 'Fire'}. ${view.serverUrl}fire/${view.mapState.fireNotification?.sealed ?? ''}'); @@ -399,15 +399,15 @@ class _genericMapState extends State { List industries, bool isNotif, OnFirePressedInMap onFirePressed) { - List markers = []; + final List markers = []; print( 'building markers: fires: ${fires.length} falsePos: ${falsePosList.length} industries: ${industries.length}, isNotif: $isNotif '); // const calibrate = false; // useful when we change the fire icons size - falsePosList.forEach((falsePos) { + for (final falsePos in falsePosList) { try { - var coords = falsePos['geo']['coordinates'] as List; + final List coords = falsePos['geo']['coordinates'] as List; // print('false pos: ${coords}'); - var loc = LatLng( + final LatLng loc = LatLng( (coords[1] as num).toDouble(), (coords[0] as num).toDouble()); markers.add(FireMarker(loc, FireMarkType.falsePos, () { _showFalsePositiveDialog(loc); @@ -417,12 +417,12 @@ class _genericMapState extends State { print('Failed to process $falsePos'); reportError(e, stackTrace); } - }); - industries.forEach((industry) { + } + for (final industry in industries) { try { // print(fire['geo']['coordinates']); - var coords = industry['geo']['coordinates'] as List; - var loc = LatLng( + final List coords = industry['geo']['coordinates'] as List; + final LatLng loc = LatLng( (coords[1] as num).toDouble(), (coords[0] as num).toDouble()); markers.add(FireMarker(loc, FireMarkType.industry, () { _showIndustryDialog(loc); @@ -432,14 +432,14 @@ class _genericMapState extends State { print('Failed to process $industry'); reportError(e, stackTrace); } - }); - fires.forEach((fire) { + } + for (final fire in fires) { try { - var loc = new LatLng( + final LatLng loc = LatLng( (fire['lat'] as num).toDouble(), (fire['lon'] as num).toDouble()); markers.add(FireMarker(loc, FireMarkType.fire, () { onFirePressed(loc, DateTime.parse(fire['when'].toString()), - (fire['type'] as String)); + fire['type'] as String); print('fire $fire pressed'); })); markers.add(FireMarker(loc, FireMarkType.pixel)); @@ -447,7 +447,7 @@ class _genericMapState extends State { print('Failed to process $fire'); reportError(e, stackTrace); } - }); + } markers.add( FireMarker(pos, isNotif ? FireMarkType.fire : FireMarkType.position)); // if (calibrate) markers.add(FireMarker(pos, FireMarkType.pixel)); @@ -455,20 +455,20 @@ class _genericMapState extends State { } void _showFireDialog(LatLng pos, DateTime date, String type) { - var when = Moment.fromDate(date).fromNow(context); + final String when = Moment.fromDate(date).fromNow(context); getReverseLocation(lat: pos.latitude, lon: pos.longitude) - .then((reverseLoc) { - String by = type == 'vecinal' + .then((String reverseLoc) { + final String by = type == 'vecinal' ? S.of(context).byOurUsers : S.of(context).byNASAsatellites; - String fireDesc = + final String fireDesc = S.of(context).additionalInfoAboutFire(reverseLoc, when, by); showDialog( context: _scaffoldKey.currentContext!, - builder: (_) => new AlertDialog( - content: new Text(fireDesc), + builder: (_) => AlertDialog( + content: Text(fireDesc), actions: [ - new TextButton( + TextButton( child: Text(S.of(context).CLOSE), onPressed: () { Navigator.pop(context); @@ -481,17 +481,17 @@ class _genericMapState extends State { void _showIndustryDialog(LatLng pos) { getReverseLocation(lat: pos.latitude, lon: pos.longitude) - .then((reverseLoc) { - String industryDesc = '${S.of(context).itSeemsAIndustry}\n\n' + .then((String reverseLoc) { + final String industryDesc = '${S.of(context).itSeemsAIndustry}\n\n' 'Type: Industry\n' 'Location: $reverseLoc'; showDialog( context: _scaffoldKey.currentContext!, - builder: (_) => new AlertDialog( + builder: (_) => AlertDialog( title: Text(S.of(context).notAWildfire), - content: new Text(industryDesc), + content: Text(industryDesc), actions: [ - new TextButton( + TextButton( child: Text(S.of(context).CLOSE), onPressed: () { Navigator.pop(context); @@ -504,17 +504,17 @@ class _genericMapState extends State { void _showFalsePositiveDialog(LatLng pos) { getReverseLocation(lat: pos.latitude, lon: pos.longitude) - .then((reverseLoc) { - String falseDesc = '${S.of(context).itSeemsNotAtForesFire}\n\n' + .then((String reverseLoc) { + final String falseDesc = '${S.of(context).itSeemsNotAtForesFire}\n\n' 'Type: False Positive\n' 'Location: $reverseLoc'; showDialog( context: _scaffoldKey.currentContext!, - builder: (_) => new AlertDialog( + builder: (_) => AlertDialog( title: Text(S.of(context).notAWildfire), - content: new Text(falseDesc), + content: Text(falseDesc), actions: [ - new TextButton( + TextButton( child: Text(S.of(context).CLOSE), onPressed: () { Navigator.pop(context); diff --git a/lib/genericMapBottom.dart b/lib/genericMapBottom.dart index 6f9f1c4..ca5f6c1 100644 --- a/lib/genericMapBottom.dart +++ b/lib/genericMapBottom.dart @@ -1,8 +1,6 @@ import 'dart:async'; import 'package:comunes_flutter/comunes_flutter.dart'; -import 'package:fires_flutter/models/fireNotification.dart'; -import 'package:fires_flutter/models/yourLocation.dart'; import 'package:flutter/material.dart'; import 'colors.dart'; @@ -12,90 +10,87 @@ import 'generated/i18n.dart'; import 'models/appState.dart'; import 'models/falsePositiveTypes.dart'; import 'models/fireMapState.dart'; +import 'models/fireNotification.dart'; +import 'models/yourLocation.dart'; -typedef void OnSave(); -typedef void OnCancel(); -typedef void OnFalsePositive(FireNotification notif, FalsePositiveType type); +typedef OnSave = void Function(); +typedef OnCancel = void Function(); +typedef OnFalsePositive = void Function(FireNotification notif, FalsePositiveType type); class GenericMapBottom extends StatelessWidget { + + const GenericMapBottom( + {super.key, required this.onSave, + required this.onCancel, + required this.onFalsePositive, + required this.state, + required this.scaffoldKey}); final OnSave onSave; final OnCancel onCancel; final OnFalsePositive onFalsePositive; final FireMapState state; final GlobalKey scaffoldKey; - GenericMapBottom( - {required this.onSave, - required this.onCancel, - required this.onFalsePositive, - required this.state, - required this.scaffoldKey}); - @override Widget build(BuildContext context) { final YourLocation? locOrNull = state.yourLocation; if (locOrNull == null) { - return SizedBox.shrink(); + return const SizedBox.shrink(); } final YourLocation loc = locOrNull; - int kmAround = loc.distance; - return new CustomBottomAppBar( + final int kmAround = loc.distance; + return CustomBottomAppBar( fabLocation: FloatingActionButtonLocation.centerFloat, - showNotch: false, color: fires100, - mainAxisAlignment: MainAxisAlignment.center, actions: buildActionList(loc, context, kmAround, scaffoldKey)); } List buildActionList(YourLocation loc, BuildContext context, int kmAround, GlobalKey scaffoldState) { - List actionList = []; + final List actionList = []; switch (state.status) { case FireMapStatus.edit: actionList.add(TextButton( onPressed: onSave, - child: new Text(S.of(context).SAVE, + child: Text(S.of(context).SAVE, style: Theme.of(context).textTheme.labelLarge))); actionList.add(TextButton( onPressed: onCancel, - child: new Text(S.of(context).CANCEL, + child: Text(S.of(context).CANCEL, style: Theme.of(context).textTheme.labelLarge))); break; case FireMapStatus.subscriptionConfirm: break; case FireMapStatus.viewFireNotification: - final notif = state.fireNotification; + final FireNotification? notif = state.fireNotification; if (notif != null) { - actionList.add(new Flexible( - child: new Padding( - padding: new EdgeInsets.all(10.0), - child: new Column( + actionList.add(Flexible( + child: Padding( + padding: const EdgeInsets.all(10.0), + child: Column( mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, children: listWithoutNulls([ - new Text(notif.description), - // TODO fire type (neighbout, NASA, etc) - new SizedBox(height: 5.0), - new Row( + Text(notif.description), + // TODOfire type (neighbout, NASA, etc) + const SizedBox(height: 5.0), + Row( children: [ - new Icon(Icons.access_time), - new SizedBox(width: 5.0), - new Text(Moment.now().from(context, notif.when)), + const Icon(Icons.access_time), + const SizedBox(width: 5.0), + 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( + if (state.industries.isNotEmpty || state.falsePos.isNotEmpty) Padding( + padding: const EdgeInsets.only(top: 10.0), + child: Text( S.of(context).itSeemsNotAtForesFire, - style: new TextStyle(color: fires600))) - : null, - new DropdownButton( - style: new TextStyle( + style: const TextStyle(color: fires600))) else null, + DropdownButton( + style: const TextStyle( color: Colors.black, // fontSize: 18.0, ), - hint: new Text(S.of(context).notAWildfire), + hint: Text(S.of(context).notAWildfire), items: FalsePositiveType.values .map((FalsePositiveType value) { String menuText; @@ -110,18 +105,18 @@ class GenericMapBottom extends StatelessWidget { case FalsePositiveType.falsealarm: menuText = S.of(context).itSeemsAFalseAlarm; } - return new DropdownMenuItem( - value: value, child: new Text(menuText)); + return DropdownMenuItem( + value: value, child: Text(menuText)); }).toList(), onChanged: (FalsePositiveType? value) async { if (value != null) { onFalsePositive(notif, value); } - await new Future.delayed( - new Duration(milliseconds: 500)); + await Future.delayed( + const Duration(milliseconds: 500)); ScaffoldMessenger.of(context) - .showSnackBar(new SnackBar( - content: new Text( + .showSnackBar(SnackBar( + content: Text( S.of(context).thanksForParticipating), )); }), @@ -130,7 +125,7 @@ class GenericMapBottom extends StatelessWidget { break; case FireMapStatus.unsubscribe: case FireMapStatus.view: - actionList.add(new Text(state.numFires > 0 + actionList.add(Text(state.numFires > 0 ? loc.currentNumFires == 1 ? S.of(context).fireAroundThisArea(loc.distance.toString()) : S.of(context).firesAroundThisArea( diff --git a/lib/globalFiresBottomStats.dart b/lib/globalFiresBottomStats.dart index 8e07d7e..704be39 100644 --- a/lib/globalFiresBottomStats.dart +++ b/lib/globalFiresBottomStats.dart @@ -10,6 +10,8 @@ import 'customMoment.dart'; import 'generated/i18n.dart'; class GlobalFiresBottomStats extends StatefulWidget { + const GlobalFiresBottomStats({super.key}); + @override _GlobalFiresBottomStatsState createState() => _GlobalFiresBottomStatsState(); } @@ -17,20 +19,20 @@ class GlobalFiresBottomStats extends StatefulWidget { class _GlobalFiresBottomStatsState extends State { late String lastCheck; int activeFires = 0; - final firesApiUrl = GetIt.instance(instanceName: "firesApiUrl"); + final String firesApiUrl = GetIt.instance(instanceName: 'firesApiUrl'); @override void initState() { super.initState(); - http.read(Uri.parse('${firesApiUrl}status/last-fire-check')).then((result) { + http.read(Uri.parse('${firesApiUrl}status/last-fire-check')).then((String result) { try { - var now = Moment.now(); - var last = DateTime.parse(json.decode(result)['value'] as String); + final Moment now = Moment.now(); + final DateTime last = DateTime.parse(json.decode(result)['value'] as String); http .read(Uri.parse('${firesApiUrl}status/active-fires-count')) - .then((result) { + .then((String result) { try { - int count = (json.decode(result)['total'] as num).toInt(); + final int count = (json.decode(result)['total'] as num).toInt(); setState(() { lastCheck = now.from(context, last); activeFires = count; @@ -51,21 +53,19 @@ class _GlobalFiresBottomStatsState extends State { Widget build(BuildContext context) { final List actionWidgets = []; if (activeFires > 0) { - actionWidgets.add(new Column( + actionWidgets.add(Column( mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - new Text( + children: [ + Text( S.of(context).activeFiresWorldWide(activeFires.toString())), - new Text(S.of(context).updatedLastCheck(lastCheck)) + Text(S.of(context).updatedLastCheck(lastCheck)) ])); } - return new CustomBottomAppBar( + return CustomBottomAppBar( fabLocation: FloatingActionButtonLocation.centerDocked, showNotch: true, color: fires100, - mainAxisAlignment: MainAxisAlignment.center, actions: actionWidgets); } } diff --git a/lib/homePage.dart b/lib/homePage.dart index b5cbc00..c13e45b 100644 --- a/lib/homePage.dart +++ b/lib/homePage.dart @@ -2,8 +2,6 @@ import 'dart:async'; import 'package:comunes_flutter/comunes_flutter.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_redux/flutter_redux.dart'; import 'package:get_it/get_it.dart'; @@ -17,12 +15,14 @@ import 'firesSpinner.dart'; import 'generated/i18n.dart'; import 'mainDrawer.dart'; import 'models/appState.dart'; +import 'models/fireNotification.dart'; +import 'objectIdUtils.dart'; import 'redux/actions.dart'; class _ViewModel { - final bool isLoaded; _ViewModel({required this.isLoaded}); + final bool isLoaded; @override bool operator ==(Object other) => @@ -36,21 +36,21 @@ class _ViewModel { } class HomePage extends StatefulWidget { - static const String routeName = '/home'; - HomePage(); + const HomePage({super.key}); + static const String routeName = '/home'; @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State { - final GlobalKey _scaffoldKey = new GlobalKey(); + final GlobalKey _scaffoldKey = GlobalKey(); final Store store = GetIt.instance>(); - final List newNotifications = []; + final List newNotifications = []; // Platform messages are asynchronous, so we initialize in an async method. - Future initConnectivity() async { + Future initConnectivity() async { // Connectivity checking removed - no longer needed } @@ -70,9 +70,9 @@ class _HomePageState extends State { // Listen for messages when app is in foreground FirebaseMessaging.onMessage.listen((RemoteMessage message) { debugPrint( - "onMessage in fireApp (isLoaded: ${store.state.isLoaded}): $message"); + 'onMessage in fireApp (isLoaded: ${store.state.isLoaded}): $message'); if (message.data.isNotEmpty) { - final notif = _notifForMessage(message.data, store.state.isLoaded); + final FireNotification? notif = _notifForMessage(message.data, store.state.isLoaded); if (notif != null) { _showItemDialog(message.data, notif); } @@ -82,7 +82,7 @@ class _HomePageState extends State { // Listen for messages when app is opened from background FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) { debugPrint( - "onMessageOpenedApp (isLoaded: ${store.state.isLoaded}): $message"); + 'onMessageOpenedApp (isLoaded: ${store.state.isLoaded}): $message'); if (message.data.isNotEmpty) { _notifForMessage(message.data, store.state.isLoaded); _navigateToItemDetail(message.data); @@ -92,7 +92,7 @@ class _HomePageState extends State { // Check if app was terminated and opened by notification tap _firebaseMessaging.getInitialMessage().then((RemoteMessage? message) { if (message != null) { - debugPrint("App opened by notification: $message"); + debugPrint('App opened by notification: $message'); if (message.data.isNotEmpty) { _navigateToItemDetail(message.data); } @@ -103,39 +103,39 @@ class _HomePageState extends State { void _getFirebaseToken() { _firebaseMessaging.getToken().then((String? token) { if (token != null) { - store.dispatch(new OnUserTokenAction(token)); + store.dispatch(OnUserTokenAction(token)); setState(() {}); } }); } - final _homeFont = const TextStyle( + final TextStyle _homeFont = const TextStyle( fontSize: 50.0, fontWeight: FontWeight.w600, ); - final _btnFont = const TextStyle( + final TextStyle _btnFont = const TextStyle( fontSize: 20.0, fontWeight: FontWeight.w600, ); @override Widget build(BuildContext context) { - return new StoreConnector( + return StoreConnector( distinct: true, - converter: (store) { - bool isLoaded = store.state.isLoaded; + converter: (Store store) { + final bool isLoaded = store.state.isLoaded; if (isLoaded && newNotifications.isNotEmpty) { - newNotifications.forEach((notif) { - store.dispatch(new AddFireNotificationAction(notif)); - }); + for (final FireNotification notif in newNotifications) { + store.dispatch(AddFireNotificationAction(notif)); + } newNotifications.clear(); } - return new _ViewModel(isLoaded: store.state.isLoaded); + return _ViewModel(isLoaded: store.state.isLoaded); }, - builder: (context, view) { - return new Scaffold( + builder: (BuildContext context, _ViewModel view) { + return Scaffold( key: _scaffoldKey, - drawer: new MainDrawer(context, HomePage.routeName), + drawer: MainDrawer(context, HomePage.routeName), floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, floatingActionButton: Column( @@ -160,7 +160,7 @@ class _HomePageState extends State { }, heroTag: 'notifyFire', backgroundColor: fires600, - label: new Text(S.of(context).notifyAFire, + label: Text(S.of(context).notifyAFire, style: _btnFont), icon: const Icon(Icons.notifications_active, size: 32.0), @@ -168,41 +168,40 @@ class _HomePageState extends State { ), ]), body: !view.isLoaded - ? new FiresSpinner() - : new SafeArea( + ? const FiresSpinner() + : SafeArea( child: Center( - child: new CenteredColumn(children: [ - new Row( - mainAxisAlignment: MainAxisAlignment.start, + child: CenteredColumn(children: [ + Row( children: [ - new IconButton( + IconButton( onPressed: () { _scaffoldKey.currentState?.openDrawer(); }, - icon: new Icon(Icons.menu, + icon: const Icon(Icons.menu, size: 30.0, color: Colors.black38)), ]), - new Expanded( - child: new FractionallySizedBox( + Expanded( + child: FractionallySizedBox( alignment: FractionalOffset.center, heightFactor: 0.7, - child: new Image.asset('images/logo-200.png', + child: Image.asset('images/logo-200.png', fit: BoxFit.fitHeight))), - new Expanded( - child: new FractionallySizedBox( + Expanded( + child: FractionallySizedBox( alignment: FractionalOffset.topCenter, heightFactor: 1.0, - child: new Column( + child: Column( children: [ - new Padding( + Padding( padding: const EdgeInsets.symmetric( vertical: 10.0, horizontal: 20.0), child: FittedBox( - child: new Text(S.of(context).appName, + fit: BoxFit.scaleDown, + child: Text(S.of(context).appName, maxLines: 2, textAlign: TextAlign.center, style: _homeFont), - fit: BoxFit.scaleDown, )), ], ))) @@ -212,14 +211,14 @@ class _HomePageState extends State { } void _showDialog(String message) { - final context = _scaffoldKey.currentContext; + final BuildContext? context = _scaffoldKey.currentContext; if (context == null) return; showDialog( context: context, - builder: (_) => new AlertDialog( - content: new Text(message), + builder: (_) => AlertDialog( + content: Text(message), actions: [ - new TextButton( + TextButton( child: Text(S.of(context).CLOSE), onPressed: () { Navigator.pop(context); @@ -230,29 +229,29 @@ class _HomePageState extends State { } void _showItemDialog(Map message, FireNotification notif) { - final context = _scaffoldKey.currentContext; + final BuildContext? context = _scaffoldKey.currentContext; if (context == null) return; showDialog( context: context, builder: (_) => _buildDialog(context, notif), ).then((bool? shouldNavigate) { - if (shouldNavigate == true) { + if (shouldNavigate ?? false) { _navigateToItemDetail(message); } - }).catchError((e) => print("$e")); + }).catchError((e) => print('$e')); } Widget _buildDialog(BuildContext context, FireNotification item) { - return new AlertDialog( - content: new Text(item.description), + return AlertDialog( + content: Text(item.description), actions: [ - new TextButton( + TextButton( child: Text(S.of(context).CLOSE), onPressed: () { Navigator.pop(context, false); }, ), - new TextButton( + TextButton( child: Text(S.of(context).SHOW), onPressed: () { Navigator.pop(context, true); @@ -263,7 +262,7 @@ class _HomePageState extends State { } void _navigateToItemDetail(Map message) { - final context = _scaffoldKey.currentContext; + final BuildContext? context = _scaffoldKey.currentContext; if (context == null) return; // Clear away dialogs Navigator.popUntil(context, (Route route) => route is PageRoute); @@ -280,7 +279,7 @@ class _HomePageState extends State { Map message, bool isLoaded) { FireNotification? notif; try { - notif = new FireNotification( + notif = FireNotification( id: objectIdFromJson(message['id'] as String), subsId: objectIdFromJson(message['subsId'] as String), lat: double.parse(message['lat'] as String), @@ -296,7 +295,7 @@ class _HomePageState extends State { // if our store is loaded, we just dispatch the notification, if not, we wait til is loaded if (notif != null) { if (isLoaded) { - store.dispatch(new AddFireNotificationAction(notif)); + store.dispatch(AddFireNotificationAction(notif)); } else { newNotifications.add(notif); } diff --git a/lib/introPage.dart b/lib/introPage.dart index 76b03de..ca6d253 100644 --- a/lib/introPage.dart +++ b/lib/introPage.dart @@ -1,13 +1,18 @@ import 'package:comunes_flutter/comunes_flutter.dart'; import 'package:flutter/material.dart'; -import 'homePage.dart'; import 'generated/i18n.dart'; +import 'homePage.dart'; class IntroPage extends AppIntroPage { + IntroPage({super.key}) + : super( + items: _fireItems, + onIntroFinish: (BuildContext context) => + Navigator.pushNamed(context, HomePage.routeName)); static const String routeName = '/intro'; - static final fireItems = (BuildContext context) => [ + static final _fireItems = (BuildContext context) => [ AppIntroItem( icon: Icons.location_on, title: S.of(context).chooseAPlace), AppIntroItem( @@ -20,11 +25,4 @@ class IntroPage extends AppIntroPage { icon: Icons.notifications_active, title: S.of(context).alertWhenThereIsAFire) ]; - - IntroPage({Key? key}) - : super( - items: fireItems, - onIntroFinish: (context) => - Navigator.pushNamed(context, HomePage.routeName), - key: key); } diff --git a/lib/layerSelectorMapPlugin.dart b/lib/layerSelectorMapPlugin.dart index c4245d2..eda11ab 100644 --- a/lib/layerSelectorMapPlugin.dart +++ b/lib/layerSelectorMapPlugin.dart @@ -4,23 +4,24 @@ import 'package:get_it/get_it.dart'; import 'package:redux/redux.dart'; import 'models/appState.dart'; -import 'models/fireMapState.dart'; import 'redux/fireMapActions.dart'; /// Layer selector widget for changing map layers class LayerSelectorMapPluginWidget extends StatelessWidget { + const LayerSelectorMapPluginWidget({super.key}); + @override Widget build(BuildContext context) { - Store store = GetIt.instance>(); + final Store store = GetIt.instance>(); return LayoutBuilder( - builder: (context, constraints) => + builder: (BuildContext context, BoxConstraints constraints) => Stack(fit: StackFit.expand, children: [ Positioned( top: constraints.maxHeight - 60, left: 10.0, - child: new CenteredRow( + child: CenteredRow( children: [ - new Column( + Column( children: [_LayerSelectorButton(store)], ) ], @@ -30,7 +31,7 @@ class LayerSelectorMapPluginWidget extends StatelessWidget { } Widget _LayerSelectorButton(Store store) { - final key = GlobalKey>(); + final GlobalKey> key = GlobalKey>(); return PopupMenuButton( key: key, @@ -40,17 +41,17 @@ class LayerSelectorMapPluginWidget extends StatelessWidget { }, itemBuilder: (BuildContext context) { return FireMapLayer.values.map((FireMapLayer layer) { - final isSelected = store.state.fireMapState.layer == layer; + final bool isSelected = store.state.fireMapState.layer == layer; return PopupMenuItem( value: layer, child: Row( - children: [ + children: [ Icon( isSelected ? Icons.check : Icons.check, color: isSelected ? Colors.blue : Colors.transparent, size: 20, ), - SizedBox(width: 8), + const SizedBox(width: 8), Text( _getLayerName(layer), style: TextStyle( @@ -66,7 +67,7 @@ class LayerSelectorMapPluginWidget extends StatelessWidget { child: FloatingActionButton( backgroundColor: Colors.black26, mini: true, - child: Icon(Icons.layers), + child: const Icon(Icons.layers), onPressed: () { key.currentState?.showButtonMenu(); }, diff --git a/lib/locationUtils.dart b/lib/locationUtils.dart index d5503e9..e9ac2d0 100644 --- a/lib/locationUtils.dart +++ b/lib/locationUtils.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:fires_flutter/models/yourLocation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:geocoding/geocoding.dart' as geo; @@ -8,42 +7,43 @@ import 'package:location/location.dart'; import 'package:objectid/objectid.dart'; import 'generated/i18n.dart'; +import 'models/yourLocation.dart'; Future getUserLocation( GlobalKey scaffoldKey) async { // Platform messages may fail, so we use a try/catch PlatformException. try { - Location _location = new Location(); + final Location location0 = Location(); - LocationData location = await _location.getLocation(); + final LocationData location = await location0.getLocation(); // It seems that the lib fails with lat/lon values - var yl = new YourLocation( + final YourLocation yl = YourLocation( id: ObjectId(), lat: location.latitude!, lon: location.longitude!); - var address; + String address; try { address = await getReverseLocation(lat: yl.lat, lon: yl.lon); - yl.description = address as String; + yl.description = address; } catch (e) { try { address = await getReverseLocation(lat: yl.lat, lon: yl.lon, external: true); - yl.description = address as String; + yl.description = address; } catch (e) { print('We cannot reverse geolocate'); } } return yl; } on PlatformException catch (e) { - final context = scaffoldKey.currentContext; + final BuildContext? context = scaffoldKey.currentContext; if (context != null) { if (e.code == 'PERMISSION_DENIED') { - ScaffoldMessenger.of(context).showSnackBar(new SnackBar( - content: new Text(S.of(context).notPermsUbication), + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: 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), + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(S.of(context).isYourUbicationEnabled), )); } return YourLocation.noLocation; @@ -53,19 +53,19 @@ Future getUserLocation( Future getReverseLocation( {required double lat, required double lon, bool external = false}) async { try { - List placemarks = + final List placemarks = await geo.placemarkFromCoordinates(lat, lon); if (placemarks.isNotEmpty) { - var first = placemarks.first; - String address = + final geo.Placemark first = placemarks.first; + final String address = '${first.street}, ${first.locality}, ${first.administrativeArea}, ${first.country}'; - print("${first.name} : $address"); + print('${first.name} : $address'); return address; } else { return 'Unable to determine address'; } } catch (e) { - print("Error in reverse geocoding: $e"); - throw Exception("Failed to reverse geocode: $e"); + print('Error in reverse geocoding: $e'); + throw Exception('Failed to reverse geocode: $e'); } } diff --git a/lib/mainCommon.dart b/lib/mainCommon.dart index 75d420d..d05c666 100644 --- a/lib/mainCommon.dart +++ b/lib/mainCommon.dart @@ -16,12 +16,12 @@ import 'redux/reducers.dart'; import 'sentryReport.dart'; Future loadPackageInfo() async { - PackageInfo packageInfo = await PackageInfo.fromPlatform(); + final PackageInfo packageInfo = await PackageInfo.fromPlatform(); return packageInfo; } Future> loadSecrets() async { - return await SecretLoader( + return SecretLoader( secretPath: globals.isDevelopment ? 'assets/private-settings-dev.json' : 'assets/private-settings.json') @@ -34,29 +34,29 @@ Future mainCommon(List> otherMiddleware) async { // Initialize Firebase before any Firebase-dependent code runs await Firebase.initializeApp(); - final getIt = GetIt.instance; + final GetIt getIt = GetIt.instance; getIt.registerSingleton(FiresApi()); - loadPackageInfo().then((packageInfo) { + loadPackageInfo().then((PackageInfo packageInfo) { globals.appVersion = packageInfo.version; print('Running version ${packageInfo.version}'); - loadSecrets().then((secrets) { - final store = Store(appStateReducer, + loadSecrets().then((Map secrets) { + final Store store = Store(appStateReducer, initialState: AppState( gmapKey: secrets['gmapKey'] as String, firesApiKey: secrets['firesApiKey'] as String, serverUrl: secrets['firesApiUrl'] as String, - firesApiUrl: (secrets['firesApiUrl'] as String) + "api/v1/"), + firesApiUrl: "${secrets['firesApiUrl'] as String}api/v1/"), middleware: List.from(otherMiddleware)..add(fetchDataMiddleware)); getIt.registerSingleton>(store); getIt.registerSingleton(store.state.firesApiUrl, - instanceName: "firesApiUrl"); + instanceName: 'firesApiUrl'); getIt.registerSingleton(store.state.firesApiKey, - instanceName: "firesApiKey"); + instanceName: 'firesApiKey'); getIt.registerSingleton(store.state.serverUrl, - instanceName: "serverUrl"); + instanceName: 'serverUrl'); getIt.registerSingleton(store.state.gmapKey, - instanceName: "gmapKey"); + instanceName: 'gmapKey'); // https://flutter.io/cookbook/maintenance/error-reporting/ runZonedGuarded>(() async { @@ -68,7 +68,7 @@ Future mainCommon(List> otherMiddleware) async { }); // Listen to store changes, and re-render when the state is updated - store.onChange.listen((state) { + store.onChange.listen((AppState state) { // print('Store onChange'); }); FlutterError.onError = (FlutterErrorDetails details) { diff --git a/lib/mainDev.dart b/lib/mainDev.dart index 5f4ad15..8fc847d 100644 --- a/lib/mainDev.dart +++ b/lib/mainDev.dart @@ -8,7 +8,7 @@ enum LogLevel { none, actions, all } void main() async { globals.isDevelopment = true; - debugPrint("Is development!"); + debugPrint('Is development!'); // Simple logging middleware para desarrollo Middleware createLoggingMiddleware() { @@ -18,10 +18,10 @@ void main() async { }; } - LogLevel logRedux = LogLevel.actions; + const LogLevel logRedux = LogLevel.actions; - List> devMiddlewares = - logRedux == LogLevel.actions ? [] : [createLoggingMiddleware()]; + final List> devMiddlewares = + logRedux == LogLevel.actions ? >[] : >[createLoggingMiddleware()]; // In development, Sentry is disabled, so we skip initialization await mainCommon(devMiddlewares); diff --git a/lib/mainDrawer.dart b/lib/mainDrawer.dart index 6f75716..cab965f 100644 --- a/lib/mainDrawer.dart +++ b/lib/mainDrawer.dart @@ -1,7 +1,7 @@ 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'; +import 'package:redux/src/store.dart'; import 'activeFires.dart'; import 'colors.dart'; @@ -17,11 +17,10 @@ import 'supportPage.dart'; @immutable class _ViewModel { - final int unreadCount; - - _ViewModel({ + const _ViewModel({ required this.unreadCount, }); + final int unreadCount; @override bool operator ==(Object other) { @@ -36,65 +35,65 @@ class _ViewModel { } class MainDrawer extends Drawer { - MainDrawer(BuildContext context, String currentRoute, {Key? key}) - : super(key: key, child: mainDrawer(context, currentRoute)); + MainDrawer(BuildContext context, String currentRoute, {super.key}) + : super(child: mainDrawer(context, currentRoute)); } Widget mainDrawer(BuildContext context, String currentRoute) { - return new StoreConnector( + return StoreConnector( distinct: true, - converter: (store) { - return new _ViewModel(unreadCount: store.state.fireNotificationsUnread); + converter: (Store store) { + return _ViewModel(unreadCount: store.state.fireNotificationsUnread); }, - builder: (context, view) { - final bottomTextStyle = - new TextStyle(fontSize: 12.0, color: Colors.grey); - return new ListView( + builder: (BuildContext context, _ViewModel view) { + const TextStyle bottomTextStyle = + TextStyle(fontSize: 12.0, color: Colors.grey); + return ListView( // Important: Remove any padding from the ListView. padding: EdgeInsets.zero, - children: ([ - new GestureDetector( + children: [ + GestureDetector( onTap: () { Navigator.popAndPushNamed(context, '/'); }, - child: new DrawerHeader( - child: new Column( + child: DrawerHeader( + decoration: const BoxDecoration( + color: fires300, + ), + child: Column( children: [ - new Image.asset( + Image.asset( 'images/logo-200.png', fit: BoxFit.scaleDown, height: 80.0, ), const SizedBox(height: 20.0), - new Text(S.of(context).appName, - style: new TextStyle( + Text(S.of(context).appName, + style: const TextStyle( fontSize: 24.0, color: Colors.white, )), ], ), - decoration: new BoxDecoration( - color: fires300, - ), ), ), - new ListTile( + ListTile( leading: const Icon(Icons.whatshot), - title: new Text(S.of(context).activeFires), + title: Text(S.of(context).activeFires), selected: currentRoute == ActiveFiresPage.routeName, onTap: () { Navigator.popAndPushNamed(context, ActiveFiresPage.routeName); }, ), - new ListTile( + ListTile( leading: const Icon(Icons.notifications_active), selected: currentRoute == FireAlert.routeName, - title: new Text(S.of(context).notifyAFire), + title: Text(S.of(context).notifyAFire), onTap: () { Navigator.popAndPushNamed(context, FireAlert.routeName); }, ), - new ListTile( + ListTile( leading: const Icon(Icons.notifications), selected: currentRoute == FireNotificationList.routeName, title: Text(S.of(context).fireNotificationsTitleShort), @@ -107,10 +106,10 @@ Widget mainDrawer(BuildContext context, String currentRoute) { position: badges_pkg.BadgePosition.topEnd( top: -10, end: -12), badgeContent: Text( - '${view.unreadCount.toString()}', - style: TextStyle(color: Colors.white), + view.unreadCount.toString(), + style: const TextStyle(color: Colors.white), ), - child: Icon(Icons.notifications)) + child: const Icon(Icons.notifications)) ])), // Text(S.of(context).fireNotificationsTitleShort), @@ -119,43 +118,44 @@ Widget mainDrawer(BuildContext context, String currentRoute) { context, FireNotificationList.routeName); }, ), - new ListTile( + ListTile( leading: const Icon(Icons.map), selected: currentRoute == MonitoredAreasPage.routeName, - title: new Text(S.of(context).monitoredAreasTitle), + title: Text(S.of(context).monitoredAreasTitle), onTap: () { Navigator.popAndPushNamed( context, MonitoredAreasPage.routeName); }, ), - new Divider(), - new ListTile( + const Divider(), + ListTile( leading: const Icon(Icons.favorite), selected: currentRoute == SupportPage.routeName, - title: new Text(S.of(context).supportThisInitiative), + title: Text(S.of(context).supportThisInitiative), onTap: () { Navigator.popAndPushNamed(context, SupportPage.routeName); }, ), - new ListTile( + ListTile( leading: const Icon(Icons.lock), selected: currentRoute == PrivacyPage.routeName, - title: new Text(S.of(context).privacyPolicy), + title: Text(S.of(context).privacyPolicy), onTap: () { Navigator.popAndPushNamed(context, PrivacyPage.routeName); }, ), - globals.isDevelopment - ? new ListTile( - leading: const Icon(Icons.bug_report), - title: new Text('Sandbox'), - selected: currentRoute == Sandbox.routeName, - onTap: () { - Navigator.popAndPushNamed(context, Sandbox.routeName); - }, - ) - : null, - new AboutListTile( + if (globals.isDevelopment) + ListTile( + leading: const Icon(Icons.bug_report), + title: const Text('Sandbox'), + selected: currentRoute == Sandbox.routeName, + onTap: () { + Navigator.popAndPushNamed(context, Sandbox.routeName); + }, + ) + else + null, + AboutListTile( icon: globals.appIcon, applicationName: S.of(context).appName, applicationVersion: globals.appVersion, @@ -163,13 +163,13 @@ Widget mainDrawer(BuildContext context, String currentRoute) { applicationLegalese: S.of(context).appLicense(DateTime.now().year.toString()), aboutBoxChildren: [ - new SizedBox(height: 10.0), - new Text(S.of(context).appMoto), + const SizedBox(height: 10.0), + Text(S.of(context).appMoto), // , style: new TextStyle(fontStyle: FontStyle.italic)), - new SizedBox(height: 10.0), - new Text(S.of(context).NASAAck, style: bottomTextStyle), + const SizedBox(height: 10.0), + Text(S.of(context).NASAAck, style: bottomTextStyle), // More ? ]) - ]).where((w) => w != null).cast().toList()); + ].where((Widget? w) => w != null).cast().toList()); }); } diff --git a/lib/mainProd.dart b/lib/mainProd.dart index 9cec661..a98ba41 100644 --- a/lib/mainProd.dart +++ b/lib/mainProd.dart @@ -1,25 +1,27 @@ import 'package:comunes_flutter/comunes_flutter.dart'; +import 'package:redux/src/store.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import 'globals.dart' as globals; import 'mainCommon.dart'; +import 'models/appState.dart'; Future main() async { globals.isDevelopment = false; // Load secrets to get Sentry DSN - final secrets = await loadSecrets(); + final Map secrets = await loadSecrets(); await SentryFlutter.init( - (options) { + (SentryFlutterOptions options) { options.dsn = secrets['sentryDSN'] as String?; }, - appRunner: () => mainCommon([]), + appRunner: () => mainCommon(>[]), ); } Future> loadSecrets() async { - return await SecretLoader( + return SecretLoader( secretPath: globals.isDevelopment ? 'assets/private-settings-dev.json' : 'assets/private-settings.json') diff --git a/lib/markdownPage.dart b/lib/markdownPage.dart index b0cbf8f..75d657a 100644 --- a/lib/markdownPage.dart +++ b/lib/markdownPage.dart @@ -8,40 +8,40 @@ import 'globals.dart' as globals; import 'mainDrawer.dart'; abstract class MarkdownPage extends StatefulWidget { + + const MarkdownPage({super.key, required this.title, required this.route, required this.file}); final String title; final Future file; final String route; - MarkdownPage({required this.title, required this.route, required this.file}); - @override _MarkdownPageState createState() => - _MarkdownPageState(title: this.title, file: this.file, route: this.route); + _MarkdownPageState(title: title, file: file, route: route); } class _MarkdownPageState extends State { - final String title; - final String route; - final Future file; - String pageData = ""; _MarkdownPageState( {required this.title, required this.file, required this.route}); + final String title; + final String route; + final Future file; + String pageData = ''; @override Widget build(BuildContext context) { - this.file.then((fileSync) { + file.then((String fileSync) { rootBundle .loadString(fileSync, cache: !globals.isDevelopment) - .then((pageData) { + .then((String pageData) { setState(() { this.pageData = pageData; }); }); }); - return new Scaffold( - appBar: new AppBar(title: new Text(title ?? '')), - drawer: new MainDrawer(context, route), - body: new Markdown(data: pageData)); + return Scaffold( + appBar: AppBar(title: Text(title ?? '')), + drawer: MainDrawer(context, route), + body: Markdown(data: pageData)); } } diff --git a/lib/models/appState.dart b/lib/models/appState.dart index 2aa6254..66187ed 100644 --- a/lib/models/appState.dart +++ b/lib/models/appState.dart @@ -1,8 +1,6 @@ import 'dart:async'; import 'package:comunes_flutter/comunes_flutter.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'; @@ -10,7 +8,9 @@ import 'package:latlong2/latlong.dart'; import 'package:meta/meta.dart'; import 'fireMapState.dart'; +import 'fireNotification.dart'; import 'user.dart'; +import 'yourLocation.dart'; export 'fireMapState.dart'; @@ -19,6 +19,25 @@ part 'appState.g.dart'; @immutable @JsonSerializable(nullable: false) class AppState { + + const AppState( + {this.yourLocations = const [], + this.fireNotifications = const [], + 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.monitoredAreas = const [], + this.fireMapState = const FireMapState.initial()}); + + @JsonKey(ignore: true) + factory AppState.fromJson(Map json) => + _$AppStateFromJson(json); @JsonKey(ignore: true) final bool isLoading; @JsonKey(ignore: true) @@ -44,25 +63,6 @@ class AppState { @JsonKey(ignore: true) final FireMapState fireMapState; - @JsonKey(ignore: true) - factory AppState.fromJson(Map json) => - _$AppStateFromJson(json); - - AppState( - {this.yourLocations = const [], - this.fireNotifications = const [], - 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.monitoredAreas = const [], - this.fireMapState = const FireMapState.initial()}); - AppState copyWith( {bool? isLoading, bool? isLoaded, @@ -77,7 +77,7 @@ class AppState { int? fireNotificationsUnread, FireMapState? fireMapState, List? monitoredAreas}) { - return new AppState( + return AppState( isLoading: isLoading ?? this.isLoading, isLoaded: isLoaded ?? this.isLoaded, user: user ?? this.user, @@ -100,24 +100,24 @@ class AppState { } } -typedef void AddYourLocationFunction(YourLocation loc); -typedef void DeleteYourLocationFunction(YourLocation loc); -typedef void OnRefreshYourLocationsFunction(Completer callback); -typedef void ToggleSubscriptionFunction(YourLocation loc); -typedef void OnLocationTapFunction(YourLocation loc); -typedef void OnSubscribeFunction(YourLocation loc); -typedef void OnSubscribeDistanceChangeFunction(YourLocation loc); -typedef void OnUnSubscribeFunction(YourLocation loc); -typedef void OnSubscribeConfirmedFunction(YourLocation loc); -typedef void OnLocationEdit(YourLocation loc); -typedef void OnLocationEditing(YourLocation loc); -typedef void OnLocationEditConfirm(YourLocation loc); -typedef void OnLocationEditCancel(YourLocation loc); -typedef void TapFireNotificationFunction(FireNotification notif); -typedef void OnFirePressedInMap(LatLng latLng, DateTime when, String source); +typedef AddYourLocationFunction = void Function(YourLocation loc); +typedef DeleteYourLocationFunction = void Function(YourLocation loc); +typedef OnRefreshYourLocationsFunction = void Function(Completer callback); +typedef ToggleSubscriptionFunction = void Function(YourLocation loc); +typedef OnLocationTapFunction = void Function(YourLocation loc); +typedef OnSubscribeFunction = void Function(YourLocation loc); +typedef OnSubscribeDistanceChangeFunction = void Function(YourLocation loc); +typedef OnUnSubscribeFunction = void Function(YourLocation loc); +typedef OnSubscribeConfirmedFunction = void Function(YourLocation loc); +typedef OnLocationEdit = void Function(YourLocation loc); +typedef OnLocationEditing = void Function(YourLocation loc); +typedef OnLocationEditConfirm = void Function(YourLocation loc); +typedef OnLocationEditCancel = void Function(YourLocation loc); +typedef TapFireNotificationFunction = void Function(FireNotification notif); +typedef OnFirePressedInMap = void Function(LatLng latLng, DateTime when, String source); // typedef void OnReceivedFireNotificationFunction(FireNotification notif); -typedef void DeleteFireNotificationFunction(FireNotification notif); -typedef void DeleteAllFireNotificationFunction(); +typedef DeleteFireNotificationFunction = void Function(FireNotification notif); +typedef DeleteAllFireNotificationFunction = void Function(); // unused // typedef void UpdateYourLocationFunction(ObjectId id, YourLocation loc); diff --git a/lib/models/basicLocation.dart b/lib/models/basicLocation.dart index 1b89a33..7b56895 100644 --- a/lib/models/basicLocation.dart +++ b/lib/models/basicLocation.dart @@ -1,18 +1,26 @@ class BasicLocation implements Comparable { + +// static BasicLocation noLocation = new BasicLocation(lat: 0.0, lon: 0.0); + + BasicLocation({required this.lat, required this.lon, this.description}); + + BasicLocation.fromJson(Map json) + : lat = (json['lat'] as num).toDouble(), + lon = (json['lon'] as num).toDouble(), + description = json['description'] as String?; final double lat; final double lon; final String? description; -// static BasicLocation noLocation = new BasicLocation(lat: 0.0, lon: 0.0); - - BasicLocation({required this.lat, required this.lon, this.description}) {} - + @override int compareTo(BasicLocation other) { return lat == other.lat && lon == other.lon ? 1 : 0; } - bool operator ==(o) => o is BasicLocation && o.lat == lat && o.lon == lon; + @override + bool operator ==(Object o) => o is BasicLocation && o.lat == lat && o.lon == lon; + @override int get hashCode { int hash = 1; hash = hash * 17 + lat.hashCode; @@ -20,12 +28,7 @@ class BasicLocation implements Comparable { return hash; } - BasicLocation.fromJson(Map json) - : lat = (json['lat'] as num).toDouble(), - lon = (json['lon'] as num).toDouble(), - description = json['description'] as String?; - - Map toJson() => { + Map toJson() => { 'lat': lat, 'lon': lon, 'description': description, diff --git a/lib/models/fireMapState.dart b/lib/models/fireMapState.dart index 8dd8f0a..c9d8ba1 100644 --- a/lib/models/fireMapState.dart +++ b/lib/models/fireMapState.dart @@ -1,7 +1,8 @@ -import 'package:fires_flutter/models/fireNotification.dart'; -import 'package:fires_flutter/models/yourLocation.dart'; import 'package:meta/meta.dart'; +import 'fireNotification.dart'; +import 'yourLocation.dart'; + enum FireMapStatus { view, subscriptionConfirm, @@ -14,6 +15,26 @@ enum FireMapLayer { osmcGrey, esriSatellite, osmc, esri, esriTerrain } @immutable class FireMapState { + + const FireMapState( + {this.status = FireMapStatus.view, + this.layer = FireMapLayer.osmcGrey, + this.yourLocation, + this.numFires = 0, + this.fires = const [], + this.fireNotification, + this.falsePos = const [], + this.industries = const []}); + + const FireMapState.initial() + : status = FireMapStatus.view, + layer = FireMapLayer.osmcGrey, + yourLocation = null, + fireNotification = null, + numFires = 0, + fires = const [], + falsePos = const [], + industries = const []; final FireMapStatus status; final FireMapLayer layer; final int numFires; @@ -23,26 +44,6 @@ class FireMapState { final List industries; final YourLocation? yourLocation; - const FireMapState.initial() - : this.status = FireMapStatus.view, - this.layer = FireMapLayer.osmcGrey, - this.yourLocation = null, - this.fireNotification = null, - this.numFires = 0, - this.fires = const [], - this.falsePos = const [], - this.industries = const []; - - FireMapState( - {this.status = FireMapStatus.view, - this.layer = FireMapLayer.osmcGrey, - this.yourLocation, - this.numFires = 0, - this.fires = const [], - this.fireNotification, - this.falsePos = const [], - this.industries = const []}); - FireMapState copyWith({ FireMapStatus? status, FireMapLayer? layer, @@ -53,9 +54,9 @@ class FireMapState { List? falsePos, List? industries, }) { - return new FireMapState( + return FireMapState( yourLocation: yourLocation ?? this.yourLocation, - fireNotification: fireNotication ?? this.fireNotification, + fireNotification: fireNotication ?? fireNotification, numFires: numFires ?? this.numFires, fires: fires ?? this.fires, falsePos: falsePos ?? this.falsePos, diff --git a/lib/models/fireNotification.dart b/lib/models/fireNotification.dart index b6522ac..7e6e5e1 100644 --- a/lib/models/fireNotification.dart +++ b/lib/models/fireNotification.dart @@ -9,6 +9,19 @@ part 'fireNotification.g.dart'; @JsonSerializable(nullable: false) class FireNotification { + + FireNotification( + {required this.id, + required this.lat, + required this.lon, + required this.description, + required this.when, + required this.read, + required this.sealed, + required this.subsId}); + + factory FireNotification.fromJson(Map json) => + _$FireNotificationFromJson(json); @JsonKey(toJson: objectIdToJson, fromJson: objectIdFromJson) ObjectId id; final double lat; @@ -20,19 +33,6 @@ class FireNotification { final ObjectId subsId; final bool read; - factory FireNotification.fromJson(Map json) => - _$FireNotificationFromJson(json); - - FireNotification( - {required this.id, - required this.lat, - required this.lon, - required this.description, - required this.when, - required this.read, - required this.sealed, - required this.subsId}) {} - FireNotification copyWith( {ObjectId? id, double? lat, @@ -42,7 +42,7 @@ class FireNotification { DateTime? when, String? sealed, ObjectId? subsId}) { - return new FireNotification( + return FireNotification( id: id ?? this.id, lat: lat ?? this.lat, lon: lon ?? this.lon, @@ -83,7 +83,7 @@ class FireNotification { return 'FireNotification {id: $id, lat: $lat, lon: $lon, when: $when, read: $read, subsId: $subsId, sealed ${ellipse(sealed)}'; } - static final Map> routes = >{}; + static final Map> routes = >{}; Map toJson() => _$FireNotificationToJson(this); /* diff --git a/lib/models/fireNotificationsPersist.dart b/lib/models/fireNotificationsPersist.dart index ff455fc..64da81d 100644 --- a/lib/models/fireNotificationsPersist.dart +++ b/lib/models/fireNotificationsPersist.dart @@ -2,30 +2,31 @@ import 'dart:async'; import 'dart:convert'; import 'package:comunes_flutter/comunes_flutter.dart'; -import 'package:fires_flutter/models/fireNotification.dart'; +import 'package:shared_preferences/src/shared_preferences_legacy.dart'; import '../globals.dart' as globals; +import 'fireNotification.dart'; -final String fireNotificationKey = 'fireNotifications'; +const String fireNotificationKey = 'fireNotifications'; Future> loadFireNotifications() async { - return await globals.prefs.then((prefs) { - List? FireNotifications = prefs.getStringList(fireNotificationKey); - List persistedList = []; - (FireNotifications ?? []).forEach((notificationString) { - Map notificationMap = + return globals.prefs.then((SharedPreferences prefs) { + final List? FireNotifications = prefs.getStringList(fireNotificationKey); + final List persistedList = []; + for (final String notificationString in (FireNotifications ?? [])) { + final Map notificationMap = json.decode(notificationString) as Map; persistedList.add(FireNotification.fromJson(notificationMap)); - }); + } return persistedList; }); } -persistFireNotifications(List notif) { +void persistFireNotifications(List notif) { // print('Persisting $notif'); - globals.prefs.then((prefs) { - List notifAsString = []; - notif.where(notNull).toList().forEach((notification) { + globals.prefs.then((SharedPreferences prefs) { + final List notifAsString = []; + notif.where(notNull).toList().forEach((FireNotification notification) { notifAsString.add(json.encode(notification.toJson())); }); prefs.setStringList(fireNotificationKey, notifAsString); diff --git a/lib/models/firesApi.dart b/lib/models/firesApi.dart index be25c68..3a92483 100644 --- a/lib/models/firesApi.dart +++ b/lib/models/firesApi.dart @@ -2,7 +2,6 @@ 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:latlong2/latlong.dart'; @@ -12,49 +11,50 @@ import '../objectIdUtils.dart'; import '../redux/actions.dart'; import 'appState.dart'; import 'falsePositiveTypes.dart'; +import 'yourLocation.dart'; class FiresApi { - late final Dio _dio; FiresApi() { _dio = Dio(); } + late final Dio _dio; Future createUser( AppState state, String mobileToken, String lang) async { - final params = { - "token": state.firesApiKey, - "mobileToken": mobileToken, - "lang": lang + final Map params = { + 'token': state.firesApiKey, + 'mobileToken': mobileToken, + 'lang': lang }; final String url = '${state.firesApiUrl}mobile/users'; try { - final response = await _dio.post(url, data: params); + final Response response = await _dio.post(url, data: params); if (response.statusCode == 200) { return response.data['data']['userId'] as String; } else { - throw "Unexpected error on create user"; + throw 'Unexpected error on create user'; } } catch (e) { - throw "Error creating user: $e"; + throw 'Error creating user: $e'; } } Future> fetchYourLocations(AppState state) async { - final apiKey = state.firesApiKey; - final mobileToken = state.user.token; + final String apiKey = state.firesApiKey; + final String mobileToken = state.user.token; final String url = '${state.firesApiUrl}mobile/subscriptions/all/$apiKey/$mobileToken'; try { - final response = await _dio.get(url); + final Response response = await _dio.get(url); if (response.statusCode == 200) { - final dataSubscriptions = + final List dataSubscriptions = response.data['data']['subscriptions'] as List; - List subscribed = []; + final List subscribed = []; for (int i = 0; i < dataSubscriptions.length; i++) { - var el = dataSubscriptions[i] as Map; - var lat = (el['location']['lat'] as num).toDouble(); - var lon = (el['location']['lon'] as num).toDouble(); + final Map el = dataSubscriptions[i] as Map; + final double lat = (el['location']['lat'] as num).toDouble(); + final double lon = (el['location']['lon'] as num).toDouble(); subscribed.add(YourLocation( id: objectIdFromJson(el['_id']['_str'] as String), lat: lat, @@ -64,50 +64,50 @@ class FiresApi { } return subscribed; } else { - throw "Unexpected error fetching your locations"; + throw 'Unexpected error fetching your locations'; } } catch (e) { - throw "Error fetching locations: $e"; + throw 'Error fetching locations: $e'; } } Future subscribe(AppState state, YourLocation loc) async { - final params = { - "token": state.firesApiKey, - "mobileToken": state.user.token, - "id": loc.id.hexString, - "lat": loc.lat, - "lon": loc.lon, - "distance": loc.distance + final Map params = { + 'token': state.firesApiKey, + 'mobileToken': state.user.token, + 'id': loc.id.hexString, + 'lat': loc.lat, + 'lon': loc.lon, + 'distance': loc.distance }; final String url = '${state.firesApiUrl}mobile/subscriptions'; try { - final response = await _dio.post(url, data: params); + final Response response = await _dio.post(url, data: params); if (response.statusCode == 200) { return response.data['data']['subsId'] as String; } else { print(response.data); - throw "Unexpected error on subscribe"; + throw 'Unexpected error on subscribe'; } } catch (e) { - throw "Error subscribing: $e"; + throw 'Error subscribing: $e'; } } Future unsubscribe(AppState state, String subsId) async { - final apiKey = state.firesApiKey; - final mobileToken = state.user.token; + final String apiKey = state.firesApiKey; + final String mobileToken = state.user.token; final String url = '${state.firesApiUrl}mobile/subscriptions/$apiKey/$mobileToken/$subsId'; try { - final response = await _dio.delete(url); + final Response response = await _dio.delete(url); if (response.statusCode == 200) { return true; } else { - throw "Unexpected error on unsubscribe"; + throw 'Unexpected error on unsubscribe'; } } catch (e) { - throw "Error unsubscribing: $e"; + throw 'Error unsubscribing: $e'; } } @@ -116,22 +116,22 @@ class FiresApi { required double lat, required double lon, required int distance}) async { - var url = + final String url = '${state.firesApiUrl}fires-in-full/${state.firesApiKey}/$lat/$lon/$distance'; if (globals.isDevelopment) print(url); try { - final response = await _dio.get(url); + final Response response = await _dio.get(url); if (response.statusCode == 200) { - var resultDecoded = response.data; - int numFires = (resultDecoded['real'] as num).toInt(); - List fires = resultDecoded['fires'] as List; - List falsePos = resultDecoded['falsePos'] as List; - List industries = resultDecoded['industries'] as List; + final resultDecoded = response.data; + final int numFires = (resultDecoded['real'] as num).toInt(); + final List fires = resultDecoded['fires'] as List; + final List falsePos = resultDecoded['falsePos'] as List; + final List industries = resultDecoded['industries'] as List; if (globals.isDevelopment) { - var firesCount = fires.length; - var industriesCount = industries.length; - var falsePosCount = falsePos.length; + final int firesCount = fires.length; + final int industriesCount = industries.length; + final int falsePosCount = falsePos.length; print( '(Pos: $lat, $lon) real: $numFires, fire: $firesCount falsePos: $falsePosCount industries: $industriesCount'); } @@ -148,25 +148,25 @@ class FiresApi { } Future> getMonitoredAreas({required AppState state}) async { - var url = + final String url = '${state.firesApiUrl}status/subs-public-union/${state.firesApiKey}'; - var color = const Color(0xFF145A32); + const Color color = Color(0xFF145A32); try { - final response = await _dio.get(url); + final Response response = await _dio.get(url); if (response.statusCode == 200) { - var resultDecoded = response.data; - List union = []; - final multipolygon = + final resultDecoded = response.data; + final List union = []; + final List multipolygon = (json.decode(resultDecoded['data']['union']['value'] as String) as Map)['geometry']['coordinates'] as List; - for (dynamic polygonDynamic in multipolygon) { - var polygon = polygonDynamic as List; - for (dynamic holeDynamic in polygon) { - var hole = holeDynamic as List; - List points = []; - for (dynamic pointDynamic in hole) { - var point = pointDynamic as List; + for (final dynamic polygonDynamic in multipolygon) { + final List polygon = polygonDynamic as List; + for (final dynamic holeDynamic in polygon) { + final List hole = holeDynamic as List; + final List points = []; + for (final dynamic pointDynamic in hole) { + final List point = pointDynamic as List; points.add(LatLng( (point[1] as num).toDouble(), (point[0] as num).toDouble())); } @@ -183,15 +183,15 @@ class FiresApi { Future markFalsePositive(AppState state, String mobileToken, String sealed, FalsePositiveType type) async { - final params = { - "token": state.firesApiKey, - "mobileToken": mobileToken, - "sealed": sealed, - "type": type.toString().split('.')[1] + final Map params = { + 'token': state.firesApiKey, + 'mobileToken': mobileToken, + 'sealed': sealed, + 'type': type.toString().split('.')[1] }; final String url = '${state.firesApiUrl}mobile/falsepositive'; try { - final response = await _dio.post(url, data: params); + final Response response = await _dio.post(url, data: params); if (response.statusCode == 200) { if (globals.isDevelopment) print(response.data['data']['upsert']); return true; @@ -200,7 +200,7 @@ class FiresApi { return false; } } catch (e) { - debugPrint("Error marking false positive: $e"); + debugPrint('Error marking false positive: $e'); return false; } } diff --git a/lib/models/user.dart b/lib/models/user.dart index 0abfddd..d330a1b 100644 --- a/lib/models/user.dart +++ b/lib/models/user.dart @@ -1,21 +1,21 @@ -import 'package:meta/meta.dart'; import 'package:comunes_flutter/comunes_flutter.dart'; +import 'package:meta/meta.dart'; @immutable class User { - final String userId; - final String lang; - final String token; + + const User({required this.userId, required this.lang, required this.token}); const User.initial() : userId = '', lang = '', token = ''; - - const User({required this.userId, required this.lang, required this.token}); + final String userId; + final String lang; + final String token; User copyWith({String? userId, String? lang, String? token}) { - return new User( + return User( userId: userId ?? this.userId, token: token ?? this.token, lang: lang ?? this.lang); diff --git a/lib/models/yourLocation.dart b/lib/models/yourLocation.dart index ff18561..55cde2c 100644 --- a/lib/models/yourLocation.dart +++ b/lib/models/yourLocation.dart @@ -1,30 +1,12 @@ -import 'package:fires_flutter/objectIdUtils.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:objectid/objectid.dart'; +import '../objectIdUtils.dart'; + part 'yourLocation.g.dart'; @JsonSerializable(nullable: false) class YourLocation { - @JsonKey(toJson: objectIdToJson, fromJson: objectIdFromJson) - ObjectId id; - final double lat; - final double lon; - String description; - bool subscribed; - int distance; - late int currentNumFires; - - factory YourLocation.fromJson(Map json) => - _$YourLocationFromJson(json); - - 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( {required this.id, @@ -36,6 +18,25 @@ class YourLocation { this.subscribed = false}) { this.currentNumFires = currentNumFires ?? 0; } + + factory YourLocation.fromJson(Map json) => + _$YourLocationFromJson(json); + @JsonKey(toJson: objectIdToJson, fromJson: objectIdFromJson) + ObjectId id; + final double lat; + final double lon; + String description; + bool subscribed; + int distance; + late int currentNumFires; + + static late final YourLocation noLocation; + static const int? withoutStats = null; + + static void _initNoLocation() { + noLocation = YourLocation( + id: ObjectId(), lat: 0.0, lon: 0.0); + } Map toJson() => _$YourLocationToJson(this); YourLocation copyWith( @@ -46,7 +47,7 @@ class YourLocation { int? distance, int? currentNumFires, bool? subscribed}) { - return new YourLocation( + return YourLocation( id: id ?? this.id, lat: lat ?? this.lat, lon: lon ?? this.lon, diff --git a/lib/models/yourLocationPersist.dart b/lib/models/yourLocationPersist.dart index 4ae923f..4193c91 100644 --- a/lib/models/yourLocationPersist.dart +++ b/lib/models/yourLocationPersist.dart @@ -2,30 +2,31 @@ import 'dart:async'; import 'dart:convert'; import 'package:comunes_flutter/comunes_flutter.dart'; -import 'package:fires_flutter/models/yourLocation.dart'; +import 'package:shared_preferences/src/shared_preferences_legacy.dart'; import '../globals.dart' as globals; +import 'yourLocation.dart'; -final String locationKey = 'yourlocations'; +const String locationKey = 'yourlocations'; Future> loadYourLocations() async { - return await globals.prefs.then((prefs) { - List? yourLocations = prefs.getStringList(locationKey); - List persistedList = []; - (yourLocations ?? []).forEach((locationString) { - Map locationMap = + return globals.prefs.then((SharedPreferences prefs) { + final List? yourLocations = prefs.getStringList(locationKey); + final List persistedList = []; + for (final String locationString in (yourLocations ?? [])) { + final Map locationMap = json.decode(locationString) as Map; persistedList.add(YourLocation.fromJson(locationMap)); - }); + } return persistedList; }); } -persistYourLocations(List yl) { +void persistYourLocations(List yl) { // debugPrint('Persisting $yl'); - globals.prefs.then((prefs) { - List ylAsString = []; - yl.where(notNull).toList().forEach((location) { + globals.prefs.then((SharedPreferences prefs) { + final List ylAsString = []; + yl.where(notNull).toList().forEach((YourLocation location) { ylAsString.add(json.encode(location.toJson())); }); prefs.setStringList(locationKey, ylAsString); diff --git a/lib/monitoredAreas.dart b/lib/monitoredAreas.dart index cd9d0a0..15120e1 100644 --- a/lib/monitoredAreas.dart +++ b/lib/monitoredAreas.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_redux/flutter_redux.dart'; import 'package:latlong2/latlong.dart'; +import 'package:redux/src/store.dart'; import 'colors.dart'; import 'compassMapPlugin.dart'; @@ -9,12 +10,11 @@ import 'customBottomAppBar.dart'; import 'generated/i18n.dart'; import 'mainDrawer.dart'; import 'models/appState.dart'; -import 'firesSpinner.dart'; class _ViewModel { - List monitoredAreas; _ViewModel(this.monitoredAreas); + List monitoredAreas; @override bool operator ==(Object other) => @@ -28,65 +28,63 @@ class _ViewModel { } class MonitoredAreasPage extends StatelessWidget { - static const String routeName = "monitoredAreasMap"; + const MonitoredAreasPage({super.key}); + + static const String routeName = 'monitoredAreasMap'; @override Widget build(BuildContext context) { - return new StoreConnector( + return StoreConnector( distinct: true, - converter: (store) { - return new _ViewModel(store.state.monitoredAreas); + converter: (Store store) { + return _ViewModel(store.state.monitoredAreas); }, - builder: (context, view) { - return new Scaffold( + builder: (BuildContext context, _ViewModel view) { + return Scaffold( appBar: - new AppBar(title: new Text(S.of(context).monitoredAreasTitle)), - drawer: new MainDrawer(context, MonitoredAreasPage.routeName), - bottomNavigationBar: new CustomBottomAppBar( + AppBar(title: Text(S.of(context).monitoredAreasTitle)), + drawer: MainDrawer(context, MonitoredAreasPage.routeName), + bottomNavigationBar: CustomBottomAppBar( fabLocation: FloatingActionButtonLocation.centerDocked, showNotch: true, color: fires100, - mainAxisAlignment: MainAxisAlignment.center, actions: [ - new Flexible( - child: new Padding( - padding: new EdgeInsets.only(left: 10.0, right: 10.0), - child: new Column( + Flexible( + child: Padding( + padding: const EdgeInsets.only(left: 10.0, right: 10.0), + child: Column( mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - new Text(S.of(context).mapPrivacy, - style: new TextStyle( + children: [ + Text(S.of(context).mapPrivacy, + style: const TextStyle( fontStyle: FontStyle.italic, color: Colors.black38)) ]))) ]), - body: !(view.monitoredAreas is List) - ? new FiresSpinner() - : new Padding( - padding: new EdgeInsets.all(10.0), - child: new Column( - children: [ - new Padding( - padding: new EdgeInsets.only( - top: 8.0, bottom: 8.0, left: 0.0, right: 0.0), - child: new Text(S.of(context).inGreenMonitoredAreas), + body: Padding( + padding: const EdgeInsets.all(10.0), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.only( + top: 8.0, bottom: 8.0), + child: Text(S.of(context).inGreenMonitoredAreas), ), - new Flexible( - child: new FlutterMap( - options: new MapOptions( - initialCenter: new LatLng(53.5775, 3.106111), + Flexible( + child: FlutterMap( + options: const MapOptions( + initialCenter: LatLng(53.5775, 3.106111), initialZoom: 1.0, ), - children: [ - new TileLayer( + children: [ + TileLayer( urlTemplate: - "https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png", - subdomains: ['a', 'b', 'c', 'd'], + 'https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png', + subdomains: const ['a', 'b', 'c', 'd'], userAgentPackageName: 'com.example.fires_flutter'), - new CompassMapPluginWidget(), - new PolylineLayer( + const CompassMapPluginWidget(), + PolylineLayer( polylines: view.monitoredAreas, ) ], diff --git a/lib/objectIdUtils.dart b/lib/objectIdUtils.dart index c574356..4a1d081 100644 --- a/lib/objectIdUtils.dart +++ b/lib/objectIdUtils.dart @@ -1,10 +1,10 @@ import 'package:objectid/objectid.dart'; ObjectId objectIdFromJson(String json) { - return new ObjectId.fromHexString( - json.replaceFirst("ObjectId(", "").replaceAll(")", "")); + return ObjectId.fromHexString( + json.replaceFirst('ObjectId(', '').replaceAll(')', '')); } -objectIdToJson(ObjectId o) { +String objectIdToJson(ObjectId o) { return o.toString(); } diff --git a/lib/placesAutocompleteUtils.dart b/lib/placesAutocompleteUtils.dart index 78148af..3368c34 100644 --- a/lib/placesAutocompleteUtils.dart +++ b/lib/placesAutocompleteUtils.dart @@ -1,8 +1,9 @@ import 'dart:async'; -import 'package:fires_flutter/models/yourLocation.dart'; import 'package:flutter/material.dart'; +import 'models/yourLocation.dart'; + /// Open a places dialog for selecting a location. /// Currently returns a default location as the google_places_autocomplete package @@ -10,9 +11,9 @@ import 'package:flutter/material.dart'; /// TODO: Implement with a modern null-safe places API integration Future openPlacesDialog(GlobalKey sc) async { // Show a snackbar informing the user that this feature is not yet available - final messenger = ScaffoldMessenger.of(sc.currentContext!); + final ScaffoldMessengerState messenger = ScaffoldMessenger.of(sc.currentContext!); messenger.showSnackBar( - SnackBar( + const SnackBar( content: Text( 'Place selection is currently unavailable. Please use manual location entry.'), ), diff --git a/lib/privacyPage.dart b/lib/privacyPage.dart index acc9501..a7daf41 100644 --- a/lib/privacyPage.dart +++ b/lib/privacyPage.dart @@ -5,9 +5,8 @@ import 'generated/i18n.dart'; import 'markdownPage.dart'; class PrivacyPage extends MarkdownPage { - static const String routeName = '/privacy'; - PrivacyPage(BuildContext context) + PrivacyPage(BuildContext context, {super.key}) : super( title: S.of(context).privacyPolicy, route: routeName, @@ -16,4 +15,5 @@ class PrivacyPage extends MarkdownPage { fileName: 'privacy', ext: 'md', lang: Localizations.localeOf(context).languageCode)); + static const String routeName = '/privacy'; } diff --git a/lib/redux/actions.dart b/lib/redux/actions.dart index e689e7f..71165d3 100644 --- a/lib/redux/actions.dart +++ b/lib/redux/actions.dart @@ -1,4 +1,4 @@ export 'appActions.dart'; -export 'yourLocationActions.dart'; export 'fireMapActions.dart'; -export 'fireNotificationActions.dart'; \ No newline at end of file +export 'fireNotificationActions.dart'; +export 'yourLocationActions.dart'; \ No newline at end of file diff --git a/lib/redux/appActions.dart b/lib/redux/appActions.dart index 5c8e006..0b2d26c 100644 --- a/lib/redux/appActions.dart +++ b/lib/redux/appActions.dart @@ -1,23 +1,25 @@ -import 'package:fires_flutter/models/yourLocation.dart'; -import 'package:fires_flutter/models/fireNotification.dart'; import 'dart:async'; -import 'package:flutter_map/flutter_map.dart'; + import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:flutter_map/flutter_map.dart'; + +import '../models/fireNotification.dart'; +import '../models/yourLocation.dart'; abstract class AppActions {} class FetchYourLocationsAction extends AppActions { - Completer? refreshCallback; FetchYourLocationsAction([this.refreshCallback]); + Completer? refreshCallback; } class PersistAppStateAction extends AppActions {} class FetchYourLocationsSucceededAction extends AppActions { - final List fetchedYourLocations; FetchYourLocationsSucceededAction(this.fetchedYourLocations); + final List fetchedYourLocations; } class FetchFireNotificationsAction extends AppActions {} @@ -25,45 +27,45 @@ class FetchFireNotificationsAction extends AppActions {} class FetchMonitoredAreasAction extends AppActions {} class FetchYourLocationsFailedAction extends AppActions { - final Exception error; FetchYourLocationsFailedAction(this.error); + final Exception error; } class OnUserTokenAction extends AppActions { - final String token; OnUserTokenAction(this.token); + final String token; } class OnUserCreatedAction extends AppActions { - final String userId; OnUserCreatedAction(this.userId); + final String userId; } class OnUserLangAction extends AppActions { - final String lang; OnUserLangAction(this.lang); + final String lang; } class FetchFireNotificationsSucceededAction extends AppActions { - final List fetchedFireNotifications; - final int unreadCount; FetchFireNotificationsSucceededAction( this.fetchedFireNotifications, this.unreadCount); + final List fetchedFireNotifications; + final int unreadCount; } class FetchMonitoredAreasSucceededAction extends AppActions { - final List monitoredAreas; FetchMonitoredAreasSucceededAction(this.monitoredAreas); + final List monitoredAreas; } class OnConnectivityChanged extends AppActions { - final List connectionStatus; OnConnectivityChanged(this.connectionStatus); + final List connectionStatus; } diff --git a/lib/redux/appReducer.dart b/lib/redux/appReducer.dart index 65a1add..fdc5932 100644 --- a/lib/redux/appReducer.dart +++ b/lib/redux/appReducer.dart @@ -1,5 +1,5 @@ -import 'actions.dart'; import '../models/appState.dart'; +import 'actions.dart'; AppState appReducer(AppState state, action) { if (action is FetchYourLocationsSucceededAction) { diff --git a/lib/redux/fetchDataMiddleware.dart b/lib/redux/fetchDataMiddleware.dart index 581ea51..08df9c9 100644 --- a/lib/redux/fetchDataMiddleware.dart +++ b/lib/redux/fetchDataMiddleware.dart @@ -1,14 +1,15 @@ import 'dart:async'; -import 'package:fires_flutter/models/fireNotification.dart'; -import 'package:fires_flutter/models/yourLocation.dart'; +import 'package:flutter_map/src/layer/polyline_layer.dart'; import 'package:get_it/get_it.dart'; import 'package:objectid/objectid.dart'; import 'package:redux/redux.dart'; import '../models/appState.dart'; +import '../models/fireNotification.dart'; import '../models/fireNotificationsPersist.dart'; import '../models/firesApi.dart'; +import '../models/yourLocation.dart'; import '../models/yourLocationPersist.dart'; import '../objectIdUtils.dart'; import 'actions.dart'; @@ -55,20 +56,20 @@ void fetchDataMiddleware(Store store, action, NextDispatcher next) { if (action is AddYourLocationAction) { if (action.loc.subscribed) { - subscribeViaApi(store, action.loc, (sub) { - store.dispatch(new AddedYourLocationAction(sub)); + subscribeViaApi(store, action.loc, (YourLocation sub) { + store.dispatch(AddedYourLocationAction(sub)); persistYourLocations(store.state.yourLocations); }); } else { // No subscribed (only local) - store.dispatch(new AddedYourLocationAction(action.loc)); + store.dispatch(AddedYourLocationAction(action.loc)); persistYourLocations(store.state.yourLocations); } getFiresStatsInLocation(store, action.loc); } if (action is DeleteYourLocationAction) { - store.dispatch(new DeletedYourLocationAction(action.loc.id)); + store.dispatch(DeletedYourLocationAction(action.loc.id)); if (action.loc.subscribed) { unsubsViaApi(store, action.loc.id, () { persistYourLocations(store.state.yourLocations); @@ -79,17 +80,17 @@ void fetchDataMiddleware(Store store, action, NextDispatcher next) { } if (action is DeleteFireNotificationAction) { - store.dispatch(new DeletedFireNotificationAction(action.notif)); + store.dispatch(DeletedFireNotificationAction(action.notif)); persistFireNotifications(store.state.fireNotifications); } if (action is DeleteAllFireNotificationAction) { - store.dispatch(new DeletedAllFireNotificationAction()); + store.dispatch(DeletedAllFireNotificationAction()); persistFireNotifications(store.state.fireNotifications); } if (action is AddFireNotificationAction) { - store.dispatch(new AddedFireNotificationAction(action.notif)); + store.dispatch(AddedFireNotificationAction(action.notif)); persistFireNotifications(store.state.fireNotifications); } @@ -104,28 +105,28 @@ void fetchDataMiddleware(Store store, action, NextDispatcher next) { if (action is UpdateYourLocationAction) { if (action.loc.subscribed) debounceLocationUpdate( - Duration(seconds: 2), + const Duration(seconds: 2), () => api .getFiresInLocation( state: store.state, lat: action.loc.lat, lon: action.loc.lon, distance: action.loc.distance) - .then((result) => store.dispatch(result))); - store.dispatch(new UpdatedYourLocationAction(action.loc)); + .then((UpdateFireMapStatsAction result) => store.dispatch(result))); + store.dispatch(UpdatedYourLocationAction(action.loc)); persistYourLocations(store.state.yourLocations); } if (action is SubscribeConfirmAction) { - subscribeViaApi(store, action.loc, (sub) { - store.dispatch(new UpdateYourLocationAction(action.loc)); + subscribeViaApi(store, action.loc, (YourLocation sub) { + store.dispatch(UpdateYourLocationAction(action.loc)); persistYourLocations(store.state.yourLocations); }); } if (action is UnSubscribeAction) { unsubsViaApi(store, action.loc.id, () { - store.dispatch(new UpdateYourLocationAction(action.loc)); + store.dispatch(UpdateYourLocationAction(action.loc)); persistYourLocations(store.state.yourLocations); }); } @@ -133,21 +134,21 @@ void fetchDataMiddleware(Store store, action, NextDispatcher next) { if (action is ToggleSubscriptionAction) { if (action.loc.subscribed) { subscribeViaApi(store, action.loc, - (sub) => store.dispatch(new ToggledSubscriptionAction(sub))); + (YourLocation sub) => store.dispatch(ToggledSubscriptionAction(sub))); } else { unsubsViaApi(store, action.loc.id, - () => store.dispatch(new ToggledSubscriptionAction(action.loc))); + () => store.dispatch(ToggledSubscriptionAction(action.loc))); } } if (action is ReadFireNotificationAction) { - store.dispatch(new ReadedFireNotificationAction(action.notif)); + store.dispatch(ReadedFireNotificationAction(action.notif)); persistFireNotifications(store.state.fireNotifications); } if (action is FetchYourLocationsAction) { // Use the api to fetch the YourLocations - loadYourLocations().then((localLocations) { + loadYourLocations().then((List localLocations) { api .fetchYourLocations(store.state) .then((List subscribedLocations) { @@ -155,61 +156,63 @@ void fetchDataMiddleware(Store store, action, NextDispatcher next) { // Our reducer will then update the State using these YourLocations. print('Subscribed to: ${subscribedLocations.length}'); // unsubscribe all locally to sync the subs state - localLocations.forEach((location) => location.subscribed = false); + for (final YourLocation location in localLocations) { + location.subscribed = false; + } print('Local persisted: ${localLocations.length}'); - subscribedLocations.forEach((subsLoc) { - var locSubs = localLocations.firstWhere( - (localLocation) => localLocation.id == subsLoc.id, orElse: () { + for (final YourLocation subsLoc in subscribedLocations) { + final YourLocation locSubs = localLocations.firstWhere( + (YourLocation localLocation) => localLocation.id == subsLoc.id, orElse: () { localLocations.add(subsLoc); return subsLoc; }); locSubs.subscribed = true; - }); + } - store.dispatch(new FetchYourLocationsSucceededAction(localLocations)); + store.dispatch(FetchYourLocationsSucceededAction(localLocations)); persistYourLocations(localLocations); - localLocations.forEach((yl) { + for (final YourLocation yl in localLocations) { api .getFiresInLocation( state: store.state, lat: yl.lat, lon: yl.lon, distance: yl.distance) - .then((value) { + .then((UpdateFireMapStatsAction value) { yl.currentNumFires = value.numFires; - store.dispatch(new UpdateYourLocationAction(yl)); + store.dispatch(UpdateYourLocationAction(yl)); }); - }); + } - final Completer? completer = action.refreshCallback; + final Completer? completer = action.refreshCallback; completer?.complete(null); }); }).catchError((Exception onError) { // If it fails, dispatch a failure action. The reducer will // update the state with the error. - store.dispatch(new FetchYourLocationsFailedAction(onError)); + store.dispatch(FetchYourLocationsFailedAction(onError)); }); } if (action is FetchFireNotificationsAction) { - loadFireNotifications().then((fireNotifications) { + loadFireNotifications().then((List fireNotifications) { int unread = 0; - for (FireNotification notif in fireNotifications) { + for (final FireNotification notif in fireNotifications) { if (!notif.read) { unread++; } } store.dispatch( - new FetchFireNotificationsSucceededAction(fireNotifications, unread)); + FetchFireNotificationsSucceededAction(fireNotifications, unread)); persistFireNotifications(fireNotifications); }); } if (action is FetchMonitoredAreasAction) { - api.getMonitoredAreas(state: store.state).then((result) { + api.getMonitoredAreas(state: store.state).then((List result) { // store.dispatch() - store.dispatch(new FetchMonitoredAreasSucceededAction(result)); + store.dispatch(FetchMonitoredAreasSucceededAction(result)); }); } @@ -217,11 +220,11 @@ void fetchDataMiddleware(Store store, action, NextDispatcher next) { api .markFalsePositive(store.state, store.state.user.token, action.notif.sealed, action.type) - .then((result) { + .then((bool result) { if (result) { // Not necessary getFiresStatsInFire(store, action.notif); - store.dispatch(new UpdatedFireNotificationAction(action.notif)); + store.dispatch(UpdatedFireNotificationAction(action.notif)); } }); } @@ -237,10 +240,10 @@ void getFiresStatsInLocation(Store store, YourLocation loc) { lat: loc.lat, lon: loc.lon, distance: loc.distance) - .then((result) { + .then((UpdateFireMapStatsAction result) { store.dispatch(result); loc.currentNumFires = result.numFires; - store.dispatch(new UpdateYourLocationAction(loc)); + store.dispatch(UpdateYourLocationAction(loc)); }); } @@ -251,11 +254,11 @@ void getFiresStatsInFire(Store store, FireNotification notif) { lat: notif.lat, lon: notif.lon, distance: 1) // FalsePositive/server/publications.js - .then((result) => store.dispatch(result)); + .then((UpdateFireMapStatsAction result) => store.dispatch(result)); } void unsubsViaApi(Store store, ObjectId id, onUnsubs) { - api.unsubscribe(store.state, id.hexString).then((res) { + api.unsubscribe(store.state, id.hexString).then((bool res) { onUnsubs(); persistYourLocations(store.state.yourLocations); }); @@ -263,8 +266,8 @@ void unsubsViaApi(Store store, ObjectId id, onUnsubs) { void subscribeViaApi( Store store, YourLocation loc, Function(YourLocation) onSubs) { - api.subscribe(store.state, loc).then((subsId) { - YourLocation sub = loc; + api.subscribe(store.state, loc).then((String subsId) { + final YourLocation sub = loc; // if (loc.id != subsId) { sub.id = objectIdFromJson(subsId); // } @@ -274,12 +277,12 @@ void subscribeViaApi( } void createUser(Store store, String lang, String token) { - assert(token != null, "User lang is null"); - assert(token != null, "User mobile token is null"); - api.createUser(store.state, token, lang).then((userId) { - store.dispatch(new OnUserCreatedAction(userId)); - store.dispatch(new FetchYourLocationsAction()); - store.dispatch(new FetchFireNotificationsAction()); - store.dispatch(new FetchMonitoredAreasAction()); + assert(token != null, 'User lang is null'); + assert(token != null, 'User mobile token is null'); + api.createUser(store.state, token, lang).then((String userId) { + store.dispatch(OnUserCreatedAction(userId)); + store.dispatch(FetchYourLocationsAction()); + store.dispatch(FetchFireNotificationsAction()); + store.dispatch(FetchMonitoredAreasAction()); }); } diff --git a/lib/redux/fireMapActions.dart b/lib/redux/fireMapActions.dart index 4cd409d..49590de 100644 --- a/lib/redux/fireMapActions.dart +++ b/lib/redux/fireMapActions.dart @@ -1,61 +1,61 @@ -import 'package:fires_flutter/models/yourLocation.dart'; -import 'package:fires_flutter/models/fireNotification.dart'; -import 'package:fires_flutter/models/fireMapState.dart'; +import '../models/fireMapState.dart'; +import '../models/fireNotification.dart'; +import '../models/yourLocation.dart'; abstract class FiresMapActions {} class UpdateYourLocationMapAction extends FiresMapActions { - final YourLocation loc; UpdateYourLocationMapAction( this.loc, ); + final YourLocation loc; } class UpdateFireMapStatsAction extends FiresMapActions { - int numFires; - List fires = []; - List falsePos = []; - List industries = []; UpdateFireMapStatsAction( {required this.numFires, required this.fires, required this.falsePos, required this.industries}); + int numFires; + List fires = []; + List falsePos = []; + List industries = []; } class ShowYourLocationMapAction extends FiresMapActions { - YourLocation loc; ShowYourLocationMapAction(this.loc); + YourLocation loc; } class ShowFireNotificationMapAction extends FiresMapActions { - FireNotification notif; ShowFireNotificationMapAction(this.notif); + FireNotification notif; } class EditYourLocationAction extends FiresMapActions { - YourLocation loc; EditYourLocationAction(this.loc); + YourLocation loc; } class EditConfirmYourLocationAction extends FiresMapActions { - YourLocation loc; EditConfirmYourLocationAction(this.loc); + YourLocation loc; } class EditCancelYourLocationAction extends FiresMapActions { - YourLocation loc; EditCancelYourLocationAction(this.loc); + YourLocation loc; } class SelectMapLayerAction extends FiresMapActions { - final FireMapLayer layer; SelectMapLayerAction(this.layer); + final FireMapLayer layer; } diff --git a/lib/redux/fireMapReducer.dart b/lib/redux/fireMapReducer.dart index 0b2590b..d5754cd 100644 --- a/lib/redux/fireMapReducer.dart +++ b/lib/redux/fireMapReducer.dart @@ -1,28 +1,29 @@ -import 'package:redux/redux.dart'; -import 'package:fires_flutter/models/yourLocation.dart'; import 'package:objectid/objectid.dart'; +import 'package:redux/redux.dart'; + import '../models/fireMapState.dart'; +import '../models/yourLocation.dart'; import 'actions.dart'; -final fireMapReducer = combineReducers([ - new TypedReducer( +final Reducer fireMapReducer = combineReducers(>[ + TypedReducer( _showYourLocationMap), - new TypedReducer( + TypedReducer( _showFireNotificationMap), - new TypedReducer( + TypedReducer( _updateYourLocationMapStats), - new TypedReducer(_subscribeYourLocationMap), - new TypedReducer( + TypedReducer(_subscribeYourLocationMap), + TypedReducer( _subscribeConfirmYourLocationMap), - new TypedReducer( + TypedReducer( _unsubscribeYourLocationMap), - new TypedReducer(_editYourLocationMap), - new TypedReducer( + TypedReducer(_editYourLocationMap), + TypedReducer( _editConfirmYourLocationMap), - new TypedReducer( + TypedReducer( _editCancelYourLocationMap), - new TypedReducer(_selectMapLayer), - new TypedReducer( + TypedReducer(_selectMapLayer), + TypedReducer( _updateYourLocationMap) ]); @@ -46,14 +47,13 @@ FireMapState _showYourLocationMap( status: action.loc.subscribed ? FireMapStatus.unsubscribe : FireMapStatus.view, - yourLocation: action.loc, - fireNotication: null); + yourLocation: action.loc); } FireMapState _showFireNotificationMap( FireMapState state, ShowFireNotificationMapAction action) { // TODO: use here you real location? - YourLocation pseudoLoc = new YourLocation( + final YourLocation pseudoLoc = YourLocation( id: ObjectId(), lat: action.notif.lat, lon: action.notif.lon, diff --git a/lib/redux/fireNotificationActions.dart b/lib/redux/fireNotificationActions.dart index 32dc82f..d464361 100644 --- a/lib/redux/fireNotificationActions.dart +++ b/lib/redux/fireNotificationActions.dart @@ -1,12 +1,12 @@ -import 'package:fires_flutter/models/fireNotification.dart'; import '../models/falsePositiveTypes.dart'; +import '../models/fireNotification.dart'; abstract class FireNotificationActions {} class DeleteFireNotificationAction extends FireNotificationActions { - final FireNotification notif; DeleteFireNotificationAction(this.notif); + final FireNotification notif; } class DeleteAllFireNotificationAction extends FireNotificationActions { @@ -14,15 +14,15 @@ class DeleteAllFireNotificationAction extends FireNotificationActions { } class AddFireNotificationAction extends FireNotificationActions { - final FireNotification notif; AddFireNotificationAction(this.notif); + final FireNotification notif; } class DeletedFireNotificationAction extends FireNotificationActions { - final FireNotification notif; DeletedFireNotificationAction(this.notif); + final FireNotification notif; } class DeletedAllFireNotificationAction extends FireNotificationActions { @@ -30,32 +30,32 @@ class DeletedAllFireNotificationAction extends FireNotificationActions { } class AddedFireNotificationAction extends FireNotificationActions { - final FireNotification notif; AddedFireNotificationAction(this.notif); + final FireNotification notif; } class ReadFireNotificationAction extends FireNotificationActions { - final FireNotification notif; ReadFireNotificationAction(this.notif); + final FireNotification notif; } class ReadedFireNotificationAction extends FireNotificationActions { - final FireNotification notif; ReadedFireNotificationAction(this.notif); + final FireNotification notif; } class MarkFireAsFalsePositiveAction extends FireNotificationActions { - final FireNotification notif; - final FalsePositiveType type; MarkFireAsFalsePositiveAction(this.notif, this.type); + final FireNotification notif; + final FalsePositiveType type; } class UpdatedFireNotificationAction extends FireNotificationActions { - final FireNotification notif; UpdatedFireNotificationAction(this.notif); + final FireNotification notif; } \ No newline at end of file diff --git a/lib/redux/fireNotificationReducer.dart b/lib/redux/fireNotificationReducer.dart index 3e62815..13c2ea1 100644 --- a/lib/redux/fireNotificationReducer.dart +++ b/lib/redux/fireNotificationReducer.dart @@ -1,44 +1,44 @@ -import 'package:fires_flutter/models/fireNotification.dart'; import 'package:redux/redux.dart'; +import '../models/fireNotification.dart'; import 'actions.dart'; -final fireNotificationReducer = combineReducers>([ - new TypedReducer, AddedFireNotificationAction>( +final Reducer> fireNotificationReducer = combineReducers>(>>[ + TypedReducer, AddedFireNotificationAction>( _addedFireNotification), - new TypedReducer, DeletedFireNotificationAction>( + TypedReducer, DeletedFireNotificationAction>( _deletedFireNotification), - new TypedReducer, UpdatedFireNotificationAction>( + TypedReducer, UpdatedFireNotificationAction>( _updatedFireNotification), - new TypedReducer, DeletedAllFireNotificationAction>( + TypedReducer, DeletedAllFireNotificationAction>( _deletedAllFireNotifications), - new TypedReducer, ReadedFireNotificationAction>( + TypedReducer, ReadedFireNotificationAction>( _readedFireNotification) ]); List _addedFireNotification( List notifications, AddedFireNotificationAction action) { - return new List.from(notifications)..insert(0, action.notif); + return List.from(notifications)..insert(0, action.notif); } List _deletedFireNotification( List notifications, DeletedFireNotificationAction action) { - return new List.from(notifications)..remove(action.notif); + return List.from(notifications)..remove(action.notif); } List _updatedFireNotification( List notifications, UpdatedFireNotificationAction action) { return notifications - .map((notif) => notif.id == action.notif.id ? action.notif : notif) + .map((FireNotification notif) => notif.id == action.notif.id ? action.notif : notif) .toList(); } List _readedFireNotification( List notifications, ReadedFireNotificationAction action) { return notifications - .map((yourLocation) => + .map((FireNotification yourLocation) => yourLocation.id == action.notif.id ? action.notif : yourLocation) .toList(); } @@ -46,5 +46,5 @@ List _readedFireNotification( List _deletedAllFireNotifications( List notifications, DeletedAllFireNotificationAction action) { - return []; + return []; } diff --git a/lib/redux/reducers.dart b/lib/redux/reducers.dart index c867825..1ef096b 100644 --- a/lib/redux/reducers.dart +++ b/lib/redux/reducers.dart @@ -10,7 +10,7 @@ import 'yourLocationsReducer.dart'; // We create the State reducer by combining many smaller reducers into one! AppState appStateReducer(AppState prevState, action) { - var state = appReducer(prevState, action); + final AppState state = appReducer(prevState, action); return AppState( yourLocations: yourLocationsReducer(state.yourLocations, action), fireNotifications: diff --git a/lib/redux/userReducer.dart b/lib/redux/userReducer.dart index 61c2b3a..f9dc707 100644 --- a/lib/redux/userReducer.dart +++ b/lib/redux/userReducer.dart @@ -1,5 +1,5 @@ -import 'actions.dart'; import '../models/user.dart'; +import 'actions.dart'; User userReducer(User user, action) { if (action is OnUserCreatedAction) return user.copyWith(userId: action.userId); diff --git a/lib/redux/yourLocationActions.dart b/lib/redux/yourLocationActions.dart index adcd573..7868e22 100644 --- a/lib/redux/yourLocationActions.dart +++ b/lib/redux/yourLocationActions.dart @@ -1,66 +1,67 @@ -import 'package:fires_flutter/models/yourLocation.dart'; import 'package:objectid/objectid.dart'; +import '../models/yourLocation.dart'; + abstract class YourLocationActions {} class AddYourLocationAction extends YourLocationActions { - YourLocation loc; AddYourLocationAction(this.loc); + YourLocation loc; } class AddedYourLocationAction extends YourLocationActions { - YourLocation loc; AddedYourLocationAction(this.loc); + YourLocation loc; } class DeleteYourLocationAction extends YourLocationActions { - YourLocation loc; DeleteYourLocationAction(this.loc); + YourLocation loc; } class DeletedYourLocationAction extends YourLocationActions { - ObjectId id; DeletedYourLocationAction(this.id); + ObjectId id; } class UpdateYourLocationAction extends YourLocationActions { - YourLocation loc; UpdateYourLocationAction(this.loc); + YourLocation loc; } class UpdatedYourLocationAction extends YourLocationActions { - YourLocation loc; UpdatedYourLocationAction(this.loc); + YourLocation loc; } class ToggleSubscriptionAction extends YourLocationActions { - YourLocation loc; ToggleSubscriptionAction(this.loc); + YourLocation loc; } class ToggledSubscriptionAction extends YourLocationActions { - YourLocation loc; ToggledSubscriptionAction(this.loc); + YourLocation loc; } class SubscribeAction extends YourLocationActions {} class SubscribeConfirmAction extends YourLocationActions { - YourLocation loc; SubscribeConfirmAction(this.loc); + YourLocation loc; } class UnSubscribeAction extends YourLocationActions { - YourLocation loc; UnSubscribeAction(this.loc); + YourLocation loc; } diff --git a/lib/redux/yourLocationsReducer.dart b/lib/redux/yourLocationsReducer.dart index aa45662..75db529 100644 --- a/lib/redux/yourLocationsReducer.dart +++ b/lib/redux/yourLocationsReducer.dart @@ -1,35 +1,35 @@ -import 'package:fires_flutter/models/yourLocation.dart'; import 'package:redux/redux.dart'; +import '../models/yourLocation.dart'; import 'actions.dart'; -final yourLocationsReducer = combineReducers>([ - new TypedReducer, AddedYourLocationAction>( +final Reducer> yourLocationsReducer = combineReducers>(>>[ + TypedReducer, AddedYourLocationAction>( _addedYourLocation), - new TypedReducer, DeletedYourLocationAction>( + TypedReducer, DeletedYourLocationAction>( _deletedYourLocation), - new TypedReducer, UpdatedYourLocationAction>( + TypedReducer, UpdatedYourLocationAction>( _updatedYourLocation), - new TypedReducer, ToggledSubscriptionAction>( + TypedReducer, ToggledSubscriptionAction>( _toggledSubscriptionAction) ]); List _addedYourLocation( List yourLocations, AddedYourLocationAction action) { - return new List.from(yourLocations)..add(action.loc); + return List.from(yourLocations)..add(action.loc); } List _deletedYourLocation( List yourLocations, DeletedYourLocationAction action) { return yourLocations - .where((yourLocation) => yourLocation.id != action.id) + .where((YourLocation yourLocation) => yourLocation.id != action.id) .toList(); } List _updatedYourLocation( List yourLocations, UpdatedYourLocationAction action) { return yourLocations - .map((yourLocation) => + .map((YourLocation yourLocation) => yourLocation.id == action.loc.id ? action.loc : yourLocation) .toList(); } @@ -37,7 +37,7 @@ List _updatedYourLocation( List _toggledSubscriptionAction( List yourLocations, ToggledSubscriptionAction action) { return yourLocations - .map((yourLocation) => + .map((YourLocation yourLocation) => yourLocation.id == action.loc.id ? action.loc : yourLocation) .toList(); } diff --git a/lib/sandbox.dart b/lib/sandbox.dart index 567a7d0..66dae68 100644 --- a/lib/sandbox.dart +++ b/lib/sandbox.dart @@ -7,31 +7,32 @@ new RandomColorBlock( child */ class Sandbox extends StatelessWidget { + const Sandbox({super.key}); + static const String routeName = '/sandbox'; @override Widget build(BuildContext context) { //showDialog(context: context, child: builder(context)); return Scaffold( - appBar: new AppBar( - title: new TextField( + appBar: AppBar( + title: TextField( autofocus: true, - controller: new TextEditingController(text: "kk"), - decoration: new InputDecoration(), - onSubmitted: (todoText) {}, + controller: TextEditingController(text: 'kk'), + onSubmitted: (String todoText) {}, )), - body: new ElevatedButton( - child: new Text('Press'), + body: ElevatedButton( + child: const Text('Press'), onPressed: () { showDialog( context: context, - builder: (context) { - return new SimpleDialog( - title: new Text('title'), + builder: (BuildContext context) { + return const SimpleDialog( + title: Text('title'), children: [ - new Padding( - padding: const EdgeInsets.only(left: 16.0), - child: new Text('hhh'), + Padding( + padding: EdgeInsets.only(left: 16.0), + child: Text('hhh'), ), ]); }); diff --git a/lib/sentryReport.dart b/lib/sentryReport.dart index 6e098e0..7825c4c 100644 --- a/lib/sentryReport.dart +++ b/lib/sentryReport.dart @@ -4,9 +4,9 @@ import 'package:sentry_flutter/sentry_flutter.dart'; import 'globals.dart' as globals; -var useSentry = !globals.isDevelopment; +bool useSentry = !globals.isDevelopment; -Future reportError(dynamic error, dynamic stackTrace) async { +Future reportError(dynamic error, dynamic stackTrace) async { // Print the exception to the console print('Caught error: $error'); diff --git a/lib/slider.dart b/lib/slider.dart index bbac840..30f77df 100644 --- a/lib/slider.dart +++ b/lib/slider.dart @@ -4,61 +4,61 @@ import 'package:flutter/material.dart'; import 'colors.dart'; import 'generated/i18n.dart'; -typedef void SlideCallback(int distance); +typedef SlideCallback = void Function(int distance); class FireDistanceSlider extends StatefulWidget { + + const FireDistanceSlider({super.key, required this.initialValue, required this.onSlide}); final int initialValue; final SlideCallback onSlide; - FireDistanceSlider({required this.initialValue, required this.onSlide}); - @override - _FireDistanceSliderState createState() => new _FireDistanceSliderState( + _FireDistanceSliderState createState() => _FireDistanceSliderState( initialValue: initialValue, onSlide: onSlide); } class _FireDistanceSliderState extends State { + + _FireDistanceSliderState({int initialValue = 10, required this.onSlide}) { + _sliderValue = initialValue; + } late int _sliderValue; final SlideCallback onSlide; - _FireDistanceSliderState({int initialValue = 10, required this.onSlide}) { - this._sliderValue = initialValue; - } - - Widget sizeText(int sliderValue) => new Text( + Widget sizeText(int sliderValue) => Text( S.of(context).subscribeToValueAroundThisArea(sliderValue.toString()), - style: new TextStyle(color: Colors.black87)); + style: const TextStyle(color: Colors.black87)); Widget warningText(int sliderValue) => _sliderValue >= 50 - ? new Text(S.of(context).warningThisIsAVeryLargeArea, - style: new TextStyle(color: fires900)) - : new Text(''); + ? Text(S.of(context).warningThisIsAVeryLargeArea, + style: const TextStyle(color: fires900)) + : const Text(''); @override Widget build(BuildContext context) { - var slider = new Slider( + final Slider slider = Slider( value: _sliderValue + 0.0, activeColor: fires900, inactiveColor: Colors.black45, min: 1.0, max: 100.0, divisions: 99, - label: '${_sliderValue.round()}', + label: '$_sliderValue', onChanged: (double value) { _sliderValue = value.round(); onSlide(_sliderValue); setState(() {}); }, ); - return new Column( + return Column( mainAxisSize: MainAxisSize.min, children: listWithoutNulls([ - new SizedBox(height: 50.0), - new Row(mainAxisSize: MainAxisSize.max, children: [slider]), + const SizedBox(height: 50.0), + Row(children: [slider]), // new SizedBox(height: 5.0), - new Column(children: [ + Column(children: [ sizeText(_sliderValue), - new SizedBox(height: 5.0), + const SizedBox(height: 5.0), warningText(_sliderValue) ]) ]), diff --git a/lib/supportPage.dart b/lib/supportPage.dart index 3f1bb71..7efb02d 100644 --- a/lib/supportPage.dart +++ b/lib/supportPage.dart @@ -1,13 +1,15 @@ import 'package:comunes_flutter/comunes_flutter.dart'; import 'package:flutter/material.dart'; import 'package:in_app_review/in_app_review.dart'; -import 'package:url_launcher/url_launcher.dart'; import 'package:share_plus/share_plus.dart'; +import 'package:url_launcher/url_launcher.dart'; import 'generated/i18n.dart'; import 'mainDrawer.dart'; class SupportPage extends StatefulWidget { + const SupportPage({super.key}); + static const String routeName = '/support'; @override @@ -15,27 +17,27 @@ class SupportPage extends StatefulWidget { } class _SupportPageState extends State { - final GlobalKey _scaffoldKey = new GlobalKey(); + final GlobalKey _scaffoldKey = GlobalKey(); Widget buildSupportButton() { - return new Align( + return Align( alignment: const Alignment(0.0, -0.2), - child: new OutlinedButton.icon( + child: OutlinedButton.icon( icon: const Icon(Icons.favorite_border), - label: new Text(S.of(context).comunesSupportBtn), + label: Text(S.of(context).comunesSupportBtn), onPressed: () { - launch("https://comunes.org/"); + launch('https://comunes.org/'); }, ), ); } Widget buildShareButton() { - return new Align( + return Align( alignment: const Alignment(0.0, -0.2), - child: new OutlinedButton.icon( + child: OutlinedButton.icon( icon: const Icon(Icons.share), - label: new Text(S.of(context).shareAppBtn), + label: Text(S.of(context).shareAppBtn), onPressed: () { Share.shareWithResult( 'https://play.google.com/store/apps/details?id=org.comunes.fires'); @@ -45,11 +47,11 @@ class _SupportPageState extends State { } Widget buildStarButton() { - return new Align( + return Align( alignment: const Alignment(0.0, -0.2), - child: new OutlinedButton.icon( + child: OutlinedButton.icon( icon: const Icon(Icons.star_border), - label: new Text(S.of(context).starAppBtn), + label: Text(S.of(context).starAppBtn), onPressed: () { InAppReview.instance.requestReview(); }, @@ -58,14 +60,14 @@ class _SupportPageState extends State { } Widget buildTranslateButton() { - return new Align( + return Align( alignment: const Alignment(0.0, -0.2), - child: new OutlinedButton.icon( + child: OutlinedButton.icon( icon: const Icon(Icons.translate), - label: new Text(S.of(context).translateBtn), + label: Text(S.of(context).translateBtn), onPressed: () { launch( - "https://translate.comunes.org/projects/todos-contra-el-fuego/"); + 'https://translate.comunes.org/projects/todos-contra-el-fuego/'); }, ), ); @@ -76,24 +78,24 @@ class _SupportPageState extends State { return Scaffold( key: _scaffoldKey, appBar: - new AppBar(title: new Text(S.of(context).supportThisInitiative)), - drawer: new MainDrawer(context, SupportPage.routeName), + AppBar(title: Text(S.of(context).supportThisInitiative)), + drawer: MainDrawer(context, SupportPage.routeName), body: Padding( padding: const EdgeInsets.all(8.0), - child: new CenteredColumn(children: [ - new Flexible( - child: new CenteredColumn(children: [ - new Text( + child: CenteredColumn(children: [ + Flexible( + child: CenteredColumn(children: [ + Text( S.of(context).supportPageDescription, textAlign: TextAlign.center, ), - new SizedBox(height: 20.0), + const SizedBox(height: 20.0), buildSupportButton(), - new SizedBox(height: 20.0), + const SizedBox(height: 20.0), buildTranslateButton(), - new SizedBox(height: 20.0), + const SizedBox(height: 20.0), buildStarButton(), - new SizedBox(height: 20.0), + const SizedBox(height: 20.0), buildShareButton() ])) ]), diff --git a/lib/theme.dart b/lib/theme.dart index 6e4e57d..9f7f902 100644 --- a/lib/theme.dart +++ b/lib/theme.dart @@ -10,16 +10,14 @@ ThemeData _buildFiresTheme() { primaryColor: fires600, scaffoldBackgroundColor: firesSurfaceWhite, cardColor: firesBackgroundWhite, - textSelectionTheme: TextSelectionThemeData(selectionColor: fires300), - colorScheme: ColorScheme.light( + textSelectionTheme: const TextSelectionThemeData(selectionColor: fires300), + colorScheme: const ColorScheme.light( primary: fires600, secondary: fires900, error: firesErrorRed, surface: firesSurfaceWhite, onSurface: fires900, - onPrimary: Colors.white, onSecondary: Colors.white, - onError: Colors.white, ), //TODO: Add the text themes (103) //TODO: Add the icon themes (103) diff --git a/lib/themeDev.dart b/lib/themeDev.dart index 9e1f390..47a1ac0 100644 --- a/lib/themeDev.dart +++ b/lib/themeDev.dart @@ -10,16 +10,14 @@ ThemeData _buildFiresTheme() { primaryColor: Colors.pink, scaffoldBackgroundColor: firesSurfaceWhite, cardColor: firesBackgroundWhite, - textSelectionTheme: TextSelectionThemeData(selectionColor: fires300), - colorScheme: ColorScheme.light( + textSelectionTheme: const TextSelectionThemeData(selectionColor: fires300), + colorScheme: const ColorScheme.light( primary: Colors.pink, secondary: fires900, error: firesErrorRed, surface: firesSurfaceWhite, onSurface: fires900, - onPrimary: Colors.white, onSecondary: Colors.white, - onError: Colors.white, ), //TODO: Add the text themes (103) //TODO: Add the icon themes (103) diff --git a/lib/zoomMapPlugin.dart b/lib/zoomMapPlugin.dart index 3831906..d1ce3b5 100644 --- a/lib/zoomMapPlugin.dart +++ b/lib/zoomMapPlugin.dart @@ -1,20 +1,23 @@ import 'package:comunes_flutter/comunes_flutter.dart'; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; +import 'package:latlong2/latlong.dart'; /// Zoom control widget for flutter_map v6+ class ZoomMapPluginWidget extends StatelessWidget { + const ZoomMapPluginWidget({super.key}); + @override Widget build(BuildContext context) { return LayoutBuilder( - builder: (context, constraints) => + builder: (BuildContext context, BoxConstraints constraints) => Stack(fit: StackFit.expand, children: [ Positioned( top: constraints.maxHeight - 100, right: 10.0, - child: new CenteredRow( + child: CenteredRow( children: [ - new Column( + Column( children: [ _zoomButton(context, Icons.zoom_in, 1), _zoomButton(context, Icons.zoom_out, -1) @@ -27,12 +30,12 @@ class ZoomMapPluginWidget extends StatelessWidget { } IconButton _zoomButton(BuildContext context, IconData zoomIcon, int inc) { - return new IconButton( + return IconButton( icon: Icon(zoomIcon), onPressed: () { - final controller = MapController.of(context); - var currentZoom = controller.camera.zoom; - var currentCenter = controller.camera.center; + final MapController controller = MapController.of(context); + final double currentZoom = controller.camera.zoom; + final LatLng currentCenter = controller.camera.center; controller.move(currentCenter, currentZoom + inc); }); } diff --git a/test/file_test.dart b/test/file_test.dart index f584d1b..17bd80a 100644 --- a/test/file_test.dart +++ b/test/file_test.dart @@ -1,29 +1,29 @@ -import 'package:test/test.dart'; import 'package:fires_flutter/fileUtils.dart'; +import 'package:test/test.dart'; void main() { test('test es-ES fallback', () { - String answer = getFallbackLang('es-ES'); + final String answer = getFallbackLang('es-ES'); expect(answer, 'es'); }); test('test en-GB fallback', () { - String answer = getFallbackLang('en-GB'); + final String answer = getFallbackLang('en-GB'); expect(answer, 'en'); }); test('test gl fallback', () { - String answer = getFallbackLang('gl'); + final String answer = getFallbackLang('gl'); expect(answer, 'es'); }); test('test fr fallback', () { - String answer = getFallbackLang('fr'); + final String answer = getFallbackLang('fr'); expect(answer, 'en'); }); test('test privacy English md page', () async { - String answer = await getFileNameOfLang(dir: 'assets/pages', fileName: 'privacy', ext: 'md', lang: 'en'); + final String answer = await getFileNameOfLang(dir: 'assets/pages', fileName: 'privacy', ext: 'md', lang: 'en'); expect(answer, 'assets/pages/privacy-en.md'); });