flutter_osm_plugin은 Android, iOS 및 웹에서 Flutter 애플리케이션에 대한 네이티브 OpenStreetMap 통합을 제공합니다. 개발자들이 사용자 정의 마커, 오버레이 및 지도 컨트롤을 갖춘 상호작용식 지도를 삽입할 수 있도록 합니다. 이 플러그인은 오픈스트리트맵의 오픈소스 데이터를 활용하여 상용 지도 서비스의 가벼운 대안을 제공합니다. 위치 추적, 주소 검색 및 경로 시각화 기능을 지원하며, 상용 API에 의존하지 않고 지도 기반 기능이 필요한 앱에 적합합니다. 이 플러그인은 활발히 유지보수되고 모든 주요 플랫폼과 호환됩니다.
recuperate information (instruction/duration/distance) of the current road (Android/iOS/web)
draw Road manually (Android/iOS/web)
draw multiple Roads (Android/iOS/web)
ClickListener on Marker (Android/iOS/web)
ClickListener on Map (Android/iOS/web)
calculate distance between 2 points
address suggestion
draw shapes (Android/iOS/web)
simple dialog location picker (Android/iOS)
listen to region change (Android/iOS/Web)
set custom tiles (Android/iOS/Web)
satellite tiles preset (Android/iOS/Web)
vector tiles support via styleURL (Android/iOS/Web)
external tracking control (Android/iOS/Web)
Getting Started
Installing
Add the following to your pubspec.yaml file:
dependencies:
flutter_osm_plugin: ^2.0.2
Integration with Hooks
To use our map library with Flutter_Hooks library use our new extension library
https://pub.dev/packages/osm_flutter_hooks
many thanks for @ben-xD
Migration to 2.0.0
Breaking: Android map engine switched from osmdroid to mapscore (openmobilemaps).
iOS minimum version raised to 15.6.
Android:
compileSdk must be 36
minSdk must be 32
Kotlin Gradle Plugin (KGP) is now required
The map rendering engine is now mapscore (openmobilemaps) instead of osmdroid
Custom tiles, markers, roads, and shapes are now handled by the new MapscoreFlutterOsmView
iOS:
Minimum iOS version is now 15.6
Swift Package Manager (SPM) is the preferred dependency manager
The OSMMapCoreIOSFramework dependency has been updated to the latest revision
What's new:
MapController.satelliteLayer — named constructor with ArcGIS World Imagery satellite tiles
CustomTile.satellite — preset for satellite tiles
CustomTile.openFreeMap — preset for OpenFreeMap vector tiles with styleURL
styleURL and isVector fields on CustomTile for vector tile support (Android/iOS/Web)
useExternalTracking parameter on all MapController constructors for external location control
Dotted road style and border customization in drawRoadManually (iOS)
Improved location permission handling with explicit checks and user-friendly errors
myLocation() now requests location permission on Android if not granted
Migration to 0.41.2 (Android Only)
open file build.gradle inside android file
* change kotlin version from `1.5.21` to `1.7.20`
* change gradle version from `7.0.4` to `7.1.3`
* change compileSdkVersion to 33
Migration to 0.34.0 (Android Only)
if you are using this plugin before Flutter 3
you should make some modification in build.gradle before that run flutter clean && flutter pub get
open file build.gradle inside android file
* change kotlin version from `1.5.21` to `1.6.21`
* change gradle version from `7.0.2` to `7.1.3` or `7.0.4`
* change compileSdkVersion to 32
Migration to 0.16.0 (Android Only)
if you are using this plugin before Flutter 2
you should make some modification in build.gradle before that run flutter clean && flutter pub get
open file build.gradle inside android file
* change kotlin version from `1.4.21` to `1.5.21`
* change gradle version from `4.1.1` to `7.0.2`
Web Integration
Technical Issue: Event Capture in Web
On web platforms, UI elements above the map cannot receive click events. This occurs because the OSM map renders as an HtmlElementView that captures all pointer events before they reach Flutter widgets.
Common Issues
Dialogs appear but buttons are unclickable
UI elements over the map don't receive click events
The map's "grab" cursor shows even when hovering over your UI elements
Solution: Add pointer_interceptor
dependencies:pointer_interceptor:^0.10.1+2
Example: Basic Marker Click Handling with PointerInterceptor
body: OSMFlutter(
controller: mapController,
osmOption: OSMOption(
zoomOption: ZoomOption(initZoom: 12),
),
onGeoPointLongPressed:(point){
print("long press on $point");
},
onGeoPointClicked: (point) {
// When a marker is clicked, show a popup with PointerInterceptorshowDialog(
context: context,
builder: (context) => PointerInterceptor(
// The PointerInterceptor is crucial here for web platforms
child: AlertDialog(
title: Text('Marker Clicked'),
content: Text('Location: ${point.latitude}, ${point.longitude}'),
actions: [
TextButton(
// This button will work on web thanks to PointerInterceptor
onPressed: () => Navigator.pop(context),
child: Text('Close'),
),
],
),
),
);
},
),
Without the PointerInterceptor wrapping the dialog, the buttons would be unresponsive on web platforms because the map underneath would capture all the click events before they reach your UI elements.
Note
using the default constructor, you should use initMapWithUserPosition or initPosition
if you want the map to initialize using static position use the named constructor withPosition
or if you want to initialize the map with user position use withUserPosition
// default constructorMapController controller = MapController(
initPosition: GeoPoint(latitude: 47.4358055, longitude: 8.4737324),
areaLimit: BoundingBox(
east: 10.4922941,
north: 47.8084648,
south: 45.817995,
west: 5.9559113,
),
);
// or set manually init positionfinal controller = MapController.withPosition(
initPosition: GeoPoint(
latitude: 47.4358055,
longitude: 8.4737324,
),
);
// init the position using the user locationfinal controller = MapController.withUserPosition(
trackUserLocation: UserTrackingOption(
enableTracking: true,
unFollowUser: false,
)
)
// init the position using the user location and control map from outsidefinal controller = MapController.withUserPosition(
trackUserLocation: UserTrackingOption(
enableTracking: true,
unFollowUser: false,
),
useExternalTracking: true
)
2) Dispose
controller.dispose();
3) Properties of default MapController
MapController has named constructors: MapController.withPosition, MapController.withUserPosition, MapController.customLayer, MapController.cyclOSMLayer, MapController.publicTransportationLayer, MapController.satelliteLayer to control initialization of the Map
Properties
Description
initMapWithUserPosition
(UserTrackingOption?) initialize map with user position
initPosition
(GeoPoint) if it isn't null, the map will be pointed at this position
areaLimit
(Bounding) set area limit of the map (default BoundingBox.world())
customLayer
(CustomTile) set customer layer using different osm server , this attribute used only with named constructor customLayer
useExternalTracking
(bool) if true, we will disable our logic to show userLocation marker or to move to the user position, only receive user location updates
3.1) Custom Layers with MapController
To change the tile source in OSMFlutter, you should used our named constructor customLayer, see the example below
for iOS,you should add those line in your info.plist file
<key>NSLocationWhenInUseUsageDescription</key>
<string>any text you want</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>any text you want</string>
from version 0.40.0 we can call only enableTracking will animate to user location
without need to call currentLocation
when enableStopFollow is true,map will not be centered if the user location changed
you can disable rotation of personIcon using [disableUserMarkerRotation] (default: false)
PS : static position cannot be removed by this method
16) Draw road,recuperate instructions ,distance in km and duration in sec
you can add an middle position to pass your route through them
change configuration of the road in runtime
zoom into the region of the road
change the type of the road that user want to use
/// to drawawait controller.drawCircle(CircleOSM(
key: "circle0",
centerPoint: GeoPoint(latitude: 47.4333594, longitude: 8.4680184),
radius: 1200.0,
color: Colors.red,
borderColor:Colors.green,
strokeWidth: 0.3,
)
);
/// to remove Circle using Keyawait controller.removeCircle("circle0");
/// to remove All Circle in the mapawait controller.removeAllCircle();
Rect
/// to drawawait controller.drawRect(RectOSM(
key: "rect",
centerPoint: GeoPoint(latitude: 47.4333594, longitude: 8.4680184),
distance: 1200.0,
color: Colors.red.withOpacity(0.4),
borderColor:Colors.green,
strokeWidth: 0.3,
));
/// to remove Rect using Keyawait controller.removeRect("rect");
/// to remove All Rect in the mapawait controller.removeAllRect();
remove all shapes in the map
await controller.removeAllShapes();
Interfaces:
OSMMixinObserver :
contain listener methods to get event from native map view like when mapIsReady,mapRestored
you should add ths line controller.addObserver(this); in initState
override mapIsReady to implement your own logic after initialisation of the map
mapIsReady will replace listenerMapIsReady
Methods
Description
mapIsReady
(callback) Should be override this method, to get notified when map is ready to go or not
mapRestored
(callback) Should be override this method, to get notified when map is restored you can also add you backup
onSingleTap
(callback) Called when the user makes single click on map
onLongTap
(callback) Called when the user makes long click on map
onMarkerClicked
(callback) Called when the user makes single click on marker
onMarkerLongPress
(callback) Called when the user makes long press click on marker
onRegionChanged
(callback) Notified when map is change region (on moves)
onRoadTap
(callback) Notified when user click on the polyline (road)
onLocationChanged
(callback) Notified when user location changed
** example
classYourOwnStateWidgetextendsState<YourWidget> withOSMMixinObserver{
//etc@overridevoidinitState() {
super.initState();
controller.addObserver(this);
}
@overrideFuture<void> mapIsReady(bool isReady) async {
if (isReady) {
/// put you logic
}
}
@overrideFuture<void> mapRestored() async {
super.mapRestored();
/// TODO
}
@overridevoidonSingleTap(GeoPoint position) {
super.onSingleTap();
/// TODO
}
@overridevoidonLongTap(GeoPoint position) {
super.onLongTap();
/// TODO
}
@overridevoidonRegionChanged(Region region) {
super.onRegionChanged();
/// TODO
}
@overridevoidonRoadTap(RoadInfo road) {
super.onRoadTap();
/// TODO
}
@overridevoidonLocationChanged(GeoPoint userLocation) {
super.onLocationChanged();
/// TODO
}
}
OSMFlutter
Properties
Description
mapIsLoading
(Widget) show custom widget when the map finish initialization
osmOption
(OSMOption) used to configure OSM Map such as zoom,road,userLocationMarker
onGeoPointClicked
(callback) listener triggered when marker is clicked ,return current geoPoint of the marker
onGeoPointLongPress
(callback) listener triggered when marker is long press clicked ,return current geoPoint of the marker
onLocationChanged
(callback) it is fired when you activate tracking and user position has been changed
onMapMoved
(callback) it is each the map moved user handler or navigate to another location using APIs
onMapIsReady
(callback) listener trigger to get map is initialized or not
OSMOption
Properties
Description
mapIsLoading
(Widget) show custom widget when the map finish initialization
trackMyPosition
enable tracking user position.
showZoomController
show default zoom controller.
userLocationMarker
change user marker or direction marker icon in tracking location
markerOption
configure marker of osm map
zoomOption
set configuration for zoom in the Map
roadConfiguration
(RoadOption) set default color,width,borderColor,borderWdith for polylines
staticPoints
List of Markers you want to show always ,should every marker have unique id
showContributorBadgeForOSM
(bool) enable to show copyright widget of osm in the map
enableRotationByGesture
(bool) enable to rotation gesture for map, default: false
showDefaultInfoWindow
(bool) enable/disable default infoWindow of marker (default = false)
isPicker
(bool) enable advanced picker from init of the map (default = false)
ZoomOption
Properties
Description
stepZoom
set step zoom to use in zoomIn()/zoomOut() (default 1)
initZoom
set init zoom level in the map (default 10)
maxZoomLevel
set maximum zoom level in the map (2 <= x <= 19)
minZoomLevel
set minimum zoom level in the map (2 <= x <= 19 )
Custom Controller
To create your own MapController to need to extends from BaseMapController,
if you want to make a custom initialization to need to call init() and put your code after super.init()
example
classCustomMapControllerextendsBaseMapController{
@overridevoiddispose() {
/// TODO put you logic heresuper.dispose();
}
@overridevoidinit() {
super.init();
/// TODO put you logic here
}
}
STATIC Map:
using OSMViewer we can show static map that contain on location
if you faced build error in fresh project you need to follow those instruction #40
1) remove flutter_osm_plugin from pubspec, after that pub get
2) open android module in android studio ( right click in name of project -> flutter-> open android module in android studio)
3) update gradle version to 4.1.1 ( IDE will show popup to make update)
4) update kotlin version to 1.4.21 & re-build the project
5) re-add flutter_osm_plugin in pubspec , pub get ( or flutter clean & pub get )
Before you publish your application using this library,
you should take care about copyright of openStreetMap Data,
that's why i add CopyrightOSMWidget see example and this issue #101