@@ -14,6 +14,96 @@ class PolylinePage extends StatefulWidget {
1414}
1515
1616class _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