Repository navigation
Expand file tree
/
Copy pathparking_map.dart
More file actions
217 lines (206 loc) · 7.63 KB
/
Copy pathparking_map.dart
File metadata and controls
217 lines (206 loc) · 7.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map/plugin_api.dart';
import 'package:flutter_map_marker_cluster/flutter_map_marker_cluster.dart';
import 'package:kb4yg/models/parking_lot.dart';
import 'package:kb4yg/widgets/maps/attribution_card.dart';
import 'package:kb4yg/widgets/maps/cached_tile_provider.dart';
import 'package:kb4yg/widgets/maps/fullscreen_button.dart';
import 'package:kb4yg/widgets/maps/parking_map_popup.dart';
import 'package:kb4yg/widgets/maps/zoom_buttons.dart';
import 'package:latlong2/latlong.dart';
class ParkingMap extends StatefulWidget {
final LatLng center;
final double zoom;
final List<ParkingLot> locations;
final Widget? title;
final void Function(BuildContext, ParkingLot)? onTap;
final void Function()? maximizeToggle;
const ParkingMap(
{Key? key,
required this.center,
required this.locations,
this.zoom = 10.0,
this.title,
this.onTap,
this.maximizeToggle})
: super(key: key);
@override
State<ParkingMap> createState() => _ParkingMapState();
}
class _ParkingMapState extends State<ParkingMap> {
late final List<Marker> markers;
// Prevent user from scrolling outside state (update if expand beyond Oregon)
final panBoundaryNE = LatLng(45.0, -118.0);
final panBoundarySW = LatLng(43.0, -124.0);
// Popup controller: used to trigger popup display/hiding
final PopupController popupController = PopupController();
// Map controller: handle zoom button actions
final MapController mapController = MapController();
bool _displaySnackBar = true;
@override
void initState() {
super.initState();
markers = [
for (var loc in widget.locations)
Marker(
anchorPos: AnchorPos.align(AnchorAlign.top),
height: 40,
width: 40,
point: LatLng(loc.lat, loc.lng),
builder: (context) => MouseRegion(
cursor: SystemMouseCursors.click,
child: Tooltip(
message: loc.name,
child: const Icon(
Icons.pin_drop,
color: Colors.redAccent,
size: 40,
),
),
))
];
}
void suppressSnackBar(Duration duration) async {
if (_displaySnackBar) {
_displaySnackBar = false;
await Future.delayed(duration);
_displaySnackBar = true;
}
}
// If map fails to load, display internet access snack bar error
void onImageLoadFail() {
if (_displaySnackBar) {
final snackBar = SnackBar(
duration: const Duration(seconds: 5),
content: const Text('Failed to retrieve map images. \n'
'Please check your internet connection.'),
action: SnackBarAction(
label: 'DISMISS',
onPressed: () {
suppressSnackBar(const Duration(seconds: 30));
ScaffoldMessenger.of(context).clearSnackBars();
},
));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
suppressSnackBar(const Duration(seconds: 10)); // Reduce # messages
}
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
FlutterMap(
mapController: mapController,
options: MapOptions(
plugins: [MarkerClusterPlugin()],
center: widget.center,
zoom: widget.zoom,
minZoom: kIsWeb ? 7.0 : 6.5,
maxZoom: 15.0,
// allowPanningOnScrollingParent: false,
interactiveFlags: InteractiveFlag.all & ~InteractiveFlag.rotate,
nePanBoundary: panBoundaryNE,
swPanBoundary: panBoundarySW,
// Hide popup when the map is tapped.
onTap: (_, __) => popupController.hideAllPopups(),
),
children: [
TileLayerWidget(
options: TileLayerOptions(
urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
subdomains: ['a', 'b', 'c'],
tileProvider: CachedTileProvider(onError: onImageLoadFail),
attributionBuilder: (_) => const AttributionCard(),
)),
MarkerClusterLayerWidget(
options: MarkerClusterLayerOptions(
animationsOptions: const AnimationsOptions(
fitBound: Duration(milliseconds: 1000)),
maxClusterRadius: 40,
size: const Size(40, 40),
showPolygon: false,
// Hide triangle on cluster click
markers: markers,
// anchor: AnchorPos.align(AnchorAlign.left),
builder: (context, markers) {
return FloatingActionButton(
heroTag: markers.length.toString(),
mouseCursor: SystemMouseCursors.click,
// backgroundColor: Colors.green, // Specify color of cluster
child: Text(markers.length.toString()),
onPressed: null,
);
},
fitBoundsOptions: const FitBoundsOptions(
maxZoom: 12.0, // Changes zoom of cluster click
padding: EdgeInsets.all(50),
),
popupOptions: PopupOptions(
popupController: popupController,
popupBuilder: (context, Marker marker) {
// Get parking lot corresponding to marker index
var index = markers.indexOf(marker);
var loc = widget.locations[index];
return ParkingMapPopup(
marker: marker,
location: loc,
onTap: () => widget.onTap == null
? null
: widget.onTap!(context, loc),
);
}),
),
)
],
),
// Title
if (widget.title != null)
Align(
alignment: Alignment.topCenter,
child: Row(
children: [
Expanded(
child: ShaderMask(
blendMode: BlendMode.dstOver,
shaderCallback: (Rect bounds) {
return LinearGradient(
colors: <Color>[
Colors.transparent,
Colors.black.withOpacity(0.4),
Colors.black.withOpacity(0.4),
Colors.black.withOpacity(0.4),
Colors.transparent,
],
stops: const [0.1, 0.3, 0.5, 0.7, 0.9],
).createShader(bounds);
},
child: Container(
padding: const EdgeInsets.all(8.0),
child: widget.title),
),
),
],
),
),
// Map Buttons
if (widget.maximizeToggle != null)
Align(
alignment: Alignment.topLeft,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: FullscreenButton(maximizeToggle: widget.maximizeToggle),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(8, 60, 8, 40),
child: Align(
alignment: kIsWeb ? Alignment.topLeft : Alignment.bottomRight,
child: ZoomButtons(mapController: mapController),
),
)
],
);
}
}