Added fire locations
This commit is contained in:
parent
c4438365ab
commit
8e4e469eb8
15 changed files with 607 additions and 49 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -19,3 +19,6 @@ pubspec.lock
|
|||
# Directory created by dartdoc
|
||||
# If you don't generate documentation locally you can remove this line.
|
||||
doc/api/
|
||||
|
||||
assets/private-settings.json
|
||||
android/app/src/main/AndroidManifest.xml
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.comunes.firesflutter">
|
||||
package="org.comunes.firesflutter">
|
||||
|
||||
<!-- The INTERNET permission is required for development. Specifically,
|
||||
flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||
|
||||
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
|
||||
calls FlutterMain.startInitialization(this); in its onCreate method.
|
||||
|
|
@ -13,27 +15,31 @@
|
|||
additional functionality it is fine to subclass or reimplement
|
||||
FlutterApplication and put your custom class here. -->
|
||||
<application
|
||||
android:name="io.flutter.app.FlutterApplication"
|
||||
android:label="All Against The Fire!"
|
||||
android:icon="@mipmap/launch_image">
|
||||
android:name="io.flutter.app.FlutterApplication"
|
||||
android:label="All Against The Fire!"
|
||||
android:icon="@mipmap/launch_image">
|
||||
<meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="PUT_YOUR_API_HERE"/>
|
||||
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version"/>
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
android:name=".MainActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- This keeps the window background of the activity showing
|
||||
until Flutter renders its first frame. It can be removed if
|
||||
there is no splash screen (such as the default splash screen
|
||||
defined in @style/LaunchTheme). -->
|
||||
<meta-data
|
||||
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
|
||||
android:value="true" />
|
||||
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
|
||||
android:value="true"/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.apptreesoftware.mapview.MapActivity"
|
||||
android:theme="@style/Theme.AppCompat.Light.DarkActionBar"/>
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
@ -6,6 +6,7 @@ buildscript {
|
|||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.0.1'
|
||||
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.2-4'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,5 +41,9 @@
|
|||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string>Your location will be used to center our map and display fires near you</string>
|
||||
<key>NSLocationAlwaysUsageDescription</key>
|
||||
<string>Your location will be used notify fires near you</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -1,34 +1,244 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:location/location.dart';
|
||||
import 'mainDrawer.dart';
|
||||
import 'genericMap.dart';
|
||||
import 'dart:async';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:comunes_flutter/comunes_flutter.dart';
|
||||
import 'colors.dart';
|
||||
import 'placesAutocompleteUtils.dart';
|
||||
import 'basicLocation.dart';
|
||||
import 'package:collection/collection.dart' show lowerBound;
|
||||
|
||||
class ActiveFiresMap extends StatelessWidget {
|
||||
static const String routeName = '/fires';
|
||||
class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
||||
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
||||
// https://docs.flutter.io/flutter/dart-core/List-class.html
|
||||
final List<BasicLocation> _saved = [];
|
||||
int length;
|
||||
|
||||
_ActiveFiresPageState();
|
||||
|
||||
Widget _buildRow(BasicLocation loc) {
|
||||
String desc = loc.description != null ? loc.description:
|
||||
'Position: ${loc.lat}, ${loc.lon}';
|
||||
return new ListTile(
|
||||
dense: false,
|
||||
leading: const Icon(Icons.location_on),
|
||||
// trailing: const Icon(Icons.delete),
|
||||
title: new Text(desc),
|
||||
onTap: () {
|
||||
Navigator.push(context,
|
||||
new MaterialPageRoute(builder: (context) => new GenericMap(title: desc, latitude: loc.lat, longitude: loc.lon)));
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildSavedLocations() {
|
||||
return new ListView.builder(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
itemCount: _saved.length,
|
||||
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(_saved.elementAt(i));
|
||||
});
|
||||
}
|
||||
|
||||
void handleUndo(BasicLocation item) {
|
||||
print('Undo $item');
|
||||
final int insertionIndex = lowerBound(_saved, item);
|
||||
setState(() {
|
||||
_saved.insert(insertionIndex, item);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildItem(BasicLocation item) {
|
||||
final ThemeData theme = Theme.of(context);
|
||||
return new Dismissible(
|
||||
key: new ObjectKey(item),
|
||||
direction: DismissDirection.horizontal,
|
||||
onDismissed: (DismissDirection direction) {
|
||||
setState(() {
|
||||
_saved.remove(item);
|
||||
});
|
||||
final String action = 'deleted';
|
||||
_scaffoldKey.currentState.showSnackBar(new SnackBar(
|
||||
content: new Text('You $action item'), // ${item.index}'),
|
||||
action: new SnackBarAction(
|
||||
label: 'UNDO',
|
||||
onPressed: () {
|
||||
handleUndo(item);
|
||||
})));
|
||||
},
|
||||
background: new Container(
|
||||
color: theme.primaryColor,
|
||||
child: const ListTile(
|
||||
leading:
|
||||
const Icon(Icons.delete, color: Colors.white, size: 36.0))),
|
||||
secondaryBackground: new Container(
|
||||
color: theme.primaryColor,
|
||||
child: const ListTile(
|
||||
trailing:
|
||||
const Icon(Icons.delete, color: Colors.white, size: 36.0))),
|
||||
child: new Container(
|
||||
decoration: new BoxDecoration(
|
||||
color: theme.canvasColor,
|
||||
border: new Border(
|
||||
bottom: new BorderSide(color: theme.dividerColor))),
|
||||
child: _buildRow(item)));
|
||||
}
|
||||
|
||||
Future<BasicLocation> getUserLocation() async {
|
||||
// FIXME do something with this
|
||||
|
||||
String error;
|
||||
// Platform messages may fail, so we use a try/catch PlatformException.
|
||||
try {
|
||||
Location _location = new Location();
|
||||
Map<String, double> location = await _location.getLocation;
|
||||
error = null;
|
||||
print('location $location');
|
||||
|
||||
// It seems that the lib fails with lat/lon values
|
||||
return new BasicLocation(
|
||||
lon: location['latitude'], lat: location['longitude']);
|
||||
} on PlatformException catch (e) {
|
||||
if (e.code == 'PERMISSION_DENIED') {
|
||||
error = 'Permission denied';
|
||||
} else if (e.code == 'PERMISSION_DENIED_NEVER_ASK') {
|
||||
error =
|
||||
'Permission denied - please ask the user to enable it from the app settings';
|
||||
}
|
||||
// FIXME
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
length = _saved.length;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
print('Building Active Fires, saved $length');
|
||||
final title = 'Active fires locations';
|
||||
return Scaffold(
|
||||
key: _scaffoldKey,
|
||||
drawer: new MainDrawer(context),
|
||||
appBar: new AppBar(
|
||||
title: Text('Active fires'),
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.menu),
|
||||
key: _scaffoldKey,
|
||||
drawer: new MainDrawer(context),
|
||||
appBar: new AppBar(
|
||||
title: Text(title),
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.menu),
|
||||
onPressed: () {
|
||||
_scaffoldKey.currentState.openDrawer();
|
||||
},
|
||||
),
|
||||
/* actions: <Widget>[
|
||||
new IconButton(
|
||||
icon: Icon(Icons.location_searching), onPressed: () {}),
|
||||
IconButton(
|
||||
icon: Icon(Icons.more_vert),
|
||||
onPressed: () {
|
||||
_scaffoldKey.currentState.openDrawer();
|
||||
print('More button');
|
||||
},
|
||||
),
|
||||
actions: <Widget>[
|
||||
new IconButton(
|
||||
icon: Icon(Icons.location_searching), onPressed: () => {}),
|
||||
IconButton(
|
||||
icon: Icon(Icons.more_vert),
|
||||
onPressed: () {
|
||||
print('More button');
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: new Text('No active fires'));
|
||||
], */
|
||||
),
|
||||
body: length > 0
|
||||
? _buildSavedLocations()
|
||||
: new Center(
|
||||
child: new CenteredColumn(children: <Widget>[
|
||||
new RoundedBtn(
|
||||
icon: Icons.location_searching,
|
||||
text: 'Add your position',
|
||||
onPressed: onAddYourLocation,
|
||||
backColor: fires600),
|
||||
const SizedBox(height: 26.0),
|
||||
new RoundedBtn(
|
||||
icon: Icons.edit_location,
|
||||
text: 'Add some other place',
|
||||
onPressed: () {
|
||||
onAddOtherLocation(context);
|
||||
},
|
||||
backColor: fires600),
|
||||
])),
|
||||
floatingActionButton: length > 0
|
||||
? Column(mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[
|
||||
FloatingActionButton.extended(
|
||||
onPressed: onAddYourLocation,
|
||||
heroTag: 'yourposition',
|
||||
label: const Text('Add your position'),
|
||||
icon: const Icon(Icons.location_searching),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 16.0),
|
||||
child: FloatingActionButton.extended(
|
||||
onPressed: () {
|
||||
onAddOtherLocation(context);
|
||||
},
|
||||
heroTag: 'otherplace',
|
||||
label: new Text('Add some other place'),
|
||||
icon: const Icon(Icons.edit_location),
|
||||
),
|
||||
)
|
||||
])
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
void onAddYourLocation() {
|
||||
Future<BasicLocation> location = getUserLocation();
|
||||
_saveLocation(location);
|
||||
}
|
||||
|
||||
void onAddOtherLocation(BuildContext context) {
|
||||
Future<BasicLocation> location = openPlacesDialog(context);
|
||||
_saveLocation(location);
|
||||
}
|
||||
|
||||
void _saveLocation(Future<BasicLocation> location) {
|
||||
location.then((newLocation) {
|
||||
this.setState(() {
|
||||
_saved.add(newLocation);
|
||||
length = _saved.length;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class MyDrawer extends StatelessWidget {
|
||||
static final MyDrawer _drawer = new MyDrawer._internal();
|
||||
|
||||
factory MyDrawer() {
|
||||
return _drawer;
|
||||
}
|
||||
|
||||
MyDrawer._internal();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new Text("Drawer");
|
||||
}
|
||||
}
|
||||
|
||||
class ActiveFiresPage extends StatefulWidget {
|
||||
static const String routeName = '/fires';
|
||||
|
||||
ActiveFiresPage();
|
||||
|
||||
@override
|
||||
_ActiveFiresPageState createState() => _ActiveFiresPageState();
|
||||
}
|
||||
|
|
|
|||
9
lib/basicLocation.dart
Normal file
9
lib/basicLocation.dart
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class BasicLocation {
|
||||
final double lat;
|
||||
final double lon;
|
||||
final String description;
|
||||
|
||||
BasicLocation({@required this.lat, @required this.lon, this.description});
|
||||
}
|
||||
|
|
@ -5,16 +5,26 @@ import 'theme.dart';
|
|||
import 'introPage.dart';
|
||||
import 'sandbox.dart';
|
||||
import 'activeFires.dart';
|
||||
import 'placesAutocompleteWidget.dart';
|
||||
import 'globals.dart' as globals;
|
||||
|
||||
class FiresApp extends StatelessWidget {
|
||||
final Map routes = <String, WidgetBuilder>{
|
||||
Sandbox.routeName: (BuildContext context) => new Sandbox(),
|
||||
ActiveFiresMap.routeName: (BuildContext context) => new ActiveFiresMap(),
|
||||
IntroPage.routeName: (BuildContext context) => new IntroPage(),
|
||||
};
|
||||
|
||||
|
||||
Map routes = <String, WidgetBuilder>{
|
||||
PlacesAutocompleteWidget.routeName: (BuildContext context) =>
|
||||
new PlacesAutocompleteWidget(),
|
||||
Sandbox.routeName: (BuildContext context) => new Sandbox(),
|
||||
ActiveFiresPage.routeName: (BuildContext context) =>
|
||||
new ActiveFiresPage(),
|
||||
IntroPage.routeName: (BuildContext context) => new IntroPage(),
|
||||
};
|
||||
final WidgetBuilder introWidget = (context) => new IntroPage();
|
||||
final WidgetBuilder continueWidget = (context) => new HomePage();
|
||||
|
||||
// globals.getIt.registerSingleton
|
||||
FiresApp();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new MaterialApp(
|
||||
|
|
|
|||
173
lib/genericMap.dart
Normal file
173
lib/genericMap.dart
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
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();
|
||||
}
|
||||
}
|
||||
6
lib/globals.dart
Normal file
6
lib/globals.dart
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
library fires.globals;
|
||||
import 'package:get_it/get_it.dart';
|
||||
|
||||
String gmapKey;
|
||||
|
||||
final GetIt getIt = new GetIt();
|
||||
|
|
@ -33,12 +33,12 @@ class HomePage extends StatelessWidget {
|
|||
heightFactor: 0.9,
|
||||
child: new CenteredColumn(
|
||||
children: <Widget>[
|
||||
customBtn(
|
||||
Icons.whatshot,
|
||||
"Active fires",
|
||||
context,
|
||||
// const EdgeInsets.only(left: 60.0),
|
||||
ActiveFiresMap.routeName),
|
||||
new RoundedBtn.nav(
|
||||
icon: Icons.whatshot,
|
||||
text: 'Active fires',
|
||||
context: context,
|
||||
route: ActiveFiresPage.routeName,
|
||||
backColor: fires600),
|
||||
],
|
||||
)))
|
||||
])),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,18 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'firesApp.dart';
|
||||
import 'package:comunes_flutter/comunes_flutter.dart';
|
||||
import 'dart:async';
|
||||
import 'globals.dart' as globals;
|
||||
|
||||
void main() => runApp(new FiresApp());
|
||||
Future<String> getGMapKey() async {
|
||||
Secret secret = await SecretLoader(secretPath: 'assets/private-settings.json')
|
||||
.load('gmap_key');
|
||||
return secret.apiKey;
|
||||
}
|
||||
|
||||
void main() {
|
||||
getGMapKey().then((String gmapKey) {
|
||||
globals.gmapKey = gmapKey;
|
||||
runApp(new FiresApp());
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'colors.dart';
|
|||
import 'sandbox.dart';
|
||||
import 'introPage.dart';
|
||||
import 'activeFires.dart';
|
||||
import 'placesAutocompleteWidget.dart';
|
||||
|
||||
class MainDrawer extends Drawer {
|
||||
MainDrawer(BuildContext context, {key})
|
||||
|
|
@ -60,7 +61,7 @@ Widget mainDrawer(BuildContext context) {
|
|||
leading: const Icon(Icons.whatshot),
|
||||
title: new Text('Active fires'),
|
||||
onTap: () {
|
||||
Navigator.pushNamed(context, ActiveFiresMap.routeName);
|
||||
Navigator.pushNamed(context, ActiveFiresPage.routeName);
|
||||
},
|
||||
),
|
||||
new ListTile(
|
||||
|
|
@ -71,6 +72,14 @@ Widget mainDrawer(BuildContext context) {
|
|||
Navigator.pushNamed(context, '/subscriptions');
|
||||
},
|
||||
),
|
||||
new ListTile(
|
||||
leading: const Icon(Icons.location_on),
|
||||
title: new Text('Places'),
|
||||
onTap: () {
|
||||
// Then close the drawer
|
||||
Navigator.pushNamed(context, PlacesAutocompleteWidget.routeName);
|
||||
},
|
||||
),
|
||||
new Divider(),
|
||||
new ListTile(
|
||||
leading: const Icon(Icons.bug_report),
|
||||
|
|
|
|||
32
lib/placesAutocompleteUtils.dart
Normal file
32
lib/placesAutocompleteUtils.dart
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import 'package:flutter_google_places_autocomplete/flutter_google_places_autocomplete.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:async';
|
||||
import 'basicLocation.dart';
|
||||
import 'globals.dart' as globals;
|
||||
|
||||
Future<BasicLocation> openPlacesDialog(BuildContext context) async {
|
||||
Mode _mode = Mode.overlay;
|
||||
GoogleMapsPlaces _places = new GoogleMapsPlaces(globals.gmapKey);
|
||||
Prediction p = await showGooglePlacesAutocomplete(
|
||||
context: context,
|
||||
hint: 'Type the name of a place, region, etc',
|
||||
apiKey: globals.gmapKey,
|
||||
onError: (res) {
|
||||
/* homeScaffoldKey.currentState.showSnackBar(
|
||||
new SnackBar(content: new Text(res.errorMessage))); */
|
||||
print('Error $res');
|
||||
},
|
||||
mode: _mode,
|
||||
// FIXME
|
||||
language: "es",
|
||||
components: [new Component(Component.country, "es")]);
|
||||
if (p != null) {
|
||||
// get detail (lat/lng)
|
||||
PlacesDetailsResponse detail = await _places.getDetailsByPlaceId(p.placeId);
|
||||
final lat = detail.result.geometry.location.lat;
|
||||
final lng = detail.result.geometry.location.lng;
|
||||
|
||||
return new BasicLocation(lat: lat, lon: lng, description: p.description);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
75
lib/placesAutocompleteWidget.dart
Normal file
75
lib/placesAutocompleteWidget.dart
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
import 'package:flutter_google_places_autocomplete/flutter_google_places_autocomplete.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:async';
|
||||
import 'globals.dart' as globals;
|
||||
|
||||
class PlacesAutocompleteWidget extends StatefulWidget {
|
||||
static const String routeName = '/place';
|
||||
|
||||
PlacesAutocompleteWidget();
|
||||
|
||||
@override
|
||||
_PlacesAutocompleteWidgetState createState() =>
|
||||
_PlacesAutocompleteWidgetState();
|
||||
}
|
||||
|
||||
// to get places detail (lat/lng)
|
||||
|
||||
final homeScaffoldKey = new GlobalKey<ScaffoldState>();
|
||||
final searchScaffoldKey = new GlobalKey<ScaffoldState>();
|
||||
|
||||
GoogleMapsPlaces _places;
|
||||
|
||||
class _PlacesAutocompleteWidgetState extends State<PlacesAutocompleteWidget> {
|
||||
Mode _mode = Mode.overlay;
|
||||
|
||||
_PlacesAutocompleteWidgetState() {
|
||||
_places = new GoogleMapsPlaces(globals.gmapKey);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new Scaffold(
|
||||
key: homeScaffoldKey,
|
||||
appBar: new AppBar(
|
||||
title: new Text("My App"),
|
||||
),
|
||||
body:
|
||||
|
||||
new RaisedButton.icon(
|
||||
icon: const Icon(Icons.location_searching),
|
||||
label: new Text('Write a location'),
|
||||
onPressed: () async {
|
||||
// show input autocomplete with selected mode
|
||||
// then get the Prediction selected
|
||||
Prediction p = await showGooglePlacesAutocomplete(
|
||||
context: context,
|
||||
hint: 'Type the name of place, region, etc',
|
||||
apiKey: globals.gmapKey,
|
||||
onError: (res) {
|
||||
homeScaffoldKey.currentState.showSnackBar(
|
||||
new SnackBar(content: new Text(res.errorMessage)));
|
||||
},
|
||||
mode: _mode,
|
||||
// FIXME
|
||||
language: "es",
|
||||
components: [new Component(Component.country, "es")]);
|
||||
displayPrediction(p, homeScaffoldKey.currentState);
|
||||
},
|
||||
)
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<Null> displayPrediction(Prediction p, ScaffoldState scaffold) async {
|
||||
if (p != null) {
|
||||
// get detail (lat/lng)
|
||||
PlacesDetailsResponse detail = await _places.getDetailsByPlaceId(p.placeId);
|
||||
final lat = detail.result.geometry.location.lat;
|
||||
final lng = detail.result.geometry.location.lng;
|
||||
|
||||
scaffold.showSnackBar(
|
||||
new SnackBar(content: new Text("${p.description} - $lat/$lng")));
|
||||
}
|
||||
}
|
||||
12
pubspec.yaml
12
pubspec.yaml
|
|
@ -4,17 +4,22 @@ description: All Against Fire
|
|||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
intl: ^0.15.6
|
||||
intl: "^0.15.6"
|
||||
# https://pub.dartlang.org/packages/padder
|
||||
padder: ^1.0.1
|
||||
padder: "^1.0.1"
|
||||
# https://pub.dartlang.org/packages/flutter
|
||||
fluttery: "^0.0.7"
|
||||
shared_preferences: "^0.4.2"
|
||||
comunes_flutter:
|
||||
path: /home/vjrj/dev/comunes_flutter
|
||||
version: "^0.0.4"
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: ^0.1.2
|
||||
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"
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
@ -39,6 +44,7 @@ flutter:
|
|||
|
||||
assets:
|
||||
- images/logo-200.png
|
||||
- assets/private-settings.json
|
||||
|
||||
# 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