The shape layer in the Syncfusion Flutter Maps widget renders geographical areas from the GeoJSON data. Another popular style of map visualization, though, is to load map tiles or images from web map tile services (WMTS) such as Bing Maps, OpenStreetMaps, Google Maps, and TomTom. We are excited to inform you that this tile layer rendering support has been included in our Syncfusion Flutter Maps widget in the Essential Studio® 2020 Volume 3 release. This allows rendering tiles from these tile providers.
In this blog, we will look at the tile layer and how to add it to the Flutter Maps widget to render tiles from providers like OpenStreetMap and Bing Maps. We will also briefly talk about adding markers and enabling the zooming and panning features in the tile layer.
The web map tile services, like OpenStreetMaps and Bing Maps, provide a single tile of 256 * 256 pixels for every request for the region we ask for. Our newly introduced tile layer does this efficiently and arranges multiple tiles in appropriate positions to render the complete map.
You can then interact with this layer to have a closer look at a specific region by pinching and then panning across the map to check the places nearby.
If you are looking for a cost-effective map tile provider, then OpenStreetMap will best suit you. This is a widely used, free WMTS for mobile applications and websites, meeting the requirement of basic map rendering.
Disclaimer: OpenStreetMaps may be used free of cost. However, we recommend you check the licensing terms on their official website to make sure. |
The following code example explains the procedure to add the tile layer to the Maps widget with OpenStreetMap.
@override Widget build(BuildContext context) { return SfMaps( layers: [ MapTileLayer( urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', initialZoomLevel: 2, initialFocalLatLng: MapLatLng(28.644800, 77.216721), ), ], ); }
The urlTemplate property accepts the URL in WMTS format, i.e. {z} zoom level and {x} and {y} tile coordinates.
This URL might vary slightly depending on the providers. The formats can be:
https://exampleprovider/{z}/{x}/{y}.png,
https://exampleprovider/z={z}/x={x}/y={y}.png,
https://exampleprovider/z={z}/x={x}/y={y}.png?key=subscription_key, etc.
We will replace the {z}, {x}, and {y} internally based on the current focal point and the zoom level.
A subscription key may be needed for some of the providers, like Bing Maps. Please include it in the urlTemplate itself.
Note: The format may vary among map providers. You can check the exact URL format needed for the providers on their official websites.
At the lowest zoom level (level 0), the map is 256 x 256 pixels and the whole world map will be rendered as a single tile. In each consecutive level, the map width and height grow by a factor of 2. So level 1 is 512 x 512 pixels with four tiles ((0, 0), (0, 1), (1, 0), (1, 1) where 0 and 1 are {x} and {y} in the urlTemplate), level 2 is 2048 x 2048 pixels with 8 tiles (from (0, 0) to (3, 3)), and so on.
However, the number of tiles needed in the current viewport alone will be requested and rendered initially. If you have enabled zooming and panning, other required tiles will be requested on demand and rendered during the interaction.
If you want to enable dynamic zooming and panning, please refer to the following code snippet. You can zoom in on the tile layer for a closer look at a specific region by:
MapZoomPanBehavior _zoomPanBehavior; @override void initState() { _zoomPanBehavior = MapZoomPanBehavior(); super.initState(); } @override Widget build(BuildContext context) { return SfMaps( layers: [ MapTileLayer( urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', initialZoomLevel: 2, initialFocalLatLng: MapLatLng(28.644800, 77.216721), zoomPanBehavior: _zoomPanBehavior, ), ], ); }
Note: MapZoomPanBehavior objects are expected to be long-lived, not recreated with each build. |
While zooming and panning, new tiles will be requested and rendered. After the interaction, the current zoom level and the focal point can be obtained from the MapZoomPanBehavior.zoomLevel and MapZoomPanBehavior.focalLatLng properties, respectively. These properties can also be set dynamically.
Adding Bing Maps to the MapTileLayer is the same as adding OpenStreetMaps except for the way we set the urlTemplate property.
For Bing Maps, an additional step is required. The format of the required URL varies from the other tile services. So, we have added a top-level getBingUrlTemplate function that returns the URL in the required format. You can use the URL returned from this function to set it to the urlTemplate property.
A subscription key is needed for Bing Maps. It has to be added to the urlTemplate itself. Also, Bing Maps provides map types like Road, Aerial, and AerialWithLabelsOnDemand. These types, too, should be added in the urlTemplate itself, as shown in the following code example. You can check the official websites of the tile providers to learn about the available types and the code for them.
Other than these differences, everything is exactly the same among all the different types of tile providers.
@override Widget build(BuildContext context) { String bingKey = 'SUBSCRIPTION_KEY'; return Scaffold( body: Center( child: Container( child: FutureBuilder( future: getBingUrlTemplate( 'https://dev.virtualearth.net/REST/V1/Imagery/Metadata/AerialWithLabelsOnDemand?output=json&uriScheme=https&include=ImageryProviders&key='+bingKey), builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) { final String urlTemplate = snapshot.data; return SfMaps( layers: [ MapTileLayer( initialZoomLevel: 2, initialFocalLatLng: MapLatLng(28.644800, 77.216721), urlTemplate: urlTemplate, ), ], ); } else { return const Center(child: CircularProgressIndicator()); } }, )), ), ); }
In addition to OpenStreetMap and Bing Maps, it is possible to render other tile providers like Google Maps or TomTom, which follow the WMTS format. For example, tiles from TomTom can be rendered like the following code.
Note: You have to append the subscription key at the end of the URL as we did for Bing Maps.
@override Widget build(BuildContext context) { return SfMaps( layers: [ MapTileLayer( urlTemplate: 'https://api.tomtom.com/map/1/tile/basic/main/{z}/{x}/{y}.png?key=SUBSCRIPTION_KEY', initialZoomLevel: 2, initialFocalLatLng: MapLatLng(28.644800, 77.216721), ), ], ); }
As in the shape layer, you can add markers in the tile layer with exactly the same approach. They can be used to denote specific latitude and longitude in the tile layer as required by the user. You can use the built-in shapes (circles, diamonds, squares, and triangles) or any type of custom widget as a marker.
The markerBuilder callback will be called the number of times the value specified in the initialMarkersCount property.
Note: If you want to dynamically add the markers, please refer to this section. |
Refer to the following code example.
class _MyHomePageState extends State { List _data; @override void initState() { _data = const [ Model('Brazil', -14.235004, -51.92528), Model('Germany', 51.16569, 10.451526), Model('Australia', -25.274398, 133.775136), Model('India', 20.593684, 78.96288), Model('Russia', 61.52401, 105.318756) ]; super.initState(); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Container( child: SfMaps( layers: [ MapTileLayer( urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', initialZoomLevel: 2, initialFocalLatLng: MapLatLng(28.644800, 77.216721), initialMarkersCount: 5, markerBuilder: (BuildContext context, int index) { return MapMarker( latitude: _data[index].latitude, longitude: _data[index].longitude, iconColor: Colors.white, iconStrokeColor: Colors.black, iconStrokeWidth: 2, ); }, ), ], ), ), ), ); } } class Model { const Model(this.country, this.latitude, this.longitude); final String country; final double latitude; final double longitude; }
In this blog post, we walked you through the new tile layer support included in the Flutter Maps widget in our 2020 Volume 3 release. You can download the latest setup here. Try out this new feature and share your feedback in the comments section below.
Check out the complete user guide and see our samples in this GitHub location. Additionally, you can check out our demo apps on Google Play Store, App Store, and our website.
If you need a new widget for the Flutter framework or new features in our existing widgets, you can contact us through our support forums, Direct-Trac, or feedback portal. As always, we are happy to assist you!