Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Slow loading and SocketExceptions for map tiles #1831

Closed
CvisionTeam opened this issue Feb 19, 2024 · 20 comments
Closed

[BUG] Slow loading and SocketExceptions for map tiles #1831

CvisionTeam opened this issue Feb 19, 2024 · 20 comments
Labels
bug This issue reports broken functionality or another error invalid This bug could not be reproduced or does not exist, or is very low quality S: core Scoped to the core flutter_map functionality

Comments

@CvisionTeam
Copy link

What is the bug?

I am encountering issues with the Flutter flutter_map plugin when attempting to load tile images from Google Maps or OpenStreetMaps. Initially, the tile images load very slowly, and eventually, errors such as the following are raised:

═══════ Exception caught by image resource service ════════════════════════════ 
Connection closed before full header was received, uri=http://mt3.google.com/vt/lyrs=m&x=243&y=204&z=9&apistyle=s.t%3A17%7Cs.e%3Alg%7Cp.v%3Aoff

and

═══════ Exception caught by image resource service ════════════════════════════
The following _ClientSocketException was thrown resolving an image codec:
ClientException with SocketException: Connection attempt cancelled, host: mt3.google.com, port: 80, uri=http://mt3.google.com/vt/lyrs=m&x=15841&y=12906&z=15&apistyle=s.t%3A17%7Cs.e%3Alg%7Cp.v%3Aoff

When the exception was thrown, this was the stack:
#0      IOClient.send (package:http/src/io_client.dart:154:7)
io_client.dart:154
<asynchronous suspension>
#1      RetryClient.send (package:http/retry.dart:115:20)
retry.dart:115
<asynchronous suspension>
#2      BaseClient._sendUnstreamed (package:http/src/base_client.dart:93:32)
base_client.dart:93
<asynchronous suspension>
#3      BaseClient.readBytes (package:http/src/base_client.dart:58:22)
base_client.dart:58
<asynchronous suspension>
#4      FlutterMapNetworkImageProvider._loadAsync (package:flutter_map/src/layer/tile_layer/tile_provider/network_image_provider.dart:84:11)
network_image_provider.dart:84
<asynchronous suspension>
#5      MultiFrameImageStreamCompleter._handleCodecReady (package:flutter/src/painting/image_stream.dart:969:3)
image_stream.dart:969
<asynchronous suspension>

URL: http://mt3.google.com/vt/lyrs=m&x=15841&y=12906&z=15&apistyle=s.t%3A17|s.e%3Alg|p.v%3Aoff
Fallback URL: null
Current provider: FlutterMapNetworkImageProvider()
════════════════════════════════════════════════════════════════════════════════

════════ Exception caught by image resource service ════════════════════════════
ClientException with SocketException: Connection attempt cancelled, host: mt3.google.com, port: 80, uri=http://mt3.google.com/vt/lyrs=m&x=15840&y=12903&z=15&apistyle=s.t%3A17%7Cs.e%3Alg%7Cp.v%3Aoff
════════════════════════════════════════════════════════════════════════════════

════════ Exception caught by image resource service ════════════════════════════
ClientException with SocketException: Connection attempt cancelled, host: mt3.google.com, port: 80, uri=http://mt3.google.com/vt/lyrs=m&x=15841&y=12902&z=15&apistyle=s.t%3A17%7Cs.e%3Alg%7Cp.v%3Aoff
════════════════════════════════════════════════════════════════════════════════

These error started to rise after I updated to Flutter_map 6.1.0, Consequently upgraded the flutter,
I have confirmed that my internet connection is stable, and I have set the necessary network permissions in AndroidManifest.xml for both debug and release modes.

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Here is my flutter doctor output:

Doctor summary (to see all details, run flutter doctor -v): 
[√] Flutter (Channel stable, 3.16.9, on Microsoft Windows [Version 10.0.19045.3930], locale en-US) 
[√] Windows Version (Installed version of Windows is version 10 or higher) 
[√] Android toolchain - develop for Android devices (Android SDK version 34.0.0) 
[√] Chrome - develop for the web 
[√] Visual Studio - develop Windows apps (Visual Studio Build Tools 2022 17.3.6) 
[√] Android Studio (version 2023.1) 
[√] VS Code (version 1.86.1) 
[√] Connected device (4 available) 
[√] Network resources

• No issues found!

I have tested this issue on both real devices (Android, iPhone) and simulators (Android, iPhone), and the problem persists across all platforms.

Here is the relevant part of the build function for the screen:

