Added fire locations

This commit is contained in:
vjrj 2018-06-09 13:09:29 +02:00
parent c4438365ab
commit 8e4e469eb8
15 changed files with 607 additions and 49 deletions

3
.gitignore vendored
View file

@ -19,3 +19,6 @@ pubspec.lock
# Directory created by dartdoc # Directory created by dartdoc
# If you don't generate documentation locally you can remove this line. # If you don't generate documentation locally you can remove this line.
doc/api/ doc/api/
assets/private-settings.json
android/app/src/main/AndroidManifest.xml

View file

@ -1,11 +1,13 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <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, <!-- The INTERNET permission is required for development. Specifically,
flutter needs it to communicate with the running application flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc. to allow setting breakpoints, to provide hot reload, etc.
--> -->
<uses-permission android:name="android.permission.INTERNET"/> <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 <!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method. calls FlutterMain.startInitialization(this); in its onCreate method.
@ -13,27 +15,31 @@
additional functionality it is fine to subclass or reimplement additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. --> FlutterApplication and put your custom class here. -->
<application <application
android:name="io.flutter.app.FlutterApplication" android:name="io.flutter.app.FlutterApplication"
android:label="All Against The Fire!" android:label="All Against The Fire!"
android:icon="@mipmap/launch_image"> 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 <activity
android:name=".MainActivity" android:name=".MainActivity"
android:launchMode="singleTop" android:launchMode="singleTop"
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<!-- This keeps the window background of the activity showing <!-- This keeps the window background of the activity showing
until Flutter renders its first frame. It can be removed if until Flutter renders its first frame. It can be removed if
there is no splash screen (such as the default splash screen there is no splash screen (such as the default splash screen
defined in @style/LaunchTheme). --> defined in @style/LaunchTheme). -->
<meta-data <meta-data
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame" android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
android:value="true" /> android:value="true"/>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name="com.apptreesoftware.mapview.MapActivity"
android:theme="@style/Theme.AppCompat.Light.DarkActionBar"/>
</application> </application>
</manifest> </manifest>

View file

@ -6,6 +6,7 @@ buildscript {
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.0.1' classpath 'com.android.tools.build:gradle:3.0.1'
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.2-4'
} }
} }

View file

@ -41,5 +41,9 @@
</array> </array>
<key>UIViewControllerBasedStatusBarAppearance</key> <key>UIViewControllerBasedStatusBarAppearance</key>
<false/> <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> </dict>
</plist> </plist>

View file

@ -1,34 +1,244 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:location/location.dart';
import 'mainDrawer.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 { class _ActiveFiresPageState extends State<ActiveFiresPage> {
static const String routeName = '/fires';
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>(); 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
print('Building Active Fires, saved $length');
final title = 'Active fires locations';
return Scaffold( return Scaffold(
key: _scaffoldKey, key: _scaffoldKey,
drawer: new MainDrawer(context), drawer: new MainDrawer(context),
appBar: new AppBar( appBar: new AppBar(
title: Text('Active fires'), title: Text(title),
leading: IconButton( leading: IconButton(
icon: Icon(Icons.menu), 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: () { onPressed: () {
_scaffoldKey.currentState.openDrawer(); print('More button');
}, },
), ),
actions: <Widget>[ ], */
new IconButton( ),
icon: Icon(Icons.location_searching), onPressed: () => {}), body: length > 0
IconButton( ? _buildSavedLocations()
icon: Icon(Icons.more_vert), : new Center(
onPressed: () { child: new CenteredColumn(children: <Widget>[
print('More button'); new RoundedBtn(
}, icon: Icons.location_searching,
), text: 'Add your position',
], onPressed: onAddYourLocation,
), backColor: fires600),
body: new Text('No active fires')); 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
View 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});
}

View file

@ -5,16 +5,26 @@ import 'theme.dart';
import 'introPage.dart'; import 'introPage.dart';
import 'sandbox.dart'; import 'sandbox.dart';
import 'activeFires.dart'; import 'activeFires.dart';
import 'placesAutocompleteWidget.dart';
import 'globals.dart' as globals;
class FiresApp extends StatelessWidget { class FiresApp extends StatelessWidget {
final Map routes = <String, WidgetBuilder>{
Sandbox.routeName: (BuildContext context) => new Sandbox(),
ActiveFiresMap.routeName: (BuildContext context) => new ActiveFiresMap(), Map routes = <String, WidgetBuilder>{
IntroPage.routeName: (BuildContext context) => new IntroPage(), 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 introWidget = (context) => new IntroPage();
final WidgetBuilder continueWidget = (context) => new HomePage(); final WidgetBuilder continueWidget = (context) => new HomePage();
// globals.getIt.registerSingleton
FiresApp();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new MaterialApp( return new MaterialApp(

173
lib/genericMap.dart Normal file
View 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
View file

@ -0,0 +1,6 @@
library fires.globals;
import 'package:get_it/get_it.dart';
String gmapKey;
final GetIt getIt = new GetIt();

View file

@ -33,12 +33,12 @@ class HomePage extends StatelessWidget {
heightFactor: 0.9, heightFactor: 0.9,
child: new CenteredColumn( child: new CenteredColumn(
children: <Widget>[ children: <Widget>[
customBtn( new RoundedBtn.nav(
Icons.whatshot, icon: Icons.whatshot,
"Active fires", text: 'Active fires',
context, context: context,
// const EdgeInsets.only(left: 60.0), route: ActiveFiresPage.routeName,
ActiveFiresMap.routeName), backColor: fires600),
], ],
))) )))
])), ])),

View file

@ -1,4 +1,18 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'firesApp.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());
});
}

View file

@ -4,6 +4,7 @@ import 'colors.dart';
import 'sandbox.dart'; import 'sandbox.dart';
import 'introPage.dart'; import 'introPage.dart';
import 'activeFires.dart'; import 'activeFires.dart';
import 'placesAutocompleteWidget.dart';
class MainDrawer extends Drawer { class MainDrawer extends Drawer {
MainDrawer(BuildContext context, {key}) MainDrawer(BuildContext context, {key})
@ -60,7 +61,7 @@ Widget mainDrawer(BuildContext context) {
leading: const Icon(Icons.whatshot), leading: const Icon(Icons.whatshot),
title: new Text('Active fires'), title: new Text('Active fires'),
onTap: () { onTap: () {
Navigator.pushNamed(context, ActiveFiresMap.routeName); Navigator.pushNamed(context, ActiveFiresPage.routeName);
}, },
), ),
new ListTile( new ListTile(
@ -71,6 +72,14 @@ Widget mainDrawer(BuildContext context) {
Navigator.pushNamed(context, '/subscriptions'); 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 Divider(),
new ListTile( new ListTile(
leading: const Icon(Icons.bug_report), leading: const Icon(Icons.bug_report),

View 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;
}

View 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")));
}
}

View file

@ -4,17 +4,22 @@ description: All Against Fire
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
intl: ^0.15.6 intl: "^0.15.6"
# https://pub.dartlang.org/packages/padder # https://pub.dartlang.org/packages/padder
padder: ^1.0.1 padder: "^1.0.1"
# https://pub.dartlang.org/packages/flutter # https://pub.dartlang.org/packages/flutter
fluttery: "^0.0.7" fluttery: "^0.0.7"
shared_preferences: "^0.4.2" shared_preferences: "^0.4.2"
comunes_flutter: comunes_flutter:
path: /home/vjrj/dev/comunes_flutter path: /home/vjrj/dev/comunes_flutter
version: "^0.0.4"
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # 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: dev_dependencies:
flutter_test: flutter_test:
@ -39,6 +44,7 @@ flutter:
assets: assets:
- images/logo-200.png - images/logo-200.png
- assets/private-settings.json
# An image asset can refer to one or more resolution-specific "variants", see # An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.io/assets-and-images/#resolution-aware. # https://flutter.io/assets-and-images/#resolution-aware.