View on GitHub

Create graphics from an XML file with key-value pairs for each graphic, and display the military symbols using a MIL-STD-2525D web style in 3D.

Image of apply dictionary renderer to graphics overlay

Use case

Use a dictionary renderer on a graphics overlay to display more transient data, such as military messages coming through a local tactical network.

How to use the sample

Run the sample and view the military symbols on the map.

How it works

  1. Create a new DictionarySymbolStyle object with the “mil2525d” specification type and set the style’s draw rule configuration to “ORDERED ANCHOR POINTS”.
  2. Create a new DictionaryRenderer object with the dictionary symbol style.
  3. Create an instance of GraphicsOverlay.
  4. Set the dictionary renderer to the graphics overlay.
  5. Parse through the local XML file creating a map of key/value pairs for each block of attributes.
  6. Create an instance of Graphic for each attribute.
  7. Use the _wkid key to get the geometry’s spatial reference.
  8. Use the _control_points key to get the geometry’s shape.
  9. Add the graphic to the graphics overlay.

Relevant API

  • DictionaryRenderer
  • DictionarySymbolStyle
  • GraphicsOverlay

About the data

The sample viewer will load MIL-STD-2525D symbol dictionary web style from ArcGIS Online before loading the sample. This ArcGIS Web Style is for use to build custom applications that incorporate the MIL-STD-2525D symbol dictionary. This style supports a configuration for modeling locations as ordered anchor points or full geometries.

A local XML file containing messages with MIL-STD-2525D fields for military symbology (MIL-STD-2525D Messages 100.13.0) is also used. This is downloaded from ArcGIS Online automatically.

Tags

defense, military, situational awareness, tactical, visualization

Sample code

apply_dictionary_renderer_to_graphics_overlay.dart
// Copyright 2026 Esri
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import 'dart:io';
import 'package:arcgis_maps/arcgis_maps.dart';
import 'package:arcgis_maps_sdk_flutter_samples/common/common.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:xml/xml.dart';
class ApplyDictionaryRendererToGraphicsOverlay extends StatefulWidget {
const ApplyDictionaryRendererToGraphicsOverlay({super.key});
@override
State<ApplyDictionaryRendererToGraphicsOverlay> createState() =>
_ApplyDictionaryRendererToGraphicsOverlayState();
}
class _ApplyDictionaryRendererToGraphicsOverlayState
extends State<ApplyDictionaryRendererToGraphicsOverlay>
with SampleStateSupport {
// Create a controller for the scene view.
final _sceneViewController = ArcGISSceneView.createController();
// The graphics overlay for displaying the message graphics on the scene.
final _graphicsOverlay = GraphicsOverlay();
// A flag for when the scene view is ready and controls can be used.
var _ready = false;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
// Add a scene view to the widget tree and set a controller.
ArcGISSceneView(
controllerProvider: () => _sceneViewController,
onSceneViewReady: onSceneViewReady,
),
// Display a progress indicator and prevent interaction until state is ready.
LoadingIndicator(visible: !_ready),
],
),
);
}
Future<void> onSceneViewReady() async {
// Create a scene with a topographic basemap style.
final scene = ArcGISScene.withBasemapStyle(.arcGISTopographic);
_sceneViewController.arcGISScene = scene;
// Set the dictionary renderer on the overlay.
_graphicsOverlay.renderer = await getMIL2525DRenderer();
// Parse the MIL-STD-2525D XML and add graphics to the overlay.
final graphics = await generateMessageGraphics();
_graphicsOverlay.graphics.addAll(graphics);
// Attach the overlay to the scene view.
_sceneViewController.graphicsOverlays.add(_graphicsOverlay);
// Set a viewpoint camera to the scene view using the extent of the graphics overlay.
final extent = _graphicsOverlay.extent;
if (extent != null) {
final camera = Camera.withLookAtPoint(
lookAtPoint: extent.center,
distance: 15000,
heading: 0,
pitch: 70,
roll: 0,
);
_sceneViewController.setViewpointCamera(camera);
}
// Set the ready state variable to true to enable the sample UI.
setState(() => _ready = true);
}
// Creates a dictionary renderer for styling with MIL-STD-2525D symbols.
Future<DictionaryRenderer> getMIL2525DRenderer() async {
// Create a dictionary symbol style from a dictionary style portal item.
final portalItem = PortalItem.withPortalAndItemId(
portal: Portal.arcGISOnline(),
itemId: 'd815f3bdf6e6452bb8fd153b654c94ca',
);
final dictionarySymbolStyle = DictionarySymbolStyle.withPortalItem(
portalItem,
);
await dictionarySymbolStyle.load();
// Use the "Ordered Anchor Points" for the symbol style draw rule.
final drawRuleConfiguration = dictionarySymbolStyle.configurations
.firstWhere((item) => item.name == 'model');
drawRuleConfiguration.value = 'ORDERED ANCHOR POINTS';
return DictionaryRenderer(dictionarySymbolStyle: dictionarySymbolStyle);
}
// Load and create message graphics.
Future<List<Graphic>> generateMessageGraphics() async {
// Create file reference for the MIL-STD-2525D XML File.
final listPaths = GoRouter.of(context).state.extra! as List<String>;
final mil2525XMLFile = File(listPaths.first);
// Read the XML as a string and parse it into a list of Message objects.
final xmlString = await mil2525XMLFile.readAsString();
final parser = _MessageParser()..parse(xmlString);
// Convert each parsed message into a Graphic.
return parser.messages
.map<Graphic?>((message) {
final wkid = message.wkid;
// Skip messages without a usable WKID.
if (wkid == null) return null;
final spatialReference = SpatialReference(wkid: wkid);
final builder = MultipointBuilder(spatialReference: spatialReference);
for (final coord in message.controlPoints) {
builder.points.addPointXY(x: coord.x, y: coord.y);
}
final multipoint = builder.toGeometry() as Multipoint;
return Graphic(geometry: multipoint, attributes: message.other);
})
.nonNulls
.toList(growable: false);
}
}
/// The parsed values from a single `<message>` element of the XML file.
class _Message {
/// The x and y values from the `<_control_points>` element.
List<({double x, double y})> controlPoints = [];
/// The integer value of the `<_wkid>` element.
int? wkid;
/// A map containing every other element name from the XML file mapped to its text content.
Map<String, String> other = {};
}
/// A DOM-based XML parser for the MIL-STD-2525D messages file.
class _MessageParser {
final List<_Message> messages = [];
void parse(String xmlString) {
final document = XmlDocument.parse(xmlString);
// Iterate every <message> element (children of the root <messages>).
for (final messageNode in document.findAllElements('message')) {
final message = _Message();
for (final child in messageNode.childElements) {
final name = child.name.local;
final text = child.innerText;
switch (name) {
case '_control_points':
// The text looks like "x1,y1;x2,y2;...".
message.controlPoints = text
.split(';')
.where((pair) => pair.contains(','))
.map((pair) {
final parts = pair.split(',');
return (
x: double.parse(parts.first.trim()),
y: double.parse(parts.last.trim()),
);
})
.toList();
case '_wkid':
message.wkid = int.tryParse(text.trim());
default:
message.other[name] = text;
}
}
messages.add(message);
}
}
}