diff --git a/lib/activeFires.dart b/lib/activeFires.dart index a896ea2..3040fcf 100644 --- a/lib/activeFires.dart +++ b/lib/activeFires.dart @@ -1,22 +1,28 @@ 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; +import 'locationUtils.dart'; +import 'globals.dart' as globals; +import 'basicLocationPersist.dart'; + +class ActiveFiresPage extends StatefulWidget { + static const String routeName = '/fires'; + + ActiveFiresPage(); + + @override + _ActiveFiresPageState createState() => _ActiveFiresPageState(); +} class _ActiveFiresPageState extends State { final GlobalKey _scaffoldKey = new GlobalKey(); - // https://docs.flutter.io/flutter/dart-core/List-class.html - final List _saved = []; - int length; - _ActiveFiresPageState(); Widget _buildRow(BasicLocation loc) { @@ -28,6 +34,12 @@ class _ActiveFiresPageState extends State { leading: const Icon(Icons.location_on), // trailing: const Icon(Icons.delete), title: new Text(desc), + onLongPress: () { + _scaffoldKey.currentState.showSnackBar(new SnackBar( + content: new Text( + 'Slide horizontally to delete this location'), + )); + }, onTap: () { Navigator.push( context, @@ -40,7 +52,7 @@ class _ActiveFiresPageState extends State { Widget _buildSavedLocations() { return new ListView.builder( padding: const EdgeInsets.all(16.0), - itemCount: _saved.length, + itemCount: globals.yourLocations.length, itemBuilder: (BuildContext _context, int i) { // Add a one-pixel-high divider widget before each row // in the ListView. @@ -56,15 +68,16 @@ class _ActiveFiresPageState extends State { // final int index = i ~/ 2; // print('$i $index'); // if (index >= _saved.length) return null; - return _buildItem(_saved.elementAt(i)); + return _buildItem(globals.yourLocations.elementAt(i)); }); } void handleUndo(BasicLocation item) { print('Undo $item'); - final int insertionIndex = lowerBound(_saved, item); + final int insertionIndex = lowerBound(globals.yourLocations, item); setState(() { - _saved.insert(insertionIndex, item); + globals.yourLocations.insert(insertionIndex, item); + persist(); }); } @@ -75,11 +88,12 @@ class _ActiveFiresPageState extends State { direction: DismissDirection.horizontal, onDismissed: (DismissDirection direction) { setState(() { - _saved.remove(item); + globals.yourLocations.remove(item); + persist(); }); final String action = 'deleted'; _scaffoldKey.currentState.showSnackBar(new SnackBar( - content: new Text('You $action item'), // ${item.index}'), + content: new Text('You $action this position'), action: new SnackBarAction( label: 'UNDO', onPressed: () { @@ -104,48 +118,24 @@ class _ActiveFiresPageState extends State { child: _buildRow(item))); } - Future getUserLocation() async { - String error; - // Platform messages may fail, so we use a try/catch PlatformException. - try { - Location _location = new Location(); - Map location = await _location.getLocation; - error = null; - print('location $location'); - - // It seems that the lib fails with lat/lon values - return new BasicLocation( - lat: location['latitude'], lon: location['longitude']); - } on PlatformException catch (e) { - if (e.code == 'PERMISSION_DENIED') { - _scaffoldKey.currentState.showSnackBar(new SnackBar( - content: new Text('We don\'t have permission to get your location'), - )); - 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'; - } - _scaffoldKey.currentState.showSnackBar(new SnackBar( - content: new Text( - 'I cannot get your current location. It\'s your ubication enabled?'), - )); - return BasicLocation.noLocation; - } + void persist() { + globals.prefs.then((prefs) { + persistYourLocations(prefs); + }); } @override void initState() { super.initState(); - length = _saved.length; } @override Widget build(BuildContext context) { - print('Building Active Fires, saved $length'); + // print('Building Active Fires, saved $length'); final title = 'Your locations'; return Scaffold( key: _scaffoldKey, + // FIXME new? drawer: new MainDrawer(context), appBar: new AppBar( title: Text(title), @@ -166,7 +156,7 @@ class _ActiveFiresPageState extends State { ), ], */ ), - body: length > 0 + body: globals.yourLocations.length > 0 ? _buildSavedLocations() : new Center( child: new CenteredColumn(children: [ @@ -184,7 +174,7 @@ class _ActiveFiresPageState extends State { }, backColor: fires600), ])), - floatingActionButton: length > 0 + floatingActionButton: globals.yourLocations.length > 0 ? Column(mainAxisAlignment: MainAxisAlignment.end, children: [ FloatingActionButton.extended( onPressed: onAddYourLocation, @@ -209,7 +199,7 @@ class _ActiveFiresPageState extends State { } void onAddYourLocation() { - Future location = getUserLocation(); + Future location = getUserLocation(_scaffoldKey); _saveLocation(location); } @@ -222,18 +212,9 @@ class _ActiveFiresPageState extends State { location.then((newLocation) { if (newLocation != BasicLocation.noLocation) this.setState(() { - _saved.add(newLocation); - length = _saved.length; + globals.yourLocations.add(newLocation); + persist(); }); }); } } - -class ActiveFiresPage extends StatefulWidget { - static const String routeName = '/fires'; - - ActiveFiresPage(); - - @override - _ActiveFiresPageState createState() => _ActiveFiresPageState(); -} diff --git a/lib/basicLocation.dart b/lib/basicLocation.dart index d7a8b25..7b638bb 100644 --- a/lib/basicLocation.dart +++ b/lib/basicLocation.dart @@ -8,4 +8,18 @@ class BasicLocation { static BasicLocation noLocation = new BasicLocation(lat:0.0, lon:0.0); BasicLocation({@required this.lat, @required this.lon, this.description}); + + BasicLocation.fromJson(Map json) + : lat = json['lat'], + lon = json['lon'], + description = json['description'] + ; + + Map toJson() => + { + 'lat': lat, + 'lon': lon, + 'description': description, + }; + } diff --git a/lib/basicLocationPersist.dart b/lib/basicLocationPersist.dart new file mode 100644 index 0000000..826f9ef --- /dev/null +++ b/lib/basicLocationPersist.dart @@ -0,0 +1,20 @@ +import 'dart:convert'; +import 'basicLocation.dart'; +import 'globals.dart' as globals; +final String locationKey = 'yourlocations'; + +void loadYourLocations(prefs) { + List yourLocations = prefs.getStringList(locationKey); + yourLocations.forEach((locationString) { + Map locationMap = json.decode(locationString); + globals.yourLocations.add(BasicLocation.fromJson(locationMap)); + }); +} + +persistYourLocations(prefs) { + List yourLocationsAsString = []; + globals.yourLocations.forEach((location) { + yourLocationsAsString.add(json.encode(location.toJson())); + }); + prefs.setStringList(locationKey, yourLocationsAsString); +} \ No newline at end of file diff --git a/lib/firesApp.dart b/lib/firesApp.dart index d64c641..870c96c 100644 --- a/lib/firesApp.dart +++ b/lib/firesApp.dart @@ -5,20 +5,16 @@ import 'theme.dart'; import 'introPage.dart'; import 'sandbox.dart'; import 'activeFires.dart'; -import 'placesAutocompleteWidget.dart'; -import 'globals.dart' as globals; class FiresApp extends StatelessWidget { static final WidgetBuilder introWidget = (context) => new IntroPage(); static final WidgetBuilder continueWidget = (context) => new HomePage(); - Map routes = { - PlacesAutocompleteWidget.routeName: (BuildContext context) => - new PlacesAutocompleteWidget(), - Sandbox.routeName: (BuildContext context) => new Sandbox(), - ActiveFiresPage.routeName: (BuildContext context) => new ActiveFiresPage(), - HomePage.routeName: continueWidget, + final Map routes = { IntroPage.routeName: introWidget, + HomePage.routeName: continueWidget, + ActiveFiresPage.routeName: (BuildContext context) => new ActiveFiresPage(), + Sandbox.routeName: (BuildContext context) => new Sandbox(), }; // globals.getIt.registerSingleton diff --git a/lib/globals.dart b/lib/globals.dart index 6ce67a5..1f65950 100644 --- a/lib/globals.dart +++ b/lib/globals.dart @@ -1,6 +1,11 @@ library fires.globals; import 'package:get_it/get_it.dart'; +import 'dart:async'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'basicLocation.dart'; String gmapKey; - -final GetIt getIt = new GetIt(); \ No newline at end of file +String appName = 'All Against Fire!'; +Future prefs = SharedPreferences.getInstance(); +final List yourLocations = []; +final GetIt getIt = new GetIt(); diff --git a/lib/homePage.dart b/lib/homePage.dart index 6e3e5f2..d4b76da 100644 --- a/lib/homePage.dart +++ b/lib/homePage.dart @@ -4,23 +4,36 @@ import 'colors.dart'; import 'package:comunes_flutter/comunes_flutter.dart'; import 'mainDrawer.dart'; import 'activeFires.dart'; +import 'globals.dart' as globals; class HomePage extends StatelessWidget { static const String routeName = '/home'; + final GlobalKey _scaffoldKey = new GlobalKey(); - final _btnFont = const TextStyle( - fontSize: 20.0, - // fontWeight: FontWeight.w500, - color: Colors.white, + final _homeFont = const TextStyle( + fontSize: 40.0, + fontWeight: FontWeight.w400, + // color: Colors.white, ); @override Widget build(BuildContext context) { return new Scaffold( + key: _scaffoldKey, drawer: new MainDrawer(context), body: new SafeArea( child: Center( child: new CenteredColumn(children: [ + new Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + new IconButton( + onPressed: () { + _scaffoldKey.currentState.openDrawer(); + }, + icon: new Icon(Icons.menu, + size: 30.0, color: Colors.black38)), + ]), new Expanded( child: new FractionallySizedBox( alignment: FractionalOffset.bottomCenter, @@ -33,6 +46,8 @@ class HomePage extends StatelessWidget { heightFactor: 0.9, child: new CenteredColumn( children: [ + new Text(globals.appName, style: _homeFont), + new SizedBox(height: 20.0), new RoundedBtn.nav( icon: Icons.whatshot, text: 'Active fires', @@ -44,30 +59,4 @@ class HomePage extends StatelessWidget { ])), )); } - - SizedBox customBtn(IconData icon, String text, BuildContext context, route) { - final btnRadius = new Radius.circular(90.0); - return new SizedBox( - // width: double.infinity, - child: new RaisedButton( - elevation: 1.0, - color: fires600, - child: new Padding( - padding: const EdgeInsets.all(10.0), - child: new Row( - mainAxisSize: MainAxisSize.min, - children: [ - new Icon(icon, size: 32.0, color: Colors.white), - new SizedBox(width: 10.0), - new Text(text, style: _btnFont), - ], - ), - ), - onPressed: () { - Navigator.pushNamed(context, route); - }, - shape: new RoundedRectangleBorder( - borderRadius: BorderRadius.all(btnRadius))), - ); - } } diff --git a/lib/locationUtils.dart b/lib/locationUtils.dart new file mode 100644 index 0000000..ff1e2bd --- /dev/null +++ b/lib/locationUtils.dart @@ -0,0 +1,30 @@ +import 'package:location/location.dart'; +import 'package:flutter/services.dart'; +import 'dart:async'; +import 'basicLocation.dart'; +import 'package:flutter/material.dart'; + +Future getUserLocation(GlobalKey scaffoldKey) async { + // Platform messages may fail, so we use a try/catch PlatformException. + try { + Location _location = new Location(); + Map location = await _location.getLocation; + print('location $location'); + + // It seems that the lib fails with lat/lon values + return new BasicLocation( + lat: location['latitude'], lon: location['longitude']); + } on PlatformException catch (e) { + if (e.code == 'PERMISSION_DENIED') { + scaffoldKey.currentState.showSnackBar(new SnackBar( + content: new Text('We don\'t have permission to get your location'), + )); + } else if (e.code == 'PERMISSION_DENIED_NEVER_ASK') { + } + scaffoldKey.currentState.showSnackBar(new SnackBar( + content: new Text( + 'I cannot get your current location. It\'s your ubication enabled?'), + )); + return BasicLocation.noLocation; + } + } diff --git a/lib/main.dart b/lib/main.dart index 33c03b2..54aef75 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,7 @@ import 'firesApp.dart'; import 'package:comunes_flutter/comunes_flutter.dart'; import 'dart:async'; import 'globals.dart' as globals; +import 'basicLocationPersist.dart'; Future getGMapKey() async { Secret secret = await SecretLoader(secretPath: 'assets/private-settings.json') @@ -13,6 +14,11 @@ Future getGMapKey() async { void main() { getGMapKey().then((String gmapKey) { globals.gmapKey = gmapKey; - runApp(new FiresApp()); + globals.prefs.then((prefs) { + loadYourLocations(prefs); + + // Run baby run! + runApp(new FiresApp()); + }); }); } diff --git a/lib/mainDrawer.dart b/lib/mainDrawer.dart index 87fe116..9e2b8f3 100644 --- a/lib/mainDrawer.dart +++ b/lib/mainDrawer.dart @@ -4,7 +4,7 @@ import 'colors.dart'; import 'sandbox.dart'; import 'introPage.dart'; import 'activeFires.dart'; -import 'placesAutocompleteWidget.dart'; +import 'globals.dart' as globals; class MainDrawer extends Drawer { MainDrawer(BuildContext context, {key}) @@ -29,7 +29,7 @@ Widget mainDrawer(BuildContext context) { height: 80.0, ), const SizedBox(height: 20.0), - new Text('seedees', + new Text(globals.appName, style: new TextStyle( fontSize: 24.0, color: Colors.white, @@ -72,14 +72,6 @@ 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), diff --git a/lib/placesAutocompleteWidget.dart b/lib/placesAutocompleteWidget.dart deleted file mode 100644 index 043e932..0000000 --- a/lib/placesAutocompleteWidget.dart +++ /dev/null @@ -1,75 +0,0 @@ -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(); -final searchScaffoldKey = new GlobalKey(); - -GoogleMapsPlaces _places; - -class _PlacesAutocompleteWidgetState extends State { - 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 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"))); - } -}