Flutter Google Maps - Custom Markers From Firestore string

25 views Asked by At

I have been trying to figure out what is wrong with the following code as it has no errors... I am trying to pull from a firestore collection which contains String fields that have the url to the firebase storage asset. When I compile and run everything is working as expected apart from the markers, they are defaulting to the default marker and the only reason that would happen is if it's getting a return null from the firestore field right? Anyways any help would be greatly appreciated.

[tag:google Maps]

  1. I have tried the util and pulling from local assets and it doesn't work either.
  2. I have tried to rewrite the code different ways and simplifying it to only pull one url.
  3. Even tried ai but i still have the same results but with more errors instead of none..
    as google_maps_flutter;
import '/flutter_flow/lat_lng.dart' as latlng;
import 'dart:async';
export 'dart:async' show Completer;
export 'package:google_maps_flutter/google_maps_flutter.dart' hide LatLng;
export '/flutter_flow/lat_lng.dart' show LatLng;
import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
import 'dart:ui';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'dart:typed_data';

class FirestoreMap extends StatefulWidget {
  const FirestoreMap({
    super.key,
    this.width,
    this.height,
    this.places,
    required this.centerLatitude,
    required this.centerLongitude,
    required this.showLocation,
    required this.showCompass,
    required this.showMapToolbar,
    required this.showTraffic,
    required this.allowZoom,
    required this.showZoomControls,
    required this.defaultZoom,
    this.mapStyleJson,
    this.onClickMarker,
  });

  final double? width;
  final double? height;
  final List<LocationsRecord>? places;
  final double centerLatitude;
  final double centerLongitude;
  final bool showLocation;
  final bool showCompass;
  final bool showMapToolbar;
  final bool showTraffic;
  final bool allowZoom;
  final bool showZoomControls;
  final double defaultZoom;
  final Future Function(LocationsRecord? placeRow)? onClickMarker;
  final String? mapStyleJson;

  @override
  State<FirestoreMap> createState() => _FirestoreMapState();
}

class _FirestoreMapState extends State<FirestoreMap> {
  Completer<google_maps_flutter.GoogleMapController> _controller = Completer();
  Map<String, google_maps_flutter.BitmapDescriptor> _customIcons = {};
  Set<google_maps_flutter.Marker> _markers = {};
  List<LocationsRecord> _currentPlaces = [];
  StreamSubscription? _locationsSubscription;

  late google_maps_flutter.LatLng _center;

  @override
  void initState() {
    super.initState();
    _center = google_maps_flutter.LatLng(
        widget.centerLatitude, widget.centerLongitude);
    _listenToLocations();
  }

  void _listenToLocations() {
    _locationsSubscription = FirebaseFirestore.instance
        .collection('locations')
        .snapshots()
        .listen((snapshot) {
      final locations = snapshot.docs
          .map((doc) => LocationsRecord.fromSnapshot(doc))
          .toList();
      setState(() {
        _currentPlaces = locations;
        _loadMarkerIcons(); // Ensure this uses _currentPlaces
      });
    });
  }

  // Be sure to cancel the subscription when the widget is disposed
  @override
  void dispose() {
    _locationsSubscription?.cancel(); // Cancel the Firestore subscription
    super.dispose();
  }

  Future<void> _loadMarkerIcons() async {
    // First, ensure we're working with the current list of places.
    final uniqueIconPaths = _currentPlaces
        .map((place) =>
            place.coverImage) // Use _currentPlaces instead of widget.places
        .where((image) => image.isNotEmpty)
        .toSet();

    // Initialize an empty map to store the future tasks of loading icons.
    final Map<String, Future<google_maps_flutter.BitmapDescriptor>>
        iconLoadTasks = {};

    // Iterate over uniqueIconPaths and initiate loading for each icon.
    for (var path in uniqueIconPaths) {
      if (path.isNotEmpty) {
        // For network images
        iconLoadTasks[path] = loadNetworkImage(path).then((imageData) =>
            google_maps_flutter.BitmapDescriptor.fromBytes(imageData));
      } else {
        // For asset images
        iconLoadTasks[path] =
            google_maps_flutter.BitmapDescriptor.fromAssetImage(
          const ImageConfiguration(devicePixelRatio: 2.5),
          "assets/images/$path",
        );
      }
    }

    // Await all load tasks and update _customIcons map.
    final icons = await Future.wait(iconLoadTasks.values);
    final paths = iconLoadTasks.keys.toList();

    // Assign loaded icons to the _customIcons map.
    for (int i = 0; i < paths.length; i++) {
      _customIcons[paths[i]] = icons[i];
    }

    // With icons loaded, update the markers.
    _updateMarkers();
  }

  Future<Uint8List> loadNetworkImage(String imageUrl) async {
    final ImageStream stream =
        NetworkImage(imageUrl).resolve(ImageConfiguration());
    final Completer<Uint8List> completer = Completer();

    void listener(ImageInfo info, bool _) async {
      // Made the listener asynchronous
      final ByteData? byteData = await info.image
          .toByteData(format: ImageByteFormat.png); // Awaiting the ByteData
      if (byteData != null) {
        completer.complete(byteData.buffer.asUint8List());
      } else {
        completer.completeError(Exception('Failed to obtain image bytes.'));
      }
      // Remove the listener once the image data is received or failed to fetch.
      stream.removeListener(ImageStreamListener(listener));
    }

    // Adding the listener
    stream.addListener(ImageStreamListener(listener));

    // Waiting for the completer to finish.
    return completer.future;
  }

  void _updateMarkers() {
    setState(() {
      _markers = _createMarkers();
    });
  }

  void _onMapCreated(google_maps_flutter.GoogleMapController controller) {
    _controller.complete(controller);
    if (widget.mapStyleJson != null) {
      // Apply the map style if provided
      controller.setMapStyle(widget.mapStyleJson);
    }
  }

  Set<google_maps_flutter.Marker> _createMarkers() {
    var tmp = <google_maps_flutter.Marker>{};
    for (int i = 0; i < (widget.places ?? []).length; i++) {
      var place = widget.places?[i];
      final latlng.LatLng coordinates = latlng.LatLng(
          place?.tempAddress?.latitude ?? 0.0,
          place?.tempAddress?.longitude ?? 0.0);
      final google_maps_flutter.LatLng googleMapsLatLng =
          google_maps_flutter.LatLng(
              coordinates.latitude, coordinates.longitude);
      google_maps_flutter.BitmapDescriptor icon =
          _customIcons[place?.coverImage] ??
              google_maps_flutter.BitmapDescriptor.defaultMarker;

      final google_maps_flutter.Marker marker = google_maps_flutter.Marker(
        markerId: google_maps_flutter.MarkerId('${place?.name ?? "Marker"}_$i'),
        position: googleMapsLatLng,
        icon: icon,
        infoWindow: google_maps_flutter.InfoWindow(
            title: place?.name, snippet: place?.description),
        onTap: () async {
          // Animate camera to the marker's position when tapped
          final google_maps_flutter.GoogleMapController controller =
              await _controller.future;
          controller
              .animateCamera(google_maps_flutter.CameraUpdate.newCameraPosition(
            google_maps_flutter.CameraPosition(
              target: googleMapsLatLng,
              zoom: 15.0, // Adjust zoom level as needed
            ),
          ));
          // Execute additional callback if provided
          final callback = widget.onClickMarker;
          if (callback != null) {
            await callback(place);
          }
        },
      );

      tmp.add(marker);
    }
    return tmp;
  }

  @override
  Widget build(BuildContext context) {
    return google_maps_flutter.GoogleMap(
      onMapCreated: _onMapCreated,
      onCameraMove: (cameraPosition) {
        // This callback is triggered on every camera move
        // If you need to update the map based on camera movement, add your logic here
      },
      onCameraIdle: () {
        // This callback is triggered when the camera stops moving
        // You can fetch and update locations here if necessary
      },
      zoomGesturesEnabled: widget.allowZoom,
      zoomControlsEnabled: widget.showZoomControls,
      myLocationEnabled: widget.showLocation,
      myLocationButtonEnabled: true,
      compassEnabled: widget.showCompass,
      mapToolbarEnabled: widget.showMapToolbar,
      mapType: google_maps_flutter.MapType.normal,
      trafficEnabled: widget.showTraffic,
      initialCameraPosition: google_maps_flutter.CameraPosition(
        target: _center,
        zoom: widget.defaultZoom,
      ),
      markers: _markers,
    );
  }
}````
0

There are 0 answers