Skip to content

Instantly share code, notes, and snippets.

@Ahmed-gubara
Last active May 29, 2024 04:19
Show Gist options
  • Save Ahmed-gubara/ebfb48dbdbff3dc7051ff0998d3e7048 to your computer and use it in GitHub Desktop.
Save Ahmed-gubara/ebfb48dbdbff3dc7051ff0998d3e7048 to your computer and use it in GitHub Desktop.
use google_maps_flutter on flutter_map package
import 'package:flutter/material.dart';
import 'package:flutter_map/plugin_api.dart' show CustomPoint, FlutterMapState;
import 'package:google_maps_flutter/google_maps_flutter.dart';
export 'package:google_maps_flutter/google_maps_flutter.dart' show MapType;
class GoogleMapTileLayer extends StatefulWidget {
final MapType mapType;
final EdgeInsets padding;
final bool trafficEnabled;
final bool buildingsEnabled;
final bool compassEnabled;
final bool myLocationEnabled;
final bool indoorViewEnabled;
final TextDirection? layoutDirection;
const GoogleMapTileLayer({
super.key,
required this.mapType,
this.padding = EdgeInsets.zero,
this.trafficEnabled = false,
this.buildingsEnabled = false,
this.compassEnabled = false,
this.myLocationEnabled = false,
this.indoorViewEnabled = false,
this.layoutDirection,
});
@override
State<StatefulWidget> createState() => _GoogleMapTileLayerState();
}
class _GoogleMapTileLayerState extends State<GoogleMapTileLayer> with TickerProviderStateMixin {
LatLng? _lastCenter;
double? _lastZoom;
GoogleMapController? controller;
@override
void initState() {
super.initState();
}
// update google map to match the latlng of flutter_map
void mapEvent(FlutterMapState map) {
final ctrl = controller;
if (ctrl != null) {
final center = convertLatLng(map);
final zoom = map.zoom;
if (center == _lastCenter && zoom == _lastZoom) {
return;
}
final newLatLngZoom = CameraUpdate.newCameraPosition(CameraPosition(target: center, zoom: zoom, bearing: -map.rotation));
_lastCenter = center;
_lastZoom = zoom;
ctrl.moveCamera(newLatLngZoom);
}
}
@override
void dispose() {
controller?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final map = FlutterMapState.of(context);
final ctrl = controller;
if (ctrl != null) {
mapEvent(map);
}
return Center(
child: OverflowBox(
minWidth: map.nonrotatedSize.x,
maxWidth: map.nonrotatedSize.x,
minHeight: map.nonrotatedSize.y,
maxHeight: map.nonrotatedSize.y,
child: Transform.rotate(
angle: -map.rotationRad,
child: Stack(
alignment: Alignment.center,
children: [
IgnorePointer(
child: GoogleMap(
myLocationButtonEnabled: false,
buildingsEnabled: widget.buildingsEnabled,
compassEnabled: widget.compassEnabled,
mapToolbarEnabled: false,
myLocationEnabled: widget.myLocationEnabled,
indoorViewEnabled: widget.indoorViewEnabled,
layoutDirection: widget.layoutDirection,
mapType: widget.mapType,
padding: widget.padding,
rotateGesturesEnabled: false,
scrollGesturesEnabled: false,
tiltGesturesEnabled: false,
trafficEnabled: widget.trafficEnabled,
zoomControlsEnabled: false,
zoomGesturesEnabled: false,
initialCameraPosition: CameraPosition(target: convertLatLng(map), zoom: map.zoom),
onMapCreated: (GoogleMapController controller) {
this.controller = controller;
final center = convertLatLng(map);
controller.moveCamera(CameraUpdate.newLatLngZoom(center, map.zoom));
},
),
),
Container(color: Colors.transparent), // This is important for touch input to work with flutter_map
],
),
),
),
);
}
// steps is used to make map move one pixel at a time by rounding to int, that is to be as close to how flutter_map render its widgets
LatLng convertLatLng(FlutterMapState map, {bool steps = true}) {
final center = map.center;
if (steps) {
final CustomPoint<num> projection = map.project(center, map.zoom);
final rounded = projection.round();
final fixedLatLng = map.unproject(rounded);
return LatLng(fixedLatLng.latitude, fixedLatLng.longitude);
} else {
return LatLng(center.latitude, center.longitude);
}
}
}
@Ahmed-gubara
Copy link
Author

Ahmed-gubara commented Jan 26, 2022

EDIT: updated.

usage:

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return FlutterMap(
      options: MapOptions(
        maxZoom: 21, // max zoom supported by google map.
        minZoom: 4, // any zoom less that 4 will make flutter_map and google_map render incorrectly using different projections.
      ),
      children: const [
        GoogleMapTileLayer(mapType: MapType.normal),
      ],
    );
  }
}

@thomashrabe
Copy link

thomashrabe commented Sep 1, 2023

Thank you for the update. However, I tried it and it seems now that your example is lagging behind. The source file does not contain GoogleMapTileLayerOptions anymore. I think they were in there before? @Ahmed-gubara

@mohyM
Copy link

mohyM commented Sep 4, 2023

could you please let me know if you fixed this issue to use Google Maps as a layer inside flutter_map @Ahmed-gubara

@mohyM
Copy link

mohyM commented Sep 4, 2023

also marker on the map didn't move correctly with map move Is there an update for this @Ahmed-gubara

@mohyM
Copy link

mohyM commented Sep 4, 2023

WhatsApp.Video.2023-09-04.at.10.07.26.AM.mp4

