Yarrow Map Flutter SDK
Flutter SDK for integrating Yarrow maps in mobile apps, with an API aligned to the Yarrow web SDK.
Table of Contents
- Getting Started
- Basic Map Manipulation
- Handling Events
- Working with Layers and Data
- Routing
- Search
- Public Transport
- Utility Methods
- API Reference
Getting Started
Installation
First, add the Yarrow Map Flutter SDK to your project:
flutter pub add yarrow_map_flutter_sdk
Initialization
To get started, create a YarrowMapController and use YarrowMapView widget to render the map.
Important - Coordinate Format: This SDK uses (lng: number, lat: number) named tuples for map configuration, matching the MapLibre convention.
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);
},
),
);
}
}
Configuration Options
The YarrowMapConfig class accepts the following configuration options:
const mapConfig = YarrowMapConfig(
center: (lng: 69.2401, lat: 41.2995), // Required: center coordinates
apiKey: 'YOUR_API_KEY', // Required: your Yarrow API key
zoom: 12, // Initial zoom level (default: 10)
minZoom: 5, // Minimum zoom level (default: 0)
maxZoom: 18, // Maximum zoom level (default: 19)
theme: YarrowMapTheme.dark, // 'light' or 'dark' (default: 'light')
brandBadgePosition: BrandBadgePosition.topRight, // Badge position
controls: YarrowControlsConfig(
enabled: true, // Controls are OFF by default
position: YarrowControlsPosition.rightBottom, // Optional placement
showZoom: true, // Show zoom buttons
showCompass: true, // Show compass
),
);
Example with all options:
import 'package:flutter/material.dart';
import 'package:yarrow_map_flutter_sdk/yarrow_map_flutter_sdk.dart';
class FullConfigMapScreen extends StatelessWidget {
const FullConfigMapScreen({super.key});
@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,
minZoom: 5,
maxZoom: 18,
theme: YarrowMapTheme.dark,
brandBadgePosition: BrandBadgePosition.topRight,
controls: YarrowControlsConfig(
enabled: true,
position: YarrowControlsPosition.rightBottom,
showZoom: true,
showCompass: true,
),
),
controller: YarrowMapController(),
onReady: (map) {
print('Map ready!');
},
),
);
}
}
When controls and the brand badge are placed in the same corner (for example rightBottom with brandBadgePosition.bottomRight), the SDK automatically adds spacing so they do not overlap.
Basic Map Manipulation
Changing the Map Style
You can change the map's visual style. The available styles are satellite, hybrid, and the default map style.
// Switch to satellite view
await map.changeStyles(styleType: YarrowMapStyleType.satellite);
// Switch to hybrid view (satellite with labels)
await map.changeStyles(styleType: YarrowMapStyleType.hybrid);
// Switch back to the default map style
await map.changeStyles();
Changing the Map Theme
You can set the initial theme in the configuration or switch between light and dark themes dynamically after initialization.
Setting initial theme:
const mapConfig = YarrowMapConfig(
center: (lng: 69.2401, lat: 41.2995),
apiKey: 'YOUR_API_KEY',
zoom: 12,
theme: YarrowMapTheme.dark, // Set initial theme to dark
);
Switching theme dynamically:
// Switch to dark mode
await map.changeTheme(YarrowMapTheme.dark);
// Switch to light mode (default)
await map.changeTheme(YarrowMapTheme.light);
Changing Brand Badge Position
You can set the initial badge position in config or change it dynamically after map initialization.
// Move badge to the top-right corner
map.changeBrandBadgePosition(BrandBadgePosition.topRight);
// Other values: BrandBadgePosition.topLeft, .bottomLeft, .bottomRight
Moving the Map View
You can programmatically move the map to a new location or fit it to a specific geographic area.
Zooming to a specific point:
// Fly to a new location with a specific zoom level
map.zoomTo(41.3111, 69.2797, 15); // Latitude, Longitude, Zoom Level
Fitting the map to a set of features:
This is useful when you want to display a set of points, lines, or polygons and ensure they are all visible.
final geojsonData = {
'type': 'FeatureCollection',
'features': [
// Your GeoJSON features here
],
};
map.fitBounds(geojsonData);
Handling Events
You can listen for various user interactions with the map.
Listen for Map Movement
Execute a callback function whenever the map finishes moving.
map.onMoveEnd((lat, lng, zoom) {
print('Map moved to: $lat, $lng with zoom: $zoom');
});
Listen for Map Clicks
Execute a callback when the user clicks on the map.
map.onMapClick((lat, lng) {
print('Map clicked at: $lat, $lng');
});
Listen for Clicks on POIs or Buildings
You can add click listeners to specific groups of features on the map, like points of interest (POIs) or buildings.
// Listen for clicks on POIs
map.onIconClick(YarrowLayerGroup.pois, (lat, lng, properties) {
print('POI clicked: $properties');
});
// Listen for clicks on buildings
map.onIconClick(YarrowLayerGroup.buildings, (lat, lng, properties) {
print('Building clicked: $properties');
});
Working with Layers and Data
Adding a GeoJSON Layer
You can add custom data to the map in the form of a GeoJSON layer. This is useful for displaying points, lines, or polygons.
final myData = {
'type': 'FeatureCollection',
'features': [
{
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [69.2797, 41.3111],
},
'properties': {
'name': 'My Point',
},
},
],
};
map.addLayer(
'my-custom-layer', // A unique name for the layer
'circle', // The type of layer (e.g., 'circle', 'line', 'fill', 'symbol')
myData,
paint: {
'circle-radius': 10,
'circle-color': '#ff0000',
},
);
Advanced Layer Configuration
The addLayer method supports advanced configuration options for different layer types:
map.addLayer(
'layerName', // String - Unique identifier for the layer
'symbol', // Layer type: 'symbol', 'fill', 'line', 'background', 'raster', 'circle', 'heatmap', 'fill-extrusion', 'hillshade'
featureCollection, // GeoJSON FeatureCollection
paint: { // Paint properties map
'text-halo-color': '#ffffff',
'text-halo-width': 2,
},
layout: { // Layout properties map
'icon-image': 'custom-icon',
'icon-size': 0.8,
'text-field': ['get', 'name'],
},
options: YarrowAddLayerOptions(
sourceId: 'my-source',
filter: ['==', ['get', 'status'], 'active'],
),
);
Managing Sources Explicitly
map.addSource('vehicles-source', vehicleFeatureCollection);
map.updateSourceData('vehicles-source', nextVehicleFeatureCollection);
map.addLayer(
'active-vehicles',
'circle',
nextVehicleFeatureCollection,
paint: {
'circle-radius': {'type': 'interpolate', 'input': {'type': 'zoom'}, 'stops': [[10, 4], [16, 10]]},
'circle-color': {'type': 'case', 'condition': ['==', ['get', 'status'], 'active'], 'true': '#16a34a', 'false': '#9ca3af']},
},
options: YarrowAddLayerOptions(
sourceId: 'vehicles-source',
filter: ['==', ['get', 'status'], 'active'],
),
);
Feature State and Rendered Queries:
map.setFeatureState(
source: 'vehicles-source',
id: 101,
state: {'selected': true},
);
final features = map.queryRenderedFeatures(
options: YarrowQueryRenderedFeaturesOptions(layers: ['active-vehicles']),
);
Adding and Removing Markers
You can add markers to the map to indicate specific locations.
// Add a simple marker
final marker = map.addMarker(41.3111, 69.2797); // latitude, longitude
// Add a marker with options
final customMarker = map.addMarker(
41.2995,
69.2401,
color: '#0000ff', // Marker color (default: '#FF0000')
draggable: true, // Allow dragging (default: false)
anchor: YarrowMarkerAnchor.bottom, // Anchor position
onClick: () { // Click event handler
print('Marker clicked!');
},
);
// Remove a marker
map.removeMarker(marker);
Removing a Layer
You can remove a layer that you've previously added.
map.removeLayer('my-custom-layer');
Routing
The SDK provides powerful routing capabilities.
Building and Displaying a Simple Route
Calculate and display a route between a start and end point.
final start = (lat: 41.2995, lng: 69.2401);
final end = (lat: 41.3111, lng: 69.2797);
const profile = 'car'; // 'car', 'bicycle', or 'pedestrian'
final result = await map.buildRouteWithLabels(start, end, profile);
print('Route built: ${result.features}');
print('Directions: ${result.directions}');
Building and Displaying a Multi-Stop Route
You can also create a route that passes through multiple waypoints.
final coordinates = [
(lat: 41.2995, lng: 69.2401), // Start
(lat: 41.3111, lng: 69.2797), // Waypoint 1
(lat: 41.325, lng: 69.285), // End
];
const profile = 'car';
const language = 'en'; // Optional: 'en', 'ru', etc. (default: 'ru')
final result = await map.buildMultiSegmentRouteWithLabels(coordinates, profile, language);
print('Multi-segment route built: ${result.features}');
print('Directions: ${result.directions}');
Clearing Routes
To remove all route-related layers from the map:
map.clearAllRoutes();
Search
Highlighting Search Results
You can perform a search and display the results on the map. The search is performed around the current map center.
const query = 'Tashkent';
final clearHighlights = map.highlightSearchResults(
query,
zoomToResults: true, // Automatically zoom to fit the results
onResultsUpdate: (results) {
print('Search results: $results');
},
onLoadingStateChange: (state) {
// state can be 'firstRender', 'rerender', or null
print('Loading state: $state');
},
);
// To remove the search results from the map later
clearHighlights();
Public Transport
Displaying Real-Time Bus Locations
You can display the real-time locations of buses on the map.
Show buses for a specific route:
const routeId = 'some-route-id';
final clearBusRoute = await map.showBusRoute(routeId);
// To stop showing the bus route later
// clearBusRoute();
Show all buses in the current map view:
If you don't provide a routeId, the map will show all buses within the visible area.
final clearBusRoutes = await map.showBusRoute();
// To stop showing all bus routes
// clearBusRoutes();
Clearing Bus Routes
The showBusRoute method returns a cleanup function that you can call to stop displaying bus locations and remove all related data:
// Store the cleanup function
final clearBuses = await map.showBusRoute('route-123');
// Later, clear the bus route display
clearBuses();
Utility Methods
Calculating Bounding Box
The SDK provides a utility method to calculate the bounding box of GeoJSON data:
final geojsonData = {
'type': 'FeatureCollection',
'features': [
{
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [69.2797, 41.3111],
},
},
{
'type': 'Feature',
'geometry': {
'type': 'LineString',
'coordinates': [
[69.240, 41.299],
[69.280, 41.311],
],
},
},
],
};
final boundingBox = map.getBoundingBox(geojsonData);
print(boundingBox);
// Output: { xMin: 69.240, xMax: 69.280, yMin: 41.299, yMax: 41.3111 }
Supported Geometry Types:
- Point
- LineString
- Polygon
The bounding box contains:
xMin: Minimum longitudexMax: Maximum longitudeyMin: Minimum latitudeyMax: Maximum latitude
API Reference
YarrowMapConfig
class YarrowMapConfig {
final ({double lng, double lat}) center; // Required
final String apiKey; // Required: Yarrow API key
final double zoom; // default: 10
final double minZoom; // default: 0
final double maxZoom; // default: 19
final YarrowMapTheme theme; // default: light
final BrandBadgePosition? brandBadgePosition;
final YarrowControlsConfig? controls;
}
YarrowControlsConfig
class YarrowControlsConfig {
final bool enabled; // default: false
final YarrowControlsPosition position; // default: right
final bool showZoom; // default: true
final bool showCompass; // default: true
}
YarrowMapController
| Method | Parameters | Return Type | Description |
|---|---|---|---|
zoomTo |
lat: double, lng: double, zoom: double |
void |
Fly to specific coordinates |
fitBounds |
data: Map<String, dynamic> |
void |
Fit map to show all features |
getBoundingBox |
data: Map<String, dynamic> |
YarrowBoundingBox |
Calculate bounding box of features |
changeStyles |
styleType?: YarrowMapStyleType |
Future<void> |
Change map style |
changeTheme |
theme: YarrowMapTheme |
Future<void> |
Switch between light and dark themes |
changeBrandBadgePosition |
position: BrandBadgePosition |
void |
Change brand badge position at runtime |
onMoveEnd |
callback: Function |
void |
Listen for map movement end |
onMapClick |
callback: Function |
void |
Listen for map clicks |
onIconClick |
layerGroup: YarrowLayerGroup, callback: Function |
void |
Listen for icon clicks |
addSource |
sourceId: String, data: Map<String, dynamic> |
void |
Add/replace a GeoJSON source |
updateSourceData |
sourceId: String, data: Map<String, dynamic> |
void |
Update data for an existing GeoJSON source |
addLayer |
name: String, type: String, data: Map<String, dynamic>, ... |
void |
Add a layer to the map |
setFeatureState |
{source: String, id: dynamic}, state: Map<String, dynamic> |
void |
Set state for a specific feature |
queryRenderedFeatures |
options: YarrowQueryRenderedFeaturesOptions |
List<Map<String, dynamic>> |
Query currently rendered features |
removeLayer |
layerName: String |
void |
Remove a layer from the map |
addMarker |
lat: double, lng: double, options?: YarrowMarkerOptions |
YarrowMarker? |
Add a marker to the map |
removeMarker |
marker: YarrowMarker |
void |
Remove a marker from the map |
buildRouteWithLabels |
start: (lat: double, lng: double), end: (lat: double, lng: double), profile: String |
Future<YarrowRouteResult> |
Build and display a route |
buildMultiSegmentRouteWithLabels |
coordinates: List<(lat: double, lng: double)>, profile: String, language?: String |
Future<YarrowRouteResult> |
Build multi-segment route |
clearAllRoutes |
None | void |
Clear all route layers and popups |
highlightSearchResults |
query: String, options?: YarrowSearchOptions |
void Function() |
Highlight search results with cleanup function |
showBusRoute |
routeId?: String |
Future<void Function()> |
Show bus locations with cleanup function |
Type Definitions
// Bounding Box
class YarrowBoundingBox {
final double xMin;
final double xMax;
final double yMin;
final double yMax;
}
// Route Result
class YarrowRouteResult {
final List<dynamic> features;
final List<dynamic> directions;
}
// Marker Options
class YarrowMarkerOptions {
final String? color;
final bool draggable;
final YarrowMarkerAnchor anchor;
final void Function()? onClick;
}
// Search Options
class YarrowSearchOptions {
final String? layerName;
final String? iconImage;
final String? highlightColor;
final bool pulseAnimation;
final bool zoomToResults;
final void Function(double lat, double lng, Map<String, dynamic> properties)? onIconClick;
final void Function(List<dynamic> results)? onResultsUpdate;
final void Function(String? state)? onLoadingStateChange;
}
// Layer Options
class YarrowAddLayerOptions {
final String? sourceId;
final List<dynamic>? filter;
}
// Query Options
class YarrowQueryRenderedFeaturesOptions {
final List<String>? layers;
final List<String>? sourceLayers;
}
Version Information
- Current Version: 0.0.5
- Dependencies: maplibre_gl ^0.25.0, dio ^5.9.0, flutter_cache_manager ^3.4.1, flutter_svg ^2.2.2
- Changelog:
CHANGELOG.md
Notes
- This package uses
maplibre_glunder the hood. - Ensure platform setup for Flutter + MapLibre is completed in your app.
Support
For issues, questions, or contributions, contact the Yarrow development team.