YourLocation and some persist work

This commit is contained in:
vjrj 2018-06-27 08:22:00 +02:00
parent 6488957148
commit c9941429f0
13 changed files with 240 additions and 85 deletions

View file

@ -13,3 +13,4 @@ For help getting started with Flutter, view our online
- private keys in assets - private keys in assets
flutter pub pub run intl_translation:extract_to_arb --output-dir=lib/l10n lib/i18n.dart flutter pub pub run intl_translation:extract_to_arb --output-dir=lib/l10n lib/i18n.dart
flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/i18n.dart lib/l10n/intl_*.arb flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/i18n.dart lib/l10n/intl_*.arb
flutter packages pub run build_runner build

28
build.yaml Normal file
View file

@ -0,0 +1,28 @@
targets:
$default:
builders:
json_serializable:
options:
# Specifies a string to add to the top of every generated file.
#
# If not specified, the default is the value of `defaultFileHeader`
# defined in `package:source_gen/source_gen.dart`.
#
# Note: use `|` to define a multi-line block.
header: |
// Copyright (c) 2018, Comunes Association.
// GENERATED CODE - DO NOT MODIFY BY HAND
# Options configure how source code is generated for every
# `@JsonSerializable`-annotated class in the package.
#
# The default value for each of them: `false`.
#
# For usage information, reference the corresponding field in
# `JsonSerializableGenerator`.
use_wrappers: true
any_map: true
checked: true
explicit_to_json: true
generate_to_json_function: true

View file

@ -4,8 +4,6 @@ import 'package:comunes_flutter/comunes_flutter.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_fab_dialer/flutter_fab_dialer.dart'; import 'package:flutter_fab_dialer/flutter_fab_dialer.dart';
import 'basicLocation.dart';
import 'basicLocationPersist.dart';
import 'colors.dart'; import 'colors.dart';
import 'generated/i18n.dart'; import 'generated/i18n.dart';
import 'genericMap.dart'; import 'genericMap.dart';
@ -14,6 +12,7 @@ import 'globals.dart' as globals;
import 'locationUtils.dart'; import 'locationUtils.dart';
import 'mainDrawer.dart'; import 'mainDrawer.dart';
import 'placesAutocompleteUtils.dart'; import 'placesAutocompleteUtils.dart';
import 'yourLocation.dart';
class ActiveFiresPage extends StatefulWidget { class ActiveFiresPage extends StatefulWidget {
static const String routeName = '/fires'; static const String routeName = '/fires';
@ -50,17 +49,20 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
_ActiveFiresPageState(); _ActiveFiresPageState();
Widget _buildRow(BasicLocation loc) { Widget _buildRow(YourLocation loc) {
return new ListTile( return new ListTile(
dense: true, dense: true,
leading: const Icon(Icons.location_on), leading: const Icon(Icons.location_on),
trailing: new IconButton( trailing: new IconButton(
icon: new Icon(loc.isSubscribed icon: new Icon(loc.subscribed
? Icons.notifications_active ? Icons.notifications_active
: Icons.notifications_off), : Icons.notifications_off),
onPressed: () { onPressed: () {
loc.subscribed = !loc.isSubscribed; loc.subscribed = !loc.subscribed;
persistYourLocations(); int i = globals.yourLocations.indexOf(loc);
YourLocationRepository.repo.update(i, loc);
globals.yourLocations.removeAt(i);
globals.yourLocations.insert(i, loc);
setState(() {}); setState(() {});
}), }),
title: new Text(loc.description), title: new Text(loc.description),
@ -72,7 +74,7 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
}); });
} }
void showLocationMap(BasicLocation loc) { void showLocationMap(YourLocation loc) {
// , VoidCallback onSubs // , VoidCallback onSubs
Navigator.push( Navigator.push(
context, context,
@ -94,32 +96,17 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
itemCount: globals.yourLocations.length, itemCount: globals.yourLocations.length,
itemBuilder: (BuildContext _context, int i) { itemBuilder: (BuildContext _context, int i) {
// Add a one-pixel-high divider widget before each row
// in the ListView.
/* if (i.isOdd) {
return new Divider();
} */
// The syntax "i ~/ 2" divides i by 2 and returns an
// integer result.
// For example: 1, 2, 3, 4, 5 becomes 0, 1, 1, 2, 2.
// This calculates the actual number of saved items
// in the ListView, minus the divider widgets.
// final int index = i ~/ 2;
// print('$i $index');
// if (index >= _saved.length) return null;
return _buildItem(globals.yourLocations.elementAt(i)); return _buildItem(globals.yourLocations.elementAt(i));
}); });
} }
void handleUndo(BasicLocation item) { void handleUndo(YourLocation item) {
setState(() { setState(() {
globals.yourLocations.add(item); YourLocationRepository.repo.save(item);
persistYourLocations();
}); });
} }
Widget _buildItem(BasicLocation item) { Widget _buildItem(YourLocation item) {
final ThemeData theme = Theme.of(context); final ThemeData theme = Theme.of(context);
return new Dismissible( return new Dismissible(
key: new ObjectKey(item), key: new ObjectKey(item),
@ -127,7 +114,8 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
onDismissed: (DismissDirection direction) { onDismissed: (DismissDirection direction) {
setState(() { setState(() {
globals.yourLocations.remove(item); globals.yourLocations.remove(item);
persistYourLocations(); int i = globals.yourLocations.indexOf(item);
YourLocationRepository.repo.remove(i);
}); });
_scaffoldKey.currentState.showSnackBar(new SnackBar( _scaffoldKey.currentState.showSnackBar(new SnackBar(
@ -195,33 +183,31 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
new RoundedBtn( new RoundedBtn(
icon: Icons.edit_location, icon: Icons.edit_location,
text: S.of(context).firesNearPlace, text: S.of(context).firesNearPlace,
onPressed: () { onPressed: onAddOtherLocation,
onAddOtherLocation();
},
backColor: fires600), backColor: fires600),
])), ])),
); );
} }
void onAddYourLocation() { void onAddYourLocation() {
Future<BasicLocation> location = getUserLocation(_scaffoldKey); Future<YourLocation> location = getUserLocation(_scaffoldKey);
_saveLocation(location); _saveLocation(location);
} }
void onAddOtherLocation() { void onAddOtherLocation() {
Future<BasicLocation> location = openPlacesDialog(_scaffoldKey); Future<YourLocation> location = openPlacesDialog(_scaffoldKey);
_saveLocation(location); _saveLocation(location);
} }
void _saveLocation(Future<BasicLocation> location) { void _saveLocation(Future<YourLocation> location) {
location.then((newLocation) { location.then((newLocation) {
if (newLocation != BasicLocation.noLocation) { if (newLocation != YourLocation.noLocation) {
if (globals.yourLocations.contains(newLocation)) { if (globals.yourLocations.contains(newLocation)) {
showSnackMsg(S.of(context).addedThisLocation); showSnackMsg(S.of(context).addedThisLocation);
} else } else
this.setState(() { this.setState(() {
globals.yourLocations.add(newLocation); globals.yourLocations.add(newLocation);
persistYourLocations(); YourLocationRepository.repo.save(newLocation);
new Timer(new Duration(milliseconds: 1000), () { new Timer(new Duration(milliseconds: 1000), () {
showLocationMap(newLocation); showLocationMap(newLocation);
}); });

View file

@ -4,11 +4,10 @@ class BasicLocation extends Comparable<BasicLocation> {
final double lat; final double lat;
final double lon; final double lon;
String description; String description;
bool subscribed;
static BasicLocation noLocation = new BasicLocation(lat: 0.0, lon: 0.0); // static BasicLocation noLocation = new BasicLocation(lat: 0.0, lon: 0.0);
BasicLocation({@required this.lat, @required this.lon, this.description, this.subscribed: false}) { BasicLocation({@required this.lat, @required this.lon, this.description}) {
if (this.description == null) if (this.description == null)
this.description = 'Position: ${this.lat}, ${this.lon}'; this.description = 'Position: ${this.lat}, ${this.lon}';
} }
@ -26,10 +25,6 @@ class BasicLocation extends Comparable<BasicLocation> {
return hash; return hash;
} }
bool get isSubscribed {
return subscribed == null ? false : subscribed;
}
BasicLocation.fromJson(Map<String, dynamic> json) BasicLocation.fromJson(Map<String, dynamic> json)
: lat = json['lat'], : lat = json['lat'],
lon = json['lon'], lon = json['lon'],

View file

@ -10,7 +10,7 @@ import 'package:http/http.dart' as http;
import 'package:just_debounce_it/just_debounce_it.dart'; import 'package:just_debounce_it/just_debounce_it.dart';
import 'package:latlong/latlong.dart'; import 'package:latlong/latlong.dart';
import 'basicLocation.dart'; import 'yourLocation.dart';
import 'colors.dart'; import 'colors.dart';
import 'customBottomAppBar.dart'; import 'customBottomAppBar.dart';
import 'dummyMapPlugin.dart'; import 'dummyMapPlugin.dart';
@ -20,11 +20,12 @@ import 'generated/i18n.dart';
import 'globals.dart' as globals; import 'globals.dart' as globals;
import 'slider.dart'; import 'slider.dart';
import 'zoomMapPlugin.dart'; import 'zoomMapPlugin.dart';
import 'basicLocation.dart';
enum MapOperation { view, subscriptionConfirm, unsubscribe } enum MapOperation { view, subscriptionConfirm, unsubscribe }
class GenericMap extends StatefulWidget { class GenericMap extends StatefulWidget {
final BasicLocation location; final YourLocation location;
final String title; final String title;
final MapOperation operation; final MapOperation operation;
@ -42,7 +43,7 @@ class _GenericMapState extends State<GenericMap> {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>(); final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
final FirebaseMessaging _firebaseMessaging = new FirebaseMessaging(); final FirebaseMessaging _firebaseMessaging = new FirebaseMessaging();
final BasicLocation location; final YourLocation location;
final String title; final String title;
int numFires; int numFires;
int kmAround = 100; int kmAround = 100;
@ -220,7 +221,7 @@ class _GenericMapState extends State<GenericMap> {
)); ));
} }
List<Marker> buildMarkers(BasicLocation yourLocation, List<dynamic> fires, List<Marker> buildMarkers(YourLocation yourLocation, List<dynamic> fires,
List<dynamic> falsePos, List<dynamic> industries) { List<dynamic> falsePos, List<dynamic> industries) {
List<Marker> markers = []; List<Marker> markers = [];
const calibrate = false; // useful when we change the fire icons size const calibrate = false; // useful when we change the fire icons size

View file

@ -1,18 +1,20 @@
library fires.globals; library fires.globals;
import 'package:get_it/get_it.dart';
import 'dart:async';
import 'package:shared_preferences/shared_preferences.dart';
import 'basicLocation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'yourLocation.dart';
String gmapKey; String gmapKey;
String firesApiKey; String firesApiKey;
String firesApiUrl; String firesApiUrl;
final String appVersion = '0.0.1'; final String appVersion = '0.0.1';
final Widget appMediumIcon = Image.asset('images/logo-200.png', width: 60.0, height: 60.0); final Widget appMediumIcon =
final Widget appIcon = Image.asset('images/logo-200.png', width: 24.0, height: 24.0); Image.asset('images/logo-200.png', width: 60.0, height: 60.0);
final Future<SharedPreferences> prefs = SharedPreferences.getInstance(); final Widget appIcon =
final List<BasicLocation> yourLocations = []; Image.asset('images/logo-200.png', width: 24.0, height: 24.0);
// final Future<SharedPreferences> prefs = SharedPreferences.getInstance();
List<YourLocation> yourLocations = [];
final GetIt getIt = new GetIt(); final GetIt getIt = new GetIt();
bool isDevelopment = false; bool isDevelopment = false;

View file

@ -37,7 +37,7 @@ class HomePage extends StatelessWidget {
new Expanded( new Expanded(
child: new FractionallySizedBox( child: new FractionallySizedBox(
alignment: FractionalOffset.center, alignment: FractionalOffset.center,
heightFactor: 0.8, heightFactor: 0.7,
child: new Image.asset('images/logo-200.png', child: new Image.asset('images/logo-200.png',
fit: BoxFit.fitHeight))), fit: BoxFit.fitHeight))),
new Expanded( new Expanded(

View file

@ -1,13 +1,13 @@
import 'package:location/location.dart'; import 'package:location/location.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'dart:async'; import 'dart:async';
import 'basicLocation.dart'; import 'yourLocation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:geocoder/geocoder.dart'; import 'package:geocoder/geocoder.dart';
import 'globals.dart' as globals; import 'globals.dart' as globals;
import 'generated/i18n.dart'; import 'generated/i18n.dart';
Future<BasicLocation> getUserLocation( Future<YourLocation> getUserLocation(
GlobalKey<ScaffoldState> scaffoldKey) async { GlobalKey<ScaffoldState> scaffoldKey) async {
// Platform messages may fail, so we use a try/catch PlatformException. // Platform messages may fail, so we use a try/catch PlatformException.
try { try {
@ -15,21 +15,21 @@ Future<BasicLocation> getUserLocation(
Map<String, double> location = await _location.getLocation; Map<String, double> location = await _location.getLocation;
// It seems that the lib fails with lat/lon values // It seems that the lib fails with lat/lon values
var basicLocation = new BasicLocation( var yourLocation = new YourLocation(
lat: location['latitude'], lon: location['longitude']); lat: location['latitude'], lon: location['longitude']);
var address; var address;
try { try {
address = await getReverseLocation(basicLocation); address = await getReverseLocation(yourLocation);
basicLocation.description = address; yourLocation.description = address;
} catch (e) { } catch (e) {
try { try {
address = await getReverseLocation(basicLocation, true); address = await getReverseLocation(yourLocation, true);
basicLocation.description = address; yourLocation.description = address;
} catch (e) { } catch (e) {
print('We cannot reverse geolocate'); print('We cannot reverse geolocate');
} }
} }
return basicLocation; return yourLocation;
} on PlatformException catch (e) { } on PlatformException catch (e) {
if (e.code == 'PERMISSION_DENIED') { if (e.code == 'PERMISSION_DENIED') {
scaffoldKey.currentState.showSnackBar(new SnackBar( scaffoldKey.currentState.showSnackBar(new SnackBar(
@ -40,11 +40,11 @@ Future<BasicLocation> getUserLocation(
content: new Text(S.of(scaffoldKey.currentContext).isYourUbicationEnabled content: new Text(S.of(scaffoldKey.currentContext).isYourUbicationEnabled
), ),
)); ));
return BasicLocation.noLocation; return YourLocation.noLocation;
} }
} }
Future<String> getReverseLocation(BasicLocation loc, Future<String> getReverseLocation(YourLocation loc,
[bool external = false]) async { [bool external = false]) async {
final coordinates = new Coordinates(loc.lat, loc.lon); final coordinates = new Coordinates(loc.lat, loc.lon);
var geoCoder = external ? Geocoder.google(globals.gmapKey) : Geocoder.local; var geoCoder = external ? Geocoder.google(globals.gmapKey) : Geocoder.local;

View file

@ -1,12 +1,12 @@
import 'package:flutter/material.dart'; import 'dart:async';
import 'firesApp.dart';
import 'package:comunes_flutter/comunes_flutter.dart'; import 'package:comunes_flutter/comunes_flutter.dart';
import 'dart:async'; import 'package:flutter/material.dart';
import 'globals.dart' as globals;
import 'basicLocationPersist.dart';
import 'firebaseMessagingConf.dart';
import 'firebaseMessagingConf.dart';
import 'firesApp.dart';
import 'globals.dart' as globals;
import 'yourLocation.dart';
Future<Map<String, dynamic>> loadSecrets() async { Future<Map<String, dynamic>> loadSecrets() async {
return await SecretLoader(secretPath: 'assets/private-settings.json').load(); return await SecretLoader(secretPath: 'assets/private-settings.json').load();
} }
@ -16,8 +16,9 @@ void mainCommon() {
globals.gmapKey = secrets['gmapKey']; globals.gmapKey = secrets['gmapKey'];
globals.firesApiKey = secrets['firesApiKey']; globals.firesApiKey = secrets['firesApiKey'];
globals.firesApiUrl = secrets['firesApiUrl'] + "api/v1/"; globals.firesApiUrl = secrets['firesApiUrl'] + "api/v1/";
globals.prefs.then((prefs) { YourLocationRepository.repo.remove(0);
loadYourLocationsWithPrefs(prefs); YourLocationRepository.repo.getAll().then((yourLocations) {
globals.yourLocations = yourLocations;
firebaseConfig(); firebaseConfig();
// Run baby run! // Run baby run!

View file

@ -1,11 +1,13 @@
import 'package:flutter_google_places_autocomplete/flutter_google_places_autocomplete.dart';
import 'package:flutter/material.dart';
import 'dart:async'; import 'dart:async';
import 'basicLocation.dart';
import 'globals.dart' as globals;
import 'generated/i18n.dart';
Future<BasicLocation> openPlacesDialog(GlobalKey<ScaffoldState> sc) async { import 'package:flutter/material.dart';
import 'package:flutter_google_places_autocomplete/flutter_google_places_autocomplete.dart';
import 'yourLocation.dart';
import 'generated/i18n.dart';
import 'globals.dart' as globals;
Future<YourLocation> openPlacesDialog(GlobalKey<ScaffoldState> sc) async {
Mode _mode = Mode.overlay; Mode _mode = Mode.overlay;
GoogleMapsPlaces _places = new GoogleMapsPlaces(globals.gmapKey); GoogleMapsPlaces _places = new GoogleMapsPlaces(globals.gmapKey);
Prediction p = await showGooglePlacesAutocomplete( Prediction p = await showGooglePlacesAutocomplete(
@ -13,8 +15,8 @@ Future<BasicLocation> openPlacesDialog(GlobalKey<ScaffoldState> sc) async {
hint: S.of(sc.currentContext).typeTheNameOfAPlace, hint: S.of(sc.currentContext).typeTheNameOfAPlace,
apiKey: globals.gmapKey, apiKey: globals.gmapKey,
onError: (res) { onError: (res) {
/* sc.currentState.showSnackBar( sc.currentState
new SnackBar(content: new Text(res.errorMessage))); */ .showSnackBar(new SnackBar(content: new Text(res.errorMessage)));
print('Error $res'); print('Error $res');
}, },
mode: _mode, mode: _mode,
@ -28,7 +30,7 @@ Future<BasicLocation> openPlacesDialog(GlobalKey<ScaffoldState> sc) async {
PlacesDetailsResponse detail = await _places.getDetailsByPlaceId(p.placeId); PlacesDetailsResponse detail = await _places.getDetailsByPlaceId(p.placeId);
final lat = detail.result.geometry.location.lat; final lat = detail.result.geometry.location.lat;
final lng = detail.result.geometry.location.lng; final lng = detail.result.geometry.location.lng;
return new BasicLocation(lat: lat, lon: lng, description: p.description); return new YourLocation(lat: lat, lon: lng, description: p.description);
} }
return BasicLocation.noLocation; return YourLocation.noLocation;
} }

78
lib/yourLocation.dart Normal file
View file

@ -0,0 +1,78 @@
import 'package:bson_objectid/bson_objectid.dart';
import 'package:flutter/material.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:pref_dessert/pref_dessert.dart';
import 'dart:convert';
part 'yourLocation.g.dart';
_objectIdFromJson(String json) {
return new ObjectId.fromHexString(json);
}
_objectIdToJson(ObjectId o) {
return o.toString();
}
@JsonSerializable(nullable: false)
class YourLocation extends Object with _$YourLocationSerializerMixin {
@JsonKey(toJson: _objectIdToJson, fromJson: _objectIdFromJson)
ObjectId id;
final double lat;
final double lon;
String description;
bool subscribed;
factory YourLocation.fromJson(Map<String, dynamic> json) =>
_$YourLocationFromJson(json);
static YourLocation noLocation = new YourLocation(lat: 0.0, lon: 0.0);
YourLocation(
{this.id,
@required this.lat,
@required this.lon,
this.description,
this.subscribed: false}) {
if (this.description == null)
this.description = 'Position: ${this.lat}, ${this.lon}';
if (this.id == null) this.id = new ObjectId();
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is YourLocation &&
runtimeType == other.runtimeType &&
id == other.id &&
lat == other.lat &&
lon == other.lon &&
description == other.description &&
subscribed == other.subscribed;
@override
int get hashCode =>
id.hashCode ^
lat.hashCode ^
lon.hashCode ^
description.hashCode ^
subscribed.hashCode;
}
class YourLocationRepository extends PreferencesRepository<YourLocation> {
static final repo = new YourLocationRepository();
YourLocationRepository() : super();
@override
YourLocation deserialize(String s) {
return YourLocation.fromJson(json.decode(s));
}
@override
String serialize(YourLocation y) {
return json.encode(y.toJson());
}
}

54
lib/yourLocation.g.dart Normal file
View file

@ -0,0 +1,54 @@
// Copyright (c) 2018, Comunes Association.
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'yourLocation.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
YourLocation _$YourLocationFromJson(Map<String, dynamic> json) =>
new YourLocation(
id: _objectIdFromJson(json['id'] as String),
lat: (json['lat'] as num).toDouble(),
lon: (json['lon'] as num).toDouble(),
description: json['description'] as String,
subscribed: json['subscribed'] as bool);
abstract class _$YourLocationSerializerMixin {
ObjectId get id;
double get lat;
double get lon;
String get description;
bool get subscribed;
Map<String, dynamic> toJson() => new _$YourLocationJsonMapWrapper(this);
}
class _$YourLocationJsonMapWrapper extends $JsonMapWrapper {
final _$YourLocationSerializerMixin _v;
_$YourLocationJsonMapWrapper(this._v);
@override
Iterable<String> get keys =>
const ['id', 'lat', 'lon', 'description', 'subscribed'];
@override
dynamic operator [](Object key) {
if (key is String) {
switch (key) {
case 'id':
return _objectIdToJson(_v.id);
case 'lat':
return _v.lat;
case 'lon':
return _v.lon;
case 'description':
return _v.description;
case 'subscribed':
return _v.subscribed;
}
}
return null;
}
}

View file

@ -12,10 +12,16 @@ dependencies:
# https://pub.dartlang.org/packages/flutter # https://pub.dartlang.org/packages/flutter
# utils # utils
shared_preferences: "^0.4.2"
http: "^0.11.3+16"
simple_moment: "^0.0.3" simple_moment: "^0.0.3"
just_debounce_it: "^1.0.4" just_debounce_it: "^1.0.4"
# net
http: "^0.11.3+16"
jaguar_resty: "^2.5.5"
# data
json_annotation: ^0.2.3
shared_preferences: "^0.4.2"
pref_dessert: "^0.0.2"
bson_objectid: "^0.1.0"
comunes_flutter: comunes_flutter:
path: /home/vjrj/dev/comunes_flutter path: /home/vjrj/dev/comunes_flutter
version: "^0.0.10" version: "^0.0.10"
@ -46,7 +52,8 @@ dependencies:
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
build_runner: ^0.8.0
json_serializable: ^0.5.0
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec # following page: https://www.dartlang.org/tools/pub/pubspec