Skip to main content
This page is the map itself: the data you give it, what the user draws, and what you get back. To fill the map from the LOCI frame index instead of your own boundaries, read Frames first — it produces the same work areas this page consumes.

Work areas

A work area is the polygon a user may collect data in, such as a frame. Pass the boundaries you hold; you do not need to convert Esri JSON first.
three ways in
Coordinates are [longitude, latitude]. The factories accept a decoded Map or the JSON text itself, so you can paste raw JSON into a string. The id comes from the FRAME_ID property by default, matched without regard to case. Change it with idProperty, or pass id yourself. A feature with no id throws, so bad data fails loudly. Spatial references. 4326 is used as is. Web Mercator (102100, 102113, 3857, 900913) is reprojected to WGS84 for you. Anything else throws MapWorkAreaFormatException; ask Esri for outSR=4326. Visibility. Work areas start hidden unless you set initiallyVisible. The user’s choices are saved per mapId and come back next time. Changing the list with setWorkAreas keeps the choices for ids that remain.

Features and entries

A feature is one saved entry drawn on the map: a point, a line or a polygon. Build a MapFeatureModel from each record you hold. The map only needs an id and the shape.
from your own records
Coordinates are [longitude, latitude], the reverse of how people say them. A point at [6.53, 3.38] lands in the ocean off Somalia.
Give them to the map with features: on MilsatMap, or controller.setFeatures(features). With layers, each layer owns its own list.

Layers

One MapOptions draws one kind of shape. To work with several at once — houses (points), roads (lines), plots (polygons) — give the map layers. A layer owns its features and says what the user draws while it is active.
three layers
  • Drawing creates the active layer’s shape; switching the layer changes the shape and cancels a drawing in progress.
  • Overlay. Every layer shows by default. The built-in Layers dialog has a checkbox to show or hide each one and a radio to choose where Draw goes. It appears with two or more layers.
  • View-only layers have no geometry: shown, but not drawable.
  • Results. DataFromMap.layerId says which layer a shape was drawn in; a tap on an entry reports the layer id as metadata.formName.
A layer rejects features that do not match it (on by default), throwing MapLayerFormatException listing every bad feature. Pass MapOptions(strictGeometry: false) to draw mismatched features anyway. Two layers with the same id always throw.
with your own widget
setLayers keeps the user’s choices for layers that stay. Changes arrive as LayerChanged and LayerVisibilityChanged events.

What you get back

When the user finishes a shape you receive a DataFromMap. Use it from onDrawCompleted when the shape exists, or from onSave once the user presses Save. While drawing, every tap passes checks in order — zoomed in enough (zoom 13 or closer), a work area visible, the point inside it, the user in the same one, and under the grouping threshold when set. A failed check reports a MapFailure and the tap is dropped. Points place at once; lines and polygons add a vertex, and a polygon closes when the user taps near its first point after at least three points.

Events

Everything the map tells you is a MapEvent on controller.events. The type is sealed, so a switch tells you if you missed one. MilsatMap also gives each event its own callback, and onEvent receives all of them. mode is one of none, pickingGeometry, categorizingGeometry or navigating.

The controller

MilsatMapController is the one object that drives the map. Commands return a Future; those that can fail return a MapResult with isSuccess, data and error. A command never throws — a failure comes back as a result with an error, or as a MapFailure event. It also exposes live state: events, layers, activeLayerId, visibleLayerIds, drawGeometry, isAttached, mode, entriesVisible, canUndo, canRedo, workAreas, totalEntriesCount, activeBaseMap.

Built-in controls

The prebuilt MilsatMap draws controls over the map. Each is a thin front for a controller command, so anything a button does you can also do yourself. Rename with MilsatMapLabels(key: 'text'), hide with MilsatMapControls(flag: false), or replace with MilsatMapBuilders(...).
rename and hide
Pass only the label keys you want to change; everything else keeps its English default, which is how you translate the whole widget.

Make it match your app

The built-in controls read your ThemeData: primary and on-primary colours from your ColorScheme, text from your TextTheme. Override any of it, or none, through these parameters.
replace one control

Geofencing

By default every drawing is checked against the work areas: one must be visible, the point must be inside a visible one, and the user must be standing in the same one. That keeps field data inside the survey boundary.
testing only
Do not ship enforceGeofencing: false. Data drawn outside a work area is accepted, and nothing marks it. A debug message prints whenever the map starts with it off.

Options and flags

Grouping lets a user draw a boundary around entries and blocks further entries once a threshold is reached. It is off by default:
grouping

Styles

Four small models describe how things look. Colours are plain ARGB ints (0xAARRGGBB), so color.toARGB32() works for a Flutter Color. They are the same on Android and iOS.
styles
A layer’s own style wins over MapOptions.style, which wins over the built-in look. Constructors assert the ranges; fromJson clamps or ignores bad values.

Errors

Every error extends MapError and has a message you can show. They arrive as MapFailure events or inside a failed MapResult. Bad work-area data throws MapWorkAreaFormatException straight from the factory, and bad layers throw MapLayerFormatException from setLayers, before anything reaches the map. These are the map’s own errors. The errors the SDK raises when it calls the LOCI API — rejected keys, rate limits, offline — are separate and live in Frames.