Avoid blank space on flutter_map with OpenStreetMap in a Flutter application - flutter

I have a flutter application and I added the library flutter_map to add an OpenStreetMap map in the application.
This is my code to add the map:
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
FlutterMap(
options: MapOptions(
onPositionChanged: (position, hasGesture) =>
_handleOnPositionChanged(position, hasGesture, context),
center: center,
zoom: zoom,
onTap: (location) {
print(location);
},
),
layers: [
TileLayerOptions(
urlTemplate:
'https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png',
subdomains: ['a', 'b', 'c'],
keepBuffer: 6,
backgroundColor: Colors.white,
tileSize: 256,
cachedTiles: true,
),
new MarkerLayerOptions(markers: getListOfMarkers()),
],
)
],
)
);
}
But in the application, when we zoom or move in the map, the loading take some time and so a blank space is displayed on the entire map or just in some places.
Do you know how I can do to avoid these blank spaces ?
Edit
I tested with many tiles servers and it's always the same.
But with google maps and the map_view library, I don't have this problem...

This is a limitation of the library at the moment.
https://github.com/johnpryan/flutter_map/issues/79

Related

How to rebuild only the changed content with Getx?

I have a map (Using flutter maps) that presents some layers that can be enabled or disabled by the user. For that I have two buttons to disable and enable these layers, and I'm controlling their state with a getX controller.
RxBool radar = false.obs;
RxBool satelite = false.obs;
I want when the user clicks on one of these buttons the layer related to it activates or deactivates.
So far so good, however, I want that when he activates the radas layer, the other layer remains without re-rendering and vice versa. What is happening is that when the user activates/deactivates a layer, the map component re-renders and ends up causing the other layer to reload its content.
GetX<MenuController>(
init: MenuController(),
builder: (menuController) {
return FlutterMap(
options: MapOptions(),
layers: [
if (menuController.radar.value) //Handle True or false
TileLayerOptions(
wmsOptions: WMSTileLayerOptions(
baseUrl: "MyUrlLocales",
),
),
if (menuController.satelite.value) //Handle True or false
TileLayerOptions(
wmsOptions: WMSTileLayerOptions(
baseUrl: "MyUrlCars",
),
),
],
);
},
),
Obviously this happens because you have given GetX to the whole FlutterMap
create a controller for MenuController
var _controller = Get.put(MenuController());
and
FlutterMap(options: MapOptions(), children: [
Obx(
() => Visibility(
visible: _menuController.radar.value,
child: TileLayerWidget(
options: TileLayerOptions(
wmsOptions: WMSTileLayerOptions(
baseUrl: "MyUrlLocales",
),
),
),
),
),
Obx(
() => Visibility(
visible: menuController.satelite.value,
child: TileLayerWidget(
options: TileLayerOptions(
wmsOptions: WMSTileLayerOptions(
baseUrl: "MyUrlCars",
),
),
),
),
),
]),
You can do something like this :
var _controller = MenuController();
And in your build function let's say you want to update one widget runtime you can do something like this,
FlutterMap(
options: MapOptions(),
layers: [
if (menuController.radar.value) //Handle True or false
Obx(()=> TileLayerOptions(
wmsOptions: WMSTileLayerOptions(
baseUrl: "MyUrlLocales",
),
)),
if (menuController.satelite.value) //Handle True or false
TileLayerOptions(
wmsOptions: WMSTileLayerOptions(
baseUrl: "MyUrlCars",
),
),
],
)
So you need to wrap the widget with Obx() which you want to update runtime.

Display multiple points in map using flutter map

Im a newbie in flutter. I'm making a project and I want to know how can I render the list of points using latlong plugin from flutter inside flutter map. I was able to render the first point but unable to show the other points in the list. Can you please guide me or correct my code.
Widget _mapBuilder(BuildContext context) {
final List<LatLng> _mapPoints = [
LatLng(9.7, 123.1),
LatLng(10.7, 124.9),
LatLng(11.7, 125.05)
];
return FlutterMap(
options: MapOptions(
center: _mapPoints[1],
zoom: 16.0,
),
layers: [
TileLayerOptions(
urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
subdomains: ['a', 'b', 'c'],
attributionBuilder: (_) {
return const Text("© OpenStreetMap contributors");
},
),
MarkerLayerOptions(markers: [
for (var point in _mapPoints) ...[
Marker(
width: 15.0,
height: 15.0,
point: point,
builder: (ctx) => Container(
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(50.0),
border: Border.all(color: Colors.white, width: 2)),
),
),
]
]),
],
);
You can create markers list like this and use that list in Markers parameters required in marker layer.
final List<LatLng> _mapPoints = [
LatLng(12.9, 77.8),
LatLng(12.1, 77.2),
LatLng(12.5,77.4),
];
for (LatLng i in _mapPoints){
lister.add(Marker(point: i, builder:(ctx) =>
Container(child: Column(
children: [
Icon(Icons.add,size: 40,),
Text("data",style: TextStyle(fontSize: 12),)
],
),
)));
}

In Flutter, how to add a marker on openstreetmap by tapping a screen or clicking a button?

