// Automatic FlutterFlow imports
import '/backend/backend.dart';
import '/backend/schema/structs/index.dart';
import '/flutter_flow/flutter_flow_theme.dart';
import '/flutter_flow/flutter_flow_util.dart';
import '/custom_code/widgets/index.dart'; // Imports other custom widgets
import '/custom_code/actions/index.dart'; // Imports custom actions
import '/flutter_flow/custom_functions.dart'; // Imports custom functions
import 'package:flutter/material.dart';
// DO NOT REMOVE OR MODIFY THE CODE ABOVE!
import 'dart:html';
import 'dart:async';
import 'dart:typed_data';
import 'dart:ui';
import 'package:google_maps_flutter/google_maps_flutter.dart' as google_maps_flutter;
import '/flutter_flow/lat_lng.dart' as latlng;
import 'package:location/location.dart';
export 'dart:async' show Completer;
export 'package:google_maps_flutter/google_maps_flutter.dart' hide LatLng;
export '/flutter_flow/lat_lng.dart' show LatLng;
class FirestoreMapNEW extends StatefulWidget {
const FirestoreMapNEW({
Key? key,
this.width,
this.height,
this.mapdata, // Changed from places to mapdata
required this.showLocation,
required this.showCompass,
required this.showMapToolbar,
required this.showTraffic,
required this.allowZoom,
required this.showZoomControls,
required this.defaultZoom,
this.onClickMarker,
}) : super(key: key);
final double? width;
final double? height;
final List<MapDataRecord>? mapdata; // Changed from PlaceRecord to MapDataRecord
final bool showLocation;
final bool showCompass;
final bool showMapToolbar;
final bool showTraffic;
final bool allowZoom;
final bool showZoomControls;
final double defaultZoom;
final Future Function(MapDataRecord? mapdataRow)? onClickMarker;
@override
State<FirestoreMapNEW> createState() => _FirestoreMapState();
}
class _FirestoreMapState extends State<FirestoreMapNEW> {
Completer<google_maps_flutter.GoogleMapController> _controller = Completer();
Map<String, google_maps_flutter.BitmapDescriptor> _customIcons = {};
Set<google_maps_flutter.Marker> _markers = {};
google_maps_flutter.LatLng? _center;
bool _showCustomInfoWindow = false;
MapDataRecord? _selectedData; // Changed from PlaceRecord to MapDataRecord
Offset _infoWindowOffset = Offset.zero;
@override
void initState() {
super.initState();
_initializeLocation();
_loadMarkerIcons();
Future<void> _initializeLocation() async {
Location location = Location();
bool _serviceEnabled;
PermissionStatus _permissionGranted;
LocationData _locationData;
_serviceEnabled = await location.serviceEnabled();
if (!_serviceEnabled) {
_serviceEnabled = await location.requestService();
if (!_serviceEnabled) {
return;
_permissionGranted = await location.hasPermission();
if (_permissionGranted == PermissionStatus.denied) {
_permissionGranted = await location.requestPermission();
if (_permissionGranted != PermissionStatus.granted) {
return;
_locationData = await location.getLocation();
setState(() {
_center = google_maps_flutter.LatLng(
_locationData.latitude!, _locationData.longitude!);
});
_updateMarkers();
Future<void> _loadMarkerIcons() async {
Set<String?> uniqueIconPaths =
widget.mapdata?.map((data) => data.icon).toSet() ?? {}; // Updated to mapdata
for (String? path in uniqueIconPaths) {
if (path != null && path.isNotEmpty) {
if (path.contains("https")) {
Uint8List? imageData = await loadNetworkImage(path);
if (imageData != null) {
google_maps_flutter.BitmapDescriptor descriptor =
await google_maps_flutter.BitmapDescriptor.fromBytes(imageData);
_customIcons[path] = descriptor;
} else {
google_maps_flutter.BitmapDescriptor descriptor =
await google_maps_flutter.BitmapDescriptor.fromAssetImage(
const ImageConfiguration(devicePixelRatio: 2.5),
"assets/images/$path",
);
_customIcons[path] = descriptor;
_updateMarkers();
Future<Uint8List?> loadNetworkImage(String path) async {
final completer = Completer<ImageInfo>();
var image = NetworkImage(path);
image.resolve(const ImageConfiguration()).addListener(ImageStreamListener(
(ImageInfo info, bool _) => completer.complete(info)));
final imageInfo = await completer.future;
final byteData =
await imageInfo.image.toByteData(format: ImageByteFormat.png);
return byteData?.buffer.asUint8List();
void _updateMarkers() {
setState(() {
_markers = _createMarkers();
});
void _onMapCreated(google_maps_flutter.GoogleMapController controller) {
_controller.complete(controller);
Set<google_maps_flutter.Marker> _createMarkers() {
var tmp = <google_maps_flutter.Marker>{};
for (int i = 0; i < (widget.mapdata ?? []).length; i++) { // Updated to mapdata
var data = widget.mapdata?[i];
final latlng.LatLng coordinates =
latlng.LatLng(data?.latitude ?? 0.0, data?.longitude ?? 0.0);
final google_maps_flutter.LatLng googleMapsLatLng =
google_maps_flutter.LatLng(
coordinates.latitude, coordinates.longitude);
google_maps_flutter.BitmapDescriptor icon = _customIcons[data?.icon] ??
google_maps_flutter.BitmapDescriptor.defaultMarker;
final google_maps_flutter.Marker marker = google_maps_flutter.Marker(
markerId:
google_maps_flutter.MarkerId('${data?.address ?? "Marker"}_$i'),
position: googleMapsLatLng,
icon: icon,
onTap: () async {
setState(() {
_selectedData = data;
_showCustomInfoWindow = true;
_infoWindowOffset = Offset(
MediaQuery.of(context).size.width / 2 - 100,
MediaQuery.of(context).size.height / 2 - 150,
);
});
final callback = widget.onClickMarker;
if (callback != null) {
await callback(data);
},
);
tmp.add(marker);
return tmp;
}
@override
Widget build(BuildContext context) {
return _center == null
? Center(child: CircularProgressIndicator())
: Stack(
children: [
google_maps_flutter.GoogleMap(
onMapCreated: _onMapCreated,
zoomGesturesEnabled: widget.allowZoom,
zoomControlsEnabled: widget.showZoomControls,
myLocationEnabled: widget.showLocation,
compassEnabled: widget.showCompass,
mapToolbarEnabled: widget.showMapToolbar,
trafficEnabled: widget.showTraffic,
initialCameraPosition: google_maps_flutter.CameraPosition(
target: _center!,
zoom: widget.defaultZoom,
),
markers: _markers,
onTap: (position) {
final latLng = google_maps_flutter.LatLng(
position.latitude, position.longitude);
setState(() {
_center = latLng; // Recenter the map based on tapped position
_showCustomInfoWindow = false; // Hide info window
});
},
),
if (_showCustomInfoWindow && _selectedData != null)
Positioned(
left: _infoWindowOffset.dx,
top: _infoWindowOffset.dy,
child: CustomInfoWindow(
data: _selectedData!,
onAddToRoute: (data) {
// Add your add-to-route logic here
print('Added to route: ${data.address}');
},
),
),
],
);
class CustomInfoWindow extends StatelessWidget {
final MapDataRecord data; // Updated from PlaceRecord to MapDataRecord
final Function(MapDataRecord data) onAddToRoute;
CustomInfoWindow({required this.data, required this.onAddToRoute});
@override
Widget build(BuildContext context) {
return Container(
width: 200,
padding: EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8.0),
boxShadow: [
BoxShadow(
color: Colors.black26,
offset: Offset(0, 2),
blurRadius: 6.0,
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
data.pic != null
? Image.network(data.pic!, width: 180, height: 100, fit: BoxFit.cover)
: Container(),
SizedBox(height: 8.0),
Text(
data.address ?? '',
style: TextStyle(fontWeight: FontWeight.bold),
),
SizedBox(height: 4.0),
Text(data.description ?? ''),
SizedBox(height: 8.0),
ElevatedButton(
onPressed: () => onAddToRoute(data),
child: Text('Add to Route'),
),
],
),
);