flutter_unified_map_android 1.0.0
flutter_unified_map_android: ^1.0.0 copied to clipboard
Android implementation of flutter_unified_map on the Google Maps SDK for Android.
flutter_unified_map_android #
Android implementation of flutter_unified_map on the Google Maps SDK for Android (play-services-maps 20.0.0).
Apps depend on flutter_unified_map; this package is endorsed and picked automatically on Android.
Setup #
Step-by-step guide with the Gradle snippet and troubleshooting: repository README.
-
Create a key for the Maps SDK for Android in the Google Cloud console (the project needs billing enabled; the "Mobile Native Dynamic Maps" SKU itself is free and unlimited). Restrict it to your package name and signing certificate SHA-1.
-
Declare it in
android/app/src/main/AndroidManifest.xml, inside<application>:<application ...> <meta-data android:name="com.google.android.geo.API_KEY" android:value="YOUR_KEY" /> </application>To keep the key out of version control, use a manifest placeholder (
android:value="${MAPS_API_KEY}") filled fromlocal.propertiesinandroid/app/build.gradle.kts(manifestPlaceholders["MAPS_API_KEY"] = ...). -
The device or emulator needs Google Play services (use a "Google APIs" or "Google Play" emulator image).
minSdkis 24.
Error reporting #
The plugin reports why the base map cannot be shown as a PlatformMapErrorEvent, which flutter_unified_map turns into an
error view:
apiKeyMissing: nocom.google.android.geo.API_KEY(or legacycom.google.android.maps.v2.API_KEY) meta-data, or an empty one (an unset${MAPS_API_KEY}placeholder). The GoogleMapViewis not created; the view shows the background color, never becomes ready, and every call on it is a no-op.servicesUnavailable: Google Play services is missing, disabled, outdated or updating (GoogleApiAvailability), checked once when the view is created. Same handling. The message says when the user can fix it (GoogleApiAvailability.makeGooglePlayServicesAvailable); the map must then be created again (a newUnifiedMap).loadTimeout: the map did not finish loading within 15 s during which it could load: the time only counts while the activity is resumed and the view has a size, and starts again after a pause. This is how a rejected key or a missing network shows up (Logcat showsAuthorization failurefor a rejected key). If the map loads later,PlatformMapLoadedEventis sent and clears the error.
PlatformMapLoadedEvent is sent after the ready event, when the map has finished loading for the first time.
User location #
MapOptions(showsUserLocation: true) needs ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION declared in the
manifest and granted at runtime (request it with a permission plugin before enabling the option). Without the
permission the option is ignored and a warning is logged.
Platform notes #
- Rendering: the map is an
AndroidView(texture layer hybrid composition). The plugin forwards the activity lifecycle to everyMapViewthroughflutter_plugin_android_lifecycle. PlatformMapStyle.mutedis a built-in grey JSON style;showsPointsOfInterest: falseadds a POI-hiding style;PlatformMapStyle.noneand tile layers withreplacesBaseMapswitch toMAP_TYPE_NONE.- Not supported by the Google Maps SDK for Android:
showsScale(no native scale bar),doubleTapZoomEnabledindependently ofzoomEnabled(Google ties double-tap zoom to zoom gestures), and changingbackgroundColorafter the view is created. - Camera:
fitBoundsalways ends with a tilt of 0 (the fit is computed for a flat camera);newCamerakeeps the current value of every field it leaves null. An instantmoveCamerareports Google's own start, move and idle events. A movement has one start event, as on MapKit: a gesture that interrupts an animated move continues that movement. - Options:
updateOptionssets only the fields that changed, so a padding that follows a panel does not reload the style. - Density: with
densityin the activity'sandroid:configChanges, a display density change keeps the view; padding, line widths, dashes and the{r}choice of tile layers follow the new density. - Icons: a marker whose icon is missing (never registered, or a PNG that cannot be decoded) is drawn transparent and a warning is logged, rather than Google's default red pin.
- Snapshots:
takeSnapshotreturns null when Google does not deliver the bitmap within 5 s (a paused map). - Taps: Google consumes every tap on a marker. A tap on a marker with
tappable: falsereaches Dart as a map tap at the marker's position, not at the finger, so polyline/polygon hit-testing andonTapsee that point. getCamera,getVisibleRegion,toScreenandtoLatLngthrow aPlatformExceptionfor a view that cannot answer, since they have no value to return:unknown-view,disposed,destroyed(the activity destroyed theMapView) ornot-ready(before the ready event, and always for a view without a key or Play services).- Tile layers: a
tileSizeof 512 or 1024 means each source tilez/x/yhas the usual Web Mercator extent and is drawntileSizelogical pixels wide, so the plugin fetches the tile one (or two) zoom levels lower and crops it to Google's 256 dp grid. Other sizes only set the image resolution. The plugin fetches every tile itself: HTTP 429 and 5xx responses and network errors are retried later; other errors leave the tile empty.{s}is chosen from the tile coordinates, so a tile always has the same URL. AbovemaxZoom(the source's last native level) the tiles of that level are cropped and scaled up, as MapKit does. Changing onlyopacityorzIndexupdates the layer in place; other changes fetch its tiles again. - Tile caching: Google keeps tiles in memory only. Tiles go through
HttpURLConnectionwith caches enabled, so an app that callsandroid.net.http.HttpResponseCache.install(...)gets a disk cache that honours the servers' caching headers (asked for by the OpenStreetMap tile policy). For OpenStreetMap, setuserAgentPackageNameon theTileLayer: without it the request carries Android's defaultDalvik/...User-Agent.