It should be very simple, but I can't seem to find how to do it.
In Flutter / openstreetmap, I need to: (1) put a marker on the map by tapping a map and/or clicking a button; (2) connect two of these markers with a line(s).
Any ideas would be appreciated.
1) For adding markers:
List<Marker> markers = [];
Inside Build method:
FlutterMap(
mapController: _mapController,
options: MapOptions(
center: LatLng(41.238250, 69.342939),
zoom: 9.6,
onTap: (latlng) {
setState(() {
markers.add(
Marker(
width: 150.0,
height: 150.0,
point: latlng,
builder: (ctx) => const Icon(
Icons.location_on,
color: Colors.red,
size: 35.0,
),
),
);
});
}),
layers: [
MarkerLayerOptions(
markers: [
for (int i = 0; i < markers.length; i++) markers[i]
],
),
],
),
2) If you want to get directions between two points, you can use this service, it's free. http://project-osrm.org
The link I am using for getting directions between two points.
https://api.openrouteservice.org/v2/directions/driving-car?api_key=$OSRM_API_KEY&start=${origin.longitude},${origin.latitude}&end=${destination.longitude},${destination.latitude}
It takes, API Key and starting point lat-long and ending point lat-long.
Response gives you multiple lat-long points, and if you give these points to polyLine parameter of map, it will draw you a road line between two points
Let me know if you need any clarification on some part of my answer

Add custom marker to map in flutter with mapbox plugin

to provide a better ui to my app I need to display a small card with the user name and a small circle with the user picture or avatar.
Is there any way to do it with the flutter-mapbox-gl plugin?
The best solutions for that is inside mapbox-gl package example folder :
https://github.com/tobrun/flutter-mapbox-gl/blob/master/example/lib/place_symbol.dart
here small example what you can do
Widget build(BuildContext context) {
mapController.addSymbol(
SymbolOptions(
geometry: LatLng(-33.86711, 151.1947171),
iconImage: "assets/images/ur_image.png",
),
);
return Scaffold(
body: Center(
child: SizedBox(
width: double.infinity,
height: 300.0,
child: MapboxMap(
styleString: themeControl.themeSet.mapStyle,
onMapCreated: _onMapCreated,
onStyleLoadedCallback: _onStyleLoaded,
zoomGesturesEnabled: _zoomGesturesEnabled,
myLocationEnabled: _myLocationEnabled,
initialCameraPosition: const CameraPosition(
target: LatLng(-33.852, 151.211),
zoom: 11.0,
),
),
),
),
);}
After fighting with this for 2h that's what I came up with:
Add to widget tree:
Center(
child: SizedBox(
width: double.infinity,
height: 300,
child: MapboxMap(
initialCameraPosition: CameraPosition(
target: latLng,
zoom: 13,
),
onMapCreated: onMapCreated,
accessToken: mapboxPublicToken,
),
),
)
Some remarks:
latLng is e.g. LatLng(-33.852, 151.211)
the SizedBox was needed for my case to avoid Horizontal viewport was given unbounded height error
Then to add the actual marker (which in my case should be just in the center of the map, same as the target of CameraPosition):
Future<Uint8List> loadMarkerImage() async {
var byteData = await rootBundle.load("images/poi.png");
return byteData.buffer.asUint8List();
}
void onMapCreated(MapboxMapController controller) async {
var markerImage = await loadMarkerImage();
controller.addImage('marker', markerImage);
await controller.addSymbol(
SymbolOptions(
iconSize: 0.3,
iconImage: "marker",
geometry: latLng,
iconAnchor: "bottom",
),
);
}
A few remarks:
I had no luck with the "built in" markers. This should be the list of supported markers but I couldn't get them to work
images/poi.png in my case is just a 256x256 PNG image with transparent background
for rootBundle to work you need to import 'package:flutter/services.dart'
iconMarker tells how to position the marker. In my case it is kind of a pin icon 📍 so I want the "center bottom" to be where the lat/lng is.
iconSize you need to find out the best size. In my case I needed to hot restart every time I did a change which was annoying…
I expanded this answer into a blog post to cover also some problems I faced during installation.

Getting the Position Stream from Geolocator into StreamBuilder Flutter

I'm just starting with Flutter and very new to State/Stream/BloC concepts. I'm trying to update a FlutterMap center based on the coordinates from a Geolocator()'s Position stream. At the moment I'm just calling setState() updating a variable with the new value, but next I'll be trying using the Bloc pattern instead..
Now, as well as I set the center on the coordinates of the Position coming from the stream I also set a Marker on the same coordinates. I was expecting to see a Steady marker in the center of the screen and a moving map underneath but is the opposite.. the Map is steady and the Icon is moving.. Can you see where I got it wrong?
Also, for the stream: property of the StreamBuilder how would I get to pass the stream from Geolocator() as an event?
Many thanks for your time and help.
This is the Stream:
StreamSubscription positionStream = _geolocator
.getPositionStream(locationOptions)
.listen((Position position) {
setState(() {
_position = position;
});
print(
_position.latitude.toString() + ',' + _position.longitude.toString()); // ok
});
And this is the Map and the Marker:
Container(
height: 670,
width: 370,
child: FlutterMap(
options: MapOptions(
center: LatLng(_position.latitude, _position.longitude),
minZoom: 16.0,
maxZoom: 19.0,
),
layers: [
TileLayerOptions(
// urlTemplate:
// 'https://api.openrouteservice.org/mapsurfer/{z}/{x}/{y}.png?api_key=omitted',
urlTemplate:
'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
subdomains: ['a', 'b', 'c'],
keepBuffer: 20),
new MarkerLayerOptions(
markers: [
new Marker(
point: new LatLng(
_position.latitude, _position.longitude),
height: 200,
width: 200,
builder: (context) => IconButton(
icon: Icon(Icons.location_on),
color: Colors.red,
iconSize: 60,
onPressed: () {
print('icon tapped');
},
)),
],
),
],
),
),
Found the problem. I didn't assign any MapController().
So I created one and assign it to the FlutterMap. Inside the setState() I added _controller.move(LatLng(_position.latitude, _position.longitude), 16.0); and now the behaviour is the expected one. Steady Icon in the center of the screen and a moving map underneath.
Hope this will help others.
But still I will try to achieve the same with a BLoC pattern.
Cheers