メインコンテンツまでスキップ
For the complete documentation index, see llms.txt.

Add interactions to predefined featuresets

The Mapbox Standard Style exposes three featuresets for buildings, place labels, and points of interest (POIs).

This example shows how to use addInteraction with a featureset to set the highlight and select feature states for these predefined featuresets, causing the interacted feature to change its visual appearance.

addInteraction is also used to add a long tap listener to the map itself, which is used to remove the feature states and reset the visual appearance of the map.

standard_style_interactions_example.dart
import 'dart:developer';
import 'dart:ui' show ImageFilter;

import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart';

class StandardStyleInteractionsExample extends StatefulWidget {
const StandardStyleInteractionsExample({super.key});


State<StandardStyleInteractionsExample> createState() =>
_StandardStyleInteractionsState();
}

class _StandardStyleInteractionsState
extends State<StandardStyleInteractionsExample> {
/// Space the bottom panel takes, so it can be handed to the camera as
/// padding: without it, the panel would cover whatever sits underneath it.
static const _controlsHeight = 178.0;

/// Extra clearance above the panel: its own outer padding.
static const _controlsClearance = 16.0;

MapboxMap? _mapboxMap;

/// Default style import config properties for Mapbox Standard style.
String lightPreset = 'day';
String theme = 'default';
String buildingHighlightColor = 'hsl(214, 94%, 59%)';

void _onMapCreated(MapboxMap mapboxMap) {
_mapboxMap = mapboxMap;

// The Mapbox logo and attribution default to the bottom edge, so they
// would otherwise sit behind the debug panel.
const ornamentMargin = _controlsHeight + _controlsClearance;
mapboxMap.logo.updateSettings(LogoSettings(marginBottom: ornamentMargin));
mapboxMap.attribution.updateSettings(
AttributionSettings(marginBottom: ornamentMargin),
);

// Tapping a POI in the Standard POIs featureset hides it. Without
// stopPropagation the tap also reaches the interactions added below.
mapboxMap.addInteraction(
TapInteraction(
StandardPOIs(),
(feature, _) {
mapboxMap.setFeatureStateForFeaturesetFeature(
feature,
StandardPOIsState(hide: true),
);
log("POI feature name: ${feature.name}");
},
radius: 10,
stopPropagation: false,
),
interactionID: "tap_interaction_poi",
);

// Tapping a building in the Standard Buildings featureset highlights it.
mapboxMap.addInteraction(
TapInteraction(StandardBuildings(), (feature, _) {
mapboxMap.setFeatureStateForFeaturesetFeature(
feature,
StandardBuildingsState(highlight: true),
);
log("Building group: ${feature.group}");
}),
);

// Tapping a place label in the Standard Place Labels featureset selects it.
mapboxMap.addInteraction(
TapInteraction(StandardPlaceLabels(), (feature, _) {
mapboxMap.setFeatureStateForFeaturesetFeature(
feature,
StandardPlaceLabelsState(select: true),
);
log("Place label: ${feature.name}");
}),
);

// On mobile, when the map is long-tapped print the screen coordinates of
// the tap and reset the state of all features in the Standard POIs,
// Buildings, and Place Labels featuresets. Long-tap isn't available on web.
if (!kIsWeb) {
mapboxMap.addInteraction(
LongTapInteraction.onMap((context) {
log(
"Long tap at: ${context.touchPosition.x}, ${context.touchPosition.y}",
);
mapboxMap.resetFeatureStatesForFeatureset(StandardPOIs());
mapboxMap.resetFeatureStatesForFeatureset(StandardBuildings());
mapboxMap.resetFeatureStatesForFeatureset(StandardPlaceLabels());
}),
);
}
}

// The Standard style's "basemap" import is only available once the style has
// loaded, then we can update the map style import's config properties.
void _onStyleLoaded(StyleLoadedEventData data) {
_updateMapStyle();
}

/// One labelled dropdown row inside the floating panel.
Widget _field(
String label,
String value,
Map<String, String> options,
ValueChanged<String> onChanged,
) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label.toUpperCase(),
style: const TextStyle(
color: Color(0x80FFFFFF),
fontSize: 10,
fontWeight: FontWeight.w600,
letterSpacing: 1.1,
),
),
DropdownButton<String>(
value: value,
isDense: true,
isExpanded: true,
underline: const SizedBox.shrink(),
dropdownColor: const Color(0xFF1C1F24),
style: const TextStyle(color: Colors.white, fontSize: 14),
onChanged: (selected) {
if (selected != null) onChanged(selected);
},
items: [
for (final option in options.entries)
DropdownMenuItem(value: option.key, child: Text(option.value)),
],
),
],
);
}

Widget _buildDebugPanel() {
return ClipRRect(
borderRadius: BorderRadius.circular(16),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 24, sigmaY: 24),
child: Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
// Translucent dark panel matching the Mapbox interactive demos.
color: const Color(0xCC0E1012),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0x1FFFFFFF)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_field(
'Building color',
buildingHighlightColor,
const {
'hsl(214, 94%, 59%)': 'Blue',
'yellow': 'Yellow',
'red': 'Red',
},
(value) => setState(() {
buildingHighlightColor = value;
_updateMapStyle();
}),
),
const SizedBox(height: 10),
_field(
'Light',
lightPreset,
const {
'dawn': 'Dawn',
'day': 'Day',
'dusk': 'Dusk',
'night': 'Night',
},
(value) => setState(() {
lightPreset = value;
_updateMapStyle();
}),
),
const SizedBox(height: 10),
_field(
'Theme',
theme,
const {
'default': 'Default',
'faded': 'Faded',
'monochrome': 'Monochrome',
},
(value) => setState(() {
theme = value;
_updateMapStyle();
}),
),
],
),
),
),
);
}


Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
MapWidget(
key: ValueKey("mapWidget"),
viewport: CameraViewportState(
center: Point(coordinates: Position(24.9453, 60.1718)),
bearing: 49.92,
zoom: 16.35,
pitch: 40,
// Keeps the map's visual center clear of the bottom debug panel.
padding: const EdgeInsets.only(bottom: _controlsHeight),
),
styleUri: MapboxStyles.STANDARD,
onMapCreated: _onMapCreated,
onStyleLoadedListener: _onStyleLoaded,
),
// Anchored bottom-center. Width-capped on web, so the panel reads
// as a floating control rather than a full-width bar on wide
// screens; full-width on mobile, where the viewport is narrow
// enough that the cap would not do anything anyway.
Positioned(
left: 0,
right: 0,
bottom: 0,
child: SafeArea(
top: false,
child: Center(
child: ConstrainedBox(
constraints: BoxConstraints(
maxWidth: kIsWeb ? 260 : double.infinity,
),
child: Padding(
padding: const EdgeInsets.all(_controlsClearance),
child: _buildDebugPanel(),
),
),
),
),
),
],
),
);
}

void _updateMapStyle() {
_mapboxMap?.setStyleImportConfigProperties("basemap", {
"lightPreset": lightPreset,
"theme": theme,
"colorBuildingHighlight": buildingHighlightColor,
});
}
}
このexampleは役に立ちましたか?