Skip to content

Commit b94de88

Browse files
ignatzJaffaKetchup
andauthored
feat: add hit detection to Polylines (#1728)
Co-authored-by: Luka S <[email protected]>
1 parent e7170da commit b94de88

File tree

2 files changed

+405
-103
lines changed

2 files changed

+405
-103
lines changed

example/lib/pages/polyline.dart

Lines changed: 188 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,96 @@ class PolylinePage extends StatefulWidget {
1414
}
1515

1616
class _PolylinePageState extends State<PolylinePage> {
17+
final PolylineHitNotifier hitNotifier = ValueNotifier(null);
18+
19+
final polylines = <Polyline, ({String title, String subtitle})>{
20+
Polyline(
21+
points: [
22+
const LatLng(51.5, -0.09),
23+
const LatLng(53.3498, -6.2603),
24+
const LatLng(48.8566, 2.3522),
25+
],
26+
strokeWidth: 8,
27+
color: const Color(0xFF60399E),
28+
): (
29+
title: 'Elizabeth Line',
30+
subtitle: 'Nothing really special here...',
31+
),
32+
Polyline(
33+
points: [
34+
const LatLng(48.5, -3.09),
35+
const LatLng(47.3498, -9.2603),
36+
const LatLng(43.8566, -1.3522),
37+
],
38+
strokeWidth: 16000,
39+
color: Colors.pink,
40+
useStrokeWidthInMeter: true,
41+
): (
42+
title: 'Pink Line',
43+
subtitle: 'Fixed radius in meters instead of pixels',
44+
),
45+
Polyline(
46+
points: [
47+
const LatLng(55.5, -0.09),
48+
const LatLng(54.3498, -6.2603),
49+
const LatLng(52.8566, 2.3522),
50+
],
51+
strokeWidth: 4,
52+
gradientColors: [
53+
const Color(0xffE40203),
54+
const Color(0xffFEED00),
55+
const Color(0xff007E2D),
56+
],
57+
): (
58+
title: 'Traffic Light Line',
59+
subtitle: 'Fancy gradient instead of a solid color',
60+
),
61+
Polyline(
62+
points: [
63+
const LatLng(50.5, -0.09),
64+
const LatLng(51.3498, 6.2603),
65+
const LatLng(53.8566, 2.3522),
66+
],
67+
strokeWidth: 20,
68+
color: Colors.blue.withOpacity(0.6),
69+
borderStrokeWidth: 20,
70+
borderColor: Colors.red.withOpacity(0.4),
71+
): (
72+
title: 'BlueRed Line',
73+
subtitle: 'Solid translucent color fill, with different color outline',
74+
),
75+
Polyline(
76+
points: [
77+
const LatLng(50.2, -0.08),
78+
const LatLng(51.2498, -10.2603),
79+
const LatLng(54.8566, -9.3522),
80+
],
81+
strokeWidth: 20,
82+
color: Colors.black.withOpacity(0.2),
83+
borderStrokeWidth: 20,
84+
borderColor: Colors.white30,
85+
): (
86+
title: 'BlackWhite Line',
87+
subtitle: 'Solid translucent color fill, with different color outline',
88+
),
89+
Polyline(
90+
points: [
91+
const LatLng(49.1, -0.06),
92+
const LatLng(52.15, -1.4),
93+
const LatLng(55.5, 0.8),
94+
],
95+
strokeWidth: 10,
96+
color: Colors.yellow,
97+
borderStrokeWidth: 10,
98+
borderColor: Colors.blue.withOpacity(0.5),
99+
): (
100+
title: 'YellowBlue Line',
101+
subtitle: 'Solid translucent color fill, with different color outline',
102+
),
103+
};
104+
105+
List<Polyline>? hoverLines;
106+
17107
@override
18108
Widget build(BuildContext context) {
19109
return Scaffold(
@@ -26,78 +116,111 @@ class _PolylinePageState extends State<PolylinePage> {
26116
),
27117
children: [
28118
openStreetMapTileLayer,
29-
PolylineLayer(
30-
polylines: [
31-
Polyline(
32-
points: [
33-
const LatLng(51.5, -0.09),
34-
const LatLng(53.3498, -6.2603),
35-
const LatLng(48.8566, 2.3522),
36-
],
37-
strokeWidth: 4,
38-
color: Colors.purple,
39-
),
40-
Polyline(
41-
points: [
42-
const LatLng(55.5, -0.09),
43-
const LatLng(54.3498, -6.2603),
44-
const LatLng(52.8566, 2.3522),
45-
],
46-
strokeWidth: 4,
47-
gradientColors: [
48-
const Color(0xffE40203),
49-
const Color(0xffFEED00),
50-
const Color(0xff007E2D),
51-
],
52-
),
53-
Polyline(
54-
points: [
55-
const LatLng(50.5, -0.09),
56-
const LatLng(51.3498, 6.2603),
57-
const LatLng(53.8566, 2.3522),
58-
],
59-
strokeWidth: 20,
60-
color: Colors.blue.withOpacity(0.6),
61-
borderStrokeWidth: 20,
62-
borderColor: Colors.red.withOpacity(0.4),
119+
MouseRegion(
120+
hitTestBehavior: HitTestBehavior.deferToChild,
121+
cursor: SystemMouseCursors.click,
122+
onHover: (_) {
123+
if (hitNotifier.value == null) return;
124+
125+
final lines = hitNotifier.value!.lines
126+
.where((e) => polylines.containsKey(e))
127+
.map(
128+
(e) => Polyline(
129+
points: e.points,
130+
strokeWidth: e.strokeWidth + e.borderStrokeWidth,
131+
color: Colors.transparent,
132+
borderStrokeWidth: 15,
133+
borderColor: Colors.green,
134+
useStrokeWidthInMeter: e.useStrokeWidthInMeter,
135+
),
136+
)
137+
.toList();
138+
setState(() => hoverLines = lines);
139+
},
140+
141+
/// Clear hovered lines when touched lines modal appears
142+
onExit: (_) => setState(() => hoverLines = null),
143+
child: GestureDetector(
144+
onTap: () => _openTouchedLinesModal(
145+
'Tapped',
146+
hitNotifier.value!.lines,
147+
hitNotifier.value!.point,
63148
),
64-
Polyline(
65-
points: [
66-
const LatLng(50.2, -0.08),
67-
const LatLng(51.2498, -10.2603),
68-
const LatLng(54.8566, -9.3522),
69-
],
70-
strokeWidth: 20,
71-
color: Colors.black.withOpacity(0.2),
72-
borderStrokeWidth: 20,
73-
borderColor: Colors.white30,
149+
onLongPress: () => _openTouchedLinesModal(
150+
'Long pressed',
151+
hitNotifier.value!.lines,
152+
hitNotifier.value!.point,
74153
),
75-
Polyline(
76-
points: [
77-
const LatLng(49.1, -0.06),
78-
const LatLng(52.15, -1.4),
79-
const LatLng(55.5, 0.8),
80-
],
81-
strokeWidth: 10,
82-
color: Colors.yellow,
83-
borderStrokeWidth: 10,
84-
borderColor: Colors.blue.withOpacity(0.5),
154+
onSecondaryTap: () => _openTouchedLinesModal(
155+
'Secondary tapped',
156+
hitNotifier.value!.lines,
157+
hitNotifier.value!.point,
85158
),
86-
Polyline(
87-
points: [
88-
const LatLng(48.1, -0.03),
89-
const LatLng(50.5, -7.8),
90-
const LatLng(56.5, 0.4),
91-
],
92-
strokeWidth: 10,
93-
color: Colors.amber,
94-
borderStrokeWidth: 10,
95-
borderColor: Colors.blue.withOpacity(0.5),
159+
child: PolylineLayer(
160+
hitNotifier: hitNotifier,
161+
polylines: polylines.keys.followedBy(hoverLines ?? []).toList(),
96162
),
97-
],
163+
),
98164
),
99165
],
100166
),
101167
);
102168
}
169+
170+
void _openTouchedLinesModal(
171+
String eventType,
172+
List<Polyline> tappedLines,
173+
LatLng coords,
174+
) {
175+
tappedLines.removeWhere((e) => !polylines.containsKey(e));
176+
177+
showModalBottomSheet<void>(
178+
context: context,
179+
builder: (context) => Padding(
180+
padding: const EdgeInsets.all(16),
181+
child: Column(
182+
crossAxisAlignment: CrossAxisAlignment.start,
183+
children: [
184+
const Text(
185+
'Tapped Polyline(s)',
186+
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
187+
),
188+
Text(
189+
'$eventType at point: (${coords.latitude.toStringAsFixed(6)}, ${coords.longitude.toStringAsFixed(6)})',
190+
),
191+
const SizedBox(height: 8),
192+
Expanded(
193+
child: ListView.builder(
194+
itemBuilder: (context, index) {
195+
final tappedLineData = polylines[tappedLines[index]]!;
196+
return ListTile(
197+
leading: index == 0
198+
? const Icon(Icons.vertical_align_top)
199+
: index == tappedLines.length - 1
200+
? const Icon(Icons.vertical_align_bottom)
201+
: const SizedBox.shrink(),
202+
title: Text(tappedLineData.title),
203+
subtitle: Text(tappedLineData.subtitle),
204+
dense: true,
205+
);
206+
},
207+
itemCount: tappedLines.length,
208+
),
209+
),
210+
const SizedBox(height: 8),
211+
Align(
212+
alignment: Alignment.bottomCenter,
213+
child: SizedBox(
214+
width: double.infinity,
215+
child: OutlinedButton(
216+
onPressed: () => Navigator.pop(context),
217+
child: const Text('Close'),
218+
),
219+
),
220+
),
221+
],
222+
),
223+
),
224+
);
225+
}
103226
}

0 commit comments

Comments
 (0)