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
- iOS
- Android
- Web
<!-- Info.plist -->
<key>MBXAccessToken</key>
<string>$(MAPBOX_ACCESS_TOKEN)</string>
<!-- strings.xml -->
<string name="mapbox_access_token">YOUR_TOKEN</string>
// build.gradle
implementation 'com.mapbox.maps:android:10.0.0'
<!-- index.html -->
<script src='https://api.mapbox.com/mapbox-gl-js/v2.14.1/mapbox-gl.js'></script>
<link href='https://api.mapbox.com/mapbox-gl-js/v2.14.1/mapbox-gl.css' rel='stylesheet' />
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
Place Search
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
- Implement zoom-based rendering for performance
- Use vector tiles for better scaling
- Cache map tiles for offline use
- Cluster markers when displaying many points
- Optimize style layers to reduce rendering overhead
- Handle location permissions properly