NaxaLibre

Pub License GitHub stars

Overview

NaxaLibre is a powerful and feature-rich MapLibre plugin for Flutter, designed to simplify and enhance geospatial mapping capabilities in mobile applications. Developed by @itheamc, this plugin provides developers with a comprehensive toolkit for integrating interactive and customizable maps into their Flutter projects.

Key Features

  • Seamless integration with MapLibre Map SDK
  • Support for both Android (v11.8.5) and iOS (v6.12.2)
  • Comprehensive layer support (Circle, Line, Fill, Symbol, Raster, Hillshade, Heatmap, Fill Extrusion, Background)
  • Multiple source types (Vector, Raster, RasterDem, GeoJson, Image)
  • Advanced location services
  • Flexible style and layer customization
  • Expression and transition support

Installation

Add the following to your pubspec.yaml:

dependencies:
  naxalibre: ^latest_version

Then run:

flutter pub get

Getting Started

1. Location Permissions

Android (AndroidManifest.xml)

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

iOS (Info.plist)

<key>NSLocationWhenInUseUsageDescription</key>
<string>We need your location to show it on the map.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>We need your location to show it on the map.</string>

2. Basic Map Implementation

NaxaLibreMap(
  style: "your-style-url-or-json-style-string",
  locationSettings: LocationSettings(
    locationEnabled: true,
    shouldRequestAuthorizationOrPermission: true,
    locationComponentOptions: LocationComponentOptions(
      pulseColor: "red",
      backgroundTintColor: "yellow",
      foregroundTintColor: "green",
    ),
    locationEngineRequestOptions: LocationEngineRequestOptions(
      displacement: 10,
      priority: LocationEngineRequestPriority.highAccuracy,
      provider: LocationProvider.gps,
    ),
  ),
  hyperComposition: true,
  onMapCreated: onMapCreated,
  onStyleLoaded: () {
    // Handle style loading
  },
  onMapLoaded: () {
    // Handle map loading
  },
  onMapClick: (latLng) {
    // Handle map click events
  },
  onMapLongClick: (latLng) {
    // Handle long press events
  },
)

3. Adding Sources

GeoJSON Source Example

await _controller.addSource<GeoJsonSource>(
  source: GeoJsonSource(
    sourceId: "geojson-source-id",
    url: "https://example.com/your-geojson-data.geojson",
    sourceProperties: GeoJsonSourceProperties(
      cluster: true,
      clusterRadius: 50,
      clusterMaxZoom: 14,
      maxZoom: 20,
    ),
  ),
)

4. Adding Layers

Circle Layer

await _controller.addLayer<CircleLayer>(
  layer: CircleLayer(
    layerId: "my-circle-layer",
    sourceId: "geojson-source-id",
    layerProperties: CircleLayerProperties(
      circleColor: [
        'case',
        ['boolean', ['has', 'point_count'], true],
        'red',
        'blue'
      ],
      circleColorTransition: StyleTransition.build(
        delay: 500,
        duration: const Duration(milliseconds: 1000),
      ),
      circleRadius: [
        'case',
        ['boolean', ['has', 'point_count'], true],
        15,
        10
      ],
      circleStrokeWidth: [
        'case',
        ['boolean', ['has', 'point_count'], true],
        3,
        2
      ],
      circleStrokeColor: "#fff",
    ),
  ),
)

Symbol Layer

await _controller.addLayer<SymbolLayer>(
  layer: SymbolLayer(
    layerId: "symbol-layer-example",
    sourceId: "geojson-source-id",
    layerProperties: SymbolLayerProperties(
      textField: ['get', 'point_count_abbreviated'],
      textSize: 12,
      textColor: '#fff',
      iconSize: 1,
      iconAllowOverlap: true,
    ),
  ),
)

5. Adding Style Images

Local Asset Image

await _controller.addStyleImage<LocalStyleImage>(
  image: LocalStyleImage(
    imageId: "local-icon",
    imageName: "assets/images/your-image.png",
  ),
)

Network Image

await _controller.addStyleImage<NetworkStyleImage>(
  image: NetworkStyleImage(
    imageId: "network-icon",
    url: "https://example.com/icon.png",
  ),
)

6. Adding Annotations

Circle Annotation

    await controller?.addAnnotation<CircleAnnotation>(
      annotation: CircleAnnotation(
        options: CircleAnnotationOptions(
          point: LatLng(27.741712, 85.331033),
          circleColor: "red",
          circleStrokeColor: "white",
          circleStrokeWidth: 2.0,
          circleRadius: 12.0,
          circleRadiusTransition: StyleTransition.build(
            delay: 1500,
            duration: const Duration(milliseconds: 2000),
          ),
          circleColorTransition: StyleTransition.build(
            delay: 1500,
            duration: const Duration(milliseconds: 2000),
          ),
        ),
      ),
    );

Polygon Annotation

    await controller?.addAnnotation<PolygonAnnotation>(
      annotation: PolygonAnnotation(
        options: PolygonAnnotationOptions(
          points: [
            [
              LatLng(27.741712, 85.331033),
              LatLng(27.7420, 85.3412),
              LatLng(27.7525, 85.3578),
            ],
          ],
          fillColor: "red",
          fillOpacity: 0.15,
          fillOutlineColor: "blue",
        ),
      ),
    );

Point Annotation

    await controller?.addAnnotation<PointAnnotation>(
      annotation: PointAnnotation(
        image: NetworkStyleImage(
          imageId: "pointImageId",
          url:
              "https://www.cp-desk.com/wp-content/uploads/2019/02/map-marker-free-download-png.png",
        ),
        options: PointAnnotationOptions(
          point: LatLng(27.7525, 85.3578),
          iconSize: 0.1,
        ),
      ),
    );

Polyline Annotation

    await controller?.addAnnotation<PolylineAnnotation>(
      annotation: PolylineAnnotation(
        options: PolylineAnnotationOptions(
          points: [LatLng(27.741712, 85.331033), LatLng(27.7420, 85.3412)],
          lineColor: "red",
          lineWidth: 3.75,
          lineCap: LineCap.round,
          lineJoin: LineJoin.round,
        ),
      ),
    );

7. Offline Region

Download Region

Using OfflineTilePyramidRegionDefinition
    final definition = OfflineTilePyramidRegionDefinition(
      styleUrl: mapStyle,
      bounds: LatLngBounds(
        southwest: LatLng(27.84, 85.23),
        northeast: LatLng(27.88, 85.60),
      ),
      minZoom: 5.0,
      maxZoom: 10.0,
    );

    final metadata = OfflineRegionMetadata(
      name: 'Region_${DateTime.now().millisecondsSinceEpoch}',
      customAttributes: {
        'custom_attribute_1': 'value_1',
        'custom_attribute_2': 'value_2',
      }
    );

    await controller?.offlineManager.download(
      definition: definition,
      metadata: metadata,
      onInitiated: (regionId) {
        setState(() {
          _statusMessage = 'Download initiated for region ID: $regionId';
        });
      },
      onDownloading: (progress) {
        
      },
      onDownloaded: (region) {
  
      },
      onError: (error) {
       
      },
    );
Using OfflineGeometryRegionDefinition
      final geometryArgs = <String, dynamic>{
        "coordinates": [
            [
                [81.43286208834144, 29.168844681981057],
                [81.43272857503553, 28.154758204111474],
                [82.82808392041403, 28.00775457992897],
                [83.36951061373333, 28.59439547536701],
                [83.4215408561538, 29.268510933276843],
                [82.86979546062628, 29.667365733566996],
                [82.12016836359783, 29.748878555461047],
                [81.43286208834144, 29.168844681981057],
            ],
        ],
        "type": "Polygon",
      };

      final geometry = Geometry.fromArgs(geometryArgs);

      final definition = OfflineGeometryRegionDefinition(
        styleUrl: mapStyle,
        geometry: geometry,
        minZoom: 5.0,
        maxZoom: 15.0,
      );

      final metadata = OfflineRegionMetadata(
        name: 'Region_${DateTime.now().millisecondsSinceEpoch}',
      );

      await controller?.offlineManager.download(
        definition: definition,
        metadata: metadata,
        onInitiated: (regionId) {
         
        },
        onDownloading: (progress) {
         
        },
        onDownloaded: (region) {
         
        },
        onError: (error) {
        
        },
      );

Delete Offline Region

    final isDeleted = await controller?.offlineManager.delete(12);

Delete All Offline Regions

    // result is a map containing id as a key and bool value as status
    final result = await controller?.offlineManager.deleteAll();

Supported MapLibre API Features

Feature Android iOS
Style
Camera
Current Location
Circle Layer
Line Layer
Fill Layer
Symbol Layer
Raster Layer
Hillshade Layer
Heatmap Layer
Fill Extrusion Layer
Background Layer
Vector Source
Raster Source
RasterDem Source
GeoJson Source
Image Source
Expressions
Transitions
Annotations
Offline Manager

Limitations and Considerations

  • SVG images are currently not supported
  • Ensure proper location permissions are set for location-based features

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the terms of the LICENSE file in the repository.

Author

@itheamc

Libraries

naxalibre