Skip to content

Commit 025e008

Browse files
committed
follow the common design to revise the sample
1 parent 21f18d1 commit 025e008

File tree

4 files changed

+206
-82
lines changed

4 files changed

+206
-82
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Show device location history
2+
3+
Display your device's location history on the map.
4+
5+
![Image of show device location history](show_device_location_history.png)
6+
7+
## Use case
8+
9+
You can track device location history and display it as lines and points on the map. The history can be used to visualize how the user moved through the world, to retrace their steps, or to create new feature geometry. An unmapped trail, for example, could be added to the map using this technique.
10+
11+
## How to use the sample
12+
13+
Tap 'Start tracking' to start tracking your location, which will appear as points on the map. A line will connect the points for easier visualization. Tap 'Stop tracking' to stop updating the location history. This sample uses a simulated data source to allow the sample to be useful on desktop/non-mobile devices. To track a user's real position, use the `LocationDataSource` instead.
14+
15+
## How it works
16+
17+
18+
1. Create a graphics overlay to show each point and another graphics overlay for displaying the route line.
19+
2. Create a `SimulatedLocationDataSource` and initialize it with a polyline. Start the `SimulatedLocationDataSource` to begin receiving location updates.
20+
- NOTE: To track a user's real position, use `LocationDataSource` instead.
21+
3. Subscribe to the `LocationChanged` event to handle location updates.
22+
4. Every time the location updates, store that location, display a point on the map, and recreate the route line.
23+
24+
## Relevant API
25+
26+
* LocationDataSource
27+
* LocationDisplay
28+
* LocationDisplayAutoPanMode
29+
* PolylineBuilder
30+
* SimpleLineSymbol
31+
* SimpleMarkerSymbol
32+
* SimpleRenderer
33+
* SimulatedLocationDataSource
34+
* Graphic
35+
* GraphicsOverlay
36+
37+
## About the data
38+
39+
A custom set of points is used to create a `Polyline` and initialize a `SimulatedLocationDataSource`. This simulated location data source enables easier testing and allows the sample to be used on devices without an actively updating GPS signal.
40+
41+
## Tags
42+
43+
bread crumb, breadcrumb, GPS, history, movement, navigation, real-time, trace, track, trail

lib/samples/show_device_location_history/README.metadata.json

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,46 @@
22
"category": "Maps",
33
"description": "Display your device's location history on the map.",
44
"ignore": false,
5-
"images": [],
6-
"keywords": [],
5+
"images": [
6+
"show_device_location_history.png"
7+
],
8+
"keywords": [
9+
"GPS",
10+
"bread crumb",
11+
"breadcrumb",
12+
"history",
13+
"movement",
14+
"navigation",
15+
"real-time",
16+
"trace",
17+
"track",
18+
"trail",
19+
"Graphic",
20+
"GraphicsOverlay",
21+
"LocationDataSource",
22+
"LocationDisplay",
23+
"LocationDisplayAutoPanMode",
24+
"PolylineBuilder",
25+
"SimpleLineSymbol",
26+
"SimpleMarkerSymbol",
27+
"SimpleRenderer",
28+
"SimulatedLocationDataSource"
29+
],
730
"redirect_from": [],
8-
"relevant_apis": [],
9-
"snippets": [],
31+
"relevant_apis": [
32+
"Graphic",
33+
"GraphicsOverlay",
34+
"LocationDataSource",
35+
"LocationDisplay",
36+
"LocationDisplayAutoPanMode",
37+
"PolylineBuilder",
38+
"SimpleLineSymbol",
39+
"SimpleMarkerSymbol",
40+
"SimpleRenderer",
41+
"SimulatedLocationDataSource"
42+
],
43+
"snippets": [
44+
"show_device_location_history_sample.dart"
45+
],
1046
"title": "Show device location history"
1147
}
204 KB
Loading

lib/samples/show_device_location_history/show_device_location_history_sample.dart

Lines changed: 123 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -26,26 +26,39 @@ class ShowDeviceLocationHistorySample extends StatefulWidget {
2626
const ShowDeviceLocationHistorySample({super.key});
2727

2828
@override
29-
State<ShowDeviceLocationHistorySample> createState() =>
30-
_ShowDeviceLocationHistorySampleState();
29+
State<ShowDeviceLocationHistorySample> createState() => Configure();
3130
}
3231

