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 860 forks source link

[BUG] `TileLayer.reset` internal listener never triggers #1808

Closed weiweiMT closed 1 month ago

weiweiMT commented 9 months ago

What is the bug?

I was trying to use reset to trigger reload one of my TileLayer, but I found that the subsciption of reset never be initialized. In lib/src/layer/tile_layer.dart, the subscription of reset dosen't be triggered properly:

class _TileLayerState extends State<TileLayer> with TickerProviderStateMixin {
  bool _initializedFromMapCamera = false;

  final _tileImageManager = TileImageManager();
  late TileBounds _tileBounds;
  late var _tileRangeCalculator =
      TileRangeCalculator(tileSize: widget.tileSize);
  late TileScaleCalculator _tileScaleCalculator;

  // We have to hold on to the mapController hashCode to determine whether we
  // need to reinitialize the listeners. didChangeDependencies is called on
  // every map movement and if we unsubscribe and resubscribe every time we
  // miss events.
  int? _mapControllerHashCode;

  StreamSubscription<TileUpdateEvent>? _tileUpdateSubscription;
  Timer? _pruneLater;

  late final _resetSub = widget.reset?.listen((_) {
    _tileImageManager.removeAll(widget.evictErrorTileStrategy);
    _loadAndPruneInVisibleBounds(MapCamera.of(context));
  });

  // This is called on every map movement so we should avoid expensive logic
  // where possible.
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();

    final camera = MapCamera.of(context);
    final mapController = MapController.of(context);
.......

Because keyword "late" was used for _resetSub, and it never be accessed which means it will not be initialized. I tried to add one line "print(_resetSub);", then this subscription works. I think that might be bug.

How can we reproduce it?

  1. Include a TileLayer in your Flutter application.
  2. Pass a proper reset stream for the TileLayer, exactly same logic with the flutter map demo app.
  3. Trigger a reset event to observe the issue. (add break point inside the reset subsription will find the program doesn't get into it)

Do you have a potential solution?

Access it somewhere might be able to solve this problem.

Platforms

macOS, web

Severity

Minimum: Allows normal functioning

JaffaKetchup commented 9 months ago

Hi @weiweiMT, Thanks for reporting! I can reproduce this by adding a print statement (rather than a breakpoint) inside the reset stream listener. Before we look into fixing this, can I ask why this is required? Changing the URL should now work without using the reset stream (unlike in previous versions) - is there some sort of improper caching you're trying to invalidate? I'm just thinking it might be worth looking into removing it if there are better alternatives.

weiweiMT commented 9 months ago

@JaffaKetchup Thank you for your help! Thank you for looking into this! The reason we rely on the reset functionality is to make the TileLayer re-fetch the sonar raster data from GeoServer WMS. Since the URL remains constant, the TileLayer won't update on its own. However, for sonar data, we require real-time updates. The reset mechanism is crucial for ensuring the timely refresh of the TileLayer in our application. Appreciate your support!

JaffaKetchup commented 9 months ago

I've fixed the issue of the listener never firing, but it looks as though the issue from #1619/#1620 has returned, but that fix doesn't resolve the issue now. There's also some similarities to #1813. I think a fix in the TileImageManager is required.