@Ahmed-gubara could you please check this video

@Ahmed-gubara
Copy link
Author

@mohyM
Google map doesn't support zoom level over 21. and FlutterMap doesn't limit maxZoom by default.
Limit max zoom on FlutterMap to 21

I just updated the example to include maxZoom: 21

@mohyM
Copy link

mohyM commented Sep 4, 2023

But with zoom markers overlying each other Is there another way to fix this @Ahmed-gubara

@Ahmed-gubara
Copy link
Author

@mohyM
Unfortunately that is the zoom limit supported by google map itself. CameraUpdateFactory

The only way is to cluster markers that are overlapping.
using any plugin supported by FlutterMap, see marker clustering

and also:
As i see from the video, you didn't disable map rotation as set by the example.
i have found a way to allow google map to rotate correctly without rotating labels and updated the code.
kindly try the updated code and tell me if it's working correctly for you.

@mohyM
Copy link

mohyM commented Sep 4, 2023

@Ahmed-gubara
I tried the new version of the code without max-zoom the pin will move as the same in the video
Also, I have a question Why u add stack over Google Maps it can work without it

@Ahmed-gubara
Copy link
Author

@mohyM
The new version doesn't fix the need for specifying maxZoom:21.
It just prevent labels from rotating with the map.

--

Why i need a Stack over GoogleMap?

I need FlutterMap to handle map gestures.
And only change GoogleMap position using code when the position of FlutterMap is changed.
See the mapEvent(FlutterMapState map) function.

If i didn't add a Stack and a Transparent Container Container(color: Colors.transparent) to be over GoogleMap.
Gestures will not be handled by FlutterMap, and it will be handled instead by GoogleMap, which has all of its gesture properties disabled ( for example scrollGesturesEnabled: false) so it will not do any thing.

it will work correctly for you without the Stack and The Transparent Container because you already have another flutter widget displayed over GoogleMap, which is the MarkerLayer. which will allow gestures to be handled by FlutterMap instead of GoogleMap.

@mohyM
Copy link

mohyM commented Sep 5, 2023

Thanks @Ahmed-gubara
I removed Stack because I have another layer as you mentioned before, also I reduced the size of pins on the map by 30% and Google Maps Max Zoom working fine without marker clustering but I will talk with the client about it,
Also, I added a callback function to update the zoom level because when u want to zoom Google Maps will zoom but flutter_map with marker will not zoom

@Ahmed-gubara
Copy link
Author

@mohyM
Welcome, have fun and i hope it goes well.
Keep in mind the stuttering on pre android 10 ( <= android 9 ) as discussed on flutter_map/discussions/1158.

This code may not work well on android devices pre android 10, because of flutter using better android platform integration starting with android 10. flutter hybrid-composition, and i don't have a device that is pre android 10 to test if this is still an issue.

Related to map zooming:
This code only forward FlutterMap changes to GoogleMap, so it's a one way binding. That's why i disabled all Gestures and interactions on GoogleMap as it will not be reflected on FlutterMap.

Because the last time i tried GoogleMap's callbacks, they were inconsistent; they sometimes misfire causing FlutterMap to lag behind.
It could have been fixed lately, but i haven't test it.

And for that i made my own Flutter Zoom buttons as a nonRotatedChildren to control FlutterMap (visible in the video on the discussions).

If GoogleMap's callbacks worked correctly i may update the code to allow for two-way binding between FlutterMap and GoogleMap.

@mohyM
Copy link

mohyM commented Sep 5, 2023

@Ahmed-gubara thanks
I tried the zoom with the two-way binding map but lagged behind. I will implement Zoom with my buttons

@mohyM
Copy link

mohyM commented Oct 25, 2023

@Ahmed-gubara hi ahmed do u think u can update "GoogleMapTileLayer" class u created before to work fine with flutter_map version > 6.0.0

@mohyM
Copy link

mohyM commented Oct 30, 2023

@Ahmed-gubara are u here to help please ?

@Ahmed-gubara
Copy link
Author

@mohyM
Because of traveling, I couldn't use my laptop in the last week.

The code for (flutter_map > 6.0.0) is in a separate gist.
google_tile_layer_widget.dart

Just replace the content of google_tile_layer_widget.dart, there is no change on the usage.

@mohyM
Copy link

mohyM commented Nov 10, 2023

thanks, @Ahmed-gubara i already solved it, but I would like to have ur email could u please share it

@Ahmed-gubara
Copy link
Author

@mohyM
That's great.

There was a bug on the old code related to the padding property of google map.
If you set an asymmetric padding for google maps, that padding will make markers appear slightly shifted from there actual locations when the maps is zoomed out.
And it's can be noticeable if padding was large.
This is fixed on function convertLatLng on the new code.

my email is ahmed.gubara@gmail.com

@mohyM
Copy link

mohyM commented Nov 14, 2023

@Ahmed-gubara I just tried. the new "convertLatLng" that u created but the pin moves over the maps in not correct way if u want I can share the video

@Ahmed-gubara
Copy link
Author

@mohyM
yes, send me a video.

@zozeei
Copy link

zozeei commented May 14, 2024

How did you solve this problem? I am experiencing the same issue.

@mohyM
Copy link

mohyM commented May 14, 2024

@zozeei which issue u have could u please let me know to help u ?

@zozeei
Copy link

zozeei commented May 14, 2024

google_map.webm

I am experiencing the same issue as shown in your video.

@zozeei
Copy link

zozeei commented May 29, 2024

Do you have any advice for me? Or how do I fix it?
thank you very much

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment