maimaps_flutter · v0.1.2 · Beta

Maimaps Flutter SDK

Flutter SDK — place search, routing, reverse geocoding, point details, loccode resolution, and a MapLibre-powered map widget.

Package

maimaps_flutter

Version

v0.1.2

Registry

pub.dev

Platforms

Flutter 3.7+ / Dart 3 • iOS, Android

Requires

dio ^5.4, maplibre ^0.3.5

Status

Beta — 0.x, minors may break

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

Terminal
flutter pub add maimaps_flutter

Requires 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.

main.dart
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)

Signature
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

Signature
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

apiKeyrequired
String
Your MAIMAPS key. Throws ArgumentError if empty.
environmentoptional
MaimapsEnvironment
staging (default) or production.
styleFamilyoptional
String
Map style family. Defaults to 'default'.

REST 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

Signature
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

Signature
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

Signature
Future<ReverseGeocodeResult> reverseGeocode({
  required double latitude,
  required double longitude,
});

Coordinate to address plus loccode. ReverseGeocodeResult.displayName gives a render-ready string.

maimaps.pointDetails

Signature
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

Signature
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

Signature
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

Signature
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(...)

Signature
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

Signature
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

Signature
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.