Skip to main content

MapBox Integration

Comprehensive guide for integrating MapBox GL in Remind Tools for advanced mapping features.

Setup

Configuration

# .env file
MAPBOX_ACCESS_TOKEN=your_mapbox_token
MAPBOX_STYLE_URL=mapbox://styles/mapbox/streets-v11

Platform Setup

<!-- Info.plist -->
<key>MBXAccessToken</key>
<string>$(MAPBOX_ACCESS_TOKEN)</string>

Map Implementation

Basic Map

import 'package:mapbox_gl/mapbox_gl.dart';

class MapScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MapboxMap(
      accessToken: Environment.mapboxToken,
      initialCameraPosition: CameraPosition(
        target: LatLng(48.8566, 2.3522), // Paris
        zoom: 12.0,
      ),
      onMapCreated: (controller) {
        // Map is ready
      },
    );
  }
}

3D Buildings

void enable3DBuildings(MapboxMapController controller) {
  controller.addLayer(
    'buildings-3d',
    '3d-buildings',
    FillExtrusionLayerProperties(
      fillExtrusionColor: '#aaa',
      fillExtrusionHeight: [
        'interpolate',
        ['linear'],
        ['zoom'],
        15, 0,
        15.05, ['get', 'height']
      ],
      fillExtrusionOpacity: 0.6,
    ),
  );
}

Features

Future<List<Place>> searchPlaces(String query) async {
  final response = await http.get(
    Uri.parse(
      'https://api.mapbox.com/geocoding/v5/mapbox.places/$query.json'
      '?access_token=${Environment.mapboxToken}'
    ),
  );
  
  final data = json.decode(response.body);
  return (data['features'] as List)
    .map((f) => Place.fromJson(f))
    .toList();
}

Route Planning

Future<Route> getRoute({
  required LatLng origin,
  required LatLng destination,
  String profile = 'driving',
}) async {
  final url = 'https://api.mapbox.com/directions/v5/mapbox/$profile/'
    '${origin.longitude},${origin.latitude};'
    '${destination.longitude},${destination.latitude}'
    '?geometries=geojson&access_token=${Environment.mapboxToken}';
  
  final response = await http.get(Uri.parse(url));
  final data = json.decode(response.body);
  
  return Route.fromJson(data['routes'][0]);
}

Custom Markers

void addCustomMarker(
  MapboxMapController controller,
  LatLng position,
  Widget markerWidget,
) async {
  final bytes = await _widgetToImage(markerWidget);
  
  await controller.addImage('custom-marker', bytes);
  
  await controller.addSymbol(
    SymbolOptions(
      geometry: position,
      iconImage: 'custom-marker',
      iconSize: 1.0,
    ),
  );
}

Offline Maps

class OfflineMapManager {
  Future<void> downloadRegion({
    required LatLngBounds bounds,
    required double minZoom,
    required double maxZoom,
  }) async {
    await installOfflineMapTiles(
      accessToken: Environment.mapboxToken,
      bounds: bounds,
      minZoom: minZoom,
      maxZoom: maxZoom,
      mapStyleUrl: Environment.mapboxStyleUrl,
    );
  }
  
  Future<List<OfflineRegion>> getOfflineRegions() async {
    return await getListOfOfflineMapTiles(
      accessToken: Environment.mapboxToken,
    );
  }
}

Performance Optimization

Clustering

void setupClustering(MapboxMapController controller) {
  controller.addSource(
    'points',
    GeojsonSourceProperties(
      data: pointsGeoJson,
      cluster: true,
      clusterMaxZoom: 14,
      clusterRadius: 50,
    ),
  );
  
  // Add cluster layers
  controller.addLayer(
    'clusters',
    'points',
    CircleLayerProperties(
      circleColor: '#51bbd6',
      circleRadius: [
        'step',
        ['get', 'point_count'],
        20, 100,
        30, 750,
        40,
      ],
    ),
  );
}

Tile Caching

class MapCacheManager {
  static const _cacheSize = 100 * 1024 * 1024; // 100MB
  
  Future<void> configureCaching() async {
    await setOfflineMapTileCountLimit(_cacheSize);
    await setHttpConnectionCount(8);
  }
}

Best Practices

  1. Implement zoom-based rendering for performance
  2. Use vector tiles for better scaling
  3. Cache map tiles for offline use
  4. Cluster markers when displaying many points
  5. Optimize style layers to reduce rendering overhead
  6. Handle location permissions properly