fleaflet / flutter_map

A versatile mapping package for Flutter. Simple and easy to learn, yet completely customizable and configurable, it's the best choice for mapping in your Flutter app.
https://pub.dev/packages/flutter_map
BSD 3-Clause "New" or "Revised" License
2.76k stars 863 forks source link

[BUG] The colour of marker changed randomly when zoom in/out #1949

Closed arpartydev closed 3 months ago

arpartydev commented 3 months ago

What is the bug?

I am generating some markers on the map. The marker is a simple icon widget. I give the fixed colours to the markers. The marker colour is displayed correctly at the first time.

Screenshot 2024-08-19 at 12 56 54 PM

However when move or zoom the map, the colour of the marker will change randomly.

Screenshot 2024-08-19 at 12 57 08 PM

How can we reproduce it?

Screenshot 2024-08-19 at 1 05 08 PM

Do you have a potential solution?

No response

Platforms

Web Browser

Severity

Minimum: Allows normal functioning

ibrierley commented 3 months ago

It may be worth displaying your code where you add the markers to the list. Is this dynamically updated, as I would probably guess the problem is in that code and then not updating properly when the widget has rebuilt

arpartydev commented 3 months ago
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';

import 'package:latlong2/latlong.dart';
import 'package:schoolmapsg/constants.dart';

import 'package:schoolmapsg/global_variables.dart';
import 'package:schoolmapsg/events/map_events.dart';
import 'package:schoolmapsg/widgets/maps/marker_builder.dart';

class SchoolMap extends StatefulWidget {
  const SchoolMap({
    super.key,
  });

  @override
  State<SchoolMap> createState() => _SchoolMapState();
}

class _SchoolMapState extends State<SchoolMap> {
  static final MapController _mapController = MapController();
  static StreamSubscription? subZoomEvent,
      subEnableGpsEvent,
      subEnabledMyLocationEvent,
      subEnabledMyLocationRangeEvent,
      subSelectMapEvent;
  static double latitude = 1.3521;
  static double longitude = 103.8198;
  static double zoom = 12.5;

  var myLocationMarkers = <Marker>[];
  var schoolMarkers = <Marker>[];
  var myLocationRangeMarkers = <CircleMarker>[];

  subEvents() {
    subZoomEvent = eventBus.on<MapControlZoomEvent>().listen((event) {
      zoom += event.zoomStep;
      _mapController.move(LatLng(latitude, longitude), zoom);
    });

    subEnableGpsEvent = eventBus.on<EnableGpsEvent>().listen((event) {
      displayMyLocRange();
      displayMyLoc();

      moveTo(myLocation.latitude, myLocation.longitude);
    });

    subEnabledMyLocationEvent =
        eventBus.on<EnableMyLocationEvent>().listen((event) {
      if (event.enabled) {
        displayMyLocRange();
        displayMyLoc();
        moveTo(myLocation.latitude, myLocation.longitude);
      } else {
        myLocationRangeMarkers.clear();
        myLocationMarkers.clear();
      }
      setState(() {});
    });

    subSelectMapEvent = eventBus.on<SelectMapEvent>().listen((event) {
      switch (event.map) {
        case SchoolMaps.moekindergarten:
          showMoeKindergartenMap();
        case SchoolMaps.primaryschool:
          showPrimarySchoolMap();
        case SchoolMaps.nomap:
          clearSchoolMarkers();
      }
    });

    subEnabledMyLocationRangeEvent =
        eventBus.on<EnableMyLocationRangeEvent>().listen((event) {
      if (event.enabled) {
        displayMyLocRange();
      } else {
        myLocationRangeMarkers.clear();
      }
      setState(() {});
    });
  }

  @override
  void deactivate() {
    super.deactivate();
    subZoomEvent?.cancel();
    subEnableGpsEvent?.cancel();
    subEnabledMyLocationEvent?.cancel();
    subEnabledMyLocationRangeEvent?.cancel();
    subSelectMapEvent?.cancel();
  }

  @override
  void initState() {
    super.initState();
    subEvents();
    myLocationMarkers.clear();
    displayMyLocRange();
    displayMyLoc();

    if (currentMap == SchoolMaps.moekindergarten) {
      eventBus.fire(SelectMapEvent(SchoolMaps.moekindergarten));
    }

    if (currentMap == SchoolMaps.primaryschool) {
      eventBus.fire(SelectMapEvent(SchoolMaps.primaryschool));
    }
  }

  void displayMyLoc() {
    if (gpsEnabled && displayMyLocation) {
      myLocationMarkers.clear();
      myLocationMarkers.add(buildMyLocation(
          LatLng(myLocation.latitude, myLocation.longitude), context));
    }
  }

  void displayMyLocRange() {
    if (gpsEnabled && displayMyLocation && displayMyLocationRange) {
      myLocationRangeMarkers.clear();
      myLocationRangeMarkers.add(buildMyLocationRange1km(
          LatLng(myLocation.latitude, myLocation.longitude), context));
      myLocationRangeMarkers.add(buildMyLocationRange2km(
          LatLng(myLocation.latitude, myLocation.longitude), context));
    }
  }

  void showMoeKindergartenMap() {
    setState(() {
      schoolMarkers.clear();
      schoolMarkers.addAll(moekindergarten_select_all(context));
    });
  }