Widget build(BuildContext context) {
  return Scaffold(
    body: Stack(
      children: [
        Container(
          color: Colors.white,
          child: FlutterMap(
            key: GlobalObjectKey("gmap"),
            mapController: mapController,
            options: mapOptions(),
            children: <Widget>[
              TileLayer(
                urlTemplate: 'http://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}&apistyle=s.t%3A17|s.e%3Alg|p.v%3Aoff',
                subdomains: ['mt0', 'mt1', 'mt2', 'mt3'],
              ),
              MarkerLayer(markers: markers),
            ],
          ),
        ), 
      ],
    ),
  );
}

Here are the relevent dependencies:

|-- flutter_map 6.1.0
|   |-- async 2.11.0
|   |   |-- collection...
|   |   '-- meta...      
|   |-- latlong2 0.9.0   
|   |   '-- intl...      
|   |-- logger 2.0.2+1   
|   |-- polylabel 1.0.1  
|   |   '-- collection...
|   |-- proj4dart 2.1.0  
|   |   |-- mgrs_dart 2.0.0
|   |   |   '-- unicode 0.3.1
|   |   |       '-- lists 1.0.1
|   |   |           '-- meta...
|   |   |-- wkt_parser 2.0.0
|   |   '-- meta...
|   |-- collection...
|   |-- flutter...
|   |-- http...
|   |-- meta...
|   '-- vector_math...
|-- flutter_map_marker_cluster 1.3.4
|   |-- flutter_map_marker_popup 6.1.2
|   |   |-- animated_stack_widget 0.0.4
|   |   |   '-- flutter...
|   |   |-- flutter...
|   |   |-- flutter_map...
|   |   |-- latlong2...
|   |   '-- provider...
|   |-- flutter...
|   |-- flutter_map...
|   '-- latlong2...
|-- http 1.2.0
|   |-- http_parser 4.0.2
|   |   |-- typed_data 1.3.2
|   |   |   '-- collection...
|   |   |-- collection...
|   |   |-- source_span...
|   |   '-- string_scanner...
|   |-- async...
|   |-- meta...
|   '-- web...
|-- http_client 1.5.3
|   |-- buffer 1.2.2
|   |-- executor 2.2.3
|   |   '-- stack_trace...
|   '-- http...
|-- intl 0.18.1
|   |-- clock 1.1.1
|   |-- path 1.8.3
|   '-- meta...

Any insights or suggestions on resolving this issue would be greatly appreciated. Thank you in advance for your assistance!

How can we reproduce it?

You can reproduce the error by updating flutter_map to 6.1.0 and flutter to latest release

Do you have a potential solution?

No response

Platforms

all platforms

Severity

Minimum: Allows normal functioning

@CvisionTeam CvisionTeam added bug This issue reports broken functionality or another error needs triage This new bug report needs reproducing and prioritizing labels Feb 19, 2024
@JaffaKetchup
Copy link
Member

JaffaKetchup commented Feb 19, 2024

Hi @CvisionTeam, thanks for the report.

Can you try using the cancellable tile provider and checking whether the issue persists?

@yezouagh
Copy link

Hi @JaffaKetchup, How can I do that, can you please provide an example?

@JaffaKetchup
Copy link
Member

Please see https://docs.fleaflet.dev/layers/tile-layer/tile-providers#cancellablenetworktileprovider. Just install and swap out the provider.

@yezouagh
Copy link

yezouagh commented Feb 20, 2024

Thank you for the update, but the problem persists even after I set the tileProvider: CancellableNetworkTileProvider().
I no longer see the errors in the console, but most of the tile images do not load or take too much time to load, please check the images below.

@JaffaKetchup
Copy link
Member

@yezouagh Sorry, I'm a little confused, are you related to the @CvisionTeam, who reported this issue initially? You look to be not using Google Maps, as in the initial post, in which case I would ask you check the speed of your tile server though a different library/on another site.

@josxha josxha added the S: core Scoped to the core flutter_map functionality label Feb 21, 2024
@yezouagh
Copy link

yezouagh commented Feb 21, 2024

Hey @JaffaKetchup, this is my personal GitHub, the @CvisionTeam is my work GitHub account.

Yes, I'm also using Google Maps, I have a button for switching between different tile providers.
Now I noticed new errors:

I/flutter ( 5059): DioException [unknown]: null
I/flutter ( 5059): Error: HttpException: Connection closed before response was received, uri = http://mt1.google.com/vt/lyrs=m&x=498&y=403&z=10&apistyle=s.t%3A17%7Cs.e%3Alg%7Cp.v%3Aoff
I/flutter ( 5059): DioException [unknown]: null
I/flutter ( 5059): Error: HttpException: Connection closed before response was received, uri = http://mt2.google.com/vt/lyrs=m&x=497&y=409&z=10&apistyle=s.t%3A17%7Cs.e%3Alg%7Cp.v%3Aoff
I/flutter ( 5059): DioException [unknown]: null
I/flutter ( 5059): Error: HttpException: Connection closed before response was received, uri = http://mt3.google.com/vt/lyrs=m&x=500&y=403&z=10&apistyle=s.t%3A17%7Cs.e%3Alg%7Cp.v%3Aoff
I/flutter ( 5059): DioException [unknown]: null
I/flutter ( 5059): Error: HttpException: Connection closed before response was received, uri = http://mt0.google.com/vt/lyrs=m&x=500&y=400&z=10&apistyle=s.t%3A17%7Cs.e%3Alg%7Cp.v%3Aoff
I/flutter ( 5059): DioException [unknown]: null

below are the screenshots of Google Maps tiles.

@bramp
Copy link
Contributor

bramp commented Feb 21, 2024

Random comment. Why are the Google Maps URLs http not https?

@yezouagh
Copy link

I didn't pay attention to that, I've always been using it like that, I don't this is the problem, because the other map tiles use HTTPS, and yet the problem arises.
These are the tiles providers I use:
Note: I added the HTTPS to the Google Maps Tiles URLs.

{
    'url':
        'https://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}&apistyle=s.t%3A17|s.e%3Alg|p.v%3Aoff',
    'sub': ['mt0', 'mt1', 'mt2', 'mt3']
  },
  {
    'url':
        'https://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}&apistyle=s.t%3A17|s.e%3Alg|p.v%3Aoff',
    'sub': ['mt0', 'mt1', 'mt2', 'mt3']
  },
  {
    'url':
        'https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}',
    'sub': ['']
  },
  {
    'url':
        'https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png',
    'sub': ['abcd']
  },
  {
    'url':
        'https://{s}.google.com/vt/lyrs=m,traffic,transit&x={x}&y={y}&z={z}&apistyle=s.t%3A17|s.e%3Alg|p.v%3Aoff&hl=fr',
    'sub': ['mt0', 'mt1', 'mt2', 'mt3']
  }

@josxha
Copy link
Contributor

josxha commented Feb 22, 2024

I find two things difficult to understand:

  • Your first series of screenshots was taken over ~2 minutes, the second series over ~1 mimute. Are these recorded for the same loading or were you constantly zooming in and out?
  • Your example code includes one TileLayer while your json in the last comment implies 5 TileLayers. How many and what layers are you using?

Please provide either the code for the complete flutter_map widget with all layers and the MapOptions. A video showcasing the behaviour would be helpful too, the screenshots are hard to follow along and make conclusions on.

@JaffaKetchup
Copy link
Member

It could also be worth testing on the 'master' branch. #1742 was designed to fixed a similar issue, but is not available in a release yet, AFAIK.

@DiyorjonNasriddinov
Copy link

Hello everyone, I had the same problem too.
I noticed a problem appeared when I call setState()
I think this is due to the TileLayer is being recreated
After that tile layer made a variable that is not recreated, my problem solved

Before:

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

  @override
  State<MapWidget> createState() => _MapWidgetState();
}

class _MapWidgetState extends State<MapWidget> {
  List<Polygon> polygons = [];
  List<Polyline> polylines = [];
  List<Marker> markers = [];

  @override
  Widget build(BuildContext context) {
    final bloc = geoDataCubit;

    return BlocListener<GeoDataCubit, GeoDataState>(
      listener: (context, state) {
        if (state is GeoDataLoaded) {
          polygons = state.polygons;
          polylines = state.polylines;
          markers = state.markers;
        }
        setState(() {});
      },
      child: FlutterMap(
        mapController: bloc.mapController,
        options: MapOptions(
          initialCenter: const LatLng(40.246973, 64.3),
          initialZoom: bloc.zoomLavel,
          cameraConstraint: CameraConstraint.contain(
            bounds: LatLngBounds(
              const LatLng(-90, -180),
              const LatLng(90, 180),
            ),
          ),
          interactionOptions: const InteractionOptions(
            flags: InteractiveFlag.all & ~InteractiveFlag.rotate,
          ),
          onMapEvent: (MapEvent event) {
            bloc.onMapMoved(event.camera.zoom);
          },
        ),
        children: [
          TileLayer(
            urlTemplate: 'https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}',
            userAgentPackageName: 'uz.ekon.mobile',
          ),
          PolylineLayer(polylines: polylines),
          PolygonLayer(polygons: polygons),
          MarkerLayer(markers: markers),
        ],
      ),
    );
  }
}

After:

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

  @override
  State<MapWidget> createState() => _MapWidgetState();
}

class _MapWidgetState extends State<MapWidget> {
  List<Polygon> polygons = [];
  List<Polyline> polylines = [];
  List<Marker> markers = [];
  final tileLayer = TileLayer(
    urlTemplate: 'https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}',
    userAgentPackageName: 'uz.ekon.mobile',
  );

  @override
  Widget build(BuildContext context) {
    final bloc = geoDataCubit;

    return BlocListener<GeoDataCubit, GeoDataState>(
      listener: (context, state) {
        if (state is GeoDataLoaded) {
          polygons = state.polygons;
          polylines = state.polylines;
          markers = state.markers;
        }
        setState(() {});
      },
      child: FlutterMap(
        mapController: bloc.mapController,
        options: MapOptions(
          initialCenter: const LatLng(40.246973, 64.3),
          initialZoom: bloc.zoomLavel,
          cameraConstraint: CameraConstraint.contain(
            bounds: LatLngBounds(
              const LatLng(-90, -180),
              const LatLng(90, 180),
            ),
          ),
          interactionOptions: const InteractionOptions(
            flags: InteractiveFlag.all & ~InteractiveFlag.rotate,
          ),
          onMapEvent: (MapEvent event) {
            bloc.onMapMoved(event.camera.zoom);
          },
        ),
        children: [
          tileLayer,
          PolylineLayer(polylines: polylines),
          PolygonLayer(polygons: polygons),
          MarkerLayer(markers: markers),
        ],
      ),
    );
  }
}

@palicka
Copy link

palicka commented Mar 28, 2024

It could also be worth testing on the 'master' branch. #1742 was designed to fixed a similar issue, but is not available in a release yet, AFAIK.

I can confirm 'master' branch works. Thank you 👍 When we can expect this to be released ? I believe it will fix couple of issues reported recently (including this one) For me it's release blocker, UX is really bad. Also my app depends on packages that depend on flutter_map and can't be used when I use git dependency of flutter_map. (Also looks like master branch contains various undocumented(yet) API changes) Thank you very much
It's better, but empty squares ale still present sometimes. I don't use Cancelable tiles, does it affect this ?

@JaffaKetchup
Copy link
Member

Hi @palicka,
This looks like you might be rebuilding the map a lot. Can you post some code of your map and it's wrappers?
Also, image handling is noticably slower when in debug mode, so I would recommend testing in profile mode to see if that helps as well.

@palicka
Copy link

palicka commented Mar 30, 2024

Hi @palicka, This looks like you might be rebuilding the map a lot. Can you post some code of your map and it's wrappers? Also, image handling is noticably slower when in debug mode, so I would recommend testing in profile mode to see if that helps as well.

thank you, I removed unnecessary animations, added CancellableNetworkTileProvider and it looks better.

@JaffaKetchup
Copy link
Member

If animations were causing the issue, that suggests it could be frequent rebuilding that's the issue.

@palicka
Copy link

palicka commented Apr 3, 2024

@JaffaKetchup I thought it's better, but it's still not, I think. Sometimes it feels ok, but when I move the map really fast it's not as smooth as it used to be..Lot's of missing squares. To fix it, I need to move around that area slowly, zoom-in, zoom-out to re-load it. It doesn't matter if I use wifi or 5G, android/iphone/emulator, OSM data or mine, it behaves the same.. Please, have a look at the video and let me know what you think(In this example I was just using fingers to move the map, but in real example I also use animation(from flutter_map example) from one point to another, and it's basically the same, so I didn't attach it here, to keep the code as minimal as possible) (it's built in release mode, not debug)

test.mov

here is the full code:

import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_cancellable_tile_provider/flutter_map_cancellable_tile_provider.dart';
import 'package:latlong2/latlong.dart' as latLng;

class MapScreenTest extends StatefulWidget {
  @override
  MapScreenTestState createState() => MapScreenTestState();
}

class MapScreenTestState extends State<MapScreenTest> {
  MapController mapController = MapController();

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Stack(
        children: [
          FlutterMap(
            options: MapOptions(
              minZoom: 7,
              maxZoom: 15,
              cameraConstraint: CameraConstraint.containCenter(
                  bounds: LatLngBounds(latLng.LatLng(50.7003884, 23.7870953),
                      latLng.LatLng(46.050174, 13.599262))),
              initialCenter: latLng.LatLng(48.826594, 19.618809),
              initialZoom: 5,
              backgroundColor: Colors.black,
              interactionOptions: InteractionOptions(
                  flags: InteractiveFlag.doubleTapZoom |
                      InteractiveFlag.drag |
                      InteractiveFlag.flingAnimation |
                      InteractiveFlag.pinchMove |
                      InteractiveFlag.pinchZoom),
            ),
            children: [
              TileLayer(
                  urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
                  tileProvider: CancellableNetworkTileProvider()),
            ],
            mapController: mapController,
          ),
          Column(
            children: [
              Padding(
                padding: EdgeInsets.all(10),
                child: GestureDetector(
                  onTap: () {
                    mapController.move(latLng.LatLng(50.7003884, 23.7870953), 15);
                  },
                  child: Text("ZOOM IN"),
                ),
              ),
              Padding(
                padding: EdgeInsets.all(10),
                child: GestureDetector(
                  onTap: () {
                    mapController.move(latLng.LatLng(50.7003884, 23.7870953), 7);
                  },
                  child: Text("ZOOM OUT"),
                ),
              ),
            ],
          )
        ],
      ),
    );
  }
}

flutter SDK: 3.19.1
flutter_map: ^6.1.0
flutter_map_cancellable_tile_provider: ^2.0.0

I see lot's of various exceptions in the console (I don't know if it's related just to CancellableNetworkTileProvider):

DioException [unknown]: null
Error: HttpException: Connection closed before response was received, uri = https://tile.openstreetmap.org/13/4543/2754.png 
DioException [unknown]: null
Error: HttpException: Connection closed before response was received, uri = https://tile.openstreetmap.org/13/4539/2756.png

or 

DioException [connection error]: The connection errored: Connection failed This indicates an error which most likely cannot be solved by the library.
Error: SocketException: Connection failed (OS Error: Network is unreachable, errno = 101), address = tile.openstreetmap.org, port = 443

This error is not that frequent, but appears sometimes :

Failed to decode image
android.graphics.ImageDecoder$DecodeException: Failed to create image decoder with message 'unimplemented'Input contained an error.
at android.graphics.ImageDecoder.nCreate(Native Method)
at android.graphics.ImageDecoder.-$$Nest$smnCreate(Unknown Source:0)
at android.graphics.ImageDecoder$ByteBufferSource.createImageDecoder(ImageDecoder.java:242)
at android.graphics.ImageDecoder.decodeBitmapImpl(ImageDecoder.java:2015)
at android.graphics.ImageDecoder.decodeBitmap(ImageDecoder.java:2008)
at io.flutter.embedding.engine.g.a(SourceFile:1)
at io.flutter.embedding.engine.FlutterJNI.decodeImage(SourceFile:17)

@JaffaKetchup
Copy link
Member

@palicka Can you try moving the CancellableNetworkTileProvider() into a variable instantiated outside of the build method? Something like final ctp = ...;, then just tileProvider: ctp.

@palicka
Copy link

palicka commented Apr 3, 2024

@JaffaKetchup I moved it, and no change. Then I removed CancellableNetworkTileProvider and move TileLayer into a variable as well, as it was mentioned in a comment above, and then it looked much better - only when used in the minimum code example. When I tried it within my real code, it didn't help, but then I found out there are indeed unnecessary rebuilds sometimes (other than before), I need to re-write it and we will see...
To sum it up, I think there are two issues here - unnecessary rebuilds within my app and tiles not refreshing when using CancellableNetworkTileProvider...
EDIT: migrating back to v5 fixed the issue...

@ethicnology
Copy link

ethicnology commented May 18, 2024

OpenStreetMap & MapBox user here.

Removing CancellableNetworkTileProvider seems to help

No more errors thrown in the logs:

flutter: DioException [connection error]: The connection errored: Dio can't establish a new connection after it was closed. This indicates an error which most likely cannot be solved by the library.

@JaffaKetchup
Copy link
Member

I'm going to close this for now, as there are too many factors here. Occasionally I run into similar slow loading, but I'm not convinced it's flutter_map's fault: on these occasions, I'm trying to do a lot of network requests at once, so maybe something else can't handle it/has to stagger it.

If people still experience this, please open a new issue with an MRE using the OpenStreetMap tile server.

@JaffaKetchup JaffaKetchup closed this as not planned Won't fix, can't repro, duplicate, stale Aug 8, 2024
@JaffaKetchup JaffaKetchup added invalid This bug could not be reproduced or does not exist, or is very low quality and removed needs triage This new bug report needs reproducing and prioritizing labels Aug 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug This issue reports broken functionality or another error invalid This bug could not be reproduced or does not exist, or is very low quality S: core Scoped to the core flutter_map functionality
Projects
Archived in project
Development

No branches or pull requests

8 participants