Locations persist. Leaflet and Rest queries
This commit is contained in:
parent
7fbe5b971d
commit
b5b8b3da51
13 changed files with 196 additions and 192 deletions
|
|
@ -6,7 +6,6 @@ buildscript {
|
|||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.0.1'
|
||||
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.2-4'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
BIN
images/fire-marker-l.png
Normal file
BIN
images/fire-marker-l.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 846 B |
BIN
images/fire-marker-m.png
Normal file
BIN
images/fire-marker-m.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 679 B |
BIN
images/fire-marker-s.png
Normal file
BIN
images/fire-marker-s.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 485 B |
BIN
images/fire-marker.png
Normal file
BIN
images/fire-marker.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 720 B |
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'mainDrawer.dart';
|
||||
import 'genericMap.dart';
|
||||
import 'leafletMap.dart';
|
||||
import 'dart:async';
|
||||
import 'package:comunes_flutter/comunes_flutter.dart';
|
||||
import 'colors.dart';
|
||||
|
|
@ -10,6 +10,7 @@ import 'package:collection/collection.dart' show lowerBound;
|
|||
import 'locationUtils.dart';
|
||||
import 'globals.dart' as globals;
|
||||
import 'basicLocationPersist.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
class ActiveFiresPage extends StatefulWidget {
|
||||
static const String routeName = '/fires';
|
||||
|
|
@ -36,16 +37,25 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
|||
title: new Text(desc),
|
||||
onLongPress: () {
|
||||
_scaffoldKey.currentState.showSnackBar(new SnackBar(
|
||||
content: new Text(
|
||||
'Slide horizontally to delete this location'),
|
||||
content: new Text('Slide horizontally to delete this location'),
|
||||
));
|
||||
},
|
||||
onTap: () {
|
||||
const String km = '100';
|
||||
var url = 'https://fires.comunes.org/api/v1/fires-in/${globals.firesApiKey}/${loc.lat}/${loc
|
||||
.lon}/$km';
|
||||
/* http.post(url, body: {"name": "doodle", "color": "blue"})
|
||||
.then((response) {
|
||||
print("Response status: ${response.statusCode}");
|
||||
print("Response body: ${response.body}");
|
||||
}); */
|
||||
// print(url);
|
||||
http.read(url).then(print);
|
||||
Navigator.push(
|
||||
context,
|
||||
new MaterialPageRoute(
|
||||
builder: (context) => new GenericMap(
|
||||
title: desc, latitude: loc.lat, longitude: loc.lon)));
|
||||
builder: (context) =>
|
||||
new LeafletMap(title: desc, location: loc)));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ final String locationKey = 'yourlocations';
|
|||
|
||||
void loadYourLocations(prefs) {
|
||||
List<String> yourLocations = prefs.getStringList(locationKey);
|
||||
if (yourLocations == null) {
|
||||
yourLocations = [];
|
||||
persistYourLocations(prefs);
|
||||
}
|
||||
yourLocations.forEach((locationString) {
|
||||
Map locationMap = json.decode(locationString);
|
||||
globals.yourLocations.add(BasicLocation.fromJson(locationMap));
|
||||
|
|
|
|||
|
|
@ -1,173 +0,0 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:map_view/map_view.dart';
|
||||
import 'globals.dart' as globals;
|
||||
|
||||
class GenericMap extends StatefulWidget {
|
||||
final String title;
|
||||
final double latitude;
|
||||
final double longitude;
|
||||
|
||||
GenericMap(
|
||||
{@required this.title,
|
||||
@required this.latitude,
|
||||
@required this.longitude});
|
||||
|
||||
@override
|
||||
_GenericMapState createState() => new _GenericMapState(
|
||||
title: this.title, latitude: this.latitude, longitude: this.longitude);
|
||||
}
|
||||
|
||||
class _GenericMapState extends State<GenericMap> {
|
||||
MapView mapView = new MapView();
|
||||
CameraPosition cameraPosition;
|
||||
var compositeSubscription = new CompositeSubscription();
|
||||
var staticMapProvider;
|
||||
Uri staticMapUri;
|
||||
final String title;
|
||||
Location location;
|
||||
|
||||
_GenericMapState(
|
||||
{@required this.title, @required latitude, @required longitude}) {
|
||||
MapView.setApiKey(globals.gmapKey);
|
||||
this.staticMapProvider = new StaticMapProvider(globals.gmapKey);
|
||||
this.location = new Location(latitude, longitude);
|
||||
}
|
||||
|
||||
List<Marker> _markers = <Marker>[
|
||||
new Marker("1", "Work", 45.523970, -122.663081, color: Colors.blue),
|
||||
new Marker("2", "Nossa Familia Coffee", 45.528788, -122.684633),
|
||||
];
|
||||
|
||||
@override
|
||||
initState() {
|
||||
super.initState();
|
||||
cameraPosition = new CameraPosition(location, 2.0);
|
||||
staticMapUri = staticMapProvider.getStaticUri(location, 12,
|
||||
width: 900, height: 400, mapType: StaticMapViewType.roadmap);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new Scaffold(
|
||||
appBar: new AppBar(
|
||||
title: new Text(title),
|
||||
),
|
||||
body: new Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
new Container(
|
||||
height: 250.0,
|
||||
child: new Stack(
|
||||
children: <Widget>[
|
||||
new InkWell(
|
||||
child: new Center(
|
||||
child: new Image.network(staticMapUri.toString()),
|
||||
),
|
||||
onTap: showMap,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
new Container(
|
||||
padding: new EdgeInsets.only(top: 10.0),
|
||||
child: new Text(
|
||||
"Tap the map to interact",
|
||||
style: new TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
]));
|
||||
}
|
||||
|
||||
showMap() {
|
||||
mapView.show(
|
||||
new MapOptions(
|
||||
mapViewType: MapViewType.normal,
|
||||
showUserLocation: true,
|
||||
initialCameraPosition: new CameraPosition(new Location(45.5235258, -122.6732493), 14.0),
|
||||
title: this.title),
|
||||
toolbarActions: [new ToolbarAction("Close", 1)]);
|
||||
|
||||
var sub = mapView.onMapReady.listen((_) {
|
||||
mapView.setMarkers(_markers);
|
||||
mapView.addMarker(new Marker("3", "10 Barrel", 45.5259467, -122.687747,
|
||||
color: Colors.purple));
|
||||
mapView.zoomToFit(padding: 100);
|
||||
});
|
||||
compositeSubscription.add(sub);
|
||||
|
||||
sub = mapView.onLocationUpdated
|
||||
.listen((location) => print("Location updated $location"));
|
||||
compositeSubscription.add(sub);
|
||||
|
||||
sub = mapView.onTouchAnnotation
|
||||
.listen((annotation) => print("annotation tapped"));
|
||||
compositeSubscription.add(sub);
|
||||
|
||||
sub = mapView.onMapTapped
|
||||
.listen((location) => print("Touched location $location"));
|
||||
compositeSubscription.add(sub);
|
||||
|
||||
sub = mapView.onCameraChanged.listen((cameraPosition) =>
|
||||
this.setState(() => this.cameraPosition = cameraPosition));
|
||||
compositeSubscription.add(sub);
|
||||
|
||||
sub = mapView.onToolbarAction.listen((id) {
|
||||
if (id == 1) {
|
||||
_handleDismiss();
|
||||
}
|
||||
});
|
||||
compositeSubscription.add(sub);
|
||||
|
||||
sub = mapView.onInfoWindowTapped.listen((marker) {
|
||||
print("Info Window Tapped for ${marker.title}");
|
||||
});
|
||||
compositeSubscription.add(sub);
|
||||
}
|
||||
|
||||
_handleDismiss() async {
|
||||
double zoomLevel = await mapView.zoomLevel;
|
||||
Location centerLocation = await mapView.centerLocation;
|
||||
List<Marker> visibleAnnotations = await mapView.visibleAnnotations;
|
||||
print("Zoom Level: $zoomLevel");
|
||||
print("Center: $centerLocation");
|
||||
print("Visible Annotation Count: ${visibleAnnotations.length}");
|
||||
var uri = await staticMapProvider.getImageUriFromMap(mapView,
|
||||
width: 900, height: 400);
|
||||
setState(() => staticMapUri = uri);
|
||||
mapView.dismiss();
|
||||
compositeSubscription.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
class CompositeSubscription {
|
||||
Set<StreamSubscription> _subscriptions = new Set();
|
||||
|
||||
void cancel() {
|
||||
for (var n in this._subscriptions) {
|
||||
n.cancel();
|
||||
}
|
||||
this._subscriptions = new Set();
|
||||
}
|
||||
|
||||
void add(StreamSubscription subscription) {
|
||||
this._subscriptions.add(subscription);
|
||||
}
|
||||
|
||||
void addAll(Iterable<StreamSubscription> subs) {
|
||||
_subscriptions.addAll(subs);
|
||||
}
|
||||
|
||||
bool remove(StreamSubscription subscription) {
|
||||
return this._subscriptions.remove(subscription);
|
||||
}
|
||||
|
||||
bool contains(StreamSubscription subscription) {
|
||||
return this._subscriptions.contains(subscription);
|
||||
}
|
||||
|
||||
List<StreamSubscription> toList() {
|
||||
return this._subscriptions.toList();
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,8 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||
import 'basicLocation.dart';
|
||||
|
||||
String gmapKey;
|
||||
String appName = 'All Against Fire!';
|
||||
String firesApiKey;
|
||||
String appName = 'All Against The Fire!';
|
||||
Future<SharedPreferences> prefs = SharedPreferences.getInstance();
|
||||
final List<BasicLocation> yourLocations = [];
|
||||
final GetIt getIt = new GetIt();
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ class HomePage extends StatelessWidget {
|
|||
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
||||
|
||||
final _homeFont = const TextStyle(
|
||||
fontSize: 40.0,
|
||||
fontSize: 35.0,
|
||||
fontWeight: FontWeight.w400,
|
||||
// color: Colors.white,
|
||||
);
|
||||
|
|
@ -37,7 +37,7 @@ class HomePage extends StatelessWidget {
|
|||
new Expanded(
|
||||
child: new FractionallySizedBox(
|
||||
alignment: FractionalOffset.bottomCenter,
|
||||
heightFactor: 0.8,
|
||||
heightFactor: 0.9,
|
||||
child: new Image.asset('images/logo-200.png',
|
||||
fit: BoxFit.fitHeight))),
|
||||
new Expanded(
|
||||
|
|
@ -46,7 +46,13 @@ class HomePage extends StatelessWidget {
|
|||
heightFactor: 0.9,
|
||||
child: new CenteredColumn(
|
||||
children: <Widget>[
|
||||
new Text(globals.appName, style: _homeFont),
|
||||
new Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: new Text(
|
||||
globals.appName,
|
||||
textAlign: TextAlign.center,
|
||||
softWrap: true, style: _homeFont),
|
||||
),
|
||||
new SizedBox(height: 20.0),
|
||||
new RoundedBtn.nav(
|
||||
icon: Icons.whatshot,
|
||||
|
|
@ -54,6 +60,13 @@ class HomePage extends StatelessWidget {
|
|||
context: context,
|
||||
route: ActiveFiresPage.routeName,
|
||||
backColor: fires600),
|
||||
new SizedBox(height: 20.0),
|
||||
new RoundedBtn.nav(
|
||||
icon: Icons.notifications_active,
|
||||
text: 'Notify a fire',
|
||||
context: context,
|
||||
route: ActiveFiresPage.routeName,
|
||||
backColor: fires600),
|
||||
],
|
||||
)))
|
||||
])),
|
||||
|
|
|
|||
144
lib/leafletMap.dart
Normal file
144
lib/leafletMap.dart
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'basicLocation.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:flutter_map/plugin_api.dart';
|
||||
import 'package:latlong/latlong.dart';
|
||||
import 'colors.dart';
|
||||
import 'mainDrawer.dart'
|
||||
'';
|
||||
class LeafletMap extends StatefulWidget {
|
||||
final BasicLocation location;
|
||||
final String title;
|
||||
|
||||
LeafletMap({@required this.title, @required this.location});
|
||||
|
||||
@override
|
||||
_LeafletMapState createState() => _LeafletMapState(title: title, location: location);
|
||||
}
|
||||
|
||||
class _LeafletMapState extends State<LeafletMap> {
|
||||
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
||||
|
||||
final BasicLocation location;
|
||||
final String title;
|
||||
_LeafletMapState({@required this.title, @required this.location});
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new Scaffold(
|
||||
key: _scaffoldKey,
|
||||
// drawer: new MainDrawer(context),
|
||||
appBar: new AppBar(
|
||||
title: new Text(title),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () {
|
||||
|
||||
},
|
||||
child: const Icon(Icons.notifications_none),
|
||||
backgroundColor: Colors.orange,
|
||||
),
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
|
||||
bottomNavigationBar: new MapBottomAppBar(
|
||||
menuCallback: () {
|
||||
// _scaffoldKey.currentState.openDrawer();
|
||||
},
|
||||
fabLocation: FloatingActionButtonLocation.centerDocked,
|
||||
showNotch: true,
|
||||
bottomActions: <Widget>[
|
||||
new Text('10 fires near you'),
|
||||
SizedBox(width: 10.0)
|
||||
]),
|
||||
body: new FlutterMap(
|
||||
options: new MapOptions(
|
||||
center: new LatLng(this.location.lat, this.location.lon),
|
||||
zoom: 13.0,
|
||||
// THIS does not works as expected
|
||||
// maxZoom: 6.0,
|
||||
onPositionChanged: (positionCallback) {
|
||||
print('$positionCallback');
|
||||
}
|
||||
),
|
||||
layers: [
|
||||
new TileLayerOptions(
|
||||
maxZoom: 6.0,
|
||||
urlTemplate: 'https://tiles.wmflabs.org/bw-mapnik/{z}/{x}/{y}.png',
|
||||
additionalOptions: {
|
||||
// 'opacity': '0.1',
|
||||
'attribution': '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
|
||||
},
|
||||
), /*
|
||||
new TileLayerOptions(
|
||||
urlTemplate: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
additionalOptions: {
|
||||
// 'opacity': '0.1',
|
||||
'attribution': '&copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
|
||||
},
|
||||
|
||||
), */
|
||||
new MarkerLayerOptions(
|
||||
markers: [
|
||||
new Marker(
|
||||
width: 80.0,
|
||||
height: 80.0,
|
||||
point: new LatLng(this.location.lat, this.location.lon),
|
||||
builder: (ctx) =>
|
||||
new Container(
|
||||
child: new Image.asset('images/fire-marker-l.png'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
class MapBottomAppBar extends StatelessWidget {
|
||||
const MapBottomAppBar(
|
||||
{this.menuCallback,
|
||||
this.fabLocation,
|
||||
this.showNotch,
|
||||
this.bottomActions});
|
||||
|
||||
final Color color = fires600;
|
||||
final FloatingActionButtonLocation fabLocation;
|
||||
final bool showNotch;
|
||||
final VoidCallback menuCallback;
|
||||
final List<Widget> bottomActions;
|
||||
|
||||
static final List<FloatingActionButtonLocation> kCenterLocations =
|
||||
<FloatingActionButtonLocation>[
|
||||
FloatingActionButtonLocation.centerDocked,
|
||||
FloatingActionButtonLocation.centerFloat,
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final List<Widget> rowContents = <Widget>[
|
||||
/* new IconButton(
|
||||
icon: const Icon(Icons.menu),
|
||||
onPressed: () {
|
||||
menuCallback();
|
||||
/* showModalBottomSheet<Null>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => const _DemoDrawer(),
|
||||
); */
|
||||
},
|
||||
), */
|
||||
];
|
||||
|
||||
if (kCenterLocations.contains(fabLocation)) {
|
||||
rowContents.add(
|
||||
const Expanded(child: const SizedBox()),
|
||||
);
|
||||
}
|
||||
|
||||
rowContents.addAll(this.bottomActions);
|
||||
|
||||
return new BottomAppBar(
|
||||
color: color,
|
||||
hasNotch: showNotch,
|
||||
child: new Row(children: rowContents),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +1,19 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'firesApp.dart';
|
||||
|
||||
import 'package:comunes_flutter/comunes_flutter.dart';
|
||||
import 'dart:async';
|
||||
import 'globals.dart' as globals;
|
||||
import 'basicLocationPersist.dart';
|
||||
|
||||
Future<String> getGMapKey() async {
|
||||
Secret secret = await SecretLoader(secretPath: 'assets/private-settings.json')
|
||||
.load('gmap_key');
|
||||
return secret.apiKey;
|
||||
Future<Map<String, dynamic>> loadSecrets() async {
|
||||
return await SecretLoader(secretPath: 'assets/private-settings.json').load();
|
||||
}
|
||||
|
||||
void main() {
|
||||
getGMapKey().then((String gmapKey) {
|
||||
globals.gmapKey = gmapKey;
|
||||
loadSecrets().then((secrets) {
|
||||
globals.firesApiKey = secrets['firesApiKey'];
|
||||
globals.gmapKey = secrets['gmapKey'];
|
||||
globals.prefs.then((prefs) {
|
||||
loadYourLocations(prefs);
|
||||
|
||||
|
|
|
|||
10
pubspec.yaml
10
pubspec.yaml
|
|
@ -12,14 +12,16 @@ dependencies:
|
|||
shared_preferences: "^0.4.2"
|
||||
comunes_flutter:
|
||||
path: /home/vjrj/dev/comunes_flutter
|
||||
version: "^0.0.5"
|
||||
version: "^0.0.7"
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: "^0.1.2"
|
||||
map_view: "^0.0.12"
|
||||
location: "^1.3.4"
|
||||
flutter_google_places_autocomplete: "^0.0.4"
|
||||
get_it: "^1.0.1+1"
|
||||
flutter_map: "^0.0.1"
|
||||
http: "^0.11.3+16"
|
||||
simple_moment: "^0.0.3"
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
@ -45,6 +47,10 @@ flutter:
|
|||
assets:
|
||||
- images/logo-200.png
|
||||
- assets/private-settings.json
|
||||
- images/fire-marker-l.png
|
||||
- images/fire-marker-m.png
|
||||
- images/fire-marker-s.png
|
||||
- images/fire-marker.png
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.io/assets-and-images/#resolution-aware.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue