Overview
Initialize once with your API key, then use the six typed clients for the REST surface and the bundled MapLibre map widget for display. RouteInfo decodes route polylines into LatLng for you, so there is no coordinate flip to remember. The map widget follows the ambient Theme's brightness unless you override it. Staging hosts are baked in; production hosts are configured explicitly until GA.
Every endpoint this SDK wraps is documented, with request parameters and response bodies, in the Maimaps API reference. This page documents the SDK surface itself.
Install
flutter pub add maimaps_flutterRequires dio ^5.4, maplibre ^0.3.5, which you install yourself as peer dependencies.
Quickstart
Replace mk_test_your_key with a MAIMAPS key from the console. Keys are mk_live_… for LIVE and mk_test_… for TEST; nothing else in your code changes between the two.
import 'package:maimaps_flutter/maimaps_flutter.dart';
void main() {
// Key minted in the Maiaddy Developers portal (MAIMAPS product).
Maimaps.initialize(MaimapsOptions(apiKey: 'mk_test_your_key'));
runApp(const MyApp());
}
class MapScreen extends StatelessWidget {
const MapScreen({super.key});
@override
Widget build(BuildContext context) {
// Style mode follows the ambient Theme unless you pass `mode`.
return const MaimapsMap(
initialCenter: LatLng(9.0579, 7.4951),
initialZoom: 12,
myLocationEnabled: true,
);
}
}
// Search + route
final maimaps = Maimaps.instance;
final results = await maimaps.search.search(
'garki market',
latitude: 9.0579,
longitude: 7.4951,
);
final route = await maimaps.routing.getRoute(
origin: const LatLng(9.0579, 7.4951),
destination: const LatLng(9.0765, 7.3986),
mode: 'drive',
);Initialization
The Flutter SDK is a singleton. Initialize once at startup, then reach for Maimaps.instance anywhere.
Maimaps.initialize(options)
static Maimaps initialize(MaimapsOptions options, {Dio? dio});
static Maimaps get instance; // throws StateError if not initialized
static bool get isInitialized;Call once in main(). Pass a Dio instance to share an HTTP client, add interceptors, or stub the network in tests.
MaimapsOptions
factory MaimapsOptions({
required String apiKey,
MaimapsEnvironment environment = MaimapsEnvironment.staging,
String? apiBaseUrl,
String? mapBaseUrl,
String styleFamily = 'default',
});Staging hosts are baked in as constants. Selecting MaimapsEnvironment.production without supplying both base URLs throws an ArgumentError, because production hostnames are not published until GA.
Parameters
apiKeyrequiredenvironmentoptionalstyleFamilyoptionalREST clients
Six typed clients hang off the singleton, one per endpoint family. Every method is async and throws a MaimapsException subclass on failure.
maimaps.search
Future<List<SearchResult>> search(
String query, {
double? latitude,
double? longitude,
int limit = 10,
String? bbox,
String? type,
String? category,
});Free-text search. Pass latitude and longitude to bias results toward the user and populate distanceKm on each result.
maimaps.routing
Future<RouteInfo> getRoute({
required LatLng origin,
required LatLng destination,
List<LatLng> waypoints = const [],
String mode = 'drive',
});
Future<RouteInfo> getRouteWithWaypoints({
double? originLat, double? originLng,
double? destLat, double? destLng,
String? originLoccode, String? destLoccode,
List<LatLng> waypoints = const [],
String mode = 'drive',
});getRoute covers the common A-to-B case. getRouteWithWaypoints adds loccode endpoints. RouteInfo decodes the polyline for you into polylinePoints, so unlike the JS SDKs there is no coordinate flip to do.
maimaps.reverseGeocode
Future<ReverseGeocodeResult> reverseGeocode({
required double latitude,
required double longitude,
});Coordinate to address plus loccode. ReverseGeocodeResult.displayName gives a render-ready string.
maimaps.pointDetails
Future<PointDetails> getPointDetails({
required double latitude,
required double longitude,
String? osmId,
String? osmType,
String? searchName,
String? searchAddress,
});Tapped-point resolution. PointDetails exposes displayName, displayAddress, and coordinatesDisplay for direct rendering.
maimaps.loccode
Future<LoccodeResult> resolve(String code);
Future<LoccodeResult> findNearest({
required double latitude,
required double longitude,
});Resolve a loccode to its street geometry, or find the nearest one to a coordinate. LoccodeResult.representativePoint is the street midpoint, and toMap()/fromJsonString() let you cache results locally.
maimaps.placeCategories
Future<List<PlaceCategory>> listCategories();The category taxonomy. PlaceCategory provides listToJsonString() and listFromJsonString() so you can cache it to disk on first launch.
MaimapsMap widget
A MapLibre-backed widget that handles keyed style loading, the wordmark, and location permissions for you.
MaimapsMap
const MaimapsMap({
MaimapsOptions? options,
String? mode, // 'light' | 'dark'; follows Theme when null
LatLng? initialCenter,
double initialZoom = 10,
double initialBearing = 0,
double initialPitch = 0,
double minZoom = 0,
double maxZoom = 22,
bool myLocationEnabled = false,
bool followUserLocation = false,
String myLocationPermissionExplanation = '…',
MapCreatedCallback? onMapCreated,
StyleLoadedCallback? onStyleLoaded,
MapEventCallback? onEvent,
List<Layer> layers = const [],
List<Widget> children = const [],
WordmarkPosition wordmarkPosition = WordmarkPosition.bottomLeft,
});Leave mode null and the map follows the ambient Theme's brightness, so it switches with the rest of your app. Reach the underlying MapController via onMapCreated for camera animations and bounds fitting.
buildStyleUrl(...)
String buildStyleUrl({
required String mapBaseUrl,
required String apiKey,
String styleFamily = 'default',
String mode = 'light',
});
String styleModeForBrightness(Brightness brightness);Build the keyed style URL yourself when you are driving a raw MapLibre widget rather than MaimapsMap.
Exceptions
Every client method throws a MaimapsException subclass. Catch the specific type to branch; catch the base to log.
The exception hierarchy
class MaimapsException implements Exception {
final String message;
final String? code;
final int? statusCode;
}
class InvalidApiKeyException extends MaimapsException {} // 401
class RateLimitException extends MaimapsException { // 429 rate_limited
final Duration? retryAfter;
}
class QuotaExceededException extends MaimapsException {} // 429 quota_exceeded
class MaimapsServerException extends MaimapsException {} // 5xx
class MaimapsNetworkException extends MaimapsException { // no response
final Object? cause;
}RateLimitException is transient, so back off for retryAfter and try again. QuotaExceededException is not: the monthly cap is spent and retrying will not help. Anything the SDK cannot map, including the navigation service's ROUTING_FAILED and ENGINE_UNAVAILABLE, arrives as the base MaimapsException with code and statusCode set.
PolylineDecoder
static List<LatLng> decode(String encoded, {int precision = 5});Decode a route polyline into LatLng points. RouteInfo already does this for you; use the decoder directly only when handling a raw geometry string.
Resources
This SDK is 0.1.0 and in beta. Breaking changes can land in minor versions until 1.0.0, so pin the version if you need stability.