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

urlTemplate does not get updated with setState #27

Closed
zegkljan opened this issue Feb 10, 2022 · 3 comments
Closed

urlTemplate does not get updated with setState #27

zegkljan opened this issue Feb 10, 2022 · 3 comments

Comments

@zegkljan
Copy link

I ran into an issue that when I setState on my stateful widget (which contains FlutterMap which uses a vector tile layer). I have the url template stored in a variable which can change (e.g. different tile providers). However, when I change the url template in setState, the NetworkVectorTileProvider still uses the old url.

Example

It is identical to the example provided in this repo except for a few changes:

  • there are two constants and one field in _MyHomePageState:
    • constant urlGood is a working url template
    • constant urlBad is a non-working url template
    • field good is a switch choosing one of those urls
  • _urlTemplate returns one of the two urls based on the value of good
  • there is a leading icon button in the app bar which toggles between the two urls
  • the app bar title shows the used url

If the app is started with good = false, the map does not load the tiles, which is correct. When I tap the button, the url should switch, which it does as indicated by the change of the app bar title, but the map tiles still don't load. To make the change, I need to change it to good = true in the code and (hot)restart the app.

Source code (my stadiamaps api key redacted):

import 'dart:developer' as developer;
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
import 'package:vector_map_tiles/vector_map_tiles.dart';
import 'package:vector_tile_renderer/vector_tile_renderer.dart';
// ignore: implementation_imports
import 'package:vector_tile_renderer/src/themes/light_theme.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'vector_map_tiles Example',
      theme: ThemeData.light(),
      home: MyHomePage(title: 'vector_map_tiles Example'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  static const String urlGood = 'https://tiles.stadiamaps.com/data/openmaptiles/{z}/{x}/{y}.pbf?api_key=...';
  static const String urlBad = 'http://localhost:12345/tileserver/map/tiles/{z}/{x}/{y}.pbf';
  bool good = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(_urlTemplate()),
          leading: IconButton(
            onPressed: () => setState(() {
              good = !good;
            }),
            icon: Icon(Icons.swap_horiz),
          ),
        ),
        body: SafeArea(
            child: Column(children: [
          Flexible(
              child: FlutterMap(
            options: MapOptions(
                center: LatLng(49.246292, -123.116226),
                zoom: 10,
                maxZoom: 18,
                interactiveFlags: InteractiveFlag.drag |
                    InteractiveFlag.flingAnimation |
                    InteractiveFlag.pinchMove |
                    InteractiveFlag.pinchZoom |
                    InteractiveFlag.doubleTapZoom,
                plugins: [VectorMapTilesPlugin()]),
            layers: <LayerOptions>[
              // normally you would see TileLayerOptions which provides raster tiles
              // instead this vector tile layer replaces the standard tile layer
              VectorTileLayerOptions(
                  theme: _mapTheme(),
                  backgroundTheme: _backgroundTheme(),
                  tileProviders: TileProviders(
                      {'openmaptiles': _cachingTileProvider(_urlTemplate())})),
            ],
          ))
        ])));
  }

  VectorTileProvider _cachingTileProvider(String urlTemplate) {
    return MemoryCacheVectorTileProvider(
        delegate: NetworkVectorTileProvider(
            urlTemplate: urlTemplate,
            // this is the maximum zoom of the provider, not the
            // maximum of the map. vector tiles are rendered
            // to larger sizes to support higher zoom levels
            maximumZoom: 14),
        maxSizeBytes: 1024 * 1024 * 2);
  }

  _mapTheme() {
    // maps are rendered using themes
    // to provide a dark theme do something like this:
    // if (MediaQuery.of(context).platformBrightness == Brightness.dark) return myDarkTheme();
    return ProvidedThemes.lightTheme();
  }

  _backgroundTheme() {
    return ThemeReader().readAsBackground(lightThemeData(),
        layerPredicate: defaultBackgroundLayerPredicate);
  }

  String _urlTemplate() {
    // Stadia Maps source https://docs.stadiamaps.com/vector/
    // ignore: undefined_identifier
    return good ? urlGood : urlBad;

    // Mapbox source https://docs.mapbox.com/api/maps/vector-tiles/#example-request-retrieve-vector-tiles
    // return 'https://api.mapbox.com/v4/mapbox.mapbox-streets-v8/{z}/{x}/{y}.mvt?access_token=$apiKey',
  }
}
@greensopinion
Copy link
Owner

Jan, thanks for the issue. So far, this plugin hasn't been designed to support your use-case (changing provider URLs.) There are a couple of things that are likely going wrong here:

  • there is an on-disk cache that is likely serving tiles from the original provider
  • the theme ID has to change for the plugin to start using any changed options

There's a chance that you can get it to work if you change the theme ID and the IDs of providers in the theme, including the IDs passed to TileProviders. By changing the theme ID, you'll cause the layer to re-initialize, and by changing the IDs of the providers referenced in the theme, you'll get different cache keys (filenames) for the cached tiles so that the cache doesn't load tiles from the wrong provider.

Let me know how it works out.

@greensopinion
Copy link
Owner

Closing, but would love to know if it worked out for you.

@zegkljan
Copy link
Author

Oh, I'm sorry, I forgot to respond. I don't actually need the switching ability in my app, I just encountered the issue when prototyping and playing around with various map sources. Knowing this is the intended behaviour, I'm ok with it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants