Many work in navegation, map, drawer, about, slider

This commit is contained in:
vjrj 2018-06-12 18:08:49 +02:00
parent 662deb3086
commit 574c1afc33
15 changed files with 446 additions and 168 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

BIN
images/industry-marker.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

View file

@ -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,39 +24,68 @@ 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'];
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => new LeafletMap(
title: desc,
location: loc,
numFires: numFires,
kmAround: km)));
});
showLocationMap(loc);
});
}
void showLocationMap(BasicLocation loc) {
// , VoidCallback onSubs
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => new LeafletMap(
title: loc.description,
location: loc,
)));
}
void showSnackMsg(String msg) {
_scaffoldKey.currentState.showSnackBar(new SnackBar(
content: new Text(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);
});
});
}
});

View file

@ -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'],

View file

@ -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) {
List<String> yourLocationsAsString = [];
globals.yourLocations.forEach((location) {
yourLocationsAsString.add(json.encode(location.toJson()));
persistYourLocations() {
globals.prefs.then((prefs) {
List<String> yourLocationsAsString = [];
globals.yourLocations.forEach((location) {
yourLocationsAsString.add(json.encode(location.toJson()));
});
prefs.setStringList(locationKey, yourLocationsAsString);
});
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));
});
});
}

View file

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

View file

@ -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();

View file

@ -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(

View file

@ -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,26 +32,94 @@ 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 = [];
@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});
_LeafletMapState({@required this.title, @required this.location,
@required this.numFires, @required this.kmAround
});
@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':
'&copy; <a href=&quot;http://osm.org/copyright&quot;>OpenStreetMap</a> contributors'
},
),
new ZoomMapPluginOptions(),
new MarkerLayerOptions(
markers: buildMarkers(
this.location, this.fires, this.industries, this.falsePos),
),
],
);
return new Scaffold(
key: _scaffoldKey,
key: _scaffoldKey,
// drawer: new MainDrawer(context),
appBar: new AppBar(
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': '&copy; <a href=&quot;http://osm.org/copyright&quot;>OpenStreetMap</a> contributors'
},
), /*
new TileLayerOptions(
urlTemplate: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
additionalOptions: {
// 'opacity': '0.1',
'attribution': '&amp;copy <a href=&quot;http://osm.org/copyright&quot;>OpenStreetMap</a> contributors'
},
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);
});
})
],
),
)
]),
));
}
), */
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'),
),
),
],
),
],
));
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');
}
}
}

View file

@ -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());

View file

@ -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,8 +96,16 @@ Widget mainDrawer(BuildContext context) {
},
),
new AboutListTile(
// FIXME
)
icon: globals.appIcon,
applicationName: globals.appName,
applicationVersion: globals.appVersion,
applicationIcon: globals.appMediumIcon,
applicationLegalese: globals.appLicense,
aboutBoxChildren: <Widget> [
// new Text('What?')
]
// FIXME
)
],
);
}

View file

@ -13,13 +13,25 @@ class Sandbox extends StatelessWidget {
@override
Widget build(BuildContext context) {
//showDialog(context: context, child: builder(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()],
),
)
]),
),
);
}

View file

@ -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) {
var slider = new Slider(
value: _sliderValue + 0.0,
activeColor: fires900,
inactiveColor: Colors.black45,
min: 1.0,
max: 100.0,
divisions: 99,
label: '${_sliderValue.round()}',
onChanged: (double value) {
_sliderValue = value.round();
if (onSlide != null) {
onSlide(_sliderValue);
}
setState(() {});
},
);
return new Column(
children: <Widget>[
new Column(
mainAxisSize: MainAxisSize.min,
children:
listWithoutNulls(<Widget>[
new Slider(
value: _discreteValue + 0.0,
activeColor: fires900,
min: 1.0,
max: 100.0,
divisions: 99,
label: '${_discreteValue.round()}',
onChanged: (double value) {
setState(() {
_discreteValue = value.round();
});
},
),
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('')
]),
),
],
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)
])))
]),
);
}
}

View file

@ -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.