I'm using the google_maps_flutter package and I'm trying to figure a way to zoom the camera between two placed markers with known positions. Any pointers or sample code would be appreciated.
To zoom between two Lat Lng bounds in google map, you can do as below:
First of all import below library in pubspec.yaml otherwise with the older version, you might not be able to see "getVisibleRegion()" method with google
map controller.
google_maps_flutter: ^0.5.12
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Completer<GoogleMapController> _controller = Completer();
GoogleMapController mapController;
LatLng _lastMapPosition = _center;
static const LatLng _center = const LatLng(45.521563, -122.677433);
final Set<Marker> _markers = {};
void _onMapCreated(GoogleMapController controller) {
mapController = controller;
_controller.complete(controller);
LatLng latLng_1 = LatLng(40.416775, -3.70379);
LatLng latLng_2 = LatLng(41.385064, 2.173403);
LatLngBounds bound = LatLngBounds(southwest: latLng_1, northeast: latLng_2);
setState(() {
_markers.clear();
addMarker(latLng_1, "Madrid", "5 Star Rating");
addMarker(latLng_2, "Barcelona", "7 Star Rating");
});
CameraUpdate u2 = CameraUpdate.newLatLngBounds(bound, 50);
this.mapController.animateCamera(u2).then((void v){
check(u2,this.mapController);
});
}
void addMarker(LatLng mLatLng, String mTitle, String mDescription){
_markers.add(Marker(
// This marker id can be anything that uniquely identifies each marker.
markerId: MarkerId((mTitle + "_" + _markers.length.toString()).toString()),
position: mLatLng,
infoWindow: InfoWindow(
title: mTitle,
snippet: mDescription,
),
icon: BitmapDescriptor.defaultMarker,
));
}
void check(CameraUpdate u, GoogleMapController c) async {
c.animateCamera(u);
mapController.animateCamera(u);
LatLngBounds l1=await c.getVisibleRegion();
LatLngBounds l2=await c.getVisibleRegion();
print(l1.toString());
print(l2.toString());
if(l1.southwest.latitude==-90 ||l2.southwest.latitude==-90)
check(u, c);
}
void _onCameraMove(CameraPosition position) {
_lastMapPosition = position.target;
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Maps Sample App'),
backgroundColor: Colors.green[700],
),
body: GoogleMap(
markers: _markers,
onMapCreated: _onMapCreated,
initialCameraPosition: CameraPosition(
target: _center,
zoom: 11.0,
),
onCameraMove: _onCameraMove,
),
),
);
}
}
The proposed solution above is good, but LatLngBounds has one important limitation:
LatLngBounds({#required this.southwest, #required this.northeast})
: assert(southwest != null),
assert(northeast != null),
assert(southwest.latitude <= northeast.latitude); // <--
This means that the first coordinate must be lower and to the left of the second coordinate.
I had to modify the method for different coordinates.
void _onMapCreated(GoogleMapController controller) {
mapController = controller;
_controller.complete(controller);
//offerLatLng and currentLatLng are custom
final LatLng offerLatLng = LatLng(
double.parse(widget.coordinates.first.latLongList.first.latitude),
double.parse(widget.coordinates.first.latLongList.first.longitude));
LatLngBounds bound;
if (offerLatLng.latitude > currentLatLng.latitude &&
offerLatLng.longitude > currentLatLng.longitude) {
bound = LatLngBounds(southwest: currentLatLng, northeast: offerLatLng);
} else if (offerLatLng.longitude > currentLatLng.longitude) {
bound = LatLngBounds(
southwest: LatLng(offerLatLng.latitude, currentLatLng.longitude),
northeast: LatLng(currentLatLng.latitude, offerLatLng.longitude));
} else if (offerLatLng.latitude > currentLatLng.latitude) {
bound = LatLngBounds(
southwest: LatLng(currentLatLng.latitude, offerLatLng.longitude),
northeast: LatLng(offerLatLng.latitude, currentLatLng.longitude));
} else {
bound = LatLngBounds(southwest: offerLatLng, northeast: currentLatLng);
}
CameraUpdate u2 = CameraUpdate.newLatLngBounds(bound, 50);
this.mapController.animateCamera(u2).then((void v){
check(u2,this.mapController);
});
}
Above suggestions are very good.
If you are working with dynamic source/destination this code might work for you:
Future<void> updateCameraLocation(
LatLng source,
LatLng destination,
GoogleMapController mapController,
) async {
if (mapController == null) return;
LatLngBounds bounds;
if (source.latitude > destination.latitude &&
source.longitude > destination.longitude) {
bounds = LatLngBounds(southwest: destination, northeast: source);
} else if (source.longitude > destination.longitude) {
bounds = LatLngBounds(
southwest: LatLng(source.latitude, destination.longitude),
northeast: LatLng(destination.latitude, source.longitude));
} else if (source.latitude > destination.latitude) {
bounds = LatLngBounds(
southwest: LatLng(destination.latitude, source.longitude),
northeast: LatLng(source.latitude, destination.longitude));
} else {
bounds = LatLngBounds(southwest: source, northeast: destination);
}
CameraUpdate cameraUpdate = CameraUpdate.newLatLngBounds(bounds, 70);
return checkCameraLocation(cameraUpdate, mapController);
}
Future<void> checkCameraLocation(
CameraUpdate cameraUpdate, GoogleMapController mapController) async {
mapController.animateCamera(cameraUpdate);
LatLngBounds l1 = await mapController.getVisibleRegion();
LatLngBounds l2 = await mapController.getVisibleRegion();
if (l1.southwest.latitude == -90 || l2.southwest.latitude == -90) {
return checkCameraLocation(cameraUpdate, mapController);
}
}
Usage:
await updateCameraLocation(source, destination, controller);
Related
i am new to flutter and i am trying to let users define zones in gmap by using marks to draw polygons.
Example of a zone, with 3 markers/point
So, now what i want is to check if the user is not trying to do some like crossing the lines like this:
Check for this not to happen
the numbers shown in the markers are not relavant, so i could do some math between coordinates and just redraw the polygon, but i wanted to avoid that just cause im lazy =)
and i noticed that when you cross the lines the polygon is not filled anymore, so it has some kind of internal check, that i would like to access if possible.
I am using Gmap and drawing the polygons with following code:
...
Map<MarkerId, Marker> markers = <MarkerId, Marker>{};
int lastMarkerID = 0;
Set<Polygon> _polygons = HashSet<Polygon>();
...
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(title: Text('Add Zone')),
body: Stack(
children: <Widget>[
GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: _kGoogleInitialCamera,
onLongPress: (latlang) {
_addMarkerLongPressed(latlang);
},
markers: Set<Marker>.of(markers.values),
polygons: _polygons,
polylines: _polylines,
),
],
),
);
}
...
Future _addMarkerLongPressed(LatLng latlong, [_lastMarkerID]) async {
_lastMarkerID ?? lastMarkerID++;
_lastMarkerID = _lastMarkerID ?? lastMarkerID;
final MarkerId markerId = MarkerId(_lastMarkerID.toString());
Marker marker = Marker(
markerId: markerId,
draggable: true,
position: latlong,
onDragEnd: ((value) {
_addMarkerLongPressed(value, _lastMarkerID);
}),
infoWindow: InfoWindow(
title: markerId.value,
),
consumeTapEvents: true,
onTap: _showDialog(
context, <String>["Delete Marker"], _lastMarkerID, _deleteMarker),
icon: await createCustomMarkerBitmap(markerId.value),
);
markers[markerId] = marker;
_setPolygons();
setState(() {});
}
...
void _setPolygons() {
_polygons.clear();
List<LatLng> polygonLatLongs = List<LatLng>();
markers.forEach((key, value) {
Commons.log(value.position);
polygonLatLongs.add(value.position);
});
if (polygonLatLongs.length >= 3) {
_setPolylines(polygonLatLongs);
polygonLatLongs.add(polygonLatLongs.first);
}
_polygons.add(
Polygon(
polygonId: PolygonId("0"),
points: polygonLatLongs,
fillColor: Commons.colorPolygonAddZone,
strokeColor: Commons.colorPolygonStrokeAddZone,
strokeWidth: Commons.strokeWidthPolygonAddZone,
),
);
}
As i said i'm new to this so feel free to sugeste other ways to achive this.
I could only find posts about check if a point is inside the polygon sorry if it's repeated question.
i am new to flutter and i need to save a value inside my user document.
i can print the values but i do not know how to assign the values of LatLan to a variable and get them saved in firestore.
here is my searchMapPlaceWidget that returns them values
SearchMapPlaceWidget(
darkMode: true,
placeType: PlaceType.establishment,
language: 'se',
apiKey: kGoogleApiKey,
onSelected: (place) async {
final geolocation =
await place.geolocation;
final chosenPlace = Marker(
markerId: MarkerId(
'chosen-location'),
icon: BitmapDescriptor
.defaultMarker,
position:
geolocation.coordinates);
GoogleMapController controller =
await _mapController.future;
setState(() {
_markers.add(chosenPlace);
});
print(chosenPlace.position);
controller.animateCamera(
CameraUpdate.newLatLng(
geolocation.coordinates));
controller.animateCamera(
CameraUpdate.newLatLngBounds(
geolocation.bounds, 50));
},
),
here is the function that i wrote which is not working 😅
static void addGeoPoint(User user)async{
var pos = await location.getLocation();
GeoFirePoint point = geo.point(latitude: pos.latitude, longitude: pos.longitude);
return usersRef.document(user.id).updateData({
'myAddress' : point.data
});
}
how do i save the position values of 'chosenPlace' ?
thank you all
solved it.
GeoFirePoint myLocation = geo.point(latitude: _location.latitude, longitude: _location.longitude);
usersRef.document(widget.user.id).updateData({
'position' : myLocation.data
Issue in brief:
trying to access location data of the user in background using location and workManager plugin.
Currently with the code mentioned below i am able to access the location information if the application is open, Since callbackDispatcher is a top level function i am not able to call the location plugin.
location plugin works when a call is done inside of the class. I am trying a way to access _getlocation() from callbackDispatcher, I am getting PlatformException(NO_ACTIVITY).
Things I have tried:
found few other guys facing similar issue here, here and here
Tired all these steps and no luck.
import 'package:flutter/material.dart';
import 'package:location/location.dart';
import 'package:workmanager/workmanager.dart';
import 'package:flutter/services.dart';
void main() {
runApp(MaterialApp(
home: MyApp(),
));
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
Location location = new Location();
void callbackDispatcher() {
Workmanager.executeTask((task, inputData) {
if (task == "simplePeriodicTask") {
print("task working");
_getLocation();
}
return Future.value(true);
});
}
LocationData _location;
String _error;
double lat;
double long;
_getLocation() async {
_error = null;
try {
var _locationResult = await location.getLocation();
_location = _locationResult;
lat = _location.latitude;
long = _location.longitude;
} on PlatformException catch (err) {
_error = err.code;
}
if (_error == null) {
// _check();
print(lat);
} else {
//dialog
print(_error);
}
}
class _MyAppState extends State<MyApp> {
#override
void initState() {
super.initState();
Workmanager.initialize(
callbackDispatcher, // The top level function, aka callbackDispatcher
isInDebugMode:
true // If enabled it will post a notification whenever the task is running. Handy for debugging tasks
);
_checkPermissions();
}
// Permission for location
PermissionStatus _permissionGranted;
// final Location location = new Location();
_checkPermissions() async {
PermissionStatus permissionGrantedResult = await location.hasPermission();
setState(() {
_permissionGranted = permissionGrantedResult;
});
if (_permissionGranted == PermissionStatus.DENIED) {
_requestPermission();
} else if (_permissionGranted == PermissionStatus.GRANTED) {
_checkService();
}
}
_requestPermission() async {
if (_permissionGranted != PermissionStatus.GRANTED) {
PermissionStatus permissionRequestedResult =
await location.requestPermission();
setState(() {
_permissionGranted = permissionRequestedResult;
});
if (permissionRequestedResult != PermissionStatus.GRANTED) {
return;
} else if (permissionRequestedResult == PermissionStatus.GRANTED) {
_checkService();
}
}
}
//Permission ends
//services enabled function
bool _serviceEnabled;
_checkService() async {
bool serviceEnabledResult = await location.serviceEnabled();
setState(() {
_serviceEnabled = serviceEnabledResult;
});
if (_serviceEnabled == false) {
_requestService();
} else {
// _getLocation();
}
}
_requestService() async {
if (_serviceEnabled == null || !_serviceEnabled) {
bool serviceRequestedResult = await location.requestService();
setState(() {
_serviceEnabled = serviceRequestedResult;
});
if (!serviceRequestedResult) {
return;
} else {
// _getLocation();
}
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Dart'),
),
body: Column(children: <Widget>[
RaisedButton(
child: Text('get Location'),
onPressed: () {
Workmanager.registerPeriodicTask(
"2",
"simplePeriodicTask",
// When no frequency is provided the default 15 minutes is set.
// Minimum frequency is 15 min. Android will automatically change your frequency to 15 min if you have configured a lower frequency.
);
print('task registered');
_getLocation();
}),
RaisedButton(
onPressed: () async {
await Workmanager.cancelAll();
print('task Destroyd');
},
child: Text("cancel"),
),
]),
);
}
}
Trying to access _getlocation() from callbackDispatcher();
Any help on this is greatly appreciated.
I was facing same issue recently. location package not work with WorkManager plugin, I dont know the reason but here is my solution;
/// This Function calls only from WorkManager
/// Used GeoLocator instead of Location package due to PlatformException(NO_ACTIVITY) error throwing
Future<String> getPlaceMarkLocationWhileAppOff() async {
Geolocator geoLocator = Geolocator()..forceAndroidLocationManager = true;
var _position = await geoLocator.getCurrentPosition(
// desiredAccuracy: LocationAccuracy.high,
);
var value = await geoLocator.placemarkFromCoordinates(_position.latitude, _position.longitude);
return _placeMark = "${value.first.subLocality}\n${value.first.subAdministrativeArea}";
}
Used Geolocator package when app offline and used Location package when app online..
I hope it will help..
i have tried this to customize marker on flutter_google_maps and this to change widget into bytes, since we could change marker using bytes, not widget.
i actually solve the problem if i use only one type of marker like this:
but things are different where the requirement design just like this:
so how do i solve the problem?
here some code i use, but the result output is first image above, not as expected.
-> method to change widget into image
import 'dart:ui' as ui;
GlobalKey<ScaffoldState> keyScaffold = new GlobalKey<ScaffoldState>();
Future<Uint8List> _capturePng() async {
try {
RenderRepaintBoundary boundary =
keyScaffold.currentContext.findRenderObject();
ui.Image image = await boundary.toImage(pixelRatio: 3.0);
ByteData byteData =
await image.toByteData(format: ui.ImageByteFormat.png);
Uint8List pngBytes = byteData.buffer.asUint8List();
return pngBytes;
} catch (e) {
print(e);
}
}
bool rendering = true;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Peta'),
),
body: rendering
? renderWidgetToImage()
: renderGoogleMap()
);
-> method to render widget before converted
String title;
Widget renderWidgetToImage() {
return RepaintBoundary(
key: keyScaffold,
child: Container(
margin: EdgeInsets.only(top: 30, left: 10, right: 10, bottom: 20),
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
color: Colors.green,
),
child: Text(
title,
style: TextStyle(
fontSize: 10
),
)),
);
}
-> method to programmatically add marker using widget
final Set<Marker> _markers = {};
#override
void initState() {
super.initState();
var arrMarker = <MarkerMap>[
MarkerMap("Text Widget 3","123",3.59196,98.672226),
MarkerMap("Text Widget 2","456",3.49196,97.572226),
MarkerMap("Text Widget 1","789",3.39196,97.772226),
];
for(int i =0; i< arrMarker.length; i++) {
setState(() {
this.title = arrMarker[i].title;
});
BitmapDescriptor.fromAssetImage(
ImageConfiguration(size: Size(48, 48)), DefaultImageLocation.iconAnalog)
.then((onValue) async {
var png = await _capturePng(keyScaffold);
setState(() {
this.myIcon = BitmapDescriptor.fromBytes(png);
this.rendering = false;
});
setState(() {
_markers.add(
Marker(
markerId: MarkerId(arrMarker[i].id),
position: LatLng(arrMarker[i].pos1, arrMarker[i].pos2),
icon: BitmapDescriptor.fromBytes(png),
),
);
});
});
setState(() {
this.rendering = true;
});
}
any help would be appreciated, thank you
Currently, this is now possible using the steps provided in this blog.
As mentioned in the intro:
We need to paint a Widget and then convert it to bitmap. But its
tricky because you cant simply do that. we have to place into widget
tree and fetch its painted bitmap.
For a rough summary, the steps mentioned were:
First
We need to get our bitmaps right after they are drawn. There
are multiple ways to do this, I use tiny AfterLayoutMixin available
here.
Second
Lets create our custom widget, yay!
My widget accepts name as an parameter. I used ClipPath for the
triangular Pointer arrow.
Third
Lets create a location object and list of locations and a
method to generate widgets from the location list.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 3 years ago.
Improve this question
I am porting over a flutter mobile project to flutter web and was wondering how to use the google maps library with Flutter Web.
Please see the other answer. It is easier than this one!
I was able to get a workable solution, but it isn't pretty. Below is the implementation. If I get some time and a legit port doesn't come a long I will post an example repo.
import 'dart:html';
import 'package:flutter_web/material.dart';
import 'package:lift_ai/base/screen_state.dart';
import 'package:lift_ai/feature/property_common/property_contract.dart';
import 'package:lift_ai/feature/property_common/property_presenter_impl.dart';
import 'package:lift_ai/model/car_status.dart';
import 'package:lift_ai/model/property.dart';
import 'package:flutter_web_ui/ui.dart' as ui;
import 'package:lift_ai/util/widget_util.dart';
class PropertyMapPage extends StatefulWidget {
final CarStatus carStatus;
PropertyMapPage(Key key, this.carStatus) : super(key: key);
#override
_PropertyMapPageState createState() => _PropertyMapPageState(carStatus);
}
class _PropertyMapPageState extends State<PropertyMapPage>
implements PropertyListView {
PropertyPresenter _propertyListPresenter;
List<Property> properties = [];
CarStatus carStatus;
String createdViewId = 'hello-world-html';
bool inProgress = true;
_PropertyMapPageState(this.carStatus) {
_propertyListPresenter = PropertyPresenterImpl(this);
}
#override
void initState() {
super.initState();
_propertyListPresenter.getProperties(carStatus, "");
}
#override
void dispose() {
super.dispose();
_propertyListPresenter = null;
}
#override
Widget build(BuildContext context) {
print("Creating html view");
if (inProgress) {
return Center(child: CircularProgressIndicator());
}
return Row(
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width - 400,
child: HtmlView(
viewType: createdViewId,
)),
Container(
width: 400,
child: properties.isEmpty
? WidgetUtil.getEmptyPropertiesView(context)
: ListView.builder(
padding: EdgeInsets.all(8.0),
itemCount: properties.length,
itemBuilder: (_, index) {
return WidgetUtil.buildListRow(
context, _propertyListPresenter, properties[index]);
},
),
),
],
);
}
#override
void showProperties(List<Property> properties) {
String markers = "";
for (Property property in properties) {
String marker =
"var marker = new google.maps.Marker({position: new google.maps.LatLng(${property.lat}, ${property.lng}), map: map, title: 'Hello ${property.id}!'});\n";
markers += marker;
}
String createdViewUpdate = DateTime.now().toString();
rootBundle.loadString('map.html').then((value) {
value = value.replaceAll(new RegExp(r'markers'), markers);
ui.platformViewRegistry.registerViewFactory(
createdViewId,
(int viewId) => IFrameElement()
..width = (MediaQuery.of(context).size.width - 400).toString()
..height = MediaQuery.of(context).size.height.toString()
..srcdoc = value
..style.border = 'none');
});
setState(() {
inProgress = false;
this.createdViewId = createdViewUpdate;
this.properties = properties;
});
}
#override
void updateScreenState(ScreenState screenState) { }
#override
void showException(String string) {
// TODO: implement showException
}
}
map.html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<title>Simple Markers</title>
<style>
/* Always set the map height explicitly to define the size of the div
* element that contains the map. */
#map {
height: 100%;
}
/* Optional: Makes the sample page fill the window. */
html, body {
height: 100%;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
function initMap() {
var myLatLng = {lat: 41.850033, lng: -87.6500523};
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 4,
center: myLatLng
});
markers
}
</script>
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=API_KEY&callback=initMap">
</script>
</body>
</html>
The answer outlined by #panavtec below also works and might have an easier api work work with!
A sample repository of his solution is here:
https://github.com/dazza5000/flutter_web_google_maps_example
If you need to use the library, I found this alternative:
Widget getMap() {
String htmlId = "7";
final mapOptions = new MapOptions()
..zoom = 8
..center = new LatLng(-34.397, 150.644);
// ignore: undefined_prefixed_name
ui.platformViewRegistry.registerViewFactory(htmlId, (int viewId) {
final elem = DivElement()
..id = htmlId
..style.width = "100%"
..style.height = "100%"
..style.border = 'none';
new GMap(elem, mapOptions);
return elem;
});
return HtmlElementView(viewType: htmlId);
}
I didn't tested it thoroughly but it seems to render the map.
A basic working example of this solution is here:
https://github.com/dazza5000/flutter_web_google_maps_example
Created a video walk-through of the project:
https://www.youtube.com/watch?v=iW7pCBL7yWk
Barebones blog article walking through solution:
http://whereisdarran.com/2020/01/google-maps-for-flutter-web/