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.
Related
I have an app that implements GoogleMap.
I can go back to the page and get a nice map with premade settings, but I cannot make the map refresh when i press the button and nothing is set beforehand .
The widget is created on Build() and I have a button that gets the lat and lng from geolocator and also cityname reverse lookup.
That calls
SaveGPS() when it is done with everything.
I normally go directly from the Prefs.lat Prefs.lng, but I tried creating member variables _lng and _lat including a _zoom variable so that setState will work.. it does not work. Not sure what I'm doing wrong.
Container(
width: 350,
height: 350,
child: GoogleMap(
markers: Set<Marker>.of(_markers),
mapType: MapType.satellite,
initialCameraPosition:
CameraPosition(zoom: _zoom, target: LatLng(_lat, _lng))),
),
void saveGps() async {
DateTime now = DateTime.now();
var timezoneOffset = now.timeZoneOffset;
Prefs.cityName = _cityName;
Prefs.offset = timezoneOffset.inMinutes / 60;
setState(() {
_lat = Prefs.lat = _position.latitude;
_lng = Prefs.lng = _position.longitude;
_markers.clear();
_markers.add(Marker(
markerId: MarkerId(Prefs.cityName),
position: LatLng(Prefs.lat, Prefs.lng),
infoWindow: InfoWindow(
title: Prefs.cityName,
)));
_zoom = 17;
});
}
I used a map controller and did an animateCamera call. This was the only way to make it work. Other items update with setState but not this one.
// create this in your class
Completer<GoogleMapController> _controller = Completer();
// build method here..
Container(
width: 350,
height: 350,
child: GoogleMap(
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
markers: Set<Marker>.of(_markers),
mapType: MapType.satellite,
initialCameraPosition: CameraPosition(
zoom: 16, target: LatLng(Prefs.lat, Prefs.lng))),
),
When you want to save the new map or display it,
call this code here:
final GoogleMapController controller = await _controller.future;
controller.animateCamera(CameraUpdate.newCameraPosition(
CameraPosition(zoom: 17, target: LatLng(Prefs.lat, Prefs.lng))));
I'm creating simple app, and I need that when the map is tapped a marker should be added. I'm using the onTap property of the google_maps_flutter plugin:
var markers = Set<Marker>();
...
body: Stack(
children: [
Positioned.fill(
child: GoogleMap(
onTap: (LatLng point) {
print(point.longitude.toString());
final snackBar = SnackBar(
content: Text(point.longitude.toString()),
action: SnackBarAction(
label: 'Undo',
onPressed: () {
// Blah.
},
),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
setState(() {
markers.add(Marker(
markerId: MarkerId(point.toString()),
position: point,
infoWindow: InfoWindow(
title: 'I am a marker in ${point}',
),
icon: BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueMagenta),
anchor: Offset(100, 160),
));
});
},
//--
myLocationEnabled: true,
zoomControlsEnabled: false,
markers: markers,
initialCameraPosition: CameraPosition(
target: currentLocation,
),
onMapCreated: (GoogleMapController controller) {
_mapController.complete(controller);
},
),
),
...
I added a print and a snackbar just to make sure I was getting the location information, and works just fine. I don't get any errors anywhere. I also added the map directly into the body (no Stack), and still doesn't do anything. I'm not sure what's happening. I can add more portions of then code if needed.
I have a compass plugin that is giving direction angle, and also location plugin that provide the current location. With these plugins, I use google maps placed under Streambuilder to put my current location as marker and with a line to another location.
The issue I am facing currently is how to rotate the map automatically when the device is rotated, as the first plugin provides direction angle (double value) that gets updated streams as the device rotate.
I wanted to use this angle value to update Google Maps bearing automatically, so that the map rotates accordingly.
I have tired many ways but of no use. Google maps does not respond.
this is my code:
Completer<GoogleMapController> _controller = Completer();
return StreamBuilder(
stream: FlutterQiblah.qiblahStream,
builder: (_, AsyncSnapshot<QiblahDirection> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) return LoadingIndicator();
final qiblahDirection = snapshot.data;
_goToTheLake(qiblahDirection);
return GoogleMap(
mapType: MapType.satellite,
zoomGesturesEnabled: true,
compassEnabled: true,
myLocationEnabled: false,
myLocationButtonEnabled: true,
rotateGesturesEnabled: true,
initialCameraPosition: CameraPosition(
target: position,
zoom: 18,
// bearing: qiblahDirection.direction,
),
markers: Set<Marker>.of(
<Marker>[
widget.meccaMarker,
Marker(
draggable: true,
markerId: MarkerId('Marker'),
position: position,
icon: BitmapDescriptor.defaultMarker,
rotation: qiblahDirection.qiblah,
onTap: () {
_goToTheLake(qiblahDirection);
},
onDragEnd: (LatLng value) {
position = value;
_positionStream.sink.add(value);
},
zIndex: 5,
),
],
),
circles: Set<Circle>.of([
Circle(
circleId: CircleId("Circle"),
radius: 10,
center: position,
fillColor: Theme.of(context).primaryColorLight.withAlpha(100),
strokeWidth: 1,
strokeColor: Theme.of(context).primaryColorDark.withAlpha(100),
zIndex: 3,
)
]),
polylines: Set<Polyline>.of([
Polyline(
polylineId: PolylineId("Line"),
points: [position, QiblahMaps.LatLong],
color: Theme.of(context).primaryColor,
width: 5,
zIndex: 4,
)
]),
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
);
},
)
I have placed this function as future called from inside stream builder in order to change the "bearing" of the map:
final GoogleMapController controller = await _controller.future;
controller.moveCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
bearing: qiblahDirection.direction,
),
),
);
} ```
Try this plugin flutter_compass
use the events to update google map bearing as you wish
along with greeting you, I wanted to ask you if someone has been able to show an InfoWindow in the flutter map maker, or create a container that is floating so that it appears next to the maker, in google map if possible.
new Marker
(
width: 45.0,
height: 45.0,
point: new LatLng(-25.963678, -51.240657),
builder: (ctx) =>
new Container //here infoWindow or Float Container
(
//child: new FlutterLogo(),
child: IconButton
(
icon: Icon(Icons.location_on),
color: Colors.blue,
iconSize: 45.0,
tooltip: "prueba",
onPressed: ()
{
print("test press");
},
)
),
),
Thank you very much for the help as always.
You cannot set a widget as the marker info-window in any of the flutter map plugins. You can only set the title and text of the info-window in the google maps plugins.
You could turn the map info-window off, center the map on the marker if the user clicks on it, and add some code to show a custom dialog at the approximate center of the screen. You would have to use the flutter Stack widget.
Stack(
children:[
Map(
markers: [Marker(/*no info-window*/, onTap: (){
setState((){ _opacity = 1; });
})],
onMapMove: (){
setState((){ _opacity = 0; });
}
),
Opacity(
opacity: _opacity,
child: /*custom info-window*/,
),
],
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
),
Use custom_info_window package as follows:
Step 1: Initialise CustomInfoWindowController.
CustomInfoWindowController _customInfoWindowController = CustomInfoWindowController();
Step 2: Call CustomInfoWindowController's addInfoWindow from marker's onTap function.
Marker(
markerId: MarkerId("marker_id"),
position: _latLng,
onTap: () {
_customInfoWindowController.addInfoWindow(
<YOUR CUSTOM WIDGET>,
_latLng,
);
},
)
Step 3: Use GoogleMap Widget with Stack.
Stack(
children: <Widget>[
GoogleMap(
onTap: (position) {
_customInfoWindowController.hideInfoWindow();
},
onCameraMove: (position) {
_customInfoWindowController.onCameraMove();
},
onMapCreated: (GoogleMapController controller) async {
_customInfoWindowController.googleMapController = controller;
},
markers: _markers,
initialCameraPosition: CameraPosition(
target: _latLng,
zoom: _zoom,
),
),
CustomInfoWindow(
controller: _customInfoWindowController,
height: 75,
width: 150,
offset: 50,
),
],
)
Call _customInfoWindowController.hideInfoWindow(); inside GoogleMap's onTap to hide CustomInfoWindow when clicking on map but not on the marker.
Call _customInfoWindowController.onCameraMove(); to maintain CustomInfoWindow's position relative to marker. [IMPORTANT]
Assign _customInfoWindowController.googleMapController = controller; inside onMapCreated. [IMPORTANT]
Add CustomInfoWindow as next child to float this on top GoogleMap.
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