33-
class _ShowDeviceLocationHistorySampleState
34-
extends State<ShowDeviceLocationHistorySample> with SampleStateSupport {
32+
class Configure extends State<ShowDeviceLocationHistorySample>
33+
with SampleStateSupport {
34+
// Create a controller for the map view.
3535
final _mapViewController = ArcGISMapView.createController();
36-
double _latitude = 0.0;
37-
double _longitude = 0.0;
38-
double _heading = 0.0;
36+
// A location data source to simulate location updates.
3937
final _locationDataSource = SimulatedLocationDataSource();
38+
// Subscriptions to listen for location status changes.
4039
StreamSubscription? _statusSubscription;
40+
// Subscriptions to listen for location changes.
4141
StreamSubscription? _locationSubscription;
42-
ArcGISException? _ldsException;
42+
// A GraphicsOverlay to display the location history polyline.
43+
final _locationHistoryLineOverlay = GraphicsOverlay();
44+
// A GraphicsOverlay to display the location history points.
45+
final _locationHistoryPointOverlay = GraphicsOverlay();
46+
// A PolylineBuilder to build the location history polyline.
47+
final _polylineBuilder = PolylineBuilder.fromSpatialReference(
48+
SpatialReference.wgs84,
49+
);
50+
// A flag for when the map view is ready and controls can be used.
51+
var _ready = false;
52+
// A flag for toggling location tracking.
53+
var _enableTracking = false;
4354

4455
@override
4556
void dispose() {
4657
_locationDataSource.stop();
4758
_locationSubscription?.cancel();
4859
_statusSubscription?.cancel();
60+
_locationHistoryLineOverlay.graphics.clear();
61+
_locationHistoryPointOverlay.graphics.clear();
4962

5063
super.dispose();
5164
}
@@ -54,80 +67,59 @@ class _ShowDeviceLocationHistorySampleState
5467
Widget build(BuildContext context) {
5568
return Scaffold(
5669
body: SafeArea(
57-
child: Column(
58-
crossAxisAlignment: CrossAxisAlignment.center,
70+
top: false,
71+
child: Stack(
5972
children: [
60-
Expanded(
61-
child: Stack(
62-
alignment: Alignment.bottomRight,
63-
children: [
64-
ArcGISMapView(
73+
Column(
74+
children: [
75+
Expanded(
76+
// Add a map view to the widget tree and set a controller.
77+
child: ArcGISMapView(
6578
controllerProvider: () => _mapViewController,
6679
onMapViewReady: onMapViewReady,
6780
),
68-
Positioned(
69-
bottom: 60.0,
70-
right: 10.0,
71-
child: Container(
72-
color: Colors.white,
73-
padding: const EdgeInsets.all(5.0),
74-
child: Column(
75-
mainAxisAlignment: MainAxisAlignment.center,
76-
crossAxisAlignment: CrossAxisAlignment.start,
77-
children: [
78-
const Text(
79-
'Device Location:',
80-
style: TextStyle(fontWeight: FontWeight.bold),
81-
),
82-
Text('Latitude: $_latitude'),
83-
Text('Longitude: $_longitude'),
84-
Text('Heading: $_heading')
85-
],
86-
),
87-
),
88-
)
89-
],
90-
),
91-
),
92-
SizedBox(
93-
height: 90,
94-
width: double.infinity,
95-
child: Column(
96-
children: [
97-
const Text('Location Data Source'),
98-
Visibility(
99-
visible: _ldsException == null,
100-
child: ElevatedButton(
101-
child: Text(_locationDataSource.status ==
102-
LocationDataSourceStatus.started
103-
? 'Stop'
104-
: 'Start'),
81+
),
82+
Row(
83+
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
84+
children: [
85+
// A button to enable or disable location tracking.
86+
ElevatedButton(
10587
onPressed: () {
106-
if (_locationDataSource.status ==
107-
LocationDataSourceStatus.started) {
108-
_mapViewController.locationDisplay.stop();
109-
} else {
110-
_mapViewController.locationDisplay.start();
111-
}
88+
setState(() {
89+
_enableTracking = !_enableTracking;
90+
});
11291
},
92+
child: Text(
93+
_enableTracking ? 'Stop Tracking' : 'Start Tracking',
94+
),
11395
),
96+
],
97+
),
98+
],
99+
),
100+
// Display a progress indicator and prevent interaction until state is ready.
101+
Visibility(
102+
visible: !_ready,
103+
child: SizedBox.expand(
104+
child: Container(
105+
color: Colors.white30,
106+
child: const Center(
107+
child: CircularProgressIndicator(),
114108
),
115-
Visibility(
116-
visible: _ldsException != null,
117-
child: Text('Exception: ${_ldsException?.message}'),
118-
),
119-
],
109+
),
120110
),
121-
)
111+
),
122112
],
123113
),
124114
),
125115
);
126116
}
127117

118+
// The method is called when the map view is ready to be used.
128119
void onMapViewReady() async {
129-
final map = ArcGISMap.withBasemapStyle(BasemapStyle.arcGISImageryStandard);
130-
120+
// Create a map with the ArcGIS Navigation basemap style.
121+
final map = ArcGISMap.withBasemapStyle(BasemapStyle.arcGISNavigation);
122+
// Set the initial viewpoint.
131123
map.initialViewpoint = Viewpoint.fromCenter(
132124
ArcGISPoint(
133125
x: -110.8258,
@@ -136,20 +128,48 @@ class _ShowDeviceLocationHistorySampleState
136128
),
137129
scale: 2e4,
138130
);
139-
131+
// Add the map to the map view controller.
140132
_mapViewController.arcGISMap = map;
141-
await _initLocationDisplay();
133+
// Add the graphics overlays to the map view.
134+
_mapViewController.graphicsOverlays.addAll([
135+
_locationHistoryLineOverlay,
136+
_locationHistoryPointOverlay,
137+
]);
138+
// Set the renderers for the graphics overlays.
139+
_locationHistoryLineOverlay.renderer = SimpleRenderer(
140+
symbol: SimpleLineSymbol(
141+
color: Colors.red[100]!,
142+
width: 2.0,
143+
),
144+
);
145+
_locationHistoryPointOverlay.renderer = SimpleRenderer(
146+
symbol: SimpleMarkerSymbol(
147+
color: Colors.red,
148+
size: 10.0,
149+
),
150+
);
151+
// Wait for the map to be displayed before starting the location display.
152+
_mapViewController.onDrawStatusChanged.listen((status) async {
153+
if (status == DrawStatus.completed &&
154+
_locationDataSource.status == LocationDataSourceStatus.stopped) {
155+
await _initLocationDisplay();
156+
}
157+
});
158+
// Set the ready state variable to true to enable the sample UI.
159+
setState(() => _ready = true);
142160
}
143161

162+
// Initialize the location display with the location data source.
144163
Future<void> _initLocationDisplay() async {
145164
final locationDisplay = _mapViewController.locationDisplay;
146165
locationDisplay.dataSource = _locationDataSource;
147166
locationDisplay.autoPanMode = LocationDisplayAutoPanMode.recenter;
148167
locationDisplay.useCourseSymbolOnMovement = true;
149-
await _initLocationDataSource();
168+
await _startLocationDataSource();
150169
}
151170

152-
Future<void> _initLocationDataSource() async {
171+
// Start the location data source and listen for location changes.
172+
Future<void> _startLocationDataSource() async {
153173
final routeLineJson =
154174
await rootBundle.loadString('assets/SimulatedRoute.json');
155175
final routeLine = Geometry.fromJsonString(routeLineJson) as Polyline;
@@ -160,19 +180,44 @@ class _ShowDeviceLocationHistorySampleState
160180
});
161181

162182
try {
183+
// Start the location data source.
163184
await _locationDataSource.start();
164-
_locationSubscription = _locationDataSource.onLocationChanged
165-
.listen(_handleLdsLocationChange);
185+
// Listen for location changes.
186+
_locationSubscription = _locationDataSource.onLocationChanged.listen(
187+
_handleLdsLocationChange,
188+
);
166189
} on ArcGISException catch (e) {
167-
setState(() => _ldsException = e);
190+
if (mounted) {
191+
showDialog(
192+
context: context,
193+
builder: (_) => AlertDialog(
194+
content: Text(
195+
e.message,
196+
),
197+
),
198+
);
199+
}
168200
}
169201
}
170202

203+
// Handle location changes from the location data source.
171204
void _handleLdsLocationChange(ArcGISLocation location) {
172-
setState(() {
173-
_latitude = location.position.y;
174-
_longitude = location.position.x;
175-
_heading = location.course;
176-
});
205+
if (!_enableTracking) return;
206+
// Add the location to the location history as a graphic point.
207+
final point = location.position;
208+
_locationHistoryPointOverlay.graphics.add(
209+
Graphic(
210+
geometry: point,
211+
),
212+
);
213+
// Add the location to the location history as a polyline.
214+
_polylineBuilder.addPoint(point);
215+
// Visualize the location history polyline on the map.
216+
_locationHistoryLineOverlay.graphics.clear();
217+
_locationHistoryLineOverlay.graphics.add(
218+
Graphic(
219+
geometry: _polylineBuilder.toGeometry() as Polyline,
220+
),
221+
);
177222
}
178223
}

0 commit comments

Comments
 (0)