List features related to the selected feature.

Use case
Related features are useful for managing relational information, like what you would store in a relational database management system (RDBMS). You can define a relationship between records as one-to-one, one-to-many, or many-to-one. For example, you could model inspections and facilities as a many-to-one relationship. Then, for any facility feature, you could list related inspection features.
How to use the sample
Tap on a feature to select it. The related features will be displayed in a list.
How it works
- With a
Feature, callqueryRelatedFeatureson the feature’s feature table. - Iterate over the result’s collection of
RelatedFeatureQueryResultobjects to get the related features and add them to a list.
Relevant API
- ArcGISFeature
- ArcGISFeatureTable
- ArcGISFeatureTable.queryRelatedFeatures
- FeatureQueryResult
- RelatedFeatureQueryResult
Tags
features, identify, query, related, relationship, search
Sample Code
query_related_features.dart
import 'dart:math';
import 'package:arcgis_maps/arcgis_maps.dart';import 'package:arcgis_maps_sdk_flutter_samples/common/common.dart';import 'package:flutter/material.dart';
class QueryRelatedFeatures extends StatefulWidget { const QueryRelatedFeatures({super.key});
@override State<QueryRelatedFeatures> createState() => _QueryRelatedFeaturesState();}
class _QueryRelatedFeaturesState extends State<QueryRelatedFeatures> with SampleStateSupport { // Create a controller for the map view. final _mapViewController = ArcGISMapView.createController(); // A flag for when the map view is ready and controls can be used. var _ready = false; // A flag for when the settings bottom sheet is visible. var _layerDataVisible = false; // A flag for when the features are loading. var _loadingFeatures = false; // Feature layer for the Alaska National Parks. late final FeatureLayer _alaskaNationalParksLayer; // The name of the selected park. var _selectedParkName = ''; // Lists to store the names of the related features. var _featurePreserves = <String>[]; var _featureSpecies = <String>[];
@override Widget build(BuildContext context) { return Scaffold( body: SafeArea( top: false, left: false, right: false, child: Stack( children: [ // Add a map view to the widget tree and set a controller. ArcGISMapView( controllerProvider: () => _mapViewController, onMapViewReady: onMapViewReady, onTap: onTap, ), // Display a progress indicator and prevent interaction until state is ready. LoadingIndicator(visible: !_ready), ], ), ), // Display the bottom sheet when the selected layer data is available. bottomSheet: _layerDataVisible ? buildLayerData(context) : null, ); }
Widget buildLayerData(BuildContext context) { return Container( constraints: BoxConstraints( maxHeight: MediaQuery.sizeOf(context).height * 0.4, ), padding: EdgeInsets.fromLTRB( 20, 5, 20, max( 20, View.of(context).viewPadding.bottom / View.of(context).devicePixelRatio, ), ), width: double.infinity, child: Column( children: [ Row( children: [ Expanded( child: _loadingFeatures ? const Center(child: CircularProgressIndicator()) : Text( _selectedParkName, style: Theme.of(context).textTheme.titleLarge, ), ), IconButton( alignment: Alignment.centerRight, icon: const Icon(Icons.close), onPressed: () => setState(() => _layerDataVisible = false), ), ], ), const Divider(), Flexible( child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( 'Alaska National Parks Preserves', style: Theme.of(context).textTheme.titleMedium, ), const Divider(), // Display the list of feature preserves for the selected park. for (final preserve in _featurePreserves) ListTile( dense: true, contentPadding: EdgeInsets.zero, title: Text( preserve, style: Theme.of(context).textTheme.titleSmall, ), ), const Divider(), Text( 'Alaska National Parks Species', style: Theme.of(context).textTheme.titleMedium, ), const Divider(), // Display the list of feature species for the selected park. for (final species in _featureSpecies) ListTile( dense: true, contentPadding: EdgeInsets.zero, title: Text( species, style: Theme.of(context).textTheme.titleSmall, ), ), ], ), ), ), ], ), ); }
Future<void> onMapViewReady() async { // Create a map with a topographic basemap style. final map = ArcGISMap.withBasemapStyle(BasemapStyle.arcGISTopographic);
// Feature table for the Alaska National Parks layer. final alaskaNationalParksFeaturesTable = ServiceFeatureTable.withUri( Uri.parse( 'https://services2.arcgis.com/ZQgQTuoyBrtmoGdP/ArcGIS/rest/services/AlaskaNationalParksPreservesSpecies_List/FeatureServer/1', ), );
// Create parks feature layer, the origin layer in the relationship. _alaskaNationalParksLayer = FeatureLayer.withFeatureTable( alaskaNationalParksFeaturesTable, );
// Add parks feature layer to the map. map.operationalLayers.add(_alaskaNationalParksLayer); await _alaskaNationalParksLayer.load();
// Create a feature table for related preserves layer. final alaskaNationalParksPreservesTable = ServiceFeatureTable.withUri( Uri.parse( 'https://services2.arcgis.com/ZQgQTuoyBrtmoGdP/ArcGIS/rest/services/AlaskaNationalParksPreservesSpecies_List/FeatureServer/0', ), ); // Create a feature table for related species layer. final alaskaNationalParksSpeciesTable = ServiceFeatureTable.withUri( Uri.parse( 'https://services2.arcgis.com/ZQgQTuoyBrtmoGdP/ArcGIS/rest/services/AlaskaNationalParksPreservesSpecies_List/FeatureServer/2', ), ); // Add these to the tables on the map. map.tables.addAll([ alaskaNationalParksSpeciesTable, alaskaNationalParksPreservesTable, ]);
// Assign map to the map view. _mapViewController.arcGISMap = map ..initialViewpoint = Viewpoint.fromCenter( ArcGISPoint( x: -16507762.575543, y: 9058828.127243, spatialReference: SpatialReference.webMercator, ), scale: 36764077, );
// Set selection color. _mapViewController.selectionProperties = SelectionProperties( color: Colors.yellow, );
// Set the ready state variable to true to enable the sample UI. setState(() => _ready = true); }
Future<void> onTap(Offset offset) async { // Clear the selection on the feature layer. _alaskaNationalParksLayer.clearSelection();
// Do an identify on the feature layer and select a feature. final identifyLayerResult = await _mapViewController.identifyLayer( _alaskaNationalParksLayer, screenPoint: offset, tolerance: 12, );
// If there are features identified, show the bottom sheet to display the // attachment information for the selected feature. setState(() { _selectedParkName = ''; _featurePreserves = []; _featureSpecies = []; }); final features = identifyLayerResult.geoElements.whereType<Feature>().toList(); if (features.isNotEmpty) { _alaskaNationalParksLayer.selectFeatures(features); final selectedFeature = features.first as ArcGISFeature; setState(() { _layerDataVisible = true; _loadingFeatures = true; }); // Query for related features. await queryRelatedFeatures(selectedFeature); } else { setState(() { _layerDataVisible = false; _loadingFeatures = false; }); } }
// Query for related features given the origin feature. Future<void> queryRelatedFeatures(ArcGISFeature selectedPark) async { // Query for related features. final selectedParkTable = selectedPark.featureTable! as ServiceFeatureTable; final relatedFeatureQueryResult = await selectedParkTable .queryRelatedFeatures(feature: selectedPark);
// Get the related species and preserves features. final relatedFeaturesLists = <List<String>>[]; for (final result in relatedFeatureQueryResult) { final relatedFeatures = <String>[]; for (final feature in result.features()) { final relatedFeature = feature as ArcGISFeature; // Get a reference to the feature's table. final relatedTable = feature.featureTable! as ArcGISFeatureTable;
// Get the display field name - this is the name of the field that is intended for display. final displayFieldName = relatedTable.layerInfo!.displayFieldName;
// Get the display name for the feature. final featureDisplayname = relatedFeature.attributes[displayFieldName];
// Add the display name to the list. relatedFeatures.add(featureDisplayname); } relatedFeaturesLists.add(relatedFeatures); }
// Update the UI with the related features. setState(() { _loadingFeatures = false; _selectedParkName = selectedPark.attributes['UNIT_NAME'];
_featurePreserves = relatedFeaturesLists[0];
_featureSpecies = relatedFeaturesLists[1]; }); }}