Many work in navegation, map, drawer, about, slider
This commit is contained in:
parent
662deb3086
commit
574c1afc33
15 changed files with 446 additions and 168 deletions
BIN
images/industry-marker-reg.png
Normal file
BIN
images/industry-marker-reg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 360 B |
BIN
images/industry-marker.png
Normal file
BIN
images/industry-marker.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 339 B |
|
|
@ -9,10 +9,8 @@ import 'basicLocation.dart';
|
|||
import 'locationUtils.dart';
|
||||
import 'globals.dart' as globals;
|
||||
import 'basicLocationPersist.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert' show json;
|
||||
import 'globalFiresBottomStats.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:flutter_fab_dialer/flutter_fab_dialer.dart';
|
||||
|
||||
class ActiveFiresPage extends StatefulWidget {
|
||||
static const String routeName = '/fires';
|
||||
|
|
@ -26,37 +24,66 @@ class ActiveFiresPage extends StatefulWidget {
|
|||
class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
||||
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
||||
|
||||
List<FabMiniMenuItem> _fabMiniMenuItemList(BuildContext context) {
|
||||
return [
|
||||
new FabMiniMenuItem.withText(
|
||||
new Icon(Icons.location_searching),
|
||||
fires600,
|
||||
8.0,
|
||||
"Add your current position",
|
||||
() {
|
||||
onAddYourLocation();
|
||||
},
|
||||
"Add your current position",
|
||||
fires300,
|
||||
Colors.white,
|
||||
),
|
||||
new FabMiniMenuItem.withText(
|
||||
new Icon(Icons.edit_location),
|
||||
fires600,
|
||||
8.0,
|
||||
"Add some other place",
|
||||
() {
|
||||
onAddOtherLocation(context);
|
||||
},
|
||||
"Add some other place",
|
||||
fires300,
|
||||
Colors.white)
|
||||
];}
|
||||
|
||||
_ActiveFiresPageState();
|
||||
|
||||
Widget _buildRow(BasicLocation loc) {
|
||||
String desc = loc.description != null
|
||||
? loc.description
|
||||
: 'Position: ${loc.lat}, ${loc.lon}';
|
||||
return new ListTile(
|
||||
dense: true,
|
||||
leading: const Icon(Icons.location_on),
|
||||
trailing: const Icon(Icons.notifications_off),
|
||||
title: new Text(desc),
|
||||
trailing: new IconButton(
|
||||
icon: new Icon(loc.isSubscribed
|
||||
? Icons.notifications_active
|
||||
: Icons.notifications_off),
|
||||
onPressed: () {
|
||||
loc.subscribed = !loc.isSubscribed;
|
||||
persistYourLocations();
|
||||
setState(() {});
|
||||
}),
|
||||
title: new Text(loc.description),
|
||||
onLongPress: () {
|
||||
showSnackMsg('Slide horizontally to delete this location');
|
||||
},
|
||||
onTap: () {
|
||||
const String km = '100';
|
||||
var url = '${globals.firesApiUrl}fires-in/${globals
|
||||
.firesApiKey}/${loc.lat}/${loc
|
||||
.lon}/$km';
|
||||
http.read(url).then((result) {
|
||||
int numFires = json.decode(result)['real'];
|
||||
showLocationMap(loc);
|
||||
});
|
||||
}
|
||||
|
||||
void showLocationMap(BasicLocation loc) {
|
||||
// , VoidCallback onSubs
|
||||
Navigator.push(
|
||||
context,
|
||||
new MaterialPageRoute(
|
||||
builder: (context) => new LeafletMap(
|
||||
title: desc,
|
||||
title: loc.description,
|
||||
location: loc,
|
||||
numFires: numFires,
|
||||
kmAround: km)));
|
||||
});
|
||||
});
|
||||
)));
|
||||
}
|
||||
|
||||
void showSnackMsg(String msg) {
|
||||
|
|
@ -91,7 +118,7 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
|||
void handleUndo(BasicLocation item) {
|
||||
setState(() {
|
||||
globals.yourLocations.add(item);
|
||||
persist();
|
||||
persistYourLocations();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -103,7 +130,7 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
|||
onDismissed: (DismissDirection direction) {
|
||||
setState(() {
|
||||
globals.yourLocations.remove(item);
|
||||
persist();
|
||||
persistYourLocations();
|
||||
});
|
||||
final String action = 'deleted';
|
||||
_scaffoldKey.currentState.showSnackBar(new SnackBar(
|
||||
|
|
@ -132,16 +159,11 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
|||
child: _buildRow(item)));
|
||||
}
|
||||
|
||||
void persist() {
|
||||
globals.prefs.then((prefs) {
|
||||
persistYourLocations(prefs);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var hasLocations = globals.yourLocations.length > 0;
|
||||
final title = hasLocations ? 'Active fires in your places': 'Active fires in the world';
|
||||
print('Building Active Fires');
|
||||
final title = 'Your locations';
|
||||
return Scaffold(
|
||||
key: _scaffoldKey,
|
||||
// FIXME new?
|
||||
|
|
@ -157,29 +179,32 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
|||
),
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
|
||||
bottomNavigationBar: new GlobalFiresBottomStats(),
|
||||
body: globals.yourLocations.length > 0
|
||||
? _buildSavedLocations()
|
||||
body: hasLocations
|
||||
? new Stack(children: <Widget>[
|
||||
_buildSavedLocations(),
|
||||
new FabDialer(_fabMiniMenuItemList(context), fires600, new Icon(Icons.add))
|
||||
])
|
||||
: new Center(
|
||||
child: new CenteredColumn(children: <Widget>[
|
||||
new RoundedBtn(
|
||||
icon: Icons.location_searching,
|
||||
text: 'Add your position',
|
||||
text: 'Fires near your',
|
||||
onPressed: onAddYourLocation,
|
||||
backColor: fires600),
|
||||
const SizedBox(height: 26.0),
|
||||
new RoundedBtn(
|
||||
icon: Icons.edit_location,
|
||||
text: 'Add some other place',
|
||||
text: 'Fires near other place',
|
||||
onPressed: () {
|
||||
onAddOtherLocation(context);
|
||||
},
|
||||
backColor: fires600),
|
||||
])),
|
||||
floatingActionButton: globals.yourLocations.length > 0
|
||||
floatingActionButton: hasLocations
|
||||
? Column(mainAxisAlignment: MainAxisAlignment.end,
|
||||
// crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
FloatingActionButton.extended(
|
||||
/* FloatingActionButton.extended(
|
||||
onPressed: onAddYourLocation,
|
||||
heroTag: 'yourposition',
|
||||
label: const Text('Add your position'),
|
||||
|
|
@ -195,7 +220,7 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
|||
label: new Text('Add some other place'),
|
||||
icon: const Icon(Icons.edit_location),
|
||||
),
|
||||
)
|
||||
), */
|
||||
])
|
||||
: null,
|
||||
);
|
||||
|
|
@ -219,7 +244,10 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
|||
} else
|
||||
this.setState(() {
|
||||
globals.yourLocations.add(newLocation);
|
||||
persist();
|
||||
persistYourLocations();
|
||||
new Timer(new Duration(milliseconds: 1000), () {
|
||||
showLocationMap(newLocation);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,10 +4,14 @@ class BasicLocation extends Comparable<BasicLocation> {
|
|||
final double lat;
|
||||
final double lon;
|
||||
String description;
|
||||
bool subscribed;
|
||||
|
||||
static BasicLocation noLocation = new BasicLocation(lat: 0.0, lon: 0.0);
|
||||
|
||||
BasicLocation({@required this.lat, @required this.lon, this.description});
|
||||
BasicLocation({@required this.lat, @required this.lon, this.description, this.subscribed: false}) {
|
||||
if (this.description == null)
|
||||
this.description = 'Position: ${this.lat}, ${this.lon}';
|
||||
}
|
||||
|
||||
int compareTo(BasicLocation other) {
|
||||
return lat == other.lat && lon == other.lon ? 1 : 0;
|
||||
|
|
@ -15,6 +19,17 @@ class BasicLocation extends Comparable<BasicLocation> {
|
|||
|
||||
bool operator ==(o) => o is BasicLocation && o.lat == lat && o.lon == lon;
|
||||
|
||||
int get hashCode {
|
||||
int hash = 1;
|
||||
hash = hash * 17 + lat.hashCode;
|
||||
hash = hash * 31 + lon.hashCode;
|
||||
return hash;
|
||||
}
|
||||
|
||||
bool get isSubscribed {
|
||||
return subscribed == null ? false : subscribed;
|
||||
}
|
||||
|
||||
BasicLocation.fromJson(Map<String, dynamic> json)
|
||||
: lat = json['lat'],
|
||||
lon = json['lon'],
|
||||
|
|
|
|||
|
|
@ -1,24 +1,36 @@
|
|||
import 'dart:convert';
|
||||
import 'basicLocation.dart';
|
||||
import 'globals.dart' as globals;
|
||||
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
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));
|
||||
void loadYourLocations() {
|
||||
globals.prefs.then((prefs) {
|
||||
loadYourLocationsWithPrefs(prefs);
|
||||
});
|
||||
}
|
||||
|
||||
persistYourLocations(prefs) {
|
||||
persistYourLocations() {
|
||||
globals.prefs.then((prefs) {
|
||||
List<String> yourLocationsAsString = [];
|
||||
globals.yourLocations.forEach((location) {
|
||||
yourLocationsAsString.add(json.encode(location.toJson()));
|
||||
});
|
||||
prefs.setStringList(locationKey, yourLocationsAsString);
|
||||
});
|
||||
}
|
||||
|
||||
void loadYourLocationsWithPrefs(SharedPreferences prefs) {
|
||||
globals.prefs.then((prefs) {
|
||||
List<String> yourLocations = prefs.getStringList(locationKey);
|
||||
if (yourLocations == null) {
|
||||
yourLocations = [];
|
||||
persistYourLocations();
|
||||
}
|
||||
yourLocations.forEach((locationString) {
|
||||
Map locationMap = json.decode(locationString);
|
||||
globals.yourLocations.add(BasicLocation.fromJson(locationMap));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -4,11 +4,13 @@ class CustomBottomAppBar extends StatelessWidget {
|
|||
const CustomBottomAppBar(
|
||||
{this.fabLocation,
|
||||
this.showNotch,
|
||||
this.height = 56.0,
|
||||
this.color = Colors.black45,
|
||||
this.mainAxisAlignment = MainAxisAlignment.center,
|
||||
this.actions});
|
||||
|
||||
final Color color;
|
||||
final double height;
|
||||
final MainAxisAlignment mainAxisAlignment;
|
||||
final FloatingActionButtonLocation fabLocation;
|
||||
final bool showNotch;
|
||||
|
|
@ -22,7 +24,7 @@ class CustomBottomAppBar extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final List<Widget> rowContents = <Widget>[new SizedBox(height: 56.0)];
|
||||
final List<Widget> rowContents = <Widget>[new SizedBox(height: height)];
|
||||
|
||||
if (kCenterLocations.contains(fabLocation)) {
|
||||
/* rowContents.add(
|
||||
|
|
|
|||
58
lib/customMapPlugin.dart
Normal file
58
lib/customMapPlugin.dart
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import 'package:flutter/src/widgets/framework.dart';
|
||||
import 'package:flutter_map/plugin_api.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:comunes_flutter/comunes_flutter.dart';
|
||||
|
||||
class ZoomMapPluginOptions extends LayerOptions {
|
||||
final String text;
|
||||
|
||||
ZoomMapPluginOptions({this.text = ""});
|
||||
}
|
||||
|
||||
// https://github.com/apptreesoftware/flutter_map/blob/master/flutter_map_example/lib/pages/plugin_api.dart
|
||||
class ZoomMapPlugin extends MapPlugin {
|
||||
IconButton zoomButton(MapState mapState, IconData zoomIcon, int inc) {
|
||||
return new IconButton(
|
||||
icon: Icon(zoomIcon),
|
||||
onPressed: () {
|
||||
var currentZoom = mapState.zoom;
|
||||
var currentCenter = mapState.center;
|
||||
mapState.move(currentCenter, currentZoom + inc);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget createLayer(LayerOptions options, MapState mapState) {
|
||||
if (options is ZoomMapPluginOptions) {
|
||||
/* print('point ${mapState
|
||||
.getPixelBounds(mapState.zoom)
|
||||
.bottomLeft}'); */
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) =>
|
||||
Stack(fit: StackFit.expand, children: <Widget>[
|
||||
Positioned(
|
||||
top: constraints.maxHeight - 100,
|
||||
right: 10.0,
|
||||
// left: 10.0,
|
||||
child: new CenteredRow(
|
||||
children: <Widget>[
|
||||
new Column(
|
||||
children: <Widget>[
|
||||
zoomButton(mapState, Icons.zoom_in, 1),
|
||||
zoomButton(mapState, Icons.zoom_out, -1)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
]));
|
||||
}
|
||||
throw ("Unknown options type for ZoomMapPlugin"
|
||||
"plugin: $options");
|
||||
}
|
||||
|
||||
@override
|
||||
bool supportsLayer(LayerOptions options) {
|
||||
return options is ZoomMapPluginOptions;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,11 +3,16 @@ 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';
|
||||
|
||||
String gmapKey;
|
||||
String firesApiKey;
|
||||
String firesApiUrl;
|
||||
String appName = 'All Against The Fire!';
|
||||
String appVersion = '0.0.1';
|
||||
String appLicense = "(c) 2017-2018 Comunes Association under the GNU Affero GPL v3";
|
||||
Widget appMediumIcon = Image.asset('images/logo-200.png', width: 60.0, height: 60.0);
|
||||
Widget appIcon = Image.asset('images/logo-200.png', width: 24.0, height: 24.0);
|
||||
Future<SharedPreferences> prefs = SharedPreferences.getInstance();
|
||||
final List<BasicLocation> yourLocations = [];
|
||||
final GetIt getIt = new GetIt();
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ class HomePage extends StatelessWidget {
|
|||
]),
|
||||
new Expanded(
|
||||
child: new FractionallySizedBox(
|
||||
alignment: FractionalOffset.bottomCenter,
|
||||
heightFactor: 0.9,
|
||||
alignment: FractionalOffset.center,
|
||||
heightFactor: 0.8,
|
||||
child: new Image.asset('images/logo-200.png',
|
||||
fit: BoxFit.fitHeight))),
|
||||
new Expanded(
|
||||
|
|
|
|||
|
|
@ -7,21 +7,24 @@ import 'package:comunes_flutter/comunes_flutter.dart';
|
|||
import 'colors.dart';
|
||||
import 'customBottomAppBar.dart';
|
||||
import 'dart:core';
|
||||
import 'globals.dart' as globals;
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert' show json;
|
||||
import 'slider.dart';
|
||||
import 'customMapPlugin.dart';
|
||||
import 'package:just_debounce_it/just_debounce_it.dart';
|
||||
|
||||
enum MarkType { position, fire, industry, falsePos }
|
||||
|
||||
class LeafletMap extends StatefulWidget {
|
||||
final BasicLocation location;
|
||||
final String title;
|
||||
final int numFires;
|
||||
final String kmAround;
|
||||
|
||||
LeafletMap({@required this.title, @required this.location,
|
||||
@required this.numFires, @required this.kmAround
|
||||
});
|
||||
LeafletMap({@required this.title, @required this.location});
|
||||
|
||||
@override
|
||||
_LeafletMapState createState() => _LeafletMapState(title: title, location: location,
|
||||
numFires: numFires, kmAround: kmAround
|
||||
);
|
||||
_LeafletMapState createState() =>
|
||||
_LeafletMapState(title: title, location: location);
|
||||
}
|
||||
|
||||
class _LeafletMapState extends State<LeafletMap> {
|
||||
|
|
@ -29,14 +32,81 @@ class _LeafletMapState extends State<LeafletMap> {
|
|||
|
||||
final BasicLocation location;
|
||||
final String title;
|
||||
final int numFires;
|
||||
final String kmAround;
|
||||
int numFires;
|
||||
int kmAround = 100;
|
||||
List<dynamic> fires = [];
|
||||
List<dynamic> falsePos = [];
|
||||
List<dynamic> industries = [];
|
||||
|
||||
_LeafletMapState({@required this.title, @required this.location,
|
||||
@required this.numFires, @required this.kmAround
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
updateFireStats();
|
||||
}
|
||||
|
||||
void updateFireStats() {
|
||||
var url = '${globals.firesApiUrl}fires-in-full/${globals
|
||||
.firesApiKey}/${location.lat}/${location.lon}/$kmAround';
|
||||
http.read(url).then((result) {
|
||||
setState(() {
|
||||
var resultDecoded = json.decode(result);
|
||||
print(resultDecoded);
|
||||
numFires = resultDecoded['real'];
|
||||
fires = resultDecoded['fires'];
|
||||
falsePos = resultDecoded['falsePos'];
|
||||
industries = resultDecoded['industries'];
|
||||
|
||||
var firesCount = fires.length;
|
||||
var industriesCount = industries.length;
|
||||
var falsePosCount = falsePos.length;
|
||||
|
||||
print(
|
||||
'fire: $firesCount falsePos: $falsePosCount industries: $industriesCount');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_LeafletMapState({@required this.title, @required this.location});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
MapOptions mapOptions = new MapOptions(
|
||||
center: new LatLng(this.location.lat, this.location.lon),
|
||||
plugins: [new ZoomMapPlugin()],
|
||||
// this works ?
|
||||
// interactive: false,
|
||||
zoom: 13.0,
|
||||
// THIS does not works as expected
|
||||
// maxZoom: 6.0,
|
||||
onPositionChanged: (positionCallback) {
|
||||
// decouple
|
||||
// print('${positionCallback.center}, ${positionCallback.zoom}');
|
||||
});
|
||||
var mapController = new MapController();
|
||||
// mapController.fitBounds(bounds);
|
||||
// mapController.center
|
||||
var fmap = new FlutterMap(
|
||||
options: mapOptions,
|
||||
mapController: mapController,
|
||||
layers: [
|
||||
new TileLayerOptions(
|
||||
maxZoom: 6.0,
|
||||
subdomains: ['a', 'b', 'c'],
|
||||
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 ZoomMapPluginOptions(),
|
||||
new MarkerLayerOptions(
|
||||
markers: buildMarkers(
|
||||
this.location, this.fires, this.industries, this.falsePos),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return new Scaffold(
|
||||
key: _scaffoldKey,
|
||||
// drawer: new MainDrawer(context),
|
||||
|
|
@ -44,11 +114,12 @@ class _LeafletMapState extends State<LeafletMap> {
|
|||
title: new Text(title),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
onPressed: () {
|
||||
|
||||
},
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.notifications_none, color: fires600),
|
||||
label: new Text('Subscribe', style: const TextStyle(color: fires600),),
|
||||
label: new Text(
|
||||
'Subscribe',
|
||||
style: const TextStyle(color: fires600),
|
||||
),
|
||||
backgroundColor: Colors.white,
|
||||
),
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
|
||||
|
|
@ -56,54 +127,90 @@ class _LeafletMapState extends State<LeafletMap> {
|
|||
fabLocation: FloatingActionButtonLocation.centerFloat,
|
||||
showNotch: false,
|
||||
color: fires100,
|
||||
// height: 170.0,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
actions: listWithoutNulls(<Widget>[
|
||||
numFires > 0 ?
|
||||
new Text('${numFires.toString()} fires at $kmAround км around this area'):
|
||||
new Text('There is no fires at $kmAround км around this area'),
|
||||
numFires == null
|
||||
? null
|
||||
: numFires > 0
|
||||
? new Text('${numFires.toString()} fires at ${kmAround
|
||||
.toString()} км around this area')
|
||||
: new Text(
|
||||
'There is no fires at ${kmAround.toString()} км around this area'),
|
||||
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'),
|
||||
),
|
||||
),
|
||||
body: LayoutBuilder(
|
||||
builder: (context, constraints) =>
|
||||
Stack(fit: StackFit.expand, children: <Widget>[
|
||||
// Material(color: Colors.yellowAccent),
|
||||
fmap,
|
||||
Positioned(
|
||||
top: constraints.maxHeight - 160,
|
||||
right: 10.0,
|
||||
left: 10.0,
|
||||
child: new CenteredRow(
|
||||
// Fit sample:
|
||||
// https://github.com/apptreesoftware/flutter_map/blob/master/flutter_map_example/lib/pages/map_controller.dart
|
||||
children: <Widget>[
|
||||
new FireDistanceSlider(
|
||||
initialValue: kmAround,
|
||||
onSlide: (distance) {
|
||||
setState(() {
|
||||
kmAround = distance;
|
||||
Debounce.seconds(1, updateFireStats);
|
||||
});
|
||||
})
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
]),
|
||||
));
|
||||
}
|
||||
|
||||
List<Marker> buildMarkers(BasicLocation yourLocation, List<dynamic> fires,
|
||||
List<dynamic> falsePos, List<dynamic> industries) {
|
||||
List<Marker> markers = [];
|
||||
markers.add(buildMarker(yourLocation, MarkType.position));
|
||||
falsePos.forEach((fire) {
|
||||
var coords = fire['geo']['coordinates'];
|
||||
var loc = BasicLocation(lon: coords[0], lat: coords[1]);
|
||||
markers.add(buildMarker(loc, MarkType.falsePos));
|
||||
});
|
||||
industries.forEach((fire) {
|
||||
// print(fire['geo']['coordinates']);
|
||||
var coords = fire['geo']['coordinates'];
|
||||
var loc = BasicLocation(lon: coords[0], lat: coords[1]);
|
||||
markers.add(buildMarker(loc, MarkType.industry));
|
||||
});
|
||||
fires.forEach((fire) {
|
||||
var loc = new BasicLocation(lat: fire['lat'], lon: fire['lon']);
|
||||
markers.add(buildMarker(loc, MarkType.fire));
|
||||
});
|
||||
return markers;
|
||||
}
|
||||
|
||||
Marker buildMarker(location, MarkType type) {
|
||||
return new Marker(
|
||||
width: 80.0,
|
||||
height: 80.0,
|
||||
point: new LatLng(location.lat, location.lon),
|
||||
builder: (ctx) => new Container(
|
||||
child: buildImageMark(type),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildImageMark(MarkType type) {
|
||||
switch (type) {
|
||||
case MarkType.position:
|
||||
return new Icon(Icons.location_on, color: fires600, size: 50.0);
|
||||
case MarkType.fire:
|
||||
return new Image.asset('images/fire-marker-l.png');
|
||||
case MarkType.industry:
|
||||
return new Image.asset('images/industry-marker-reg.png');
|
||||
case MarkType.falsePos:
|
||||
default:
|
||||
return new Image.asset('images/industry-marker.png');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ void main() {
|
|||
globals.firesApiKey = secrets['firesApiKey'];
|
||||
globals.firesApiUrl = secrets['firesApiUrl'];
|
||||
globals.prefs.then((prefs) {
|
||||
loadYourLocations(prefs);
|
||||
loadYourLocationsWithPrefs(prefs);
|
||||
|
||||
// Run baby run!
|
||||
runApp(new FiresApp());
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ Widget mainDrawer(BuildContext context) {
|
|||
),
|
||||
),
|
||||
),
|
||||
new ListTile(
|
||||
/* new ListTile(
|
||||
// https://docs.flutter.io/flutter/material/CircleAvatar-class.html
|
||||
leading: new CircleAvatar(
|
||||
backgroundColor: Colors.brown.shade800,
|
||||
|
|
@ -49,14 +49,10 @@ Widget mainDrawer(BuildContext context) {
|
|||
),
|
||||
title: new Text('Your profile'),
|
||||
onTap: () {
|
||||
// Update the state of the app
|
||||
// ...
|
||||
// Then close the drawer
|
||||
// Navigator.pop(context);
|
||||
Navigator.pushNamed(context, IntroPage.routeName);
|
||||
},
|
||||
),
|
||||
new Divider(),
|
||||
new Divider(), */
|
||||
new ListTile(
|
||||
leading: const Icon(Icons.whatshot),
|
||||
title: new Text('Active fires'),
|
||||
|
|
@ -65,14 +61,22 @@ Widget mainDrawer(BuildContext context) {
|
|||
},
|
||||
),
|
||||
new ListTile(
|
||||
leading: const Icon(Icons.location_on),
|
||||
title: new Text('My subscribed areas'),
|
||||
leading: const Icon(Icons.notifications_active),
|
||||
title: new Text('Notify a fire'),
|
||||
onTap: () {
|
||||
// Then close the drawer
|
||||
Navigator.pushNamed(context, Sandbox.routeName);
|
||||
},
|
||||
),
|
||||
new Divider(),
|
||||
new ListTile(
|
||||
leading: const Icon(Icons.favorite),
|
||||
title: new Text('Support this initiative'),
|
||||
onTap: () {
|
||||
// Then close the drawer
|
||||
Navigator.pushNamed(context, '/subscriptions');
|
||||
},
|
||||
),
|
||||
new Divider(),
|
||||
new ListTile(
|
||||
leading: const Icon(Icons.bug_report),
|
||||
title: new Text('Sandbox'),
|
||||
|
|
@ -92,6 +96,14 @@ Widget mainDrawer(BuildContext context) {
|
|||
},
|
||||
),
|
||||
new AboutListTile(
|
||||
icon: globals.appIcon,
|
||||
applicationName: globals.appName,
|
||||
applicationVersion: globals.appVersion,
|
||||
applicationIcon: globals.appMediumIcon,
|
||||
applicationLegalese: globals.appLicense,
|
||||
aboutBoxChildren: <Widget> [
|
||||
// new Text('What?')
|
||||
]
|
||||
// FIXME
|
||||
)
|
||||
],
|
||||
|
|
|
|||
|
|
@ -15,11 +15,23 @@ class Sandbox extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
//showDialog(context: context, child: builder(context));
|
||||
return Scaffold(
|
||||
body: new CenteredColumn(
|
||||
children: <Widget>[
|
||||
new FireDistanceSlider()
|
||||
|
||||
],
|
||||
body: LayoutBuilder(
|
||||
builder: (context, constraints) =>
|
||||
Stack(fit: StackFit.expand, children: <Widget>[
|
||||
// Material(color: Colors.yellowAccent),
|
||||
Positioned(
|
||||
top: 0.0,
|
||||
child: Icon(Icons.star, size: 40.0),
|
||||
),
|
||||
Positioned(
|
||||
top: constraints.maxHeight - 80,
|
||||
right: 10.0,
|
||||
left: 10.0,
|
||||
child: new CenteredRow(
|
||||
children: <Widget>[new FireDistanceSlider()],
|
||||
),
|
||||
)
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,45 +1,68 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'colors.dart';
|
||||
import 'package:comunes_flutter/comunes_flutter.dart';
|
||||
import 'package:padder/padding.dart';
|
||||
|
||||
typedef void SlideCallback(int distance);
|
||||
|
||||
class FireDistanceSlider extends StatefulWidget {
|
||||
final int initialValue;
|
||||
final SlideCallback onSlide;
|
||||
|
||||
FireDistanceSlider({this.initialValue, this.onSlide});
|
||||
|
||||
@override
|
||||
_FireDistanceSliderState createState() => new _FireDistanceSliderState();
|
||||
_FireDistanceSliderState createState() => new _FireDistanceSliderState(
|
||||
initialValue: initialValue, onSlide: onSlide);
|
||||
}
|
||||
|
||||
class _FireDistanceSliderState extends State<FireDistanceSlider> {
|
||||
int _discreteValue = 10;
|
||||
int _sliderValue;
|
||||
final SlideCallback onSlide;
|
||||
|
||||
_FireDistanceSliderState({int initialValue = 10, this.onSlide}) {
|
||||
this._sliderValue = initialValue;
|
||||
}
|
||||
|
||||
sizeText(_sliderValue) =>
|
||||
new Text('Subscribe to $_sliderValue км around this area');
|
||||
|
||||
warningText(_sliderValue) => _sliderValue >= 50
|
||||
? new Text('Warning: this is a very large area',
|
||||
style: new TextStyle(color: fires900))
|
||||
: new Text('');
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new Column(
|
||||
children: <Widget>[
|
||||
new Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children:
|
||||
listWithoutNulls(<Widget>[
|
||||
new Slider(
|
||||
value: _discreteValue + 0.0,
|
||||
var slider = new Slider(
|
||||
value: _sliderValue + 0.0,
|
||||
activeColor: fires900,
|
||||
inactiveColor: Colors.black45,
|
||||
|
||||
min: 1.0,
|
||||
max: 100.0,
|
||||
divisions: 99,
|
||||
label: '${_discreteValue.round()}',
|
||||
label: '${_sliderValue.round()}',
|
||||
onChanged: (double value) {
|
||||
setState(() {
|
||||
_discreteValue = value.round();
|
||||
});
|
||||
_sliderValue = value.round();
|
||||
if (onSlide != null) {
|
||||
onSlide(_sliderValue);
|
||||
}
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
new Text('Subscribe to $_discreteValue км around this area'),
|
||||
_discreteValue >= 50 ?
|
||||
new Text(
|
||||
'Warning: this is a very large area',
|
||||
style: new TextStyle(color: Colors.orange)
|
||||
): new Text('')
|
||||
);
|
||||
return new Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: listWithoutNulls(<Widget>[
|
||||
slider,
|
||||
new Card(
|
||||
color: const Color(0x60FFFFFF),
|
||||
child: PaddingAll(10.0,
|
||||
child: new Column(children: <Widget>[
|
||||
sizeText(_sliderValue),
|
||||
warningText(_sliderValue)
|
||||
])))
|
||||
]),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ dependencies:
|
|||
http: "^0.11.3+16"
|
||||
simple_moment: "^0.0.3"
|
||||
geocoder: "^0.0.1"
|
||||
just_debounce_it: "^1.0.4"
|
||||
flutter_fab_dialer: "^0.0.5"
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
@ -52,6 +54,8 @@ flutter:
|
|||
- images/fire-marker-m.png
|
||||
- images/fire-marker-s.png
|
||||
- images/fire-marker.png
|
||||
- images/industry-marker-reg.png
|
||||
- images/industry-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