  void showPrimarySchoolMap() {
    setState(() {
      schoolMarkers.clear();
      schoolMarkers.addAll(primaryschool_select_all(context));
    });
  }

  void clearSchoolMarkers() {
    setState(() {
      schoolMarkers.clear();
    });
  }

  void moveTo(double lat, double longi) {
    latitude = myLocation.latitude;
    longitude = myLocation.longitude;
    _mapController.move(LatLng(latitude, longitude), zoom);
  }

  @override
  Widget build(BuildContext context) {
    // debugPrint(
    //     "current location $currentPostion.latitude $currentPostion.longitude");
    return FlutterMap(
        mapController: _mapController,
        options: MapOptions(
            initialCenter: LatLng(latitude, longitude),
            initialZoom: zoom,
            maxZoom: 20.0,
            minZoom: 10,
            keepAlive: true,
            interactionOptions: const InteractionOptions(
              enableMultiFingerGestureRace: true,
              flags: InteractiveFlag.doubleTapDragZoom |
                  InteractiveFlag.doubleTapZoom |
                  InteractiveFlag.drag |
                  InteractiveFlag.flingAnimation |
                  InteractiveFlag.pinchZoom |
                  InteractiveFlag.scrollWheelZoom,
            ),
            onMapEvent: (event) {
              if (event is MapEventMove) {
                latitude = _mapController.camera.center.latitude;
                longitude = _mapController.camera.center.longitude;
                // debugPrint(
                //     "Move ${_mapController.camera.center.latitude} ${_mapController.camera.center.longitude}");
              }
              if (event is MapEventScrollWheelZoom) {
                zoom = _mapController.camera.zoom;
                // eventBus.fire(SelectMapEvent(SchoolMaps.primaryschool));
                // debugPrint("Zoom ${_mapController.camera.zoom}");
              }
            }),
        children: [
          TileLayer(
            urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
          ),
          CircleLayer(
            circles: myLocationRangeMarkers,
          ),
          MarkerLayer(
            markers: schoolMarkers,
          ),
          MarkerLayer(
            markers: myLocationMarkers,
          ),
        ]);
  }
}
arpartydev commented 3 months ago

Code for returning the marker

import 'package:flutter/material.dart';
import 'package:info_popup/info_popup.dart';
import 'package:schoolmapsg/events/map_events.dart';
import 'package:schoolmapsg/global_variables.dart';
import 'package:schoolmapsg/models/primary_school.dart';

class MarkerPrimarySchool extends StatefulWidget {
  final PrimarySchool item;

  const MarkerPrimarySchool({super.key, required this.item});

  @override
  State<MarkerPrimarySchool> createState() => _MarkerPrimarySchoolState();
}

class _MarkerPrimarySchoolState extends State<MarkerPrimarySchool> {
  bool isSelected = false;

  late MaterialColor iconColor;

  @override
  void initState() {
    super.initState();

    if (widget.item.nature == "Co-Ed School") {
      iconColor = Colors.green;
    } else if (widget.item.nature == "Boys School") {
      iconColor = Colors.blue;
    } else if (widget.item.nature == "Girls School") {
      iconColor = Colors.pink;
    }
  }

  @override
  Widget build(BuildContext context) {
    return InfoPopupWidget(
      contentTitle: widget.item.name,
      arrowTheme: InfoPopupArrowTheme(
        color: Theme.of(context).dividerColor,
        arrowDirection: ArrowDirection.down,
      ),
      child: MouseRegion(
        onEnter: (event) {},
        onExit: (event) {},
        cursor: SystemMouseCursors.click,
        child: GestureDetector(
          onTap: () {
            debugPrint(widget.item.name);
            isSelected = true;
            setState(() {
              eventBus.fire(SelectPrimarySchoolEvent(widget.item, true));
            });
          },
          child: Container(
            decoration: BoxDecoration(
              // color: Colors.white,
              borderRadius: const BorderRadius.all(Radius.circular(15)),
              boxShadow: [
                BoxShadow(
                  color: Colors.grey.withOpacity(0.5),
                  spreadRadius: 1,
                  blurRadius: 1,
                  offset: const Offset(1, 1), // changes position of shadow
                ),
              ],
            ),
            child: Icon(
              Icons.circle,
              size: 15,
              color: iconColor,
            ),
          ),
        ),
      ),
    );
  }
}
arpartydev commented 3 months ago

Thanks for the fast reply. I paste the full code here.

showPrimarySchoolMap() will return the marker array which has the repaint issue

lstPrimarySchool will be initialised only once when the app start. Won't be changed after

List<Marker> primaryschool_select_all(context) {
  debugPrint("Show Primary School Map");
  var schoolMarkers = <Marker>[];
  for (int i = 0; i < lstPrimarySchool.length; i++) {
    PrimarySchool item = lstPrimarySchool[i];
    schoolMarkers.add(Marker(
        point:
            LatLng(double.parse(item.latitude), double.parse(item.longitude)),
        width: 15,
        height: 15,
        child: MarkerPrimarySchool(
          item: item,
        )));
  }

  return schoolMarkers;
}
ibrierley commented 3 months ago

What happens out of interest if you add a unique Key to your schoolmarkers Marker ?

arpartydev commented 3 months ago

It works. You're amazing man!