yarrow_map_flutter_sdk 0.1.0
yarrow_map_flutter_sdk: ^0.1.0 copied to clipboard
Flutter SDK for Yarrow Maps with API parity to the web SDK.
Yarrow Map Flutter SDK #
Flutter SDK for integrating Yarrow maps in mobile apps, with an API aligned to the Yarrow web SDK.
This README is a quick reference. The full manual — every option table, resolution pipelines, error handling, common use cases, and troubleshooting — lives in the User Guide.
Table of Contents #
- Getting Started
- Basic Map Manipulation
- Handling Events
- Working with Layers and Data
- POI Highlighting
- Routing
- Search
- Public Transport
- Utility Methods
- API Reference
- Platform Limitations
Getting Started #
Installation #
flutter pub add yarrow_map_flutter_sdk
Requires Flutter >= 3.29. Get an API key at dashboard.yarrow.uz.
Initialization #
Render the map with the YarrowMapView widget. The runtime API lives on the YarrowMap object delivered by onReady:
import 'package:flutter/material.dart';
import 'package:yarrow_map_flutter_sdk/yarrow_map_flutter_sdk.dart';
class MapScreen extends StatefulWidget {
const MapScreen({super.key});
@override
State<MapScreen> createState() => _MapScreenState();
}
class _MapScreenState extends State<MapScreen> {
final controller = YarrowMapController();
@override
Widget build(BuildContext context) {
return Scaffold(
body: YarrowMapView(
config: const YarrowMapConfig(
center: (lng: 69.2401, lat: 41.2995),
apiKey: 'YOUR_API_KEY',
zoom: 12,
),
controller: controller,
onReady: (map) async {
await map.zoomTo(41.2995, 69.2401, 14);
},
),
);
}
}
YarrowMapController is optional; it holds the YarrowMap (controller.map) so other widgets can reach it.
Configuration Options #
const mapConfig = YarrowMapConfig(
center: (lng: 69.2401, lat: 41.2995), // required
apiKey: 'YOUR_API_KEY', // required
zoom: 10,
minZoom: 0,
maxZoom: 19,
theme: YarrowTheme.light, // light | dark
brandBadgePosition: BrandBadgePosition.bottomLeft,
cache: YarrowCacheConfig(enabled: false, lifespanDays: 30),
controls: YarrowControlsConfig(
enabled: false,
position: YarrowControlsPosition.right,
zoom: true, // zoom in/out buttons
compass: true, // reset-north compass
),
excludePoiLayer: false, // strip POIs from the base style
clustering: YarrowClusteringConfig(), // clustering of highlighted POIs
);
Coordinate Order #
YarrowMapConfig.centeruses a named tuple:(lng: ..., lat: ...).addMarkerand all routing coordinates use[lng, lat]lists (GeoJSON order, matching the web SDK). This changed in 0.1.0 — previously[lat, lng].zoomTo(lat, lng, zoom)takes separate named-position arguments (lat first), matching the web SDK.
Basic Map Manipulation #
Changing the Map Style #
await map.changeStyles(YarrowStyleType.satellite); // default_ | satellite | hybrid
await map.changeStyles(); // back to default
map.getStyleType(); // current YarrowStyleType
Changing the Map Theme #
await map.changeTheme(YarrowTheme.dark);
Theme changes preserve the current style type (satellite/hybrid), and custom layers/highlights are re-applied automatically after any style or theme change.
Changing Brand Badge Position #
map.changeBrandBadgePosition(BrandBadgePosition.topRight);
Moving the Map View #
await map.zoomTo(41.2995, 69.2401, 14);
await map.zoomTo(41.2995, 69.2401, 14, const ZoomToOptions(durationMs: 2000));
await map.fitBounds(featureCollection); // any GeoJSON FeatureCollection
await map.fitBounds(
featureCollection,
const FitBoundsOptions(padding: 80, durationMs: 1000, maxZoom: 15),
);
await map.zoomIn();
await map.zoomOut();
await map.resetNorth();
Read Accessors #
map.isReady(); // bool
map.getTheme(); // YarrowTheme
map.getStyleType(); // YarrowStyleType
map.getCenter(); // [lng, lat]
map.getZoom(); // double
Handling Events #
All subscriptions return an unsubscribe function:
final unsubscribe = map.onMoveEnd((lat, lng, zoom) {
print('moved to $lat, $lng @ $zoom');
});
unsubscribe(); // stop listening
map.onMapClick((lat, lng) { ... });
// POI / building icon taps. For pois, `selected` reports whether the tapped
// POI is currently highlighted (null for buildings).
map.onIconClick(YarrowLayerGroup.pois, (lat, lng, properties, selected) {
print('POI ${properties['name']} selected=$selected');
});
// Base-layer taps: buildings, pois, and admin label layers.
map.onLayerClick(YarrowBaseLayerName.district, (lat, lng, properties) { ... });
// Targets: buildings | pois | mahalla | district | region | country
// Taps on layers you added yourself:
map.onCustomLayerClick('my-layer', (lat, lng, properties) { ... });
Streams are also available: map.onMoveEndStream, map.onMapClickStream.
Working with Layers and Data #
Adding a GeoJSON Layer #
await map.addLayer(
layerName: 'my-points',
layerType: 'circle', // circle | symbol | line | fill | heatmap | ...
featureCollection: geojson,
paint: {'circle-radius': 8, 'circle-color': '#ef4444'},
layout: {},
);
await map.removeLayer('my-points');
Clustering options for the backing GeoJSON source: cluster, clusterRadius, clusterMaxZoom, clusterProperties. Use YarrowAddLayerOptions(sourceId: ..., filter: ...) to attach the layer to a shared source or filter it.
Custom layers are re-added automatically after changeStyles / changeTheme.
Managing Sources Explicitly #
await map.addSource('vehicles', geojson);
await map.updateSourceData('vehicles', newGeojson); // efficient live updates
await map.addLayer(
layerName: 'vehicles-layer',
layerType: 'circle',
featureCollection: geojson,
options: const YarrowAddLayerOptions(sourceId: 'vehicles'),
);
Adding and Removing Markers #
final marker = await map.addMarker(
[69.2401, 41.2995], // [lng, lat]
options: YarrowMapMarkerOptions(
color: Colors.purple, // tint of the default pin
draggable: true,
anchor: YarrowMarkerAnchor.bottom,
onClick: () => print('marker tapped'),
// iconImage: 'my-registered-image', // use a custom style image instead
// iconSize: 1.2, rotation: 45,
),
);
await map.removeMarker(marker);
Querying Rendered Features #
final features = await map.queryRenderedFeatures(
point: const Point<double>(120, 80), // or rect: Rect.fromLTWH(...)
options: const YarrowRenderedFeaturesQueryOptions(layers: ['my-points']),
);
setFeatureState(...) is also exposed, but maplibre_gl currently implements it on web only — see Platform Limitations.
POI Highlighting #
Highlight POIs from the base map — the POI is hidden from the base layer and re-rendered as a prominent marker:
final highlight = map.highlightPOI(
poiId,
HighlightPOIOptions(
location: (lat: 41.31, lng: 69.28), // skip lookup if you have coords
title: 'Coffee House',
icon: 'https://.../icon.png', // optional explicit icon URL
iconSize: 56, // logical px
panTo: true,
zoom: 15,
replace: false, // true replaces existing highlights
onClick: (info) => print('tapped highlight ${info.id}'),
),
);
highlight.remove(); // or:
map.resetPOIHighlight(poiId); // one
map.resetPOIHighlight([id1, id2]); // several
map.resetPOIHighlight(); // all
map.isPOIHighlighted(poiId);
map.getHighlightedPOIs();
// Multiple at once (Dart has no overloads — separate method):
map.highlightPOIs([id1, id2, id3], const HighlightPOIOptions(panTo: true));
When no location is given, coordinates and icon are resolved from loaded map tiles first, then from the Yarrow API. If no icon can be resolved, a blue dot marker is shown.
Clustering #
Nearby highlight markers can collapse into numbered badges:
map.setClustering(YarrowClusteringConfig(
enabled: true,
radius: 60, // screen px
maxZoom: 16, // above this zoom all markers show individually
minPoints: 2,
onClusterClick: (children) {
print('cluster of ${children.length}: ${children.map((c) => c.id)}');
return false; // suppress the default zoom-to-expand
},
));
map.getClustering();
Can also be set at startup via YarrowMapConfig(clustering: ...).
POI Base Layer Toggle #
// At startup:
YarrowMapConfig(excludePoiLayer: true, ...);
// At runtime:
map.disablePoiLayer();
map.enablePoiLayer();
map.setPoiLayerEnabled(true);
Routing #
final result = await map.buildRoute(BuildRouteOptions(
profile: 'car', // car | bicycle | pedestrian | foot ...
coordinates: [
[69.2797, 41.3111], // [lng, lat] — 2 or more waypoints
[69.2332, 41.3267],
],
routeColor: '#22c55e',
routeWidth: 5,
alternativeColor: '#aaaaaa',
fitBounds: true,
clearOldRoutes: true,
language: 'ru', // Accept-Language for directions
cancelToken: myCancelToken, // dio CancelToken to abort the request
onRouteClick: (ctx) => print('route ${ctx.index}, best=${ctx.isBest}'),
));
print(result.meta.distance); // meters (best route)
print(result.meta.duration); // minutes (best route)
print(result.features); // GeoJSON LineStrings, best first
print(result.directions); // turn-by-turn steps per route
await map.clearAllRoutes();
Pre-computed routes can be drawn with renderRoutes(routes, baseLayerName: 'route') using a distinct-series palette.
buildRouteWithLabels and buildMultiSegmentRouteWithLabels still work but are deprecated delegates of buildRoute (and now take [lng, lat]).
Search #
final search = map.highlightSearchResults(
'cafe',
options: YarrowHighlightOptions(
layerName: 'search-results',
iconImage: 'geo-icon', // fallback style image; null skips icons
zoomToResults: true,
page: 1,
pageSize: 15,
onResultsUpdate: (results) => print('${results.length} results'),
onLoadingStateChange: (state) {
// YarrowSearchLoadingState.firstRender | .rerender | null (done)
},
onIconClick: (lat, lng, properties) => print(properties['name']),
),
);
search.cancel(); // remove result layers and stop refreshing
Results refresh automatically when the map center moves at least 500 m.
Public Transport #
final bus = await map.showBusRoute(); // all buses near viewport
final one = await map.showBusRoute(routeId: '52'); // one route + its geometry
bus.cancel();
Positions are polled every 16 s and animated smoothly between updates.
Utility Methods #
final bbox = map.getBoundingBox(featureCollection);
// YarrowBoundingBox(xMin, yMin, xMax, yMax) — lng/lat extents
await map.clearCache(); // clear the HTTP cache (when cache.enabled)
map.destroy(); // alias of dispose(); the view calls dispose for you
API Reference #
YarrowMap (via onReady / controller.map) #
| Method | Description |
|---|---|
isReady() / getTheme() / getStyleType() / getCenter() / getZoom() |
Read accessors |
changeStyles([YarrowStyleType?]) |
Switch base style |
changeTheme(YarrowTheme) |
Switch light/dark, keeps style type |
changeBrandBadgePosition(BrandBadgePosition) |
Move the badge |
changeBackgroundColor(Color) |
Overlay a background fill |
zoomTo(lat, lng, zoom, [ZoomToOptions]) |
Fly to a point |
fitBounds(geojson, [FitBoundsOptions]) |
Fit camera to data |
zoomIn() / zoomOut() / resetNorth() |
Camera helpers |
onMoveEnd / onMapClick / onIconClick / onLayerClick / onCustomLayerClick |
Events; return unsubscribe fn |
addLayer(...) / removeLayer(name) |
Custom GeoJSON layers |
addSource(id, data) / updateSourceData(id, data) |
Explicit sources |
queryRenderedFeatures(...) / setFeatureState(...) |
Feature queries/state |
addMarker([lng, lat], options) / removeMarker(handle) |
Markers |
highlightPOI(id, options) / highlightPOIs(ids, options) |
POI highlighting |
resetPOIHighlight([idOrIds]) / isPOIHighlighted(id) / getHighlightedPOIs() |
Highlight management |
setClustering(config) / getClustering() |
Highlight clustering |
enablePoiLayer() / disablePoiLayer() / setPoiLayerEnabled(bool) |
POI base layer |
buildRoute(BuildRouteOptions) / renderRoutes(...) / clearAllRoutes() |
Routing |
highlightSearchResults(query, options) |
Search |
showBusRoute({routeId}) |
Live buses |
getBoundingBox(geojson) / clearCache() / destroy() |
Utilities |
Key Types #
YarrowTheme—light,darkYarrowStyleType—default_,satellite,hybrid(wireNamegives the API string)BrandBadgePosition—topLeft,topRight,bottomLeft,bottomRightYarrowControlsPosition—left,leftTop,leftBottom,right,rightTop,rightBottomYarrowLayerGroup—pois,buildingsYarrowBaseLayerName—buildings,pois,mahalla,district,region,countryYarrowMarkerAnchor—center,top,bottom,left,right, cornersHighlightPOIOptions,POIHighlight,PoiHighlightClickInfoYarrowClusteringConfig,ClusterChildBuildRouteOptions,BuildRouteResult,BuildRouteMeta,RouteEventContextZoomToOptions,FitBoundsOptionsYarrowHighlightOptions,YarrowSearchLoadingStateYarrowCancelable— handle with acancel()function
Platform Limitations #
Differences from the web SDK that stem from the native platform:
- Hover effects (
hoverconfig,onRouteHover) are not implemented — mouse hover has no equivalent on touch devices. setFeatureStateis exposed but only functional on web; Android/iOS throwUnimplementedError(upstream maplibre_gl limitation).- Highlight markers appear without the web SDK's enter/exit scale animation.
- Highlight clusters recompute on camera idle and throttled camera movement, not per rendered frame.
ZoomToOptions.speedapproximates the web SDK's flyTo speed by scaling the animation duration.FitBoundsOptions.maxZoomis applied as a post-animation clamp.
Support #
Issues and questions: git.yarrow.uz