> ## Documentation Index
> Fetch the complete documentation index at: https://docs.loci.ng/llms.txt
> Use this file to discover all available pages before exploring further.

# Flutter SDK

> milsat_map: an Android and iOS map for field data collection, driven from Dart. It draws work areas, lets a user draw inside them, and can fill itself from the frame index using an API key.

`milsat_map` is a Flutter package that puts a map in your app for field data
collection. Your users draw points, lines and polygons inside their work
areas, and you get the GeoJSON back, checked against the boundaries. It runs
on Android and iOS.

The map works on its own with boundaries you already hold. To fill it from
LOCI instead, you give the SDK an **API key** and it preloads frames and the
data recorded on them over the same public API this site documents. The map
never calls the network itself: every request happens in an explicit preload
step before the map appears.

<Note>
  The source of truth for the SDK is the package itself. This section
  documents how to use it and, in [Frames](/sdks/flutter-frames), exactly
  which API calls it makes. The request and response contract for those
  calls is the [API reference](/api-reference/introduction).
</Note>

<CardGroup cols={2}>
  <Card title="Quickstart" icon="rocket" href="/sdks/flutter-quickstart">
    Three steps to a working map, plus the example app.
  </Card>

  <Card title="The map" icon="map" href="/sdks/flutter-maps">
    Work areas, layers, drawing, events, the controller, and styling.
  </Card>

  <Card title="Frames" icon="key" href="/sdks/flutter-frames">
    Initialise with a key and preload frames. The part that uses this API.
  </Card>

  <Card title="Native SDKs" icon="smartphone" href="/sdks/native">
    Standalone Android (Kotlin) and iOS (Swift) SDKs, coming soon.
  </Card>
</CardGroup>

## Install

The package ships Android and iOS support, so a path or git dependency is
enough. No map key is needed for the map to render; the only credential you
supply is an API key, and only if you use frames.

```yaml pubspec.yaml theme={null}
dependencies:
  milsat_map:
    path: ../milsat_map # or a git dependency
```

## Platform setup

### Android

* Minimum **Android 5.0 (API 21)**.
* The Android map library comes from Mapbox's Maven repository. Put your
  Mapbox downloads token (`SDK_REGISTRY_TOKEN`) in your Gradle properties or
  environment so Gradle can fetch it.
* Release builds with R8 need no extra rules.

The map checks that a user is inside a work area, so add the location
permissions:

```xml AndroidManifest.xml theme={null}
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.INTERNET"/>
```

<Note>
  The Play Console can ask you to declare foreground service permissions
  after you add the package. `geolocator_android` 5.1 and later merges
  `FOREGROUND_SERVICE_LOCATION` into your manifest. If your app runs no
  foreground location service, remove it with `tools:node="remove"` on the
  `FOREGROUND_SERVICE` and `FOREGROUND_SERVICE_LOCATION` permissions, and
  test location on Android 14 or later.
</Note>

### iOS

* Minimum **iOS 15**. Set it in your `Podfile` and keep `use_frameworks!`.
* The package pins Mapbox Maps to `11.32.1`, so your app carries one copy.

```ruby ios/Podfile theme={null}
platform :ios, '15.0'
target 'Runner' do
  use_frameworks!
  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
```

Turn off Flutter's Swift Package Manager integration so CocoaPods is the
only source of Mapbox Maps:

```yaml pubspec.yaml theme={null}
flutter:
  config:
    enable-swift-package-manager: false
```

Then add the location permission the map needs:

```xml ios/Runner/Info.plist theme={null}
<key>NSLocationWhenInUseUsageDescription</key>
<string>The map uses your location to check you are inside your work area.</string>
```

<Note>
  The first time a user taps **Locate Me** the OS asks for location access.
  That prompt is the system's, and the app waits behind it until the user
  answers.
</Note>

## Two ways to use it

Both share one controller. The controller runs every behaviour and reports
everything on one event stream, so you lose nothing by building your own
interface.

<CardGroup cols={2}>
  <Card title="Prebuilt widget" icon="layout-dashboard">
    `MilsatMap` draws the map, the gradient and the controls — options,
    locate, zoom, undo, redo, work areas, imagery, navigate, Draw and Save.
    It reads your `ThemeData`.
  </Card>

  <Card title="Your own widget" icon="square-pen">
    Place `MilsatMapSurface`, the bare map, and build your own buttons. Use
    every command and listen to every event with no built-in UI.
  </Card>
</CardGroup>

```dart prebuilt theme={null}
MilsatMap(
  options: const MapOptions(mapId: 'survey-1', geometry: DrawGeometry.polygon),
  workAreas: workAreas,
  features: entries, // saved shapes
  onFeatureTap: (m) => openEntry(m.id),
  onSave: (data) => saveEntry(data),
)
```

```dart your own widget theme={null}
final controller = MilsatMapController();
MilsatMapSurface(
  controller: controller,
  options: const MapOptions(mapId: 'mine', geometry: DrawGeometry.point),
  workAreas: workAreas,
  onAttached: () => controller.setFeatures(entries),
);
controller.events.listen((event) {
  switch (event) {
    case DrawCompleted(:final data):
      saveEntry(data);
    case FeatureTapped(:final metadata):
      openEntry(metadata.id);
    case MapFailure(:final error):
      showMessage(error.message);
    default:
      break;
  }
});
// Your own buttons call the controller.
onPressed: controller.startDrawing,
```

<Note>
  One map at a time. Attaching a second map replaces the first. Connecting
  and disconnecting are queued, so swapping maps (for example switching
  tabs) is safe. Call `controller.dispose()` when you own the controller and
  are done.
</Note>

## What the map needs, and what it does not

* **No map key** to render. The map works out of the box.
* **An API key** only if you preload frames from LOCI. See
  [Frames](/sdks/flutter-frames).
* The web and desktop targets are a placeholder: the map reports a
  `MapInitializationError` there. This is an Android and iOS SDK.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.