├── .fvmrc ├── example ├── lib │ ├── design │ │ └── ui_widgets.dart │ ├── util │ │ ├── string_util.dart │ │ ├── location_util.dart │ │ ├── example_location_tracker.dart │ │ └── alert_util.dart │ └── pages │ │ └── others │ │ ├── example_bottom_main.dart │ │ └── example_page_data.dart ├── ios │ ├── Runner │ │ ├── Runner-Bridging-Header.h │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── 100.png │ │ │ │ ├── 114.png │ │ │ │ ├── 120.png │ │ │ │ ├── 128.png │ │ │ │ ├── 144.png │ │ │ │ ├── 152.png │ │ │ │ ├── 16.png │ │ │ │ ├── 167.png │ │ │ │ ├── 172.png │ │ │ │ ├── 180.png │ │ │ │ ├── 196.png │ │ │ │ ├── 20.png │ │ │ │ ├── 216.png │ │ │ │ ├── 256.png │ │ │ │ ├── 29.png │ │ │ │ ├── 32.png │ │ │ │ ├── 40.png │ │ │ │ ├── 48.png │ │ │ │ ├── 50.png │ │ │ │ ├── 512.png │ │ │ │ ├── 55.png │ │ │ │ ├── 57.png │ │ │ │ ├── 58.png │ │ │ │ ├── 60.png │ │ │ │ ├── 64.png │ │ │ │ ├── 66.png │ │ │ │ ├── 72.png │ │ │ │ ├── 76.png │ │ │ │ ├── 80.png │ │ │ │ ├── 87.png │ │ │ │ ├── 88.png │ │ │ │ ├── 92.png │ │ │ │ └── 1024.png │ │ │ └── LaunchImage.imageset │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ ├── README.md │ │ │ │ └── Contents.json │ │ ├── AppDelegate.swift │ │ ├── Base.lproj │ │ │ └── Main.storyboard │ │ └── Info.plist │ ├── Flutter │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── AppFrameworkInfo.plist │ ├── Runner.xcodeproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── swiftpm │ │ │ └── Package.resolved │ ├── .gitignore │ ├── Podfile.lock │ └── Podfile ├── android │ ├── gradle.properties │ ├── app │ │ ├── src │ │ │ ├── main │ │ │ │ ├── ic_launcher-playstore.png │ │ │ │ ├── res │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ │ ├── values │ │ │ │ │ │ ├── ic_launcher_background.xml │ │ │ │ │ │ ├── string.xml │ │ │ │ │ │ └── styles.xml │ │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ │ │ ├── ic_launcher.xml │ │ │ │ │ │ └── ic_launcher_round.xml │ │ │ │ │ ├── drawable │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── drawable-v21 │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ └── values-night │ │ │ │ │ │ └── styles.xml │ │ │ │ ├── kotlin │ │ │ │ │ └── dev │ │ │ │ │ │ └── note11 │ │ │ │ │ │ └── flutter_naver_map │ │ │ │ │ │ └── flutter_naver_map_example │ │ │ │ │ │ └── MainActivity.kt │ │ │ │ └── AndroidManifest.xml │ │ │ ├── debug │ │ │ │ └── AndroidManifest.xml │ │ │ └── profile │ │ │ │ └── AndroidManifest.xml │ │ └── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ ├── .gitignore │ ├── build.gradle │ └── settings.gradle ├── assets │ └── ground_img.png ├── analysis_options.yaml ├── integration_test │ ├── golden │ │ └── location_overlay_sync_test.png │ ├── all_tests.dart │ ├── clustering_test.dart │ ├── camera_update_test.dart │ └── now_camera_position_test.dart ├── README.md ├── .gitignore └── pubspec.yaml ├── android ├── settings.gradle ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ └── kotlin │ │ └── dev │ │ └── note11 │ │ └── flutter_naver_map │ │ └── flutter_naver_map │ │ ├── controller │ │ ├── overlay │ │ │ ├── OverlaySender.kt │ │ │ └── handler │ │ │ │ ├── ClusterMarkerHandler.kt │ │ │ │ ├── ClusterableMarkerHandler.kt │ │ │ │ ├── GroundOverlayHandler.kt │ │ │ │ ├── InfoWindowHandler.kt │ │ │ │ ├── CircleOverlayHandler.kt │ │ │ │ ├── PolylineOverlayHandler.kt │ │ │ │ └── PolygonOverlayHandler.kt │ │ ├── NaverMapSenderExtensions.kt │ │ └── NaverMapControlSender.kt │ │ ├── model │ │ ├── exception │ │ │ └── NFlutterException.kt │ │ ├── enum │ │ │ ├── NOverlayImageMode.kt │ │ │ └── NOverlayType.kt │ │ ├── map │ │ │ ├── info │ │ │ │ ├── NSymbolInfo.kt │ │ │ │ ├── NPickableInfo.kt │ │ │ │ ├── NOverlayQuery.kt │ │ │ │ └── NOverlayInfo.kt │ │ │ ├── overlay │ │ │ │ ├── overlay │ │ │ │ │ ├── LazyOrAddableOverlay.kt │ │ │ │ │ ├── LazyOverlay.kt │ │ │ │ │ └── NGroundOverlay.kt │ │ │ │ ├── clustering │ │ │ │ │ ├── NaverMapClusterOptions.kt │ │ │ │ │ └── NClusterMergeStrategy.kt │ │ │ │ └── NOverlayImage.kt │ │ │ └── NLayerGroups.kt │ │ └── base │ │ │ ├── NLocale.kt │ │ │ ├── NSize.kt │ │ │ ├── NEdgeInsets.kt │ │ │ └── NPoint.kt │ │ ├── util │ │ ├── CalcUtil.kt │ │ ├── location │ │ │ ├── NDefaultMyLocationTrackerPermissionStatus.kt │ │ │ └── NDefaultMyLocationTrackerHandler.kt │ │ └── DisplayUtil.kt │ │ ├── converter │ │ └── DefaultTypeConverter.kt │ │ ├── applier │ │ └── option │ │ │ └── NaverMapOptionApplier.kt │ │ └── view │ │ └── NaverMapViewFactory.kt ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── .gitignore └── build.gradle ├── readme_summary.webp ├── docs_asset ├── start_0_1.png ├── start_0_2.png └── start_0_3.png ├── assets ├── icon │ ├── location_overlay_icon.png │ ├── location_overlay_sub_icon.png │ └── location_overlay_sub_icon_face.png └── font │ └── Inter-fnm-scalebar-ss540.otf ├── ios ├── flutter_naver_map │ ├── Sources │ │ └── flutter_naver_map │ │ │ ├── controller │ │ │ ├── overlay │ │ │ │ ├── OverlaySender.swift │ │ │ │ └── handler │ │ │ │ │ ├── ClusterMarkerHandler.swift │ │ │ │ │ ├── ClusterableMarkerHandler.swift │ │ │ │ │ ├── GroundOverlayHandler.swift │ │ │ │ │ ├── InfoWindowHandler.swift │ │ │ │ │ ├── CircleOverlayHandler.swift │ │ │ │ │ ├── PolylineOverlayHandler.swift │ │ │ │ │ └── PolygonOverlayHandler.swift │ │ │ ├── NaverMapControlSenderExtensions.swift │ │ │ └── NaverMapControlSender.swift │ │ │ ├── model │ │ │ ├── enum │ │ │ │ ├── NOverlayImageMode.swift │ │ │ │ └── NOverlayType.swift │ │ │ ├── map │ │ │ │ ├── info │ │ │ │ │ ├── NSymbolInfo.swift │ │ │ │ │ ├── NPickableInfo.swift │ │ │ │ │ ├── NOverlayQuery.swift │ │ │ │ │ ├── NOverlayInfo.swift │ │ │ │ │ └── NClusterableMarkerInfo.swift │ │ │ │ ├── overlay │ │ │ │ │ ├── overlay │ │ │ │ │ │ ├── LazyOrAddableOverlay.swift │ │ │ │ │ │ ├── LazyOverlay.swift │ │ │ │ │ │ ├── NGroundOverlay.swift │ │ │ │ │ │ ├── NCircleOverlay.swift │ │ │ │ │ │ └── NPolylineOverlay.swift │ │ │ │ │ ├── clustering │ │ │ │ │ │ ├── NaverMapClusterOptions.swift │ │ │ │ │ │ └── NClusterMergeStrategy.swift │ │ │ │ │ ├── NOverlayCaption.swift │ │ │ │ │ └── NOverlayImage.swift │ │ │ │ ├── NLayerGroups.swift │ │ │ │ └── NaverMapViewOptions.swift │ │ │ ├── exception │ │ │ │ └── NFlutterException.swift │ │ │ └── flutter_default_custom │ │ │ │ ├── NEdgeInsets.swift │ │ │ │ ├── NLocale.swift │ │ │ │ ├── NPoint.swift │ │ │ │ ├── NSize.swift │ │ │ │ └── NRange.swift │ │ │ ├── util │ │ │ ├── CalcUtil.swift │ │ │ ├── DisplayUtil.swift │ │ │ └── location │ │ │ │ ├── NDefaultMyLocationTrackerHandler.swift │ │ │ │ ├── NDefaultMyLocationTrackerPermissionStatus.swift │ │ │ │ ├── NDefaultMyLocationTrackerHeadingStreamHandler.swift │ │ │ │ └── NDefaultMyLocationTrackerLocationStreamHandler.swift │ │ │ ├── view │ │ │ ├── NaverMapViewFactory.swift │ │ │ └── NaverMapView.swift │ │ │ ├── applier │ │ │ └── option │ │ │ │ └── NaverMapOptionApplier.swift │ │ │ └── converter │ │ │ └── DefaultTypeConverter.swift │ ├── Package.resolved │ └── Package.swift ├── .gitignore └── flutter_naver_map.podspec ├── .pubignore ├── analysis_options.yaml ├── lib └── src │ ├── widget │ └── elements │ │ └── util │ │ └── canvas_util.dart │ ├── messaging │ ├── messaging_util.dart │ ├── messaging.dart │ ├── channel_types.dart │ └── channel_wrapper.dart │ ├── controller │ └── overlay │ │ └── overlay_controller.dart │ ├── type │ ├── map │ │ ├── overlay │ │ │ ├── overlay │ │ │ │ ├── addable │ │ │ │ │ ├── addable.dart │ │ │ │ │ ├── path_overlay │ │ │ │ │ │ └── multipart_path.dart │ │ │ │ │ └── ground_overlay.dart │ │ │ │ ├── overlay_handler.dart │ │ │ │ └── overlay_sender.dart │ │ │ ├── overlay_caption.dart │ │ │ └── clustering │ │ │ │ └── clusterable_marker.dart │ │ ├── info │ │ │ ├── symbol_info.dart │ │ │ ├── pickable_info.dart │ │ │ ├── overlay_query.dart │ │ │ └── overlay_info.dart │ │ ├── indoor │ │ │ ├── indoor_level.dart │ │ │ ├── selected_indoor.dart │ │ │ ├── indoor_region.dart │ │ │ └── indoor_zone.dart │ │ └── camera │ │ │ └── camera_position.dart │ └── base │ │ ├── size.dart │ │ ├── padding.dart │ │ ├── locale.dart │ │ └── point.dart │ ├── util │ ├── custom_data_stream.dart │ ├── location │ │ └── builtin │ │ │ ├── default_my_location_tracker_platform_interface.dart │ │ │ └── default_my_location_tracker.dart │ ├── app_lifecycle_binder.dart │ ├── math.dart │ └── image_util.dart │ └── initializer │ ├── flutter_naver_map_legacy_initializer.dart │ ├── flutter_naver_map_legacy_initializer_impl.dart │ └── flutter_naver_map_initializer.dart ├── version.json ├── test ├── json_test.dart ├── messageable_test.dart ├── camera_update_test.dart └── calc_test.dart ├── .gitignore ├── .github └── workflows │ ├── docs_publish.yml │ └── publish.yml ├── test_README.md ├── pubspec.yaml ├── .metadata ├── LICENSE └── tool └── update_readme_images.dart /.fvmrc: -------------------------------------------------------------------------------- 1 | { 2 | "flutter": "3.29.2" 3 | } -------------------------------------------------------------------------------- /example/lib/design/ui_widgets.dart: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'flutter_naver_map' 2 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /readme_summary.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/readme_summary.webp -------------------------------------------------------------------------------- /docs_asset/start_0_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/docs_asset/start_0_1.png -------------------------------------------------------------------------------- /docs_asset/start_0_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/docs_asset/start_0_2.png -------------------------------------------------------------------------------- /docs_asset/start_0_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/docs_asset/start_0_3.png -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true -------------------------------------------------------------------------------- /example/assets/ground_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/assets/ground_img.png -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | linter: 4 | rules: 5 | avoid_print: false -------------------------------------------------------------------------------- /assets/icon/location_overlay_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/assets/icon/location_overlay_icon.png -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /assets/font/Inter-fnm-scalebar-ss540.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/assets/font/Inter-fnm-scalebar-ss540.otf -------------------------------------------------------------------------------- /assets/icon/location_overlay_sub_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/assets/icon/location_overlay_sub_icon.png -------------------------------------------------------------------------------- /assets/icon/location_overlay_sub_icon_face.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/assets/icon/location_overlay_sub_icon_face.png -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .cxx 10 | -------------------------------------------------------------------------------- /example/android/app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/android/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/100.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/114.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/120.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/128.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/144.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/152.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/16.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/167.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/172.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/172.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/180.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/196.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/20.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/216.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/216.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/256.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/29.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/32.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/40.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/48.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/50.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/512.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/55.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/55.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/57.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/58.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/60.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/64.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/66.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/66.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/72.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/76.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/80.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/87.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/88.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/88.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/92.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/92.png -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/controller/overlay/OverlaySender.swift: -------------------------------------------------------------------------------- 1 | internal protocol OverlaySender { 2 | func onOverlayTapped(info: NOverlayInfo) 3 | } 4 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/integration_test/golden/location_overlay_sync_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/integration_test/golden/location_overlay_sync_test.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/1024.png -------------------------------------------------------------------------------- /.pubignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.ipr 3 | *.iws 4 | .idea/ 5 | .github/ 6 | doc/ 7 | build/ 8 | docs_asset/ 9 | tool/ 10 | .fvm/ 11 | .dart_tool/ 12 | ./local.properties 13 | ./readme_summary.webp -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #0D7FF2 4 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/string.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | assertk.Assert 4 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/note11g/flutter_naver_map/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/model/enum/NOverlayImageMode.swift: -------------------------------------------------------------------------------- 1 | enum NOverlayImageMode : String { 2 | case asset = "asset" 3 | case file = "file" 4 | case temp = "temp" 5 | case widget = "widget" 6 | } 7 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | # Additional information about this file can be found at 4 | # https://dart.dev/guides/language/analysis-options 5 | 6 | linter: 7 | rules: 8 | prefer_double_quotes: true 9 | -------------------------------------------------------------------------------- /lib/src/widget/elements/util/canvas_util.dart: -------------------------------------------------------------------------------- 1 | import "dart:ui"; 2 | import "package:meta/meta.dart"; 3 | 4 | @internal 5 | extension OffsetExtension on Offset { 6 | Offset move({double x = 0, double y = 0}) { 7 | return Offset(dx + x, dy + y); 8 | } 9 | } -------------------------------------------------------------------------------- /lib/src/messaging/messaging_util.dart: -------------------------------------------------------------------------------- 1 | part of "messaging.dart"; 2 | 3 | extension MessagingUtil on Object { 4 | /// is String or int or double or bool 5 | bool get isDefaultType => 6 | this is String || this is int || this is double || this is bool; 7 | } 8 | -------------------------------------------------------------------------------- /example/lib/util/string_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_naver_map/flutter_naver_map.dart'; 2 | 3 | extension NLatLngExtension on NLatLng { 4 | String toShortString([int length = 5]) { 5 | return "${latitude.toStringAsFixed(length)}, ${longitude.toStringAsFixed(length)}"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map_example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map_example 2 | 3 | import android.os.Bundle 4 | import io.flutter.embedding.android.FlutterActivity 5 | 6 | class MainActivity : FlutterActivity() {} 7 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Apr 03 20:57:02 KST 2025 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip 5 | w\#Sat=Nov 02 04\:41\:36 KST 2024 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/controller/overlay/OverlaySender.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.controller.overlay 2 | 3 | import dev.note11.flutter_naver_map.flutter_naver_map.model.map.info.NOverlayInfo 4 | 5 | internal interface OverlaySender { 6 | fun onOverlayTapped(info: NOverlayInfo) 7 | } -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | /app/.cxx/ -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /version.json: -------------------------------------------------------------------------------- 1 | { 2 | "_comment": "This file is auto-generated by ./tool/update_version.dart script. Do not edit manually.", 3 | "package_version": "1.4.2", 4 | "native_dependency_version": { 5 | "android": { 6 | "com.naver.maps:map-sdk": "3.23.0", 7 | "com.google.android.gms:play-services-location": "21.3.0" 8 | }, 9 | "ios": { 10 | "NMapsMap": "3.23.0" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/util/CalcUtil.swift: -------------------------------------------------------------------------------- 1 | import CoreGraphics 2 | 3 | internal struct CalcUtil { 4 | static func float32To64(f32: CGFloat) -> Double { 5 | let convertedInt: Int = Int(f32 * CGFloat(F32_MULTIPLIER_CONSTANT)) 6 | return Double(convertedInt) / Double(F32_MULTIPLIER_CONSTANT) 7 | } 8 | 9 | private static let F32_MULTIPLIER_CONSTANT: Int = 1_000_000 10 | } 11 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/src/controller/overlay/overlay_controller.dart: -------------------------------------------------------------------------------- 1 | part of "../../../flutter_naver_map.dart"; 2 | 3 | abstract class _NOverlayController with NChannelWrapper { 4 | NLocationOverlay? locationOverlay; 5 | 6 | /// dependent at lifecycle with view(widget) 7 | int get viewId; 8 | 9 | void add(NOverlayInfo info, NOverlay overlay); 10 | 11 | void deleteWithInfo(NOverlayInfo info); 12 | 13 | void clear(NOverlayType? type); 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/type/map/overlay/overlay/addable/addable.dart: -------------------------------------------------------------------------------- 1 | part of "../../../../../../flutter_naver_map.dart"; 2 | 3 | /// 지도에 개발자가 직접 추가할 수 있는 오버레이들의 부모 클래스입니다. 4 | sealed class NAddableOverlay> extends NOverlay 5 | with NMessageableWithMap { 6 | NAddableOverlay({required NOverlayType type, required String id}) 7 | : super(NOverlayInfo(type: type, id: id)); 8 | 9 | bool get isAdded => _isAdded; 10 | } 11 | -------------------------------------------------------------------------------- /lib/src/messaging/messaging.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: prefer_double_quotes 2 | 3 | library flutter_naver_map_messaging; 4 | 5 | import 'package:flutter/services.dart'; 6 | import "package:flutter/widgets.dart"; 7 | import "package:flutter_naver_map/flutter_naver_map.dart"; 8 | import "package:meta/meta.dart"; 9 | 10 | part 'channel_types.dart'; 11 | part 'channel_wrapper.dart'; 12 | part 'messageable.dart'; 13 | part 'messaging_util.dart'; 14 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/model/exception/NFlutterException.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.model.exception 2 | 3 | data class NFlutterException( 4 | val code: String, 5 | val message: String?, 6 | ) { 7 | fun toMessageable(): Map { 8 | return mapOf( 9 | "code" to code, 10 | "message" to message, 11 | ) 12 | } 13 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/util/CalcUtil.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.util 2 | 3 | internal object CalcUtil { 4 | fun float32To64(f32: Float): Double { 5 | val convertedInt: Int = (f32 * F32_MULTIPLIER_CONSTANT).toInt() 6 | return convertedInt.toDouble() / F32_MULTIPLIER_CONSTANT 7 | } 8 | 9 | private const val F32_MULTIPLIER_CONSTANT: Int = 1_000_000 10 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/util/location/NDefaultMyLocationTrackerPermissionStatus.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.util.location 2 | 3 | enum class NDefaultMyLocationTrackerPermissionStatus(val value: String) { 4 | Granted("granted"), 5 | Denied("denied"), 6 | DeniedForever("deniedForever"); 7 | 8 | fun toMessageable(): String { 9 | return value 10 | } 11 | } -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @main 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/type/map/info/symbol_info.dart: -------------------------------------------------------------------------------- 1 | part of "../../../../flutter_naver_map.dart"; 2 | 3 | /// 심볼의 정보를 나타내는 객체입니다. 4 | class NSymbolInfo implements NPickableInfo { 5 | final String caption; 6 | final NLatLng position; 7 | 8 | NSymbolInfo._(this.caption, this.position); 9 | 10 | factory NSymbolInfo._fromMessageable(dynamic map) => NSymbolInfo._( 11 | map["caption"]!.toString(), 12 | NLatLng.fromMessageable(map["position"]!), 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/controller/NaverMapControlSenderExtensions.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | typealias CustomStyleCallbacks = (loadHandler: () -> Void, failHandler: (any Error) -> Void) 4 | 5 | internal extension NaverMapControlSender { 6 | func getCustomStyleCallback() -> CustomStyleCallbacks { 7 | return ( 8 | loadHandler: onCustomStyleLoaded, 9 | failHandler: onCustomStyleLoadFailed 10 | ) 11 | } 12 | } -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/model/map/info/NSymbolInfo.swift: -------------------------------------------------------------------------------- 1 | import NMapsMap 2 | 3 | internal struct NSymbolInfo: NPickableInfo { 4 | let caption: String 5 | let position: NMGLatLng 6 | 7 | init(symbol: NMFSymbol) { 8 | caption = symbol.caption 9 | position = symbol.position 10 | } 11 | 12 | func toMessageable() -> Dictionary { 13 | ["caption": caption, "position": position.toMessageable()] 14 | } 15 | } -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/util/DisplayUtil.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DisplayUtil.swift 3 | // flutter_naver_map 4 | // 5 | // Created by 김소연 on 5/29/25. 6 | // 7 | 8 | import UIKit 9 | 10 | internal struct DisplayUtil { 11 | static private var _scale: CGFloat = UIScreen.main.scale 12 | 13 | static var scale: CGFloat { 14 | return _scale 15 | } 16 | 17 | static func dpToPx(dp: Double) -> Double { 18 | return dp * scale 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /example/integration_test/all_tests.dart: -------------------------------------------------------------------------------- 1 | import 'camera_update_test.dart'; 2 | import 'clustering_test.dart'; 3 | import 'now_camera_position_test.dart'; 4 | import 'overlay_test.dart'; 5 | import 'widget_to_image_test.dart'; 6 | 7 | /// golden tests command: 8 | /// ``` 9 | /// flutter test integration_test/all_tests.dart -d --update-goldens 10 | /// ``` 11 | void main() { 12 | cameraUpdateTests(); 13 | overlayTests(); 14 | nowCameraPositionTests(); 15 | widgetToImageTests(); 16 | clusteringTests(); 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/type/base/size.dart: -------------------------------------------------------------------------------- 1 | part of "../../../flutter_naver_map.dart"; 2 | 3 | class NSize extends Size with NMessageableWithMap { 4 | const NSize(super.width, super.height); 5 | 6 | NSize.fromSize(Size size) : this(size.width, size.height); 7 | 8 | @override 9 | NPayload toNPayload() => NPayload.make({"width": width, "height": height}); 10 | 11 | // factory NSize._fromMessageable(dynamic m) => NSize(m["width"], m["height"]); 12 | 13 | @override 14 | String toString() => "NSize(w: $width, h: $height)"; 15 | } 16 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/src/util/custom_data_stream.dart: -------------------------------------------------------------------------------- 1 | import "dart:async"; 2 | 3 | class NValueHoldHotStreamController { 4 | T _currentValue; 5 | 6 | NValueHoldHotStreamController(T initialData) : _currentValue = initialData; 7 | 8 | final StreamController _controller = StreamController.broadcast(); 9 | 10 | Stream get stream => _controller.stream; 11 | 12 | T get currentData => _currentValue; 13 | 14 | void add(T data) { 15 | _currentValue = data; 16 | _controller.add(data); 17 | } 18 | 19 | void close() { 20 | _controller.close(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/src/type/base/padding.dart: -------------------------------------------------------------------------------- 1 | part of "../../../flutter_naver_map.dart"; 2 | 3 | class NEdgeInsets extends EdgeInsets with NMessageableWithMap { 4 | const NEdgeInsets.fromLTRB(super.left, super.top, super.right, super.bottom) 5 | : super.fromLTRB(); 6 | 7 | NEdgeInsets.fromEdgeInsets(EdgeInsets insets) 8 | : super.fromLTRB(insets.left, insets.top, insets.right, insets.bottom); 9 | 10 | @override 11 | NPayload toNPayload() => NPayload.make({ 12 | "left": left, 13 | "top": top, 14 | "right": right, 15 | "bottom": bottom, 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /test/json_test.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter_naver_map/flutter_naver_map.dart"; 2 | import "package:flutter_test/flutter_test.dart"; 3 | 4 | void main() { 5 | test("option type test", () { 6 | const option = NaverMapViewOptions(activeLayerGroups: [ 7 | NLayerGroup.transit, 8 | NLayerGroup.bicycle, 9 | NLayerGroup.traffic, 10 | NLayerGroup.cadastral 11 | ]); 12 | 13 | expect(option.activeLayerGroups, [ 14 | NLayerGroup.transit, 15 | NLayerGroup.bicycle, 16 | NLayerGroup.traffic, 17 | NLayerGroup.cadastral 18 | ]); 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /lib/src/messaging/channel_types.dart: -------------------------------------------------------------------------------- 1 | part of "messaging.dart"; 2 | 3 | enum NChannel { 4 | naverMapNativeView("flutter_naver_map_view"), 5 | overlayChannelName("flutter_naver_map_overlay"); 6 | 7 | final String str; 8 | 9 | const NChannel(this.str); 10 | 11 | static const _separateString = "#"; 12 | 13 | MethodChannel _create(int id) => MethodChannel("$str$_separateString$id"); 14 | 15 | @override 16 | String toString() => name; 17 | 18 | /* ----- Pre-defined Channel ----- */ 19 | static const MethodChannel sdkChannel = 20 | MethodChannel("flutter_naver_map_sdk"); 21 | } 22 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/model/map/overlay/overlay/LazyOrAddableOverlay.swift: -------------------------------------------------------------------------------- 1 | internal protocol LazyOrAddableOverlay { 2 | associatedtype OverlayType: Any 3 | 4 | func createMapOverlay() -> OverlayType 5 | } 6 | 7 | internal func asLazyOverlayOrAddableOverlayFromMessageable( 8 | info: NOverlayInfo, 9 | args: Dictionary 10 | ) throws -> any LazyOrAddableOverlay { 11 | if (info.type.isLazy) { 12 | return try asLazyOverlayFromMessageable(info: info, args: args) 13 | } 14 | return try asAddableOverlayFromMessageable(info: info, json: args) 15 | } 16 | -------------------------------------------------------------------------------- /lib/src/type/map/info/pickable_info.dart: -------------------------------------------------------------------------------- 1 | part of "../../../../flutter_naver_map.dart"; 2 | 3 | /// [NaverMapController.pickAll]을 통해서 가져올 수 있는 요소들을 나타냅니다. 4 | /// 5 | /// 심볼의 정보를 나타내는 [NSymbolInfo]와 오버레이의 정보를 나타내는 [NOverlayInfo]가 구현체로 존재합니다. 6 | sealed class NPickableInfo { 7 | static NPickableInfo _fromMessageable(dynamic m) { 8 | final signature = m["signature"] as String; 9 | return switch (signature) { 10 | "symbol" => NSymbolInfo._fromMessageable(m), 11 | "overlay" => NOverlayInfo._fromMessageable(m), 12 | _ => throw NUnknownTypeCastException(unknownValue: m), 13 | }; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/model/exception/NFlutterException.swift: -------------------------------------------------------------------------------- 1 | struct NFlutterException { 2 | let code: String 3 | let message: String? 4 | 5 | init(code: String, message: String?) { 6 | self.code = code 7 | self.message = message 8 | } 9 | 10 | init(error: Error) { 11 | self.code = error._code.description 12 | self.message = error.localizedDescription 13 | } 14 | 15 | func toMessageable() -> [String: Any?] { 16 | return [ 17 | "code": code, 18 | "message": message 19 | ] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/Generated.xcconfig 37 | /Flutter/ephemeral/ 38 | /Flutter/flutter_export_environment.sh 39 | 40 | .build/ 41 | .swiftpm/ -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/model/enum/NOverlayImageMode.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.model.enum 2 | 3 | internal enum class NOverlayImageMode(private val str: String) { 4 | ASSET("asset"), 5 | FILE("file"), 6 | TEMP("temp"), 7 | WIDGET("widget"); 8 | 9 | override fun toString(): String = str 10 | 11 | companion object { 12 | fun fromString(str: String): NOverlayImageMode = 13 | NOverlayImageMode.values().find { it.str == str } 14 | ?: throw Exception("this overlay type not defined. $str") 15 | } 16 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/controller/NaverMapSenderExtensions.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.controller 2 | 3 | import com.naver.maps.map.NaverMap.OnCustomStyleLoadCallback 4 | 5 | internal fun NaverMapControlSender.getCustomStyleCallback(): OnCustomStyleLoadCallback = 6 | object : OnCustomStyleLoadCallback { 7 | override fun onCustomStyleLoaded() = this@getCustomStyleCallback.onCustomStyleLoaded() 8 | 9 | override fun onCustomStyleLoadFailed(exception: Exception) = 10 | this@getCustomStyleCallback.onCustomStyleLoadFailed(exception) 11 | } -------------------------------------------------------------------------------- /lib/src/type/base/locale.dart: -------------------------------------------------------------------------------- 1 | part of "../../../flutter_naver_map.dart"; 2 | 3 | /// Flutter 네이버 지도 SDK에서 사용하는 Locale 객체입니다. 4 | /// 5 | /// 현재 네이버 지도 SDK는 한국어, 영어, 중국어, 일본어만을 지원합니다. 6 | class NLocale extends Locale with NMessageableWithMap { 7 | const NLocale(super.languageCode, [super.countryCode]); 8 | 9 | NLocale.fromLocale(Locale locale) 10 | : super(locale.languageCode, locale.countryCode); 11 | 12 | @override 13 | NPayload toNPayload() => NPayload.make({ 14 | "languageCode": languageCode, 15 | "countryCode": countryCode, 16 | }); 17 | 18 | static const NLocale systemLocale = NLocale("sys"); 19 | } 20 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # flutter_naver_map_example 2 | 3 | Demonstrates how to use the flutter_naver_map plugin. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) 13 | 14 | For help getting started with Flutter development, view the 15 | [online documentation](https://docs.flutter.dev/), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /lib/src/type/base/point.dart: -------------------------------------------------------------------------------- 1 | part of "../../../flutter_naver_map.dart"; 2 | 3 | /// NPoint 의 x, y는 소수점 6자리 이내의 정확도만 보장합니다. 4 | class NPoint extends math.Point with NMessageableWithMap { 5 | const NPoint(super.x, super.y); 6 | 7 | /// NPoint(0, 0)~NPoint(1, 1)의 중앙 값입니다. 8 | /// 9 | /// 화면의 절대 위치를 나타낼 때는 사용하지 않습니다. 10 | static const relativeCenter = NPoint(0.5, 0.5); 11 | 12 | @override 13 | NPayload toNPayload() => NPayload.make({ 14 | "x": x, 15 | "y": y, 16 | }); 17 | 18 | factory NPoint._fromMessageable(dynamic m) => NPoint(m["x"], m["y"]); 19 | 20 | @override 21 | String toString() => "NPoint(x: $x, y: $y)"; 22 | } 23 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/model/enum/NOverlayType.swift: -------------------------------------------------------------------------------- 1 | enum NOverlayType: String { 2 | case marker = "ma" 3 | case infoWindow = "in" 4 | case circleOverlay = "ci" 5 | case groundOverlay = "gr" 6 | case polygonOverlay = "pg" 7 | case polylineOverlay = "pl" 8 | case pathOverlay = "pa" 9 | case multipartPathOverlay = "mp" 10 | case arrowheadPathOverlay = "ah" 11 | case locationOverlay = "lo" 12 | case clusterableMarker = "cm" 13 | 14 | var isLazy: Bool { 15 | switch(self) { 16 | case .clusterableMarker: 17 | true 18 | default: 19 | false 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/src/type/map/overlay/overlay/overlay_handler.dart: -------------------------------------------------------------------------------- 1 | part of "../../../../../flutter_naver_map.dart"; 2 | 3 | mixin _NOverlayHandler { 4 | Function(O overlay)? get _onTapListener; 5 | 6 | set _onTapListener(Function(O overlay)? listener); 7 | 8 | void _handle(String methodName) { 9 | if (methodName == _onTapName) _onTapListener?.call(this as O); 10 | } 11 | 12 | /// 오버레이가 사용자에 의해 터치되었을 때 실행할 함수를 지정합니다. 13 | void setOnTapListener(Function(O overlay) listener) => 14 | _onTapListener = listener; 15 | 16 | /// 오버레이가 사용자에 의해 터치되었을 때 실행할 함수를 제거합니다. 17 | void removeOnTapListener() => _onTapListener = null; 18 | 19 | static const _onTapName = "onTap"; 20 | } 21 | -------------------------------------------------------------------------------- /test/messageable_test.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter_naver_map/src/messaging/messaging.dart"; 2 | import "package:flutter_test/flutter_test.dart"; 3 | 4 | void main() { 5 | test("Iterable in payload serialization test (#217)", () { 6 | final rawMap = { 7 | "list": [1, 2, 3], 8 | "set": {"a", "b", "c"}, 9 | }; 10 | expect(rawMap["list"].runtimeType.toString(), "List"); 11 | expect(rawMap["set"].runtimeType.toString(), "_Set"); 12 | 13 | final payload = NPayload.make(rawMap); 14 | expect(payload.map["list"].runtimeType.toString(), "List"); 15 | expect(payload.map["set"].runtimeType.toString(), "List"); 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/model/map/info/NSymbolInfo.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.model.map.info 2 | 3 | import com.naver.maps.geometry.LatLng 4 | import com.naver.maps.map.Symbol 5 | import dev.note11.flutter_naver_map.flutter_naver_map.converter.MapTypeConverter.toMessageable 6 | 7 | internal data class NSymbolInfo(val caption: String, val position: LatLng) : NPickableInfo { 8 | constructor(symbol: Symbol) : this(symbol.caption, symbol.position) 9 | 10 | override fun toMessageable(): Map = mapOf( 11 | "caption" to caption, 12 | "position" to position.toMessageable(), 13 | ) 14 | } -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/model/map/overlay/clustering/NaverMapClusterOptions.swift: -------------------------------------------------------------------------------- 1 | internal struct NaverMapClusterOptions { 2 | let enableZoomRange: NRange 3 | let animationDuration: Int 4 | let mergeStrategy: NClusterMergeStrategy 5 | 6 | static func fromMessageable(_ v: Any) -> NaverMapClusterOptions { 7 | let d = asDict(v) 8 | return NaverMapClusterOptions( 9 | enableZoomRange: NRange.fromMessageableWithExactType(args: d["enableZoomRange"]!), 10 | animationDuration: asInt(d["animationDuration"]!), 11 | mergeStrategy: NClusterMergeStrategy.fromMessageable(d["mergeStrategy"]!) 12 | ) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/model/map/overlay/clustering/NClusterMergeStrategy.swift: -------------------------------------------------------------------------------- 1 | internal struct NClusterMergeStrategy { 2 | let willMergedScreenDistance: Dictionary, Double> 3 | let maxMergeableScreenDistance: Double 4 | 5 | static func fromMessageable(_ v: Any) -> NClusterMergeStrategy { 6 | let d = asDict(v) 7 | return NClusterMergeStrategy( 8 | willMergedScreenDistance: asDictWithObjectKey(d["willMergedScreenDistance"]!, 9 | keyCaster: NRange.fromMessageableWithExactType, valueCaster: asDouble), 10 | maxMergeableScreenDistance: asDouble(d["maxMergeableScreenDistance"]!) 11 | ) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "pins" : [ 3 | { 4 | "identity" : "spm-nmapsgeometry", 5 | "kind" : "remoteSourceControl", 6 | "location" : "https://github.com/navermaps/SPM-NMapsGeometry.git", 7 | "state" : { 8 | "revision" : "436d5e2e684f557faf5ef5862fd6633a42d7af11", 9 | "version" : "1.0.2" 10 | } 11 | }, 12 | { 13 | "identity" : "spm-nmapsmap", 14 | "kind" : "remoteSourceControl", 15 | "location" : "https://github.com/navermaps/SPM-NMapsMap.git", 16 | "state" : { 17 | "revision" : "fb4eef37db9904c0a0dcdf7a828c892e13782cfa", 18 | "version" : "3.23.0" 19 | } 20 | } 21 | ], 22 | "version" : 2 23 | } 24 | -------------------------------------------------------------------------------- /lib/src/initializer/flutter_naver_map_legacy_initializer.dart: -------------------------------------------------------------------------------- 1 | part of "../../flutter_naver_map.dart"; 2 | 3 | @Deprecated("Use FlutterNaverMap class instead") 4 | abstract class NaverMapSdk { 5 | static final NaverMapSdk instance = _NaverMapSdkImpl(); 6 | 7 | bool get _isInitialized; 8 | 9 | int? get _androidSdkVersion; 10 | 11 | Function(NAuthFailedException ex)? get onAuthFailed; 12 | 13 | @Deprecated("Use [FlutterNaverMap.init] method instead") 14 | 15 | /// [FlutterNaverMap.init] 메서드를 대신 사용하세요. 16 | /// 17 | /// 지도 사용 전에 호출해야 하는 초기화 메서드입니다. 18 | Future initialize({ 19 | String? clientId, 20 | bool gov = false, 21 | Function(NAuthFailedException ex)? onAuthFailed, 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/model/flutter_default_custom/NEdgeInsets.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | internal struct NEdgeInsets { 4 | let left: CGFloat 5 | let top: CGFloat 6 | let right: CGFloat 7 | let bottom: CGFloat 8 | 9 | var uiEdgeInsets: UIEdgeInsets { 10 | UIEdgeInsets(top: top, left: left, bottom: bottom, right: right) 11 | } 12 | 13 | static func fromMessageable(_ args: Any) -> NEdgeInsets { 14 | let d = asDict(args, valueCaster: asCGFloat) 15 | return NEdgeInsets( 16 | left: d["left"]!, 17 | top: d["top"]!, 18 | right: d["right"]!, 19 | bottom: d["bottom"]! 20 | ) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/controller/NaverMapControlSender.swift: -------------------------------------------------------------------------------- 1 | import NMapsMap 2 | 3 | internal protocol NaverMapControlSender : AnyObject { 4 | func onMapReady() 5 | 6 | func onMapLoaded() 7 | 8 | func onMapTapped(nPoint: NPoint, latLng: NMGLatLng) 9 | 10 | func onMapLongTapped(nPoint: NPoint, latLng: NMGLatLng) 11 | 12 | func onSymbolTapped(symbol: NMFSymbol) -> Bool? 13 | 14 | func onCameraChange(cameraUpdateReason: Int, animated: Bool) 15 | 16 | func onCameraIdle() 17 | 18 | func onSelectedIndoorChanged(selectedIndoor: NMFIndoorSelection?) 19 | 20 | func onCustomStyleLoaded() 21 | 22 | func onCustomStyleLoadFailed(exception: any Error) 23 | 24 | func dispose() 25 | } 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 26 | /pubspec.lock 27 | **/doc/api/ 28 | .dart_tool/ 29 | .packages 30 | build/ 31 | 32 | # FVM Version Cache 33 | .fvm/ 34 | 35 | # Android build folder 36 | local.properties 37 | 38 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.9.20' 3 | ext.agp_version = '8.11.0' 4 | 5 | repositories { 6 | google() 7 | mavenCentral() 8 | } 9 | 10 | dependencies { 11 | classpath "com.android.tools.build:gradle:$agp_version" 12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | google() 19 | mavenCentral() 20 | } 21 | } 22 | 23 | rootProject.buildDir = '../build' 24 | subprojects { 25 | project.buildDir = "${rootProject.buildDir}/${project.name}" 26 | } 27 | subprojects { 28 | project.evaluationDependsOn(':app') 29 | } 30 | 31 | tasks.register("clean", Delete) { 32 | delete rootProject.buildDir 33 | } -------------------------------------------------------------------------------- /lib/src/type/map/indoor/indoor_level.dart: -------------------------------------------------------------------------------- 1 | part of "../../../../flutter_naver_map.dart"; 2 | 3 | /// 실내 지도에서 하나의 층을 나타내는 객체입니다. 4 | /// 5 | /// 실내 지도에서 하나의 구역을 나타내는 [NIndoorZone]의 구성요소입니다. 6 | class NIndoorLevel { 7 | final String name; 8 | @override 9 | final int hashCode; 10 | 11 | NIndoorLevel._(this.name, this.hashCode); 12 | 13 | factory NIndoorLevel._fromMessageable(dynamic m) => 14 | NIndoorLevel._(m["name"], m["hashCode"]); 15 | 16 | @override 17 | bool operator ==(Object other) { 18 | return identical(this, other) || 19 | other is NIndoorLevel && 20 | runtimeType == other.runtimeType && 21 | hashCode == other.hashCode; 22 | } 23 | 24 | @override 25 | String toString() { 26 | return "IndoorLevel{name: $name}"; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.github/workflows/docs_publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - docs 7 | 8 | jobs: 9 | publishing: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - uses: actions/setup-node@v3 14 | with: 15 | node-version: 18 16 | cache: npm 17 | 18 | - name: Install dependencies 19 | run: npm ci 20 | - name: Build website 21 | run: npm run build 22 | 23 | - name: Deploy to GitHub Pages 24 | uses: peaceiris/actions-gh-pages@v3 25 | with: 26 | github_token: ${{ secrets.GITHUB_TOKEN }} 27 | publish_dir: ./build 28 | user_name: github-actions[bot] 29 | user_email: 41898282+github-actions[bot]@users.noreply.github.com -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/controller/overlay/handler/ClusterMarkerHandler.swift: -------------------------------------------------------------------------------- 1 | import NMapsMap 2 | import Flutter 3 | 4 | internal protocol ClusterMarkerHandler { 5 | func syncClusterMarker(_ marker: NMFMarker, rawClusterMarker: Any, success: (Any?) -> ()) 6 | } 7 | 8 | internal extension ClusterMarkerHandler { 9 | func handleClusterMarker( 10 | marker: NMFOverlay, 11 | method: String, 12 | arg: Any?, 13 | result: @escaping FlutterResult) { 14 | let marker = marker as! NMFMarker 15 | 16 | switch method { 17 | case "lSyncClusterMarker": 18 | syncClusterMarker(marker, rawClusterMarker: arg!, success: result) 19 | default: result(FlutterMethodNotImplemented) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/model/map/overlay/overlay/LazyOrAddableOverlay.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.model.map.overlay.overlay 2 | 3 | import android.content.Context 4 | import dev.note11.flutter_naver_map.flutter_naver_map.model.map.info.NOverlayInfo 5 | 6 | internal sealed interface LazyOrAddableOverlay { 7 | fun createMapOverlay(): Any 8 | 9 | companion object { 10 | fun fromMessageable( 11 | info: NOverlayInfo, 12 | args: Map, 13 | context: Context, 14 | ): LazyOrAddableOverlay { 15 | if (info.type.isLazy) return LazyOverlay.fromMessageable(info, args) 16 | return AddableOverlay.fromMessageable(info, args, context) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | /Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/ 36 | */Package.resolved 37 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/model/map/info/NPickableInfo.swift: -------------------------------------------------------------------------------- 1 | import NMapsMap 2 | 3 | internal protocol NPickableInfo { 4 | func toMessageable() -> Dictionary 5 | } 6 | 7 | extension NPickableInfo { 8 | func toSignedMessageable() -> Dictionary { 9 | toMessageable().merging(["signature": (self is NSymbolInfo ? "symbol" : "overlay")], 10 | uniquingKeysWith: { (current, _) in current }) 11 | } 12 | 13 | static func fromPickable(pickable: NMFPickable) -> NPickableInfo { 14 | switch (pickable) { 15 | case let symbol as NMFSymbol:return NSymbolInfo(symbol: symbol) 16 | case let overlay as NMFOverlay:return NOverlayInfo.fromOverlay(overlay) 17 | default: fatalError("Unknown pickable type") 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /example/lib/pages/others/example_bottom_main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_naver_map_example/design/custom_widget.dart'; 3 | import 'package:flutter_naver_map_example/pages/others/example_page_data.dart'; 4 | 5 | class ExampleMain extends StatelessWidget { 6 | final List pages; 7 | 8 | const ExampleMain({super.key, required this.pages}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Padding( 13 | padding: const EdgeInsets.all( 14 | 8) /* padding of all direction + bottomSafeArea */ 15 | .copyWith(bottom: 8 + MediaQuery.paddingOf(context).bottom), 16 | child: HalfActionButtonGrid( 17 | buttons: pages.map((page) => page.getItemWidget(context)).toList(), 18 | )); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.9 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "flutter_naver_map", 8 | platforms: [ 9 | .iOS("12.0"), 10 | ], 11 | products: [ 12 | .library(name: "flutter-naver-map", targets: ["flutter_naver_map"]) 13 | ], 14 | dependencies: [ 15 | .package(url: "https://github.com/navermaps/SPM-NMapsMap.git", from: "3.23.0"), 16 | ], 17 | targets: [ 18 | .target( 19 | name: "flutter_naver_map", 20 | dependencies: [ 21 | .product(name: "NMapsMap", package: "SPM-NMapsMap") 22 | ], 23 | resources: [], 24 | ) 25 | ] 26 | ) 27 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/util/location/NDefaultMyLocationTrackerHandler.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Flutter 3 | 4 | internal protocol NDefaultMyLocationTrackerHandler { 5 | func requestLocationPermission(result: @escaping FlutterResult) 6 | 7 | func getCurrentPositionOnce(result: @escaping FlutterResult) 8 | } 9 | 10 | internal extension NDefaultMyLocationTrackerHandler { 11 | func handle(call: FlutterMethodCall, result: @escaping FlutterResult) { 12 | switch (call.method) { 13 | case "requestLocationPermission": 14 | requestLocationPermission(result: result) 15 | case "getCurrentPositionOnce": 16 | getCurrentPositionOnce(result: result) 17 | default: 18 | result(FlutterMethodNotImplemented) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/controller/overlay/handler/ClusterMarkerHandler.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.controller.overlay.handler 2 | 3 | import com.naver.maps.map.overlay.Marker 4 | import com.naver.maps.map.overlay.Overlay 5 | import io.flutter.plugin.common.MethodChannel 6 | 7 | internal interface ClusterMarkerHandler { 8 | fun handleClusterMarker( 9 | marker: Overlay, method: String, arg: Any?, result: MethodChannel.Result, 10 | ) = (marker as Marker).let { m -> 11 | when (method) { 12 | "lSyncClusterMarker" -> syncClusterMarker(m, arg!!, result::success) 13 | else -> result.notImplemented() 14 | } 15 | } 16 | 17 | fun syncClusterMarker(marker: Marker, rawClusterMarker: Any, success: (Any?) -> Unit) 18 | } -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "originHash" : "1305285950facf55a7f581d423c1e480d6dd2ff2086ac845f21b26ecbf5661fd", 3 | "pins" : [ 4 | { 5 | "identity" : "spm-nmapsgeometry", 6 | "kind" : "remoteSourceControl", 7 | "location" : "https://github.com/navermaps/SPM-NMapsGeometry.git", 8 | "state" : { 9 | "revision" : "436d5e2e684f557faf5ef5862fd6633a42d7af11", 10 | "version" : "1.0.2" 11 | } 12 | }, 13 | { 14 | "identity" : "spm-nmapsmap", 15 | "kind" : "remoteSourceControl", 16 | "location" : "https://github.com/navermaps/SPM-NMapsMap.git", 17 | "state" : { 18 | "revision" : "fb4eef37db9904c0a0dcdf7a828c892e13782cfa", 19 | "version" : "3.23.0" 20 | } 21 | } 22 | ], 23 | "version" : 3 24 | } 25 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | }() 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 21 | id "com.android.application" version '8.11.0' apply false 22 | id "org.jetbrains.kotlin.android" version "1.9.20" apply false 23 | } 24 | 25 | include ":app" -------------------------------------------------------------------------------- /test_README.md: -------------------------------------------------------------------------------- 1 | # flutter_naver_map for Testing 2 | 3 | ## Unit Tests 4 | ``` 5 | // dir: flutter_naver_map 6 | flutter test 7 | ``` 8 | 9 | ## Integration test 10 | ``` 11 | // dir: flutter_naver_map/example 12 | 13 | flutter devices 14 | >>> Found 2 connected devices: 15 | >>> sdk gphone64 arm64 (mobile) • emulator-5554 • android-arm64 • Android 14 (API 34) (emulator) 16 | >>> iPhone 15 Pro (mobile) • FBC3B1D9-EF6E-4A42-AE42-7A96F5F2F39C • ios • com.apple.CoreSimulator.SimRuntime.iOS-17-2 (simulator) 17 | 18 | flutter test integration_test/all_tests.dart -d 19 | ``` 20 | 21 | ## Update Golden Test Snapshots 22 | 23 | can't on android. related issue: https://github.com/flutter/flutter/issues/103222 24 | ``` 25 | flutter test integration_test/all_tests.dart -d --update-goldens 26 | ``` -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/model/flutter_default_custom/NLocale.swift: -------------------------------------------------------------------------------- 1 | internal struct NLocale { 2 | let languageCode: String 3 | let countryCode: String? 4 | 5 | var localeStr: String { 6 | if (countryCode != nil && countryCode?.isEmpty == false) { 7 | return "\(languageCode)-\(countryCode!)" 8 | } else { 9 | return languageCode 10 | } 11 | } 12 | 13 | static func fromMessageable(_ args: Any) -> NLocale? { 14 | let d = asDict(args) 15 | let languageCode: String = asString(d["languageCode"]!) 16 | if languageCode == "sys" { 17 | return nil 18 | } 19 | return NLocale( 20 | languageCode: languageCode, 21 | countryCode: castOrNull(d["countryCode"], caster: asString) 22 | ) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/src/util/location/builtin/default_my_location_tracker_platform_interface.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter_naver_map/flutter_naver_map.dart"; 2 | 3 | abstract interface class NDefaultMyLocationTrackerPlatformInterface { 4 | Future requestLocationPermission(); 5 | 6 | Future getCurrentPositionOnce(); 7 | 8 | Stream getLocationStream(); 9 | 10 | Stream getHeadingStream(); 11 | } 12 | 13 | enum NDefaultMyLocationTrackerPermissionStatus { 14 | granted, 15 | denied, 16 | deniedForever; 17 | 18 | factory NDefaultMyLocationTrackerPermissionStatus.fromMessageable(dynamic m) { 19 | if (m case String m) { 20 | return NDefaultMyLocationTrackerPermissionStatus.values.byName(m); 21 | } else { 22 | throw UnsupportedError("Unsupported type: $m"); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/model/map/overlay/overlay/LazyOverlay.swift: -------------------------------------------------------------------------------- 1 | import NMapsMap 2 | 3 | internal protocol LazyOverlay: LazyOrAddableOverlay { 4 | override associatedtype OverlayType: Any 5 | associatedtype RealOverlayType: AddableOverlay // Wrapped 6 | 7 | override func createMapOverlay() -> OverlayType 8 | 9 | var info: NOverlayInfo { get } 10 | var wrappedOverlay: RealOverlayType { get } 11 | } 12 | 13 | internal func asLazyOverlayFromMessageable(info: NOverlayInfo, args: Dictionary) throws -> any LazyOverlay { 14 | return switch info.type { 15 | case .clusterableMarker: NClusterableMarker.fromMessageable(args) 16 | default: throw NSError(domain: "LazyOverlay", code: 0, userInfo: [ 17 | NSLocalizedDescriptionKey: "\(info.type) is not LazyOverlay" 18 | ]) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/model/enum/NOverlayType.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.model.enum 2 | 3 | internal enum class NOverlayType(private val str: String, val isLazy: Boolean = false) { 4 | MARKER("ma"), 5 | INFO_WINDOW("in"), 6 | CIRCLE_OVERLAY("ci"), 7 | GROUND_OVERLAY("gr"), 8 | POLYGON_OVERLAY("pg"), 9 | POLYLINE_OVERLAY("pl"), 10 | PATH_OVERLAY("pa"), 11 | MULTIPART_PATH_OVERLAY("mp"), 12 | ARROWHEAD_PATH_OVERLAY("ah"), 13 | LOCATION_OVERLAY("lo"), 14 | CLUSTERABLE_MARKER("cm", isLazy = true); 15 | 16 | override fun toString(): String = str 17 | 18 | companion object { 19 | fun fromString(str: String): NOverlayType = values().find { it.str == str } 20 | ?: throw Exception("this overlay type not defined. $str") 21 | } 22 | } -------------------------------------------------------------------------------- /lib/src/type/map/overlay/overlay/addable/path_overlay/multipart_path.dart: -------------------------------------------------------------------------------- 1 | part of "../../../../../../../flutter_naver_map.dart"; 2 | 3 | class NMultipartPath with NMessageableWithMap { 4 | final Iterable coords; 5 | final Color color; 6 | final Color outlineColor; 7 | final Color passedColor; 8 | final Color passedOutlineColor; 9 | 10 | const NMultipartPath({ 11 | required this.coords, 12 | this.color = Colors.white, 13 | this.outlineColor = Colors.black, 14 | this.passedColor = Colors.white, 15 | this.passedOutlineColor = Colors.black, 16 | }); 17 | 18 | @override 19 | NPayload toNPayload() => NPayload.make({ 20 | "coords": coords, 21 | "color": color, 22 | "outlineColor": outlineColor, 23 | "passedColor": passedColor, 24 | "passedOutlineColor": passedOutlineColor, 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /example/lib/pages/others/example_page_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:go_router/go_router.dart'; 3 | import '../../design/theme.dart'; 4 | import '../../design/custom_widget.dart'; 5 | 6 | class ExamplePageData { 7 | final String title; 8 | final String description; 9 | final IconData icon; 10 | final VoidCallback? onTap; 11 | final String? route; 12 | 13 | const ExamplePageData({ 14 | required this.title, 15 | required this.description, 16 | required this.icon, 17 | this.onTap, 18 | required this.route, 19 | }); 20 | 21 | Widget getItemWidget(BuildContext context) { 22 | return HalfActionButton( 23 | action: onTap ?? () => context.push(route ?? "/"), 24 | title: title, 25 | color: getColorTheme(context).onSurface, 26 | description: description, 27 | icon: icon, 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/controller/overlay/handler/ClusterableMarkerHandler.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.controller.overlay.handler 2 | 3 | import com.naver.maps.map.overlay.Marker 4 | import com.naver.maps.map.overlay.Overlay 5 | import io.flutter.plugin.common.MethodChannel 6 | 7 | internal interface ClusterableMarkerHandler : MarkerHandler, ClusterMarkerHandler { 8 | fun handleClusterableMarker( 9 | marker: Overlay, method: String, arg: Any?, result: MethodChannel.Result, 10 | ) = (marker as Marker).let { m -> 11 | val isClusterMarkerMode = method.startsWith("l") 12 | if (isClusterMarkerMode) { 13 | return handleClusterMarker(m, method, arg, result) 14 | } 15 | 16 | when (method) { 17 | else -> handleMarker(m, method, arg, result) 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - lat_compass (0.0.1): 4 | - Flutter 5 | - permission_handler_apple (9.3.0): 6 | - Flutter 7 | 8 | DEPENDENCIES: 9 | - Flutter (from `Flutter`) 10 | - lat_compass (from `.symlinks/plugins/lat_compass/ios`) 11 | - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) 12 | 13 | EXTERNAL SOURCES: 14 | Flutter: 15 | :path: Flutter 16 | lat_compass: 17 | :path: ".symlinks/plugins/lat_compass/ios" 18 | permission_handler_apple: 19 | :path: ".symlinks/plugins/permission_handler_apple/ios" 20 | 21 | SPEC CHECKSUMS: 22 | Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 23 | lat_compass: eadcaf7fa6194ac03e557b3c0ee7168b5415ff43 24 | permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d 25 | 26 | PODFILE CHECKSUM: 865bfa77219454a712543ad6494f6458c48be43e 27 | 28 | COCOAPODS: 1.16.2 29 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/controller/overlay/handler/ClusterableMarkerHandler.swift: -------------------------------------------------------------------------------- 1 | import NMapsMap 2 | import Flutter 3 | 4 | internal protocol ClusterableMarkerHandler: MarkerHandler, ClusterMarkerHandler { 5 | 6 | } 7 | 8 | internal extension ClusterableMarkerHandler { 9 | func handleClusterableMarker( 10 | marker: NMFOverlay, 11 | method: String, 12 | arg: Any?, 13 | result: @escaping FlutterResult) 14 | { 15 | let marker = marker as! NMFMarker 16 | let isMarkerClusterMode = method.starts(with: "l") 17 | 18 | if isMarkerClusterMode { 19 | return handleClusterMarker(marker: marker, method: method, arg: arg, result: result) 20 | } 21 | 22 | switch method { 23 | default: handleMarker(marker: marker, method: method, args: arg, result: result) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/model/flutter_default_custom/NPoint.swift: -------------------------------------------------------------------------------- 1 | import CoreGraphics 2 | 3 | internal struct NPoint { 4 | let x, y: Double 5 | 6 | var cgPoint: CGPoint { 7 | CGPoint.init(x: x, y: y) 8 | } 9 | 10 | func toMessageable() -> Dictionary { 11 | [ 12 | "x": x, 13 | "y": y 14 | ] 15 | } 16 | 17 | static func fromMessageable(_ args: Any) -> NPoint { 18 | let d = asDict(args, valueCaster: asDouble) 19 | return NPoint(x: d["x"]!, y: d["y"]!) 20 | } 21 | 22 | static func fromCGPointWithDisplay(_ cgPoint: CGPoint) -> NPoint { 23 | NPoint(x: cgPoint.x, y: cgPoint.y) 24 | } 25 | 26 | static func fromCGPointWithOutDisplay(_ cgPoint: CGPoint) -> NPoint { 27 | NPoint(x: CalcUtil.float32To64(f32: cgPoint.x), y: CalcUtil.float32To64(f32: cgPoint.y)) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to pub.dev 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v[0-9]+.[0-9]+.[0-9]+*' 7 | 8 | jobs: 9 | publish: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | id-token: write # Required for OIDC authentication 13 | steps: 14 | - name: 'Checkout' 15 | uses: actions/checkout@v3 16 | 17 | - name: 'Setup Flutter' 18 | uses: subosito/flutter-action@v2 19 | with: 20 | channel: 'stable' 21 | cache: true 22 | cache-key: "flutter-:os:-:channel:-:version:-:arch:" 23 | pub-cache-key: "flutter-pub-:os:-:channel:-:version:-:arch:" 24 | 25 | - name: 'Credential Setup' 26 | uses: flutter-actions/setup-pubdev-credentials@v1 27 | 28 | - name: 'Install dependencies' 29 | run: flutter pub get 30 | 31 | - name: 'Publish to pub.dev' 32 | run: flutter pub publish --force -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .build/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | .swiftpm/ 13 | migrate_working_dir/ 14 | 15 | # IntelliJ related 16 | *.iml 17 | *.ipr 18 | *.iws 19 | .idea/ 20 | 21 | # The .vscode folder contains launch configuration and tasks you configure in 22 | # VS Code which you may wish to be included in version control, so this line 23 | # is commented out by default. 24 | #.vscode/ 25 | 26 | # Flutter/Dart/Pub related 27 | **/doc/api/ 28 | **/ios/Flutter/.last_build_id 29 | .dart_tool/ 30 | .flutter-plugins 31 | .flutter-plugins-dependencies 32 | .packages 33 | .pub-cache/ 34 | .pub/ 35 | /build/ 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /lib/src/type/map/info/overlay_query.dart: -------------------------------------------------------------------------------- 1 | part of "../../../../flutter_naver_map.dart"; 2 | 3 | class _NOverlayQuery { 4 | final NOverlayInfo info; 5 | final String methodName; 6 | 7 | _NOverlayQuery(this.info, {required this.methodName}); 8 | 9 | factory _NOverlayQuery.fromQuery(String query) => _decode(query); 10 | 11 | String get query => _encode(); 12 | 13 | // ----- Encode / Decode ----- 14 | 15 | String _encode() => [info.type.payload, info.id, methodName].join(_separator); 16 | 17 | static _NOverlayQuery _decode(String string) { 18 | final split = string.split(_separator); 19 | final type = split.first; 20 | final method = split.last; 21 | final id = split.sublist(1, split.length - 1).join(_separator); 22 | return _NOverlayQuery( 23 | NOverlayInfo(type: NOverlayType._fromMessageable(type), id: id), 24 | methodName: method); 25 | } 26 | 27 | static const _separator = "\""; 28 | } 29 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/util/location/NDefaultMyLocationTrackerHandler.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.util.location 2 | 3 | import io.flutter.plugin.common.MethodCall 4 | import io.flutter.plugin.common.MethodChannel 5 | 6 | internal interface NDefaultMyLocationTrackerHandler { 7 | fun handle(call: MethodCall, result: MethodChannel.Result) = when (call.method) { 8 | "requestLocationPermission" -> requestLocationPermission(result) 9 | "getCurrentPositionOnce" -> getCurrentPositionOnce(result) 10 | "cancelAllWaitingGetCurrentPositionOnceTask" -> cancelAllWaitingGetCurrentPositionOnceTask() 11 | else -> result.notImplemented() 12 | } 13 | 14 | fun requestLocationPermission(result: MethodChannel.Result) 15 | 16 | fun getCurrentPositionOnce(result: MethodChannel.Result) 17 | 18 | fun cancelAllWaitingGetCurrentPositionOnceTask() 19 | } 20 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/model/base/NLocale.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.model.base 2 | 3 | import dev.note11.flutter_naver_map.flutter_naver_map.converter.DefaultTypeConverter.asMap 4 | import java.util.* 5 | 6 | internal data class NLocale( 7 | val languageCode: String, val countryCode: String?, 8 | ) { 9 | 10 | fun toLocale(): Locale = if (countryCode == null) { 11 | Locale(languageCode) 12 | } else { 13 | Locale(languageCode, countryCode) 14 | } 15 | 16 | companion object { 17 | fun fromMessageable(rawMap: Any): NLocale? = rawMap.asMap().let { map -> 18 | val languageCode = map["languageCode"].toString() 19 | if (languageCode == "sys") null 20 | else NLocale( 21 | languageCode = languageCode, 22 | countryCode = map["countryCode"]?.toString() 23 | ) 24 | } 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /example/lib/util/location_util.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:permission_handler/permission_handler.dart'; 4 | 5 | class ExampleLocationUtil { 6 | static const locationPermission = Permission.locationWhenInUse; 7 | 8 | static Future get _hasPermission async { 9 | final denied = await locationPermission.isDenied; 10 | return !denied; 11 | } 12 | 13 | static Future _requestPermission() async { 14 | final status = await locationPermission.request(); 15 | return status.isGranted; 16 | } 17 | 18 | static Future requestAndGrantedCheck() async { 19 | if (Platform.isAndroid) { 20 | final enabled = await _hasPermission; // 권한이 있는지 확인합니다. 21 | if (enabled) return true; // 있다면, true를 반환합니다. 22 | } 23 | 24 | final granted = await _requestPermission(); // 권한이 없으므로, 권한 요청을 합니다. 25 | if (granted) return true; // 권한이 허용되었다면, true를 반환합니다. 26 | 27 | return false; // 거부되었음을 알리기 위해, false를 반환합니다. 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/controller/NaverMapControlSender.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.controller 2 | 3 | import com.naver.maps.geometry.LatLng 4 | import com.naver.maps.map.Symbol 5 | import com.naver.maps.map.indoor.IndoorSelection 6 | import dev.note11.flutter_naver_map.flutter_naver_map.model.base.NPoint 7 | 8 | internal interface NaverMapControlSender { 9 | fun onMapReady() 10 | 11 | fun onMapLoaded() 12 | 13 | fun onMapTapped(nPoint: NPoint, latLng: LatLng) 14 | 15 | fun onMapLongTapped(nPoint: NPoint, latLng: LatLng) 16 | 17 | fun onSymbolTapped(symbol: Symbol): Boolean? 18 | 19 | fun onCameraChange(cameraUpdateReason: Int, animated: Boolean) 20 | 21 | fun onCameraIdle() 22 | 23 | fun onSelectedIndoorChanged(selectedIndoor: IndoorSelection?) 24 | 25 | fun onCustomStyleLoaded() 26 | 27 | fun onCustomStyleLoadFailed(exception: Exception) 28 | 29 | fun dispose() 30 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/util/DisplayUtil.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.util 2 | 3 | import android.content.res.Resources 4 | import androidx.annotation.Px 5 | import kotlin.math.roundToInt 6 | 7 | internal object DisplayUtil { 8 | private val density: Float 9 | get() { 10 | val density = Resources.getSystem().displayMetrics.density 11 | if (density != 0.0F) return density 12 | else throw Exception("getDisplayDensity Failed") 13 | } 14 | 15 | @Px 16 | fun dpToPx(dp: Double): Int { 17 | return (dp * density).roundToInt() 18 | } 19 | 20 | @Px 21 | fun dpToPxF(dp: Double): Float { 22 | return (dp * density).toFloat() 23 | } 24 | 25 | fun pxToDp(@Px px: Int): Double { 26 | return px.toDouble() / density 27 | } 28 | 29 | fun pxToDp(@Px px: Float): Double { 30 | return px.toDouble() / density 31 | } 32 | } -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/model/flutter_default_custom/NSize.swift: -------------------------------------------------------------------------------- 1 | import CoreGraphics 2 | 3 | internal struct NSize { 4 | let width, height: CGFloat 5 | 6 | var isAutoSize: Bool { 7 | return self == Self.autoSize; 8 | } 9 | 10 | static let autoSize = NSize(width: 0.0, height: 0.0) 11 | 12 | func use(widthFun: (CGFloat) -> Void, heightFun: (CGFloat) -> Void) { 13 | widthFun(width) 14 | heightFun(height) 15 | } 16 | 17 | func toMessageable() -> Dictionary { 18 | [ 19 | "width": width, 20 | "height": height 21 | ] 22 | } 23 | 24 | static func fromMessageable(_ args: Any) -> NSize { 25 | let d = asDict(args, valueCaster: asCGFloat) 26 | return NSize(width: d["width"]!, height: d["height"]!) 27 | } 28 | 29 | static func ==(lhs: Self, rhs: Self) -> Bool { 30 | return lhs.width == rhs.width && lhs.height == rhs.height 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/src/type/map/indoor/selected_indoor.dart: -------------------------------------------------------------------------------- 1 | part of "../../../../flutter_naver_map.dart"; 2 | 3 | /// 하나의 실내 지도의 영역에서 선택된 구역, 층을 나타내는 객체입니다. 4 | /// 5 | /// [NaverMap.onSelectedIndoorChanged]에서 사용됩니다. 6 | class NSelectedIndoor { 7 | final int levelIndex; 8 | final int zoneIndex; 9 | 10 | /// 현재 보이는 실내 지도 영역입니다. 11 | final NIndoorRegion region; 12 | 13 | /// 현재 보이는 층입니다. 14 | NIndoorLevel get level => zone.levels[levelIndex]; 15 | 16 | /// 현재 보이는 구역입니다. 17 | NIndoorZone get zone => region.zones[zoneIndex]; 18 | 19 | NSelectedIndoor._(this.levelIndex, this.zoneIndex, this.region); 20 | 21 | factory NSelectedIndoor._fromMessageable(dynamic m) { 22 | return NSelectedIndoor._( 23 | m["levelIndex"] as int, 24 | m["zoneIndex"] as int, 25 | NIndoorRegion._fromMessageable(m["region"]), 26 | ); 27 | } 28 | 29 | @override 30 | String toString() => """SelectedIndoor{ 31 | levelIndex: $levelIndex, 32 | zoneIndex: $zoneIndex, 33 | region: $region 34 | }"""; 35 | } 36 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/model/map/info/NPickableInfo.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.model.map.info 2 | 3 | import com.naver.maps.map.Pickable 4 | import com.naver.maps.map.Symbol 5 | import com.naver.maps.map.overlay.Overlay 6 | import dev.note11.flutter_naver_map.flutter_naver_map.controller.NaverMapControlHandler 7 | 8 | internal interface NPickableInfo { 9 | fun toMessageable(): Map 10 | 11 | /** @see [NaverMapControlHandler.pickAll] */ 12 | fun toSignedMessageable(): Map = 13 | toMessageable() + mapOf("signature" to if (this is NSymbolInfo) "symbol" else "overlay") 14 | 15 | companion object { 16 | fun fromPickable(pickable: Pickable): NPickableInfo = when (pickable) { 17 | is Symbol -> NSymbolInfo(pickable) 18 | is Overlay -> NOverlayInfo.fromOverlay(pickable) 19 | else -> throw IllegalArgumentException("Unknown pickable type") 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/util/location/NDefaultMyLocationTrackerPermissionStatus.swift: -------------------------------------------------------------------------------- 1 | import CoreLocation 2 | 3 | enum NDefaultMyLocationTrackerPermissionStatus: String { 4 | case granted = "granted" 5 | case denied = "denied" 6 | case deniedForever = "deniedForever" 7 | 8 | func toMessageable() -> String { 9 | return self.rawValue 10 | } 11 | } 12 | 13 | extension CLAuthorizationStatus { 14 | func toNDefaultMyLocationTrackerPermissionStatus() -> NDefaultMyLocationTrackerPermissionStatus? { 15 | switch self { 16 | case .authorizedAlways, .authorizedWhenInUse: 17 | return .granted 18 | case .denied: 19 | return .denied 20 | case .restricted: 21 | return .deniedForever 22 | case .notDetermined: 23 | return nil 24 | } 25 | } 26 | 27 | func toMessageable() -> String? { 28 | self.toNDefaultMyLocationTrackerPermissionStatus()?.toMessageable() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_naver_map_example 2 | description: Demonstrates how to use the flutter_naver_map plugin. 3 | 4 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 5 | 6 | version: 1.0.0+100 7 | 8 | environment: 9 | sdk: '>=3.2.1 <4.0.0' 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | 15 | flutter_naver_map: 16 | path: ../ 17 | 18 | flutter_bottom_drawer: 19 | git: 20 | url: https://github.com/note11g/flutter_bottom_drawer.git 21 | ref: migrate_3.20 22 | permission_handler: ^11.3.0 23 | flutter_styled_toast: 2.2.1 24 | pull_to_refresh_flutter3: 2.0.2 25 | balloon_widget: ^0.0.1+1 26 | go_router: ^13.2.1 27 | get_it: ^7.6.7 28 | 29 | geolocator: ^14.0.1 30 | lat_compass: ^1.1.0 31 | 32 | dev_dependencies: 33 | flutter_test: 34 | sdk: flutter 35 | integration_test: 36 | sdk: flutter 37 | 38 | flutter_lints: ^2.0.0 39 | meta: ^1.10.0 40 | 41 | flutter: 42 | uses-material-design: true 43 | assets: 44 | - assets/ -------------------------------------------------------------------------------- /lib/src/messaging/channel_wrapper.dart: -------------------------------------------------------------------------------- 1 | part of "messaging.dart"; 2 | 3 | mixin NChannelWrapper { 4 | MethodChannel get channel; 5 | 6 | // optional implementation 7 | set channel(MethodChannel channel) {} 8 | 9 | // flag state 10 | bool isChannelInitialized = false; 11 | 12 | void initChannel( 13 | NChannel channelType, { 14 | required int id, 15 | Future Function(MethodCall)? handler, 16 | }) { 17 | channel = channelType._create(id)..setMethodCallHandler(handler); 18 | isChannelInitialized = true; 19 | } 20 | 21 | void disposeChannel() { 22 | if (isChannelInitialized) channel.setMethodCallHandler(null); 23 | } 24 | 25 | Future invokeMethod(String funcName, [NMessageable? arg]) { 26 | return channel.invokeMethod(funcName, arg?.payload); 27 | } 28 | 29 | Future invokeMethodWithIterable( 30 | String funcName, Iterable list) { 31 | final payload = list.map((item) => item.payload); 32 | return channel.invokeMethod(funcName, payload.toList()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/model/map/overlay/overlay/LazyOverlay.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.model.map.overlay.overlay 2 | 3 | import dev.note11.flutter_naver_map.flutter_naver_map.model.enum.NOverlayType 4 | import dev.note11.flutter_naver_map.flutter_naver_map.model.map.info.NOverlayInfo 5 | 6 | internal sealed interface LazyOverlay> : LazyOrAddableOverlay { 7 | val info: NOverlayInfo 8 | abstract override fun createMapOverlay(): NO 9 | 10 | val wrappedMarker: O 11 | 12 | companion object { 13 | fun fromMessageable( 14 | info: NOverlayInfo, 15 | args: Map, 16 | ): LazyOverlay<*, *> { 17 | require(info.type.isLazy) 18 | 19 | return when (info.type) { 20 | NOverlayType.CLUSTERABLE_MARKER -> NClusterableMarker.fromMessageable(args) 21 | else -> throw IllegalArgumentException("${info.type} is not LazyOverlay") 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/model/map/NLayerGroups.swift: -------------------------------------------------------------------------------- 1 | import NMapsMap 2 | 3 | internal struct NLayerGroups { 4 | let groups: Set 5 | 6 | func useWithEnableAndDisableGroups(fun: (_ enableGroups: Set, _ disableGroups: Set) -> Void) { 7 | let disableGroups = NLayerGroups.allLayerGroups.groups.subtracting(groups) 8 | 9 | fun(groups, disableGroups) 10 | } 11 | 12 | static func fromRawArr(rawArr: Any) -> NLayerGroups { 13 | NLayerGroups(groups: Set(asArr(rawArr, elementCaster: asString))) 14 | } 15 | 16 | private static let building: String = "building" 17 | private static let traffic: String = "ctt" 18 | private static let transit: String = "transit" 19 | private static let bicycle: String = "bike" 20 | private static let mountain: String = "mountain" 21 | private static let cadastral: String = "landparcel" 22 | 23 | private static let allLayerGroups: NLayerGroups = NLayerGroups(groups: [ 24 | building, traffic, transit, bicycle, mountain, cadastral 25 | ]) 26 | } -------------------------------------------------------------------------------- /ios/flutter_naver_map.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. 3 | # Run `pod lib lint flutter_naver_map.podspec` to validate before publishing. 4 | 5 | Pod::Spec.new do |s| 6 | s.name = 'flutter_naver_map' 7 | 8 | s.version = '1.4.2' 9 | s.summary = 'flutter naver map plugin' 10 | s.description = <<-DESC 11 | flutter naver map plugin 12 | DESC 13 | s.homepage = 'https://github.com/note11g/flutter_naver_map' 14 | s.license = { :file => '../LICENSE' } 15 | s.author = { 'note11g' => 'note11@kakao.com' } 16 | s.source = { :path => '.' } 17 | s.source_files = 'flutter_naver_map/Sources/flutter_naver_map/**/*.swift' 18 | s.dependency 'Flutter' 19 | s.dependency 'NMapsMap','3.23.0' 20 | s.platform = :ios, '12.0' 21 | 22 | # Flutter.framework does not contain a i386 slice. 23 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } 24 | s.swift_version = '5.0' 25 | end 26 | -------------------------------------------------------------------------------- /lib/src/util/app_lifecycle_binder.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/widgets.dart"; 2 | import "package:meta/meta.dart"; 3 | 4 | @internal 5 | mixin AppLifeCycleBinder { 6 | _AppLifeCycleBinderHelper? _helper; 7 | 8 | void bindAppLifecycleChange() { 9 | _helper = _AppLifeCycleBinderHelper( 10 | didChangeAppLifecycleStateCallback: didChangeAppLifecycleState); 11 | WidgetsBinding.instance.addObserver(_helper!); 12 | } 13 | 14 | void unbindAppLifecycleChange() { 15 | if (_helper == null) return; 16 | WidgetsBinding.instance.removeObserver(_helper!); 17 | _helper = null; 18 | } 19 | 20 | void didChangeAppLifecycleState(AppLifecycleState state) {} 21 | } 22 | 23 | class _AppLifeCycleBinderHelper with WidgetsBindingObserver { 24 | final Function(AppLifecycleState state) didChangeAppLifecycleStateCallback; 25 | 26 | _AppLifeCycleBinderHelper({ 27 | required this.didChangeAppLifecycleStateCallback, 28 | }); 29 | 30 | @override 31 | void didChangeAppLifecycleState(AppLifecycleState state) { 32 | didChangeAppLifecycleStateCallback(state); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/model/map/info/NOverlayQuery.swift: -------------------------------------------------------------------------------- 1 | internal struct NOverlayQuery { 2 | let info: NOverlayInfo 3 | let methodName: String 4 | 5 | var query: String { 6 | encode() 7 | } 8 | 9 | static func fromQuery(_ query: String) -> NOverlayQuery { 10 | decode(string: query) 11 | } 12 | 13 | // ----- Encode / Decode ----- 14 | 15 | private func encode() -> String { 16 | [info.type.rawValue, info.id, methodName].joined(separator: NOverlayQuery.SEPARATOR) 17 | } 18 | 19 | private static func decode(string: String) -> NOverlayQuery { 20 | let split = string.components(separatedBy: NOverlayQuery.SEPARATOR) 21 | let type = split.first! 22 | let method = split.last! 23 | let id = split.dropFirst().dropLast().joined(separator: NOverlayQuery.SEPARATOR) 24 | return NOverlayQuery( 25 | info: NOverlayInfo(type: NOverlayType(rawValue: type)!, id: id), 26 | methodName: method 27 | ) 28 | } 29 | 30 | private static let SEPARATOR = "\"" 31 | } -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/model/map/NaverMapViewOptions.swift: -------------------------------------------------------------------------------- 1 | import NMapsMap 2 | 3 | internal struct NaverMapViewOptions { 4 | private let args: Dictionary 5 | let consumeSymbolTapEvents: Bool 6 | 7 | static func fromMessageable(_ messageableDict: Dictionary) -> NaverMapViewOptions { 8 | let consumeSymbolTapEvents = asBool(messageableDict["consumeSymbolTapEvents"]!) 9 | return NaverMapViewOptions(args: messageableDict, consumeSymbolTapEvents: consumeSymbolTapEvents) 10 | } 11 | 12 | func updateWithNaverMapView(naverMap: NMFNaverMapView, isFirst: Bool, customStyleCallbacks: CustomStyleCallbacks? = nil) { 13 | naverMap.showCompass = false 14 | naverMap.showZoomControls = false 15 | naverMap.showScaleBar = false 16 | naverMap.showLocationButton = false 17 | naverMap.mapView.logoInteractionEnabled = false 18 | 19 | let applier = NaverMapApplierImpl(naverMap, isFirst: isFirst, customStyleCallbacks: customStyleCallbacks) 20 | try! applier.applyOptions(args: args) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_naver_map 2 | description: Naver Map plugin for Flutter, which provides map service of Korea. 3 | version: 1.4.2 4 | homepage: https://github.com/note11g/flutter_naver_map 5 | documentation: https://note11.dev/flutter_naver_map 6 | 7 | environment: 8 | sdk: '>=3.0.0 <4.0.0' 9 | flutter: ">=3.22.0" 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | vector_math: ^2.1.4 15 | path_provider: ^2.1.4 16 | crypto: ^3.0.5 17 | meta: ^1.12.0 # for flutter 3.22.0 18 | 19 | dev_dependencies: 20 | flutter_test: 21 | sdk: flutter 22 | flutter_lints: ^3.0.2 23 | 24 | flutter: 25 | plugin: 26 | platforms: 27 | android: 28 | package: dev.note11.flutter_naver_map.flutter_naver_map 29 | pluginClass: FlutterNaverMapPlugin 30 | ios: 31 | pluginClass: SwiftFlutterNaverMapPlugin 32 | 33 | assets: 34 | - ./assets/icon/location_overlay_icon.png 35 | - ./assets/icon/location_overlay_sub_icon.png 36 | - ./assets/icon/location_overlay_sub_icon_face.png 37 | - ./assets/font/Inter-fnm-scalebar-ss540.otf 38 | - ./version.json 39 | -------------------------------------------------------------------------------- /example/integration_test/clustering_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter_naver_map/flutter_naver_map.dart'; 4 | import 'package:flutter_test/flutter_test.dart'; 5 | import 'package:meta/meta.dart'; 6 | 7 | import 'util/test_util.dart'; 8 | 9 | @isTestGroup 10 | void clusteringTests() { 11 | group("clustering tests", () { 12 | testNaverMap("clustering marker setOnTapListener test", 13 | (controller, tester) async { 14 | final clusterableMarker = NClusterableMarker( 15 | id: "test", 16 | position: controller.nowCameraPosition.target, 17 | ); 18 | 19 | final completer = Completer(); 20 | clusterableMarker.setOnTapListener((cm) => completer.complete(cm.info)); 21 | 22 | await controller.addOverlay(clusterableMarker); 23 | await tester.flutterWidgetTester.pumpAndSettle(); 24 | 25 | await clusterableMarker.performClick(); 26 | 27 | final tappedInfo = await completer.future; 28 | print("Tapped info: $tappedInfo"); 29 | expect(tappedInfo, clusterableMarker.info); 30 | }); 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/model/map/overlay/clustering/NaverMapClusterOptions.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.model.map.overlay.clustering 2 | 3 | import dev.note11.flutter_naver_map.flutter_naver_map.converter.DefaultTypeConverter.asLong 4 | import dev.note11.flutter_naver_map.flutter_naver_map.converter.DefaultTypeConverter.asMap 5 | import dev.note11.flutter_naver_map.flutter_naver_map.model.base.NRange 6 | 7 | internal data class NaverMapClusterOptions( 8 | val enableZoomRange: NRange, 9 | val animationDuration: Long, 10 | val mergeStrategy: NClusterMergeStrategy, 11 | ) { 12 | companion object { 13 | fun fromMessageable(args: Any): NaverMapClusterOptions = args.asMap().let { map -> 14 | NaverMapClusterOptions( 15 | enableZoomRange = NRange.fromMessageableWithExactType(map["enableZoomRange"]!!), 16 | animationDuration = map["animationDuration"]!!.asLong(), 17 | mergeStrategy = NClusterMergeStrategy.fromMessageable(map["mergeStrategy"]!!), 18 | ) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/controller/overlay/handler/GroundOverlayHandler.swift: -------------------------------------------------------------------------------- 1 | import NMapsMap 2 | import Flutter 3 | 4 | internal protocol GroundOverlayHandler: OverlayHandler { 5 | func setBounds(_ groundOverlay: NMFGroundOverlay, rawBounds: Any) 6 | 7 | func setImage(_ groundOverlay: NMFGroundOverlay, rawNOverlayImage: Any) 8 | 9 | func setAlpha(_ groundOverlay: NMFGroundOverlay, rawAlpha: Any) 10 | } 11 | 12 | internal extension GroundOverlayHandler { 13 | func handleGroundOverlay(groundOverlay: NMFOverlay, 14 | method: String, 15 | args: Any?, 16 | result: @escaping FlutterResult) { 17 | let groundOverlay = groundOverlay as! NMFGroundOverlay 18 | switch method { 19 | case NGroundOverlay.boundsName: setBounds(groundOverlay, rawBounds: args!) 20 | case NGroundOverlay.imageName: setImage(groundOverlay, rawNOverlayImage: args!) 21 | case NGroundOverlay.alphaName: setAlpha(groundOverlay, rawAlpha: args!) 22 | default: result(FlutterMethodNotImplemented) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/model/map/info/NOverlayQuery.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.model.map.info 2 | 3 | import dev.note11.flutter_naver_map.flutter_naver_map.model.enum.NOverlayType 4 | 5 | internal data class NOverlayQuery(val info: NOverlayInfo, val methodName: String) { 6 | val query: String get() = encode() 7 | 8 | // ----- Encode ----- 9 | private fun encode(): String = 10 | listOf(info.type.toString(), info.id, methodName).joinToString(SEPARATOR) 11 | 12 | companion object { 13 | fun fromQuery(query: String): NOverlayQuery = decode(query) 14 | 15 | // ----- Decode ----- 16 | private fun decode(string: String): NOverlayQuery { 17 | val split = string.split(SEPARATOR) 18 | val type = split.first() 19 | val method = split.last() 20 | val id = split.drop(1).dropLast(1).joinToString(SEPARATOR) 21 | 22 | return NOverlayQuery( 23 | NOverlayInfo(NOverlayType.fromString(type), id), 24 | methodName = method 25 | ) 26 | } 27 | 28 | private const val SEPARATOR = "\"" 29 | } 30 | } -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled. 5 | 6 | version: 7 | revision: 52b3dc25f6471c27b2144594abb11c741cb88f57 8 | channel: stable 9 | 10 | project_type: plugin 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57 17 | base_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57 18 | - platform: android 19 | create_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57 20 | base_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57 21 | - platform: ios 22 | create_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57 23 | base_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57 24 | 25 | # User provided section 26 | 27 | # List of Local paths (relative to this file) that should be 28 | # ignored by the migrate tool. 29 | # 30 | # Files that are not part of the templates will be ignored by default. 31 | unmanaged_files: 32 | - 'lib/main.dart' 33 | - 'ios/Runner.xcodeproj/project.pbxproj' 34 | -------------------------------------------------------------------------------- /example/integration_test/camera_update_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_naver_map/flutter_naver_map.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | import 'package:meta/meta.dart'; 4 | 5 | import 'util/test_util.dart'; 6 | 7 | @isTestGroup 8 | void cameraUpdateTests() { 9 | testNaverMap("Camera Update With Reason Test", (controller, tester) async { 10 | (NCameraUpdateReason, bool)? lastMovedData; 11 | 12 | await Future.delayed(const Duration(milliseconds: 300)); 13 | final onCameraChangeStreamSubscription = tester 14 | .testPageState.onCameraChangeStreamController.stream 15 | .listen((event) { 16 | lastMovedData = event; 17 | print("[moved] $event"); 18 | }); 19 | 20 | final cameraUpdate = NCameraUpdate.zoomIn() 21 | ..setAnimation(duration: Duration.zero) 22 | ..setReason(NCameraUpdateReason.control); // 위치 UI는 location. 23 | 24 | await controller.updateCamera(cameraUpdate); 25 | await Future.delayed(const Duration(milliseconds: 100)); 26 | 27 | print(lastMovedData); 28 | 29 | expect(lastMovedData?.$1, NCameraUpdateReason.control); 30 | expect(lastMovedData?.$2, false); 31 | onCameraChangeStreamSubscription.cancel(); 32 | }); 33 | } 34 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/model/map/NLayerGroups.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.model.map 2 | 3 | import dev.note11.flutter_naver_map.flutter_naver_map.converter.DefaultTypeConverter.asList 4 | 5 | internal data class NLayerGroups(val groups: List) { 6 | 7 | fun useWithEnableAndDisableGroups(func: (enableGroups: List, disableGroups: List) -> Unit) { 8 | val disableGroups = ALL_LAYER_GROUPS.groups - groups.toSet() 9 | func(groups, disableGroups) 10 | } 11 | 12 | companion object { 13 | fun fromRawList(rawList: Any): NLayerGroups = NLayerGroups(rawList.asList { it.toString() }) 14 | 15 | private const val building: String = "building" 16 | private const val traffic: String = "ctt" 17 | private const val transit: String = "transit" 18 | private const val bicycle: String = "bike" 19 | private const val mountain: String = "mountain" 20 | private const val cadastral: String = "landparcel" 21 | 22 | private val ALL_LAYER_GROUPS: NLayerGroups = NLayerGroups( 23 | listOf(building, traffic, transit, bicycle, mountain, cadastral) 24 | ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/src/type/map/indoor/indoor_region.dart: -------------------------------------------------------------------------------- 1 | part of "../../../../flutter_naver_map.dart"; 2 | 3 | /// 실내 지도가 존재하는 영역을 나타내는 객체입니다. 4 | /// 5 | /// 여러 개의 구역([NIndoorZone])으로 구성되어 있습니다. 6 | class NIndoorRegion { 7 | final List zones; 8 | 9 | NIndoorRegion._(this.zones); 10 | 11 | factory NIndoorRegion._fromMessageable(dynamic m) { 12 | final listWithRawZone = m["zones"] as List; 13 | final zones = 14 | listWithRawZone.map((rawZone) => NIndoorZone._fromMessageable(rawZone)); 15 | return NIndoorRegion._(zones.toList()); 16 | } 17 | 18 | /// 이 실내 지도 영역에서 구역ID([zoneId])와 일치하는 id를 가진 구역([NIndoorZone])을 반환합니다. 19 | /// 일치하는 id를 가진 구역이 없다면, `null`을 반환합니다. 20 | NIndoorZone? getZone(String zoneId) { 21 | final filteredZone = zones.where((zone) => zone.id == zoneId); 22 | return filteredZone.isNotEmpty ? filteredZone.first : null; 23 | } 24 | 25 | /// 이 실내 지도 영역에서 구역ID([zoneId])와 일치하는 id를 가진 구역([NIndoorZone])의 인덱스를 반환합니다. 26 | /// 일치하는 id를 가진 구역이 없다면, `-1`을 반환합니다. 27 | int getZoneIndex(String zoneId) { 28 | final zone = getZone(zoneId); 29 | return zone != null ? zones.indexOf(zone) : -1; 30 | } 31 | 32 | @override 33 | String toString() { 34 | return "IndoorRegion{zones: $zones}"; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/converter/DefaultTypeConverter.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.converter 2 | 3 | internal object DefaultTypeConverter { 4 | fun Any.asBoolean(): Boolean = this as Boolean 5 | fun Any.asDouble(): Double = if (this is Int) toDouble() else this as Double 6 | fun Any.asFloat(): Float = if (this is Double) toFloat() else this as Float 7 | fun Any.asInt(): Int = if (this is Long) toInt() else this as Int 8 | fun Any.asLong(): Long = if (this is Int) toLong() else this as Long 9 | 10 | @Suppress("UNCHECKED_CAST") 11 | fun Any.asMap(): Map = this as Map 12 | 13 | @Suppress("UNCHECKED_CAST") 14 | fun Any.asNullableMap(): Map = this as Map 15 | 16 | @Suppress("UNCHECKED_CAST") 17 | fun Any.asStringMap(): Map = this as Map 18 | 19 | @Suppress("UNCHECKED_CAST") 20 | fun Any.asObjectKeyMap(): Map = this as Map 21 | 22 | @Suppress("UNCHECKED_CAST") 23 | fun Any.asList(elementCaster: ((Any) -> T)? = null): List = 24 | if (elementCaster == null) this as List 25 | else (this as List).map(elementCaster) 26 | } -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/model/map/overlay/NOverlayCaption.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | internal struct NOverlayCaption { 4 | let text: String 5 | let textSize: CGFloat 6 | let color: UIColor 7 | let haloColor: UIColor 8 | let minZoom: Double 9 | let maxZoom: Double 10 | let requestWidth: Double 11 | 12 | func toMessageable() -> Dictionary { 13 | [ 14 | "text": text, 15 | "textSize": textSize, 16 | "color": color.toInt(), 17 | "haloColor": haloColor.toInt(), 18 | "minZoom": minZoom, 19 | "maxZoom": maxZoom, 20 | "requestWidth": requestWidth 21 | ] 22 | } 23 | 24 | static func fromMessageable(_ v: Any) -> NOverlayCaption { 25 | let d = asDict(v) 26 | return NOverlayCaption( 27 | text: asString(d["text"]!), 28 | textSize: asCGFloat(d["textSize"]!), 29 | color: asUIColor(d["color"]!), 30 | haloColor: asUIColor(d["haloColor"]!), 31 | minZoom: asDouble(d["minZoom"]!), 32 | maxZoom: asDouble(d["maxZoom"]!), 33 | requestWidth: asDouble(d["requestWidth"]!) 34 | ) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/util/location/NDefaultMyLocationTrackerHeadingStreamHandler.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import Foundation 3 | import CoreLocation 4 | 5 | class NDefaultMyLocationTrackerHeadingStreamHandler: NSObject, FlutterStreamHandler, CLLocationManagerDelegate { 6 | private var eventSink: FlutterEventSink? 7 | private lazy var location: CLLocationManager = CLLocationManager() 8 | 9 | func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { 10 | self.eventSink = events; 11 | 12 | location.delegate = self; 13 | location.headingFilter = 0.1; 14 | location.startUpdatingHeading(); 15 | 16 | return nil; 17 | } 18 | 19 | func onCancel(withArguments arguments: Any?) -> FlutterError? { 20 | eventSink = nil 21 | 22 | location.stopUpdatingHeading(); 23 | location.delegate = nil; 24 | 25 | return nil 26 | } 27 | 28 | func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) { 29 | let accuracy = newHeading.headingAccuracy 30 | guard accuracy > 0 else { return } 31 | eventSink?(newHeading.trueHeading) 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/controller/overlay/handler/GroundOverlayHandler.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.controller.overlay.handler 2 | 3 | import com.naver.maps.map.overlay.GroundOverlay 4 | import com.naver.maps.map.overlay.Overlay 5 | import dev.note11.flutter_naver_map.flutter_naver_map.controller.overlay.OverlayHandler 6 | import dev.note11.flutter_naver_map.flutter_naver_map.model.map.overlay.overlay.NGroundOverlay 7 | import io.flutter.plugin.common.MethodChannel 8 | 9 | internal interface GroundOverlayHandler : OverlayHandler { 10 | fun handleGroundOverlay( 11 | groundOverlay: Overlay, method: String, arg: Any?, result: MethodChannel.Result, 12 | ) = (groundOverlay as GroundOverlay).let { g -> 13 | when (method) { 14 | NGroundOverlay.boundsName -> setBounds(g, arg!!) 15 | NGroundOverlay.imageName -> setImage(g, arg!!) 16 | NGroundOverlay.alphaName -> setAlpha(g, arg!!) 17 | else -> result.notImplemented() 18 | } 19 | } 20 | 21 | fun setBounds(groundOverlay: GroundOverlay, rawBounds: Any) 22 | 23 | fun setImage(groundOverlay: GroundOverlay, rawNOverlayImage: Any) 24 | 25 | fun setAlpha(groundOverlay: GroundOverlay, rawAlpha: Any) 26 | } -------------------------------------------------------------------------------- /lib/src/type/map/overlay/overlay/overlay_sender.dart: -------------------------------------------------------------------------------- 1 | part of "../../../../../flutter_naver_map.dart"; 2 | 3 | mixin _NOverlaySender { 4 | bool get _isAdded; 5 | 6 | List<_NOverlayController> get _overlayControllers; 7 | 8 | NOverlayInfo get info; 9 | 10 | Future _send(String method, [dynamic arguments]) async { 11 | if (!_isAdded) { 12 | throw NOverlayNotAddedOnMapException("Overlay Not added on Map!"); 13 | } 14 | 15 | final query = _NOverlayQuery(info, methodName: method).query; 16 | 17 | final messageable = arguments != null 18 | ? NMessageable.forOnce(NPayload.convertToMessageable(arguments!)) 19 | : null; 20 | 21 | dynamic lastValue; 22 | 23 | for (final overlayController in _overlayControllers) { 24 | lastValue = await overlayController.invokeMethod(query, messageable); 25 | } 26 | 27 | return lastValue; 28 | } 29 | 30 | void _set(String name, dynamic value) async { 31 | if (!_isAdded) return; 32 | await _send(name, value); 33 | } 34 | 35 | Future _getAsyncWithCast(String name, T Function(dynamic) cast) { 36 | return _send("get$name").then((value) => cast(value)); 37 | } 38 | 39 | Future _runAsync(String method, [dynamic arguments]) async => 40 | await _send(method, arguments); 41 | } 42 | -------------------------------------------------------------------------------- /lib/src/type/map/info/overlay_info.dart: -------------------------------------------------------------------------------- 1 | part of "../../../../flutter_naver_map.dart"; 2 | 3 | /// 오버레이의 정보를 나타내는 객체입니다. 4 | /// 5 | /// 식별자인 [id]와 오버레이의 종류를 나타내는 값인 [type]으로 구성되어 있습니다. 6 | /// 7 | /// type과 id로 고유하게 나타낼 수 있습니다. (id가 같아도, type이 다르면 다른 오버레이로 판별합니다) 8 | class NOverlayInfo with NMessageableWithMap implements NPickableInfo { 9 | /// 오버레이의 종류 10 | final NOverlayType type; 11 | 12 | /// 타입별 오버레이 식별자 (id가 같아도, type이 다르면 다른 오버레이로 판별합니다) 13 | final String id; 14 | 15 | /* ----- Constructor ----- */ 16 | 17 | const NOverlayInfo({required this.type, required this.id}); 18 | 19 | /* ----- NMessageable ----- */ 20 | 21 | factory NOverlayInfo._fromMessageable(dynamic m) => 22 | NOverlayInfo(type: NOverlayType._fromMessageable(m["type"]), id: m["id"]); 23 | 24 | @override 25 | NPayload toNPayload() => NPayload.make({"type": type, "id": id}); 26 | 27 | /* ----- Default Override ----- */ 28 | 29 | @override 30 | String toString() => "NOverlayInfo(type: $type, id: $id)"; 31 | 32 | @override 33 | bool operator ==(Object other) => 34 | identical(this, other) || 35 | other is NOverlayInfo && 36 | // runtimeType check pass (subclasses support needed) 37 | type == other.type && 38 | id == other.id; 39 | 40 | @override 41 | int get hashCode => type.hashCode ^ id.hashCode; 42 | } 43 | -------------------------------------------------------------------------------- /lib/src/initializer/flutter_naver_map_legacy_initializer_impl.dart: -------------------------------------------------------------------------------- 1 | part of "../../flutter_naver_map.dart"; 2 | 3 | class _NaverMapSdkImpl implements NaverMapSdk { 4 | @override 5 | bool _isInitialized = false; 6 | 7 | @override 8 | int? _androidSdkVersion; 9 | 10 | @override 11 | Function(NAuthFailedException ex)? onAuthFailed; 12 | 13 | @override 14 | Future initialize({ 15 | String? clientId, 16 | bool gov = false, 17 | Function(NAuthFailedException ex)? onAuthFailed, 18 | }) async { 19 | if (_isInitialized) return; 20 | 21 | NChannel.sdkChannel.setMethodCallHandler(_handler); 22 | this.onAuthFailed = onAuthFailed; 23 | 24 | final result = await NChannel.sdkChannel.invokeMethod("initialize", { 25 | "clientId": clientId, 26 | "gov": gov, 27 | "setAuthFailedListener": onAuthFailed != null 28 | }); 29 | 30 | if (result != null) _androidSdkVersion = result["androidSdkVersion"]; 31 | _isInitialized = true; 32 | 33 | log("SDK Initialized! (${Platform.operatingSystem}${Platform.isAndroid ? ", SDK $_androidSdkVersion" : ""})", 34 | name: "NaverMapSdk"); 35 | } 36 | 37 | Future _handler(MethodCall call) async { 38 | if (call.method == "onAuthFailed" && onAuthFailed != null) { 39 | onAuthFailed!.call(NAuthFailedException.fromMessageable(call.arguments)); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/src/type/map/indoor/indoor_zone.dart: -------------------------------------------------------------------------------- 1 | part of "../../../../flutter_naver_map.dart"; 2 | 3 | /// 실내 지도에서 하나의 구역을 나타내는 객체입니다. 4 | /// 5 | /// 실내 지도가 존재하는 영역을 나타내는 [NIndoorRegion]의 구성요소입니다. 6 | /// (여러개의 구역 = 하나의 영역) 7 | class NIndoorZone { 8 | /// 구역마다 존재하는 고유의 ID입니다. 9 | final String id; 10 | 11 | /// 기본으로 선택되는 구역의 층 인덱스를 나타냅니다. 12 | final int defaultLevelIndex; 13 | 14 | /// 구역의 층들을 나타냅니다. 15 | final List levels; 16 | 17 | /// 기본으로 선택되는 구역을 나타냅니다. 18 | NIndoorLevel get defaultLevel => levels[defaultLevelIndex]; 19 | 20 | NIndoorZone._(this.id, this.defaultLevelIndex, this.levels); 21 | 22 | factory NIndoorZone._fromMessageable(dynamic m) { 23 | final listWithRawLevel = m["levels"] as List; 24 | final levels = listWithRawLevel 25 | .map((rawLevel) => NIndoorLevel._fromMessageable(rawLevel)); 26 | return NIndoorZone._( 27 | m["id"], m["defaultLevelIndex"] as int, levels.toList()); 28 | } 29 | 30 | @override 31 | bool operator ==(Object other) => 32 | identical(this, other) || 33 | other is NIndoorZone && 34 | runtimeType == other.runtimeType && 35 | id == other.id; 36 | 37 | @override 38 | int get hashCode => id.hashCode; 39 | 40 | @override 41 | String toString() => """IndoorZone{ 42 | id: $id, 43 | defaultLevelIndex: $defaultLevelIndex, 44 | levels: $levels 45 | }"""; 46 | } 47 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/util/location/NDefaultMyLocationTrackerLocationStreamHandler.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import Foundation 3 | import CoreLocation 4 | import NMapsGeometry 5 | 6 | class NDefaultMyLocationTrackerLocationStreamHandler: NSObject, FlutterStreamHandler, CLLocationManagerDelegate { 7 | private var eventSink: FlutterEventSink? 8 | private lazy var location: CLLocationManager = CLLocationManager() 9 | 10 | func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { 11 | self.eventSink = events 12 | 13 | location.delegate = self 14 | location.desiredAccuracy = kCLLocationAccuracyBestForNavigation 15 | location.startUpdatingLocation() 16 | 17 | return nil 18 | } 19 | 20 | func onCancel(withArguments arguments: Any?) -> FlutterError? { 21 | eventSink = nil 22 | location.stopUpdatingLocation() 23 | location.delegate = nil 24 | 25 | return nil 26 | } 27 | 28 | func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { 29 | if let loc = locations.last { 30 | let latLng = NMGLatLng(lat: loc.coordinate.latitude.toDouble(), lng: loc.coordinate.longitude.toDouble()) 31 | eventSink?(latLng.toMessageable()) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /example/lib/util/example_location_tracker.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_naver_map/flutter_naver_map.dart'; 2 | import 'package:geolocator/geolocator.dart'; 3 | import 'package:lat_compass/lat_compass.dart'; 4 | 5 | class NExampleMyLocationTracker extends NMyLocationTracker { 6 | @override 7 | Stream get headingStream => 8 | LatCompass().onUpdate.map((e) => e.trueHeading); 9 | 10 | @override 11 | Stream get locationStream => Geolocator.getPositionStream( 12 | locationSettings: const LocationSettings( 13 | accuracy: LocationAccuracy.bestForNavigation, distanceFilter: 10)) 14 | .map((e) => NLatLng(e.latitude, e.longitude)); 15 | 16 | @override 17 | Future startLocationService() async { 18 | final serviceEnabled = await Geolocator.isLocationServiceEnabled(); 19 | if (!serviceEnabled) return null; 20 | 21 | final permission = await Geolocator.requestPermission(); 22 | final isGranted = switch (permission) { 23 | LocationPermission.whileInUse || LocationPermission.always => true, 24 | LocationPermission.denied || 25 | LocationPermission.deniedForever || 26 | LocationPermission.unableToDetermine => 27 | false, 28 | }; 29 | if (!isGranted) return null; 30 | 31 | final position = await Geolocator.getCurrentPosition(); 32 | return NLatLng(position.latitude, position.longitude); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/model/map/overlay/overlay/NGroundOverlay.swift: -------------------------------------------------------------------------------- 1 | import NMapsMap 2 | 3 | internal struct NGroundOverlay: AddableOverlay { 4 | typealias OverlayType = NMFGroundOverlay 5 | var overlayPayload: Dictionary = [:] 6 | 7 | let info: NOverlayInfo 8 | let bounds: NMGLatLngBounds 9 | let image: NOverlayImage 10 | let alpha: CGFloat 11 | 12 | func createMapOverlay() -> OverlayType { 13 | let overlay = NMFGroundOverlay(bounds: bounds, image: image.overlayImage) 14 | return applyAtRawOverlay(overlay) 15 | } 16 | 17 | func applyAtRawOverlay(_ overlay: NMFGroundOverlay) -> NMFGroundOverlay { 18 | overlay.alpha = alpha 19 | return overlay 20 | } 21 | 22 | static func fromMessageable(_ v: Any) -> NGroundOverlay { 23 | let d = asDict(v) 24 | return NGroundOverlay( 25 | info: NOverlayInfo.fromMessageable(d[infoName]!), 26 | bounds: asLatLngBounds(d[boundsName]!), 27 | image: NOverlayImage.fromMessageable(d[imageName]!), 28 | alpha: asCGFloat(d[alphaName]!) 29 | ) 30 | } 31 | 32 | /* 33 | --- Messaging Name Define --- 34 | */ 35 | 36 | private static let infoName = "info" 37 | static let boundsName = "bounds" 38 | static let imageName = "image" 39 | static let alphaName = "alpha" 40 | } 41 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | group 'dev.note11.flutter_naver_map.flutter_naver_map' 2 | version '1.0-SNAPSHOT' 3 | 4 | buildscript { 5 | ext.kotlin_version = '1.9.20' 6 | repositories { 7 | google() 8 | mavenCentral() 9 | } 10 | 11 | dependencies { 12 | classpath 'com.android.tools.build:gradle:8.11.1' 13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 14 | } 15 | } 16 | 17 | rootProject.allprojects { 18 | repositories { 19 | google() 20 | mavenCentral() 21 | maven { 22 | url 'https://repository.map.naver.com/archive/maven' 23 | } 24 | } 25 | } 26 | 27 | apply plugin: 'com.android.library' 28 | apply plugin: 'kotlin-android' 29 | 30 | android { 31 | namespace "dev.note11.flutter_naver_map.flutter_naver_map" 32 | 33 | compileSdk 36 34 | 35 | compileOptions { 36 | sourceCompatibility JavaVersion.VERSION_1_8 37 | targetCompatibility JavaVersion.VERSION_1_8 38 | } 39 | 40 | kotlinOptions { 41 | jvmTarget = '1.8' 42 | } 43 | 44 | sourceSets { 45 | main.java.srcDirs += 'src/main/kotlin' 46 | } 47 | 48 | defaultConfig { 49 | minSdkVersion 23 50 | } 51 | 52 | dependencies { 53 | implementation 'com.naver.maps:map-sdk:3.23.0' 54 | implementation 'com.google.android.gms:play-services-location:21.3.0' 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /test/camera_update_test.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter_naver_map/flutter_naver_map.dart"; 2 | import "package:flutter_test/flutter_test.dart"; 3 | 4 | void main() { 5 | test("cameraUpdateTest", () { 6 | const target1 = NLatLng(37.5666102, 126.9783881); 7 | const zoom1 = 17.0; 8 | const tilt1 = 30.0; 9 | const bearing1 = 45.0; 10 | final cu1 = NCameraUpdate.withParams( 11 | tilt: tilt1, zoom: zoom1, bearing: bearing1, target: target1); 12 | 13 | expect(cu1.toString(), 14 | "NCameraUpdate: {target: {lat: ${target1.latitude}, lng: ${target1.longitude}}, zoom: $zoom1, tilt: $tilt1, bearing: $bearing1, animation: easing, duration: 800, sign: withParams}"); 15 | 16 | const target2 = NLatLng(37.5662, 126.97); 17 | const zoom2 = 17.0; 18 | const tilt2 = 30.0; 19 | const bearing2 = 45.0; 20 | final cu2 = NCameraUpdate.withParams( 21 | tiltBy: tilt2, zoomBy: zoom2, bearingBy: bearing2, target: target2); 22 | 23 | expect(cu2.toString(), 24 | "NCameraUpdate: {target: {lat: ${target2.latitude}, lng: ${target2.longitude}}, zoomBy: $zoom2, tiltBy: $tilt2, bearingBy: $bearing2, animation: easing, duration: 800, sign: withParams}"); 25 | }); 26 | 27 | test("cameraUpdateAssertionTest", () { 28 | try { 29 | final cu = NCameraUpdate.withParams(tilt: 1, zoom: 1, zoomBy: 1); 30 | expect(cu, 1); 31 | } catch (e) { 32 | expect(e.runtimeType.toString(), "_AssertionError"); 33 | } 34 | }); 35 | } 36 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/model/base/NSize.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.model.base 2 | 3 | import androidx.annotation.Px 4 | import dev.note11.flutter_naver_map.flutter_naver_map.converter.DefaultTypeConverter.asDouble 5 | import dev.note11.flutter_naver_map.flutter_naver_map.converter.DefaultTypeConverter.asMap 6 | import dev.note11.flutter_naver_map.flutter_naver_map.util.DisplayUtil.dpToPx 7 | import dev.note11.flutter_naver_map.flutter_naver_map.util.DisplayUtil.pxToDp 8 | 9 | data class NSize( 10 | val width: Double, 11 | val height: Double, 12 | ) { 13 | 14 | fun useAsPixelSize(widthFunc: (px: Int) -> Unit, heightFunc: (px: Int) -> Unit) { 15 | widthFunc(dpToPx(width)) 16 | heightFunc(dpToPx(height)) 17 | } 18 | 19 | fun toMessageable(): Map { 20 | return mapOf( 21 | "width" to width, 22 | "height" to height 23 | ) 24 | } 25 | 26 | companion object { 27 | fun fromPixelSize(@Px width: Int, @Px height: Int): NSize { 28 | return NSize( 29 | width = pxToDp(width), 30 | height = pxToDp(height) 31 | ) 32 | } 33 | 34 | fun fromMessageable(rawNSize: Any): NSize = rawNSize.asMap().let { 35 | return NSize( 36 | width = it["width"]!!.asDouble(), 37 | height = it["height"]!!.asDouble() 38 | ) 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /lib/src/util/location/builtin/default_my_location_tracker.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter_naver_map/flutter_naver_map.dart"; 2 | import "default_my_location_tracker_platform_impl.dart"; 3 | 4 | class NDefaultMyLocationTracker extends NMyLocationTracker 5 | with NDefaultMyLocationTrackerPlatformImplMixin { 6 | final NDefaultMyLocationTrackerOptions options; 7 | final Function(bool isForeverDenied)? onPermissionDenied; 8 | 9 | NDefaultMyLocationTracker({ 10 | this.options = const NDefaultMyLocationTrackerOptions(), 11 | this.onPermissionDenied, 12 | }); 13 | 14 | @override 15 | Future startLocationService() async { 16 | final permissionStatus = await requestLocationPermission(); 17 | switch (permissionStatus) { 18 | case NDefaultMyLocationTrackerPermissionStatus.granted: 19 | return getCurrentPositionOnce(); 20 | case NDefaultMyLocationTrackerPermissionStatus.denied || 21 | NDefaultMyLocationTrackerPermissionStatus.deniedForever: 22 | onPermissionDenied?.call(permissionStatus == 23 | NDefaultMyLocationTrackerPermissionStatus.deniedForever); 24 | return null; 25 | } 26 | } 27 | 28 | @override 29 | void disposeLocationService() {} 30 | 31 | @override 32 | Stream get locationStream => getLocationStream(); 33 | 34 | @override 35 | Stream get headingStream => getHeadingStream(); 36 | } 37 | 38 | class NDefaultMyLocationTrackerOptions { 39 | const NDefaultMyLocationTrackerOptions(); 40 | } 41 | -------------------------------------------------------------------------------- /lib/src/util/math.dart: -------------------------------------------------------------------------------- 1 | import "dart:math"; 2 | 3 | import "package:flutter_naver_map/flutter_naver_map.dart"; 4 | import "package:vector_math/vector_math.dart" show radians, degrees; 5 | 6 | class MathUtil { 7 | static const earthRadius = 6378137.0; // wgs84 8 | static const earthDiameter = 12756274.0; // wgs84 9 | static const _tileSize = 256.0; 10 | 11 | static double measureDistance( 12 | double lat1, double lng1, double lat2, double lng2) { 13 | final rLat1 = radians(lat1), rLng1 = radians(lng1); 14 | final rLat2 = radians(lat2), rLng2 = radians(lng2); 15 | 16 | return earthDiameter * 17 | asin(sqrt(pow(sin((rLat1 - rLat2) / 2), 2) + 18 | cos(rLat1) * cos(rLat2) * pow(sin((rLng1 - rLng2) / 2), 2))); 19 | } 20 | 21 | static double meterToLatitude(double verticalMeter) { 22 | return degrees(verticalMeter / earthRadius); 23 | } 24 | 25 | static double meterToLongitude(double horizontalMeter, double latitude) { 26 | return degrees(horizontalMeter / (earthRadius * cos(radians(latitude)))); 27 | } 28 | 29 | static double calcMeterPerDp(double latitude, double zoom) { 30 | assert(NLatLng.minimumLatitude <= latitude && 31 | latitude <= NLatLng.maximumLatitude); 32 | assert(NaverMapViewOptions.minimumZoom <= zoom && 33 | zoom <= NaverMapViewOptions.maximumZoom); 34 | final latitudeScale = cos(radians(latitude)); 35 | final zoomScale = pi / pow(2.0, zoom) / _tileSize * earthRadius; 36 | return latitudeScale * zoomScale; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/model/base/NEdgeInsets.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.model.base 2 | 3 | import dev.note11.flutter_naver_map.flutter_naver_map.converter.DefaultTypeConverter.asDouble 4 | import dev.note11.flutter_naver_map.flutter_naver_map.converter.DefaultTypeConverter.asMap 5 | import dev.note11.flutter_naver_map.flutter_naver_map.util.DisplayUtil 6 | 7 | internal data class NEdgeInsets( 8 | private val left: Double, 9 | private val top: Double, 10 | private val right: Double, 11 | private val bottom: Double, 12 | val leftPx: Int, 13 | val topPx: Int, 14 | val rightPx: Int, 15 | val bottomPx: Int, 16 | ) { 17 | 18 | fun use(func: (left: Int, top: Int, right: Int, bottom: Int) -> T): T = 19 | func.invoke(leftPx, topPx, rightPx, bottomPx) 20 | 21 | companion object { 22 | fun fromMessageable(args: Any): NEdgeInsets = args.asMap().let { map -> 23 | val left = map["left"]!!.asDouble() 24 | val top = map["top"]!!.asDouble() 25 | val right = map["right"]!!.asDouble() 26 | val bottom = map["bottom"]!!.asDouble() 27 | NEdgeInsets( 28 | left, top, right, bottom, 29 | leftPx = DisplayUtil.dpToPx(left), 30 | topPx = DisplayUtil.dpToPx(top), 31 | rightPx = DisplayUtil.dpToPx(right), 32 | bottomPx = DisplayUtil.dpToPx(bottom) 33 | ) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/view/NaverMapViewFactory.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Flutter 3 | 4 | internal class NaverMapFactory: NSObject, FlutterPlatformViewFactory { 5 | private let messenger: FlutterBinaryMessenger 6 | 7 | init(messenger: FlutterBinaryMessenger) { 8 | self.messenger = messenger 9 | super.init() 10 | setHttpConnectionMaximum() 11 | } 12 | 13 | func create( 14 | withFrame frame: CGRect, 15 | viewIdentifier viewId: Int64, 16 | arguments args: Any? 17 | ) -> FlutterPlatformView { 18 | let channel = FlutterMethodChannel(name: SwiftFlutterNaverMapPlugin.createViewMethodChannelName(id: viewId), binaryMessenger: messenger) 19 | let overlayChannel = FlutterMethodChannel(name: SwiftFlutterNaverMapPlugin.createOverlayMethodChannelName(id: viewId), binaryMessenger: messenger) 20 | let overlayController = OverlayController(channel: overlayChannel) 21 | 22 | let convertedArgs = asNullableDict(args!) 23 | let options = NaverMapViewOptions.fromMessageable(convertedArgs) 24 | 25 | return NaverMapView(frame: frame, options: options, channel: channel, overlayController: overlayController) 26 | } 27 | 28 | func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol { 29 | FlutterStandardMessageCodec.sharedInstance() 30 | } 31 | 32 | private func setHttpConnectionMaximum() { 33 | URLSession.shared.configuration.httpMaximumConnectionsPerHost = 8 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) <2023-2025>, 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 2. Redistributions in binary form must reproduce the above copyright notice, 9 | this list of conditions and the following disclaimer in the documentation 10 | and/or other materials provided with the distribution. 11 | 3. Neither the name of the copyright holder nor the names of its contributors 12 | may be used to endorse or promote products derived from this software without 13 | specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /example/integration_test/now_camera_position_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_naver_map/flutter_naver_map.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | import 'package:meta/meta.dart'; 4 | 5 | import 'util/test_util.dart'; 6 | 7 | @isTestGroup 8 | void nowCameraPositionTests() { 9 | testNaverMap("now Camera position test", (controller, tester) async { 10 | final NCameraPosition cameraPositionFromFlutter = 11 | controller.nowCameraPosition; 12 | final cameraPositionFromPlatform = await controller.getCameraPosition(); 13 | 14 | // 오차범위 존재. 32bit 미만 자를 수 있는지 검토 필요. 15 | expectCameraPosition(cameraPositionFromFlutter, cameraPositionFromPlatform); 16 | 17 | const position1 = 18 | NCameraPosition(target: NLatLng(37.21312, 127.02321), zoom: 20); 19 | await controller.updateCamera(NCameraUpdate.fromCameraPosition(position1) 20 | ..setAnimation(duration: Duration.zero)); 21 | print("future completed: ${DateTime.now().millisecondsSinceEpoch}"); 22 | 23 | // iOS에서만 모두 이동되지 않았음에도 Future Complete 됨. Report needed. 24 | // (onCameraIdle) - (FutureComplete) 25 | // 3s animation : 76ms(ios) / 2ms(android), 26 | // 0s animation : 79ms(ios) / 1ms(android) 27 | await Future.delayed(const Duration(milliseconds: 200)); 28 | 29 | expectCameraPosition(controller.nowCameraPosition, position1); 30 | final cameraPositionFromPlatform2 = await controller.getCameraPosition(); 31 | expectCameraPosition( 32 | controller.nowCameraPosition, cameraPositionFromPlatform2); 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/model/base/NPoint.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.model.base 2 | 3 | import dev.note11.flutter_naver_map.flutter_naver_map.converter.DefaultTypeConverter.asDouble 4 | import dev.note11.flutter_naver_map.flutter_naver_map.converter.DefaultTypeConverter.asMap 5 | import dev.note11.flutter_naver_map.flutter_naver_map.util.CalcUtil 6 | import dev.note11.flutter_naver_map.flutter_naver_map.util.DisplayUtil 7 | 8 | internal data class NPoint( 9 | val x: Double, 10 | val y: Double, 11 | ) { 12 | companion object { 13 | fun fromMessageable(args: Any): NPoint = args.asMap().let { map -> 14 | NPoint(map["x"]!!.asDouble(), map["y"]!!.asDouble()) 15 | } 16 | 17 | fun fromPointF(pointF: android.graphics.PointF): NPoint { 18 | val x = CalcUtil.float32To64(pointF.x) 19 | val y = CalcUtil.float32To64(pointF.y) 20 | 21 | return NPoint(x, y) 22 | } 23 | 24 | fun fromPointFWithPx(pointF: android.graphics.PointF): NPoint { 25 | val x = DisplayUtil.pxToDp(pointF.x) 26 | val y = DisplayUtil.pxToDp(pointF.y) 27 | 28 | return NPoint(x, y) 29 | } 30 | } 31 | 32 | fun toPointF(): android.graphics.PointF = android.graphics.PointF(x.toFloat(), y.toFloat()) 33 | 34 | fun toPointFWithPx(): android.graphics.PointF = 35 | android.graphics.PointF(DisplayUtil.dpToPxF(x), DisplayUtil.dpToPxF(y)) 36 | 37 | fun toMessageable(): Map = mapOf("x" to x, "y" to y) 38 | } 39 | 40 | -------------------------------------------------------------------------------- /example/lib/util/alert_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_styled_toast/flutter_styled_toast.dart'; 3 | 4 | import '../design/theme.dart'; 5 | 6 | class AlertUtil { 7 | static void openAlert( 8 | String title, { 9 | required BuildContext context, 10 | Color? toastColor, 11 | Color? textColor, 12 | }) { 13 | toastColor ??= getColorTheme(context).onBackground; 14 | textColor ??= getColorTheme(context).background; 15 | 16 | showToast( 17 | title, 18 | context: context, 19 | textStyle: getTextTheme(context).titleSmall?.copyWith(color: textColor), 20 | backgroundColor: toastColor.withOpacity(0.92), 21 | toastHorizontalMargin: 24, 22 | position: 23 | const StyledToastPosition(align: Alignment.bottomCenter, offset: 36), 24 | animation: StyledToastAnimation.fade, 25 | reverseAnimation: StyledToastAnimation.fade, 26 | animDuration: const Duration(milliseconds: 300), 27 | ); 28 | } 29 | 30 | static void openErrorAlert(String title, {required BuildContext context}) { 31 | openAlert( 32 | title, 33 | context: context, 34 | toastColor: Colors.red.shade700, 35 | textColor: Colors.white, 36 | ); 37 | } 38 | 39 | static void openAlertIfResultTrue(String title, 40 | {required BuildContext context, 41 | required Future Function() callback}) { 42 | void openToast() => openErrorAlert(title, context: context); 43 | 44 | callback.call().then((open) { 45 | if (open) openToast(); 46 | }); 47 | } 48 | 49 | AlertUtil._(); 50 | } 51 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/applier/option/NaverMapOptionApplier.swift: -------------------------------------------------------------------------------- 1 | internal protocol NaverMapOptionApplier { 2 | func setInitialCameraPosition(_ rawPosition: Any) 3 | func setExtent(_ rawLatLngBounds: Any?) 4 | func setMapType(_ rawMapType: Any) 5 | func setLiteModeEnable(_ rawEnable: Any) 6 | func setNightModeEnable(_ rawEnable: Any) 7 | func setIndoorEnable(_ rawEnable: Any) 8 | func setActiveLayerGroups(_ rawLayerGroups: Any) 9 | func setBuildingHeight(_ rawHeight: Any) 10 | func setLightness(_ rawLightness: Any) 11 | func setSymbolScale(_ rawScale: Any) 12 | func setSymbolPerspectiveRatio(_ rawRatio: Any) 13 | func setIndoorFocusRadius(_ rawDp: Any) 14 | func setPickTolerance(_ rawDp: Any) 15 | func setRotationGesturesEnable(_ rawEnable: Any) 16 | func setScrollGesturesEnable(_ rawEnable: Any) 17 | func setTiltGesturesEnable(_ rawEnable: Any) 18 | func setZoomGesturesEnable(_ rawEnable: Any) 19 | func setStopGesturesEnable(_ rawEnable: Any) 20 | func setScrollGesturesFriction(_ rawFriction: Any) 21 | func setZoomGesturesFriction(_ rawFriction: Any) 22 | func setRotationGesturesFriction(_ rawFriction: Any) 23 | func setIndoorLevelPickerEnable(_ rawEnable: Any) 24 | func setLogoAlign(_ rawAlign: Any) 25 | func setLogoMargin(_ rawEdgeInsets: Any) 26 | func setContentPadding(_ rawEdgeInsets: Any) 27 | func setMinZoom(_ rawLevel: Any) 28 | func setMaxZoom(_ rawLevel: Any) 29 | func setMaxTilt(_ rawTilt: Any) 30 | func setLocale(_ rawLocale: Any) 31 | func setCustomStyleId(_ rawCustomStyleId: Any?) 32 | } 33 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/applier/option/NaverMapOptionApplier.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.applier.option 2 | 3 | internal interface NaverMapOptionApplier { 4 | fun setInitialCameraPosition(rawPosition: Any) 5 | fun setExtent(rawLatLngBounds: Any?) 6 | fun setMapType(rawMapType: Any) 7 | fun setLiteModeEnable(rawEnable: Any) 8 | fun setNightModeEnable(rawEnable: Any) 9 | fun setIndoorEnable(rawEnable: Any) 10 | fun setActiveLayerGroups(rawLayerGroups: Any) 11 | fun setBuildingHeight(rawHeight: Any) 12 | fun setLightness(rawLightness: Any) 13 | fun setSymbolScale(rawScale: Any) 14 | fun setSymbolPerspectiveRatio(rawRatio: Any) 15 | fun setIndoorFocusRadius(rawDp: Any) 16 | fun setPickTolerance(rawDp: Any) 17 | fun setRotationGesturesEnable(rawEnable: Any) 18 | fun setScrollGesturesEnable(rawEnable: Any) 19 | fun setTiltGesturesEnable(rawEnable: Any) 20 | fun setZoomGesturesEnable(rawEnable: Any) 21 | fun setStopGesturesEnable(rawEnable: Any) 22 | fun setScrollGesturesFriction(rawFriction: Any) 23 | fun setZoomGesturesFriction(rawFriction: Any) 24 | fun setRotationGesturesFriction(rawFriction: Any) 25 | fun setIndoorLevelPickerEnable(rawEnable: Any) 26 | fun setLogoAlign(rawAlign: Any) 27 | fun setLogoMargin(rawEdgeInsets: Any) 28 | fun setContentPadding(rawEdgeInsets: Any) 29 | fun setMinZoom(rawLevel: Any) 30 | fun setMaxZoom(rawLevel: Any) 31 | fun setMaxTilt(rawTilt: Any) 32 | fun setLocale(rawLocale: Any) 33 | fun setCustomStyleId(rawCustomStyleId: Any?) 34 | } -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/controller/overlay/handler/InfoWindowHandler.swift: -------------------------------------------------------------------------------- 1 | import NMapsMap 2 | import Flutter 3 | 4 | internal protocol InfoWindowHandler: OverlayHandler { 5 | func setText(_ infoWindow: NMFInfoWindow, rawText: Any) 6 | 7 | func setAnchor(_ infoWindow: NMFInfoWindow, rawNPoint: Any) 8 | 9 | func setAlpha(_ infoWindow: NMFInfoWindow, rawAlpha: Any) 10 | 11 | func setPosition(_ infoWindow: NMFInfoWindow, rawPosition: Any) 12 | 13 | func setOffsetX(_ infoWindow: NMFInfoWindow, rawOffsetX: Any) 14 | 15 | func setOffsetY(_ infoWindow: NMFInfoWindow, rawOffsetY: Any) 16 | 17 | func close(_ infoWindow: NMFInfoWindow) 18 | } 19 | 20 | internal extension InfoWindowHandler { 21 | func handleInfoWindow(infoWindow: NMFOverlay, 22 | method: String, 23 | args: Any?, 24 | result: @escaping FlutterResult) { 25 | let infoWindow = infoWindow as! NMFInfoWindow 26 | switch method { 27 | case NInfoWindow.textName: setText(infoWindow, rawText: args!) 28 | case NInfoWindow.anchorName: setAnchor(infoWindow, rawNPoint: args!) 29 | case NInfoWindow.alphaName: setAlpha(infoWindow, rawAlpha: args!) 30 | case NInfoWindow.positionName: setPosition(infoWindow, rawPosition: args!) 31 | case NInfoWindow.offsetXName: setOffsetX(infoWindow, rawOffsetX: args!) 32 | case NInfoWindow.offsetYName: setOffsetY(infoWindow, rawOffsetY: args!) 33 | case NInfoWindow.closeName: close(infoWindow) 34 | default: result(FlutterMethodNotImplemented) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/model/map/overlay/clustering/NClusterMergeStrategy.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.model.map.overlay.clustering 2 | 3 | import dev.note11.flutter_naver_map.flutter_naver_map.converter.DefaultTypeConverter.asDouble 4 | import dev.note11.flutter_naver_map.flutter_naver_map.converter.DefaultTypeConverter.asMap 5 | import dev.note11.flutter_naver_map.flutter_naver_map.converter.DefaultTypeConverter.asObjectKeyMap 6 | import dev.note11.flutter_naver_map.flutter_naver_map.model.base.NRange 7 | 8 | internal data class NClusterMergeStrategy( 9 | // val mergeByEachTagEnableZoomRanges: Map>, 10 | val willMergedScreenDistance: Map, Double>, 11 | val maxMergeableScreenDistance: Double, 12 | // val minClusterChildCount: Int, 13 | ) { 14 | 15 | companion object { 16 | fun fromMessageable(args: Any): NClusterMergeStrategy = args.asMap().let { map -> 17 | NClusterMergeStrategy( 18 | // mergeByEachTagEnableZoomRanges = map["mergeByEachTagEnableZoomRanges"]!!.asMap() 19 | // .mapValues { NRange.fromMessageableWithExactType(it.value) }, 20 | willMergedScreenDistance = map["willMergedScreenDistance"]!!.asObjectKeyMap() 21 | .mapKeys { 22 | NRange.fromMessageableWithExactType(it.key) 23 | }.mapValues { it.value.asDouble() }, 24 | maxMergeableScreenDistance = map["maxMergeableScreenDistance"]!!.asDouble(), 25 | // minClusterChildCount = map["minClusterChildCount"]!!.asInt(), 26 | ) 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /lib/src/type/map/overlay/overlay_caption.dart: -------------------------------------------------------------------------------- 1 | part of "../../../../flutter_naver_map.dart"; 2 | 3 | /// 마커에 텍스트로 부가정보를 나타낼 때 사용할 수 있는 캡션 객체입니다. 4 | /// 5 | /// 캡션의 크기나, 색깔, 캡션의 테두리 색깔, 폭, 보이는 줌 범위를 지정할 수 있습니다. 6 | class NOverlayCaption with NMessageableWithMap { 7 | /// 캡션 텍스트 8 | final String text; 9 | 10 | /// 캡션 텍스트 사이즈 11 | /// 12 | /// 기본값은 `12`입니다. 13 | final double textSize; 14 | 15 | /// 캡션 텍스트 색상 16 | /// 17 | /// 기본값은 [Colors.black]입니다. 18 | final Color color; 19 | 20 | /// 캡션 테두리 색상 21 | /// 22 | /// 기본값은 [Colors.white]입니다. 23 | final Color haloColor; 24 | 25 | /// 캡션이 보이는 최소 줌 레벨 26 | /// 27 | /// 기본 값은 지도의 최소 줌 레벨인 [NaverMapViewOptions.minimumZoom] (0)입니다. 28 | final double minZoom; 29 | 30 | /// 캡션이 보이는 최대 줌 레벨 31 | /// 32 | /// 기본 값은 지도의 최대 줌 레벨인 [NaverMapViewOptions.maximumZoom] (21)입니다. 33 | final double maxZoom; 34 | 35 | /// 개행될 너비를 지정합니다. 36 | /// 단위는 플러터에서 사용하는 것과 동일한 DP(논리픽셀)입니다. 37 | /// 38 | /// 개행은 어절단위로 이루어집니다. 39 | /// 40 | /// 기본 값은 제한하지 않음을 의미하는 `0`입니다. 41 | final double requestWidth; 42 | 43 | const NOverlayCaption({ 44 | required this.text, 45 | this.textSize = 12.0, 46 | this.color = Colors.black, 47 | this.haloColor = Colors.white, 48 | this.minZoom = NaverMapViewOptions.minimumZoom, 49 | this.maxZoom = NaverMapViewOptions.maximumZoom, 50 | this.requestWidth = 0, 51 | }); 52 | 53 | @override 54 | NPayload toNPayload() => NPayload.make({ 55 | "text": text, 56 | "textSize": textSize, 57 | "color": color, 58 | "haloColor": haloColor, 59 | "minZoom": minZoom, 60 | "maxZoom": maxZoom, 61 | "requestWidth": requestWidth, 62 | }); 63 | } 64 | -------------------------------------------------------------------------------- /lib/src/type/map/overlay/clustering/clusterable_marker.dart: -------------------------------------------------------------------------------- 1 | part of "../../../../../flutter_naver_map.dart"; 2 | 3 | /// 너무 많은 마커가 몰려 있을 때, 사용자 경험과 지도의 성능을 향상시키기 위해 클러스터링 기능을 사용할 수 있는 마커입니다. 4 | /// 5 | /// 기존 `NMarker`를 대신하여 [NaverMapController.addOverlay] 혹은 [NaverMapController.addOverlayAll]을 통해 지도에 추가하면 됩니다. 6 | /// 7 | /// `NaverMap.clusterOptions.mergeStrategy`에 따라, 여러 개의 [NClusterableMarker] 대신 [NClusterMarker]로 병합할 지 전략을 지정할 수 있습니다. 8 | class NClusterableMarker extends _NMarkerWrapper { 9 | // todo: 병합 전략 설명 추가 10 | /// `NClusterableMarker`의 태그를 지정할 수 있습니다. 11 | /// 12 | /// 이 태그에 따라, 여러 개의 클러스터블 마커를 [NClusterMarker]로 어떻게 병합할 지 전략을 지정할 수도 있습니다. 13 | 14 | @override 15 | NClusterableMarkerInfo get info => 16 | NClusterableMarkerInfo(id: super.info.id, tags: tags, position: position); 17 | 18 | final Map tags; 19 | 20 | NClusterableMarker({ 21 | required super.id, 22 | required super.position, 23 | this.tags = const {}, 24 | super.icon, 25 | super.iconTintColor, 26 | super.alpha, 27 | super.angle, 28 | super.anchor, 29 | super.size, 30 | super.caption, 31 | super.subCaption, 32 | super.captionAligns, 33 | super.captionOffset, 34 | super.isCaptionPerspectiveEnabled, 35 | super.isIconPerspectiveEnabled, 36 | super.isFlat, 37 | super.isForceShowCaption, 38 | super.isForceShowIcon, 39 | super.isHideCollidedCaptions, 40 | super.isHideCollidedMarkers, 41 | super.isHideCollidedSymbols, 42 | }) : super(type: NOverlayType.clusterableMarker); 43 | 44 | @override 45 | NPayload toNPayload() { 46 | return super.toNPayload().expandWith({ 47 | "info": info, 48 | }); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/controller/overlay/handler/CircleOverlayHandler.swift: -------------------------------------------------------------------------------- 1 | import NMapsMap 2 | import Flutter 3 | 4 | internal protocol CircleOverlayHandler: OverlayHandler { 5 | func setCenter(_ circleOverlay: NMFCircleOverlay, rawCenter: Any) 6 | 7 | func setRadius(_ circleOverlay: NMFCircleOverlay, rawRadius: Any) 8 | 9 | func setColor(_ circleOverlay: NMFCircleOverlay, rawColor: Any) 10 | 11 | func setOutlineColor(_ circleOverlay: NMFCircleOverlay, rawOutlineColor: Any) 12 | 13 | func setOutlineWidth(_ circleOverlay: NMFCircleOverlay, rawOutlineWidth: Any) 14 | 15 | func getBounds(_ circleOverlay: NMFCircleOverlay, result: (_ bounds: Dictionary) -> Void) 16 | } 17 | 18 | internal extension CircleOverlayHandler { 19 | func handleCircleOverlay(circleOverlay: NMFOverlay, 20 | method: String, 21 | args: Any?, 22 | result: @escaping FlutterResult) { 23 | let circleOverlay = circleOverlay as! NMFCircleOverlay 24 | switch method { 25 | case NCircleOverlay.centerName: setCenter(circleOverlay, rawCenter: args!) 26 | case NCircleOverlay.radiusName: setRadius(circleOverlay, rawRadius: args!) 27 | case NCircleOverlay.colorName: setColor(circleOverlay, rawColor: args!) 28 | case NCircleOverlay.outlineColorName: setOutlineColor(circleOverlay, rawOutlineColor: args!) 29 | case NCircleOverlay.outlineWidthName: setOutlineWidth(circleOverlay, rawOutlineWidth: args!) 30 | case getterName(NCircleOverlay.boundsName): getBounds(circleOverlay, result: result) 31 | default: result(FlutterMethodNotImplemented) 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/controller/overlay/handler/InfoWindowHandler.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.controller.overlay.handler 2 | 3 | import com.naver.maps.map.overlay.InfoWindow 4 | import com.naver.maps.map.overlay.Overlay 5 | import dev.note11.flutter_naver_map.flutter_naver_map.controller.overlay.OverlayHandler 6 | import dev.note11.flutter_naver_map.flutter_naver_map.model.map.overlay.overlay.NInfoWindow 7 | import io.flutter.plugin.common.MethodChannel 8 | 9 | internal interface InfoWindowHandler : OverlayHandler { 10 | fun handleInfoWindow( 11 | infoWindow: Overlay, method: String, arg: Any?, result: MethodChannel.Result, 12 | ) = (infoWindow as InfoWindow).let { i -> 13 | when (method) { 14 | NInfoWindow.textName -> setText(i, arg!!) 15 | NInfoWindow.anchorName -> setAnchor(i, arg!!) 16 | NInfoWindow.alphaName -> setAlpha(i, arg!!) 17 | NInfoWindow.positionName -> setPosition(i, arg!!) 18 | NInfoWindow.offsetXName -> setOffsetX(i, arg!!) 19 | NInfoWindow.offsetYName -> setOffsetY(i, arg!!) 20 | NInfoWindow.closeName -> close(i) 21 | else -> result.notImplemented() 22 | } 23 | } 24 | 25 | fun setText(infoWindow: InfoWindow, rawText: Any) 26 | 27 | fun setAnchor(infoWindow: InfoWindow, rawNPoint: Any) 28 | 29 | fun setAlpha(infoWindow: InfoWindow, rawAlpha: Any) 30 | 31 | fun setPosition(infoWindow: InfoWindow, rawPosition: Any) 32 | 33 | fun setOffsetX(infoWindow: InfoWindow, rawOffsetXDp: Any) 34 | 35 | fun setOffsetY(infoWindow: InfoWindow, rawOffsetYDp: Any) 36 | 37 | fun close(infoWindow: InfoWindow) 38 | } -------------------------------------------------------------------------------- /tool/update_readme_images.dart: -------------------------------------------------------------------------------- 1 | import "dart:io"; 2 | 3 | void main(List args) { 4 | // Get current branch name 5 | final branchResult = Process.runSync("git", ["rev-parse", "--abbrev-ref", "HEAD"]); 6 | final currentBranch = branchResult.stdout.toString().trim(); 7 | 8 | print("Current branch: $currentBranch"); 9 | 10 | // Read README.md 11 | final readmeFile = File("README.md"); 12 | if (!readmeFile.existsSync()) { 13 | print("README.md not found!"); 14 | exit(1); 15 | } 16 | 17 | var content = readmeFile.readAsStringSync(); 18 | 19 | // Define patterns to match various image URL formats 20 | final patterns = [ 21 | // Relative paths 22 | RegExp(r"!\[([^\]]*)\]\(\.?/?(docs_asset/[^)]+)\)"), 23 | // Existing jsDelivr URLs with any branch 24 | RegExp(r"!\[([^\]]*)\]\(https://cdn\.jsdelivr\.net/gh/note11g/flutter_naver_map@[^/]+/(docs_asset/[^)]+)\)"), 25 | // GitHub raw URLs 26 | RegExp(r"!\[([^\]]*)\]\(https://raw\.githubusercontent\.com/note11g/flutter_naver_map/[^/]+/(docs_asset/[^)]+)\)"), 27 | ]; 28 | 29 | // Replace all image URLs with jsDelivr CDN URLs for current branch 30 | for (final pattern in patterns) { 31 | content = content.replaceAllMapped(pattern, (match) { 32 | final altText = match.group(1) ?? ""; 33 | final imagePath = match.group(2) ?? match.group(match.groupCount); 34 | return "![$altText](https://cdn.jsdelivr.net/gh/note11g/flutter_naver_map@$currentBranch/$imagePath)"; 35 | }); 36 | } 37 | 38 | // Write back to README.md 39 | readmeFile.writeAsStringSync(content); 40 | 41 | print("README.md image URLs updated for branch: $currentBranch"); 42 | print("All images now use jsDelivr CDN with current branch."); 43 | } -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/view/NaverMapView.swift: -------------------------------------------------------------------------------- 1 | import NMapsMap 2 | import Flutter 3 | 4 | internal class NaverMapView: NSObject, FlutterPlatformView { 5 | private let naverMap: NMFNaverMapView! 6 | private let naverMapViewOptions: NaverMapViewOptions 7 | private let naverMapControlSender: NaverMapControlSender 8 | private var eventDelegate: NaverMapViewEventDelegate! 9 | 10 | init(frame: CGRect, options: NaverMapViewOptions, channel: FlutterMethodChannel, overlayController: OverlayController) { 11 | naverMap = NMFNaverMapView(frame: frame) 12 | naverMapViewOptions = options 13 | naverMapControlSender = NaverMapController(naverMap: naverMap, channel: channel, overlayController: overlayController) 14 | super.init() 15 | 16 | naverMapViewOptions.updateWithNaverMapView(naverMap: naverMap, isFirst: true) 17 | onMapReady() 18 | } 19 | 20 | private func onMapReady() { 21 | setMapTapListener() 22 | naverMapControlSender.onMapReady() 23 | deactivateLogo() 24 | } 25 | 26 | private func deactivateLogo() { 27 | let subviews = naverMap.mapView.subviews 28 | if (subviews.count < 2) { return } 29 | let logoIncludedView = subviews[1] 30 | logoIncludedView.isHidden = true 31 | } 32 | 33 | private func setMapTapListener() { 34 | eventDelegate = NaverMapViewEventDelegate(sender: naverMapControlSender, 35 | initializeConsumeSymbolTapEvents: naverMapViewOptions.consumeSymbolTapEvents) 36 | eventDelegate.registerDelegates(mapView: naverMap.mapView) 37 | } 38 | 39 | func view() -> UIView { 40 | naverMap 41 | } 42 | 43 | deinit { 44 | naverMapControlSender.dispose() 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/model/map/overlay/NOverlayImage.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.model.map.overlay 2 | 3 | import com.naver.maps.map.overlay.OverlayImage 4 | import dev.note11.flutter_naver_map.flutter_naver_map.FlutterNaverMapPlugin 5 | import dev.note11.flutter_naver_map.flutter_naver_map.converter.DefaultTypeConverter.asMap 6 | import dev.note11.flutter_naver_map.flutter_naver_map.model.enum.NOverlayImageMode 7 | 8 | internal data class NOverlayImage( 9 | val path: String, 10 | val mode: NOverlayImageMode, 11 | ) { 12 | fun applyToOverlay(setImgFunc: (OverlayImage) -> Unit) = setImgFunc.invoke(overlayImage) 13 | 14 | private val overlayImage: OverlayImage 15 | get() = when (mode) { 16 | NOverlayImageMode.FILE, NOverlayImageMode.TEMP, NOverlayImageMode.WIDGET -> makeOverlayImageWithPath() 17 | NOverlayImageMode.ASSET -> makeOverlayImageWithAssetPath() 18 | } 19 | 20 | 21 | private fun makeOverlayImageWithPath(): OverlayImage = OverlayImage.fromPath(path) 22 | 23 | private fun makeOverlayImageWithAssetPath(): OverlayImage { 24 | val assetPath = FlutterNaverMapPlugin.getAssets(path) 25 | return OverlayImage.fromAsset(assetPath) 26 | } 27 | 28 | fun toMessageable(): Map = mapOf("path" to path, "mode" to mode.toString()) 29 | 30 | companion object { 31 | fun fromMessageable(args: Any): NOverlayImage = args.asMap().let { 32 | NOverlayImage( 33 | path = it["path"]!!.toString(), 34 | mode = NOverlayImageMode.fromString(it["mode"]!!.toString()), 35 | ) 36 | } 37 | 38 | val none = NOverlayImage(path = "", mode = NOverlayImageMode.TEMP) 39 | } 40 | } -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/model/map/overlay/NOverlayImage.swift: -------------------------------------------------------------------------------- 1 | import NMapsMap 2 | 3 | internal struct NOverlayImage { 4 | let path: String 5 | let mode: NOverlayImageMode 6 | 7 | var overlayImage: NMFOverlayImage { 8 | switch mode { 9 | case .file, .temp, .widget: return makeOverlayImageWithPath() 10 | case .asset: return makeOverlayImageWithAssetPath() 11 | } 12 | } 13 | 14 | private func makeOverlayImageWithPath() -> NMFOverlayImage { 15 | let image = UIImage(contentsOfFile: path) 16 | let scaledImage = UIImage(data: image!.pngData()!, scale: DisplayUtil.scale) 17 | let overlayImg = NMFOverlayImage(image: scaledImage!) 18 | return overlayImg 19 | } 20 | 21 | private func makeOverlayImageWithAssetPath() -> NMFOverlayImage { 22 | let key = SwiftFlutterNaverMapPlugin.getAssets(path: path) 23 | let assetPath = Bundle.main.path(forResource: key, ofType: nil) ?? "" 24 | let image = UIImage(contentsOfFile: assetPath) 25 | let scaledImage = UIImage(data: image!.pngData()!, scale: DisplayUtil.scale) 26 | let overlayImg = NMFOverlayImage(image: scaledImage!, reuseIdentifier: assetPath) 27 | return overlayImg 28 | } 29 | 30 | func toMessageable() -> Dictionary { 31 | [ 32 | "path": path, 33 | "mode": mode.rawValue 34 | ] 35 | } 36 | 37 | static func fromMessageable(_ v: Any) -> NOverlayImage { 38 | let d = asDict(v) 39 | return NOverlayImage( 40 | path: asString(d["path"]!), 41 | mode: NOverlayImageMode(rawValue: asString(d["mode"]!))! 42 | ) 43 | } 44 | 45 | static let none = NOverlayImage(path: "", mode: .temp) 46 | } 47 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/model/map/overlay/overlay/NCircleOverlay.swift: -------------------------------------------------------------------------------- 1 | import NMapsMap 2 | 3 | internal struct NCircleOverlay: AddableOverlay { 4 | typealias OverlayType = NMFCircleOverlay 5 | var overlayPayload: Dictionary = [:] 6 | 7 | let info: NOverlayInfo 8 | let center: NMGLatLng 9 | let radius: Double 10 | let color: UIColor 11 | let outlineColor: UIColor 12 | let outlineWidth: Double 13 | 14 | func createMapOverlay() -> OverlayType { 15 | let overlay = NMFCircleOverlay(center, radius: radius) 16 | return applyAtRawOverlay(overlay) 17 | } 18 | 19 | func applyAtRawOverlay(_ overlay: NMFCircleOverlay) -> NMFCircleOverlay { 20 | overlay.fillColor = color 21 | overlay.outlineColor = outlineColor 22 | overlay.outlineWidth = outlineWidth 23 | return overlay 24 | } 25 | 26 | static func fromMessageable(_ v: Any) -> NCircleOverlay { 27 | let d = asDict(v) 28 | return NCircleOverlay( 29 | info: NOverlayInfo.fromMessageable(d[infoName]!), 30 | center: asLatLng(d[centerName]!), 31 | radius: asDouble(d[radiusName]!), 32 | color: asUIColor(d[colorName]!), 33 | outlineColor: asUIColor(d[outlineColorName]!), 34 | outlineWidth: asDouble(d[outlineWidthName]!) 35 | ) 36 | } 37 | 38 | /* 39 | --- Messaging Name Define --- 40 | */ 41 | private static let infoName = "info" 42 | static let centerName = "center" 43 | static let radiusName = "radius" 44 | static let colorName = "color" 45 | static let outlineColorName = "outlineColor" 46 | static let outlineWidthName = "outlineWidth" 47 | static let boundsName = "bounds" 48 | } 49 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/model/map/info/NOverlayInfo.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.model.map.info 2 | 3 | import com.naver.maps.map.overlay.Overlay 4 | import dev.note11.flutter_naver_map.flutter_naver_map.converter.DefaultTypeConverter.asMap 5 | import dev.note11.flutter_naver_map.flutter_naver_map.model.enum.NOverlayType 6 | 7 | internal open class NOverlayInfo(open val type: NOverlayType, open val id: String) : NPickableInfo { 8 | companion object { 9 | fun fromMessageable(rawMap: Any): NOverlayInfo = rawMap.asMap().let { map -> 10 | NOverlayInfo( 11 | type = NOverlayType.fromString(map["type"]!!.toString()), 12 | id = map["id"]!!.toString(), 13 | ) 14 | } 15 | 16 | fun fromOverlay(overlay: Overlay): NOverlayInfo = overlay.tag as NOverlayInfo 17 | 18 | val locationOverlayInfo = NOverlayInfo(NOverlayType.LOCATION_OVERLAY, "L") 19 | } 20 | 21 | override fun toMessageable(): Map = mapOf("type" to type.toString(), "id" to id) 22 | 23 | fun saveAtOverlay(overlay: Overlay) { 24 | overlay.tag = this 25 | } 26 | 27 | 28 | // ----- HashCode & Equals ----- 29 | 30 | override fun equals(other: Any?): Boolean { 31 | if (this === other) return true 32 | if (other !is NOverlayInfo) return false 33 | 34 | if (type != other.type) return false 35 | if (id != other.id) return false 36 | 37 | return true 38 | } 39 | 40 | override fun hashCode(): Int { 41 | var result = type.hashCode() 42 | result = 31 * result + id.hashCode() 43 | return result 44 | } 45 | 46 | override fun toString(): String { 47 | return "NOverlayInfo(type=$type, id='$id')" 48 | } 49 | } -------------------------------------------------------------------------------- /test/calc_test.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter_naver_map/flutter_naver_map.dart"; 2 | import "package:flutter_naver_map/src/util/math.dart"; 3 | import "package:flutter_test/flutter_test.dart"; 4 | 5 | void main() { 6 | test("create random latlng test", () { 7 | // 계산 오차 허용 범위 1000dp 기준으로 측정시 0.1mm 8 | expect(MathUtil.calcMeterPerDp(37.566658794690994, 11), 9 | closeTo(30.29369736797371, 0.0000001)); // 0.01m == 1cm, 10 | expect(MathUtil.calcMeterPerDp(37.58233329511972, 15.111723569204258), 11 | closeTo(1.751897698224819, 0.0000001)); 12 | expect(MathUtil.calcMeterPerDp(33.117793960720206, 16.180300007459696), 13 | closeTo(0.8827919701389101, 0.0000001)); 14 | 15 | /// 32bit float test (latlng) 16 | const originLat1 = NLatLng.maximumLatitude - 0.01; 17 | const originLat2 = 43.00972; 18 | // worst case: lat=90, zoomLevel=0 (NLatLng.maximumLatitude, NaverMapViewOptions.minimumZoom) 19 | // 약 13.661m/dp (±0.0013661m), 1000dp 기준 오차범위 ±0.1dp (pixelRatio ≦ 3일때, < 0.5 hardware pixel) 20 | // 대한민국 기준 worst case: lat=43.00972 (한반도 최북단, 출처: https://library.krihs.re.kr/local/html/landGuide) 21 | // 약 57235.1m/dp (±0.0009318m), 1000dp 기준 오차범위 ±0.0163dp 22 | // 23 | // 따라서, 테스트 허용 오차범위는 worst 값인 ±0.0014m로 설정. 24 | final original1 = 25 | MathUtil.calcMeterPerDp(originLat1, NaverMapViewOptions.minimumZoom); 26 | final margined1 = MathUtil.calcMeterPerDp( 27 | originLat1 + 0.000001, NaverMapViewOptions.minimumZoom); 28 | expect(margined1, closeTo(original1, 0.0014)); 29 | 30 | final original2 = 31 | MathUtil.calcMeterPerDp(originLat2, NaverMapViewOptions.minimumZoom); 32 | final margined2 = MathUtil.calcMeterPerDp( 33 | originLat2 + 0.000001, NaverMapViewOptions.minimumZoom); 34 | expect(margined2, closeTo(original2, 0.001)); 35 | }); 36 | } 37 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '12.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | 41 | target.build_configurations.each do |config| 42 | config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [ 43 | '$(inherited)', 44 | ## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse] 45 | 'PERMISSION_LOCATION=1', 46 | ] 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/model/map/info/NOverlayInfo.swift: -------------------------------------------------------------------------------- 1 | import NMapsMap 2 | 3 | internal class NOverlayInfo: NSObject, NPickableInfo { 4 | let type: NOverlayType 5 | let id: String 6 | 7 | init(type: NOverlayType, id: String) { 8 | self.type = type 9 | self.id = id 10 | } 11 | 12 | static func fromMessageable(_ v: Any) -> NOverlayInfo { 13 | let d = asDict(v) 14 | return NOverlayInfo( 15 | type: NOverlayType(rawValue: asString(d["type"]!))!, 16 | id: asString(d["id"]!) 17 | ) 18 | } 19 | 20 | static func fromOverlay(_ overlay: NMFOverlay) -> NOverlayInfo { 21 | overlay.userInfo["info"] as! NOverlayInfo 22 | } 23 | 24 | func toMessageable() -> Dictionary { 25 | ["type": type.rawValue, "id": id] 26 | } 27 | 28 | func saveAtOverlay(_ overlay: NMFOverlay) { 29 | overlay.userInfo["info"] = self 30 | } 31 | 32 | static let locationOverlayInfo = NOverlayInfo(type: .locationOverlay, id: "L") 33 | 34 | // ----- Hashable ----- 35 | 36 | override var hash: Int { 37 | var hasher = Hasher() 38 | hasher.combine(type) 39 | hasher.combine(id) 40 | return hasher.finalize() 41 | } 42 | 43 | override func isEqual(_ o: Any?) -> Bool { 44 | guard let o = o as? NOverlayInfo else { 45 | return false 46 | } 47 | if self === o { 48 | return true 49 | } 50 | return o.type == self.type && o.id == self.id 51 | } 52 | 53 | static func ==(i1: NOverlayInfo, i2: NOverlayInfo) -> Bool { 54 | if i1.type != i2.type { 55 | return false 56 | } 57 | if i1.id != i2.id { 58 | return false 59 | } 60 | return true 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/model/map/info/NClusterableMarkerInfo.swift: -------------------------------------------------------------------------------- 1 | import NMapsMap 2 | 3 | internal class NClusterableMarkerInfo: NOverlayInfo, NMCClusteringKey { 4 | let tags: Dictionary 5 | let position: NMGLatLng 6 | 7 | var messageOverlayInfo: NOverlayInfo { NOverlayInfo(type: .clusterableMarker, id: id) } 8 | 9 | init(id: String, tags: Dictionary, position: NMGLatLng) { 10 | self.tags = tags 11 | self.position = position 12 | super.init(type: .clusterableMarker, id: id) 13 | } 14 | 15 | static func ==(i1: NClusterableMarkerInfo, i2: NClusterableMarkerInfo) -> Bool { 16 | if i1.id != i2.id { 17 | return false 18 | } 19 | return true 20 | } 21 | 22 | override func isEqual(_ o: Any?) -> Bool { 23 | guard let o = o as? NOverlayInfo else { 24 | return false 25 | } 26 | if self === o { 27 | return true 28 | } 29 | return o.id == self.id 30 | } 31 | 32 | override var hash: Int { 33 | return self.id.hashValue 34 | } 35 | 36 | func copy(with zone: NSZone? = nil) -> Any { 37 | return NClusterableMarkerInfo(id: self.id, tags: self.tags, position: self.position) 38 | } 39 | 40 | override func toMessageable() -> Dictionary { 41 | super.toMessageable().merging([ 42 | "tags": tags, 43 | "position": position.toMessageable() 44 | ]) { _, v2 in v2 } 45 | } 46 | 47 | static func fromMessageableAtClusterableMarker(_ v: Any) -> NClusterableMarkerInfo { 48 | let d = asDict(v) 49 | return NClusterableMarkerInfo( 50 | id: asString(d["id"]!), 51 | tags: asStringDict(d["tags"]!), 52 | position: asLatLng(d["position"]!) 53 | ) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/controller/overlay/handler/CircleOverlayHandler.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.controller.overlay.handler 2 | 3 | import com.naver.maps.map.overlay.CircleOverlay 4 | import com.naver.maps.map.overlay.Overlay 5 | import dev.note11.flutter_naver_map.flutter_naver_map.controller.overlay.OverlayHandler 6 | import dev.note11.flutter_naver_map.flutter_naver_map.controller.overlay.OverlayHandler.Companion.getterName 7 | import dev.note11.flutter_naver_map.flutter_naver_map.model.map.overlay.overlay.NCircleOverlay 8 | import io.flutter.plugin.common.MethodChannel 9 | 10 | internal interface CircleOverlayHandler : OverlayHandler { 11 | fun handleCircleOverlay( 12 | circleOverlay: Overlay, method: String, arg: Any?, result: MethodChannel.Result, 13 | ) = (circleOverlay as CircleOverlay).let { c -> 14 | when (method) { 15 | NCircleOverlay.centerName -> setCenter(c, arg!!) 16 | NCircleOverlay.radiusName -> setRadius(c, arg!!) 17 | NCircleOverlay.colorName -> setColor(c, arg!!) 18 | NCircleOverlay.outlineColorName -> setOutlineColor(c, arg!!) 19 | NCircleOverlay.outlineWidthName -> setOutlineWidth(c, arg!!) 20 | getterName(NCircleOverlay.boundsName) -> getBounds(c, result::success) 21 | else -> result.notImplemented() 22 | } 23 | } 24 | 25 | fun setCenter(circleOverlay: CircleOverlay, rawCenter: Any) 26 | 27 | fun setRadius(circleOverlay: CircleOverlay, rawRadius: Any) 28 | 29 | fun setColor(circleOverlay: CircleOverlay, rawColor: Any) 30 | 31 | fun setOutlineColor(circleOverlay: CircleOverlay, rawOutlineColor: Any) 32 | 33 | fun setOutlineWidth(circleOverlay: CircleOverlay, rawOutlineWidth: Any) 34 | 35 | fun getBounds(circleOverlay: CircleOverlay, result: (bounds: Map) -> Unit) 36 | } -------------------------------------------------------------------------------- /lib/src/type/map/camera/camera_position.dart: -------------------------------------------------------------------------------- 1 | part of "../../../../flutter_naver_map.dart"; 2 | 3 | /// 카메라가 보여주는 곳을 나타낼 때 사용하는 객체입니다. 4 | /// 5 | /// 문서를 참고하세요. [문서 보러가기](https://note11.dev/flutter_naver_map/element/camera#ncameraposition) 6 | class NCameraPosition with NMessageableWithMap { 7 | final NLatLng target; 8 | final double zoom; 9 | final double tilt; 10 | final double bearing; 11 | 12 | const NCameraPosition( 13 | {required this.target, 14 | required this.zoom, 15 | this.tilt = 0, 16 | this.bearing = 0}); 17 | 18 | factory NCameraPosition._fromMessageable(dynamic map) { 19 | return NCameraPosition( 20 | target: NLatLng.fromMessageable(map["target"]), 21 | zoom: map["zoom"], 22 | tilt: map["tilt"], 23 | bearing: map["bearing"], 24 | ); 25 | } 26 | 27 | NCameraPosition copyWith({ 28 | NLatLng? target, 29 | double? zoom, 30 | double? tilt, 31 | double? bearing, 32 | }) => 33 | NCameraPosition( 34 | target: target ?? this.target, 35 | zoom: zoom ?? this.zoom, 36 | bearing: bearing ?? this.bearing, 37 | tilt: tilt ?? this.tilt, 38 | ); 39 | 40 | @override 41 | bool operator ==(Object other) => 42 | identical(this, other) || 43 | other is NCameraPosition && 44 | runtimeType == other.runtimeType && 45 | target == other.target && 46 | zoom == other.zoom && 47 | tilt == other.tilt && 48 | bearing == other.bearing; 49 | 50 | @override 51 | int get hashCode => 52 | target.hashCode ^ zoom.hashCode ^ tilt.hashCode ^ bearing.hashCode; 53 | 54 | @override 55 | String toString() => "$runtimeType: ${toNPayload().map}"; 56 | 57 | @override 58 | NPayload toNPayload() => NPayload.make({ 59 | "target": target, 60 | "zoom": zoom, 61 | "tilt": tilt, 62 | "bearing": bearing, 63 | }); 64 | } 65 | -------------------------------------------------------------------------------- /lib/src/util/image_util.dart: -------------------------------------------------------------------------------- 1 | import "dart:developer" show log; 2 | import "dart:io" show Directory, File, FileSystemException; 3 | import "dart:typed_data" show Uint8List; 4 | 5 | import "package:crypto/crypto.dart" show sha256; 6 | import "package:path_provider/path_provider.dart" show getTemporaryDirectory; 7 | 8 | class ImageUtil { 9 | static final Map _hashPathMap = {}; 10 | 11 | static Future saveImage(Uint8List bytes) async { 12 | final hash = _generateImageHashFromBytes(bytes); 13 | late final String path; 14 | if (_hashPathMap.containsKey(hash)) { 15 | path = _hashPathMap[hash]!; 16 | log("이미 저장된 이미지입니다. 저장된 path를 반환합니다. $path", name: "ImageSaveUtil"); 17 | } else { 18 | path = await _makeFile(hash, bytes); 19 | _hashPathMap[hash] = path; 20 | } 21 | return path; 22 | } 23 | 24 | /* ----- Hashing ----- */ 25 | 26 | static String _generateImageHashFromBytes(Uint8List bytes) { 27 | final digest = sha256.convert(bytes); 28 | return digest.toString(); 29 | } 30 | 31 | /* ----- File ----- */ 32 | 33 | static Future _makeFile(String hash, Uint8List bytes) async { 34 | final tempDirPath = await _getDir().then((d) => d.path); 35 | final path = "$tempDirPath/$hash.png"; 36 | try { 37 | final file = await File(path).writeAsBytes(bytes); 38 | return file.path; 39 | } on FileSystemException catch (e) { 40 | log("저장중 오류가 발생했습니다. 메시지: ${e.message}", name: "ImageSaveUtil"); 41 | rethrow; 42 | } 43 | } 44 | 45 | /* ----- TempDir ----- */ 46 | static Directory? _imageTempDir; 47 | 48 | static Future _getDir() async { 49 | if (_imageTempDir == null) { 50 | final tempDir = await getTemporaryDirectory(); 51 | final imageTempDir = await tempDir.createTemp("img_"); 52 | _imageTempDir = imageTempDir; 53 | } 54 | return _imageTempDir!; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/controller/overlay/handler/PolylineOverlayHandler.swift: -------------------------------------------------------------------------------- 1 | import NMapsMap 2 | import Flutter 3 | 4 | internal protocol PolylineOverlayHandler: OverlayHandler { 5 | func setCoords(_ polylineOverlay: NMFPolylineOverlay, rawCoords: Any) 6 | 7 | func setColor(_ polylineOverlay: NMFPolylineOverlay, rawColor: Any) 8 | 9 | func setWidth(_ polylineOverlay: NMFPolylineOverlay, rawWidth: Any) 10 | 11 | func setLineCap(_ polylineOverlay: NMFPolylineOverlay, rawLineCap: Any) 12 | 13 | func setLineJoin(_ polylineOverlay: NMFPolylineOverlay, rawLineJoin: Any) 14 | 15 | func setPattern(_ polylineOverlay: NMFPolylineOverlay, patternList: Any) 16 | 17 | func getBounds(_ polylineOverlay: NMFPolylineOverlay, success: (_ bounds: Dictionary) -> Void) 18 | } 19 | 20 | internal extension PolylineOverlayHandler { 21 | func handlePolylineOverlay(polylineOverlay: NMFOverlay, 22 | method: String, 23 | args: Any?, 24 | result: @escaping FlutterResult) { 25 | let polylineOverlay = polylineOverlay as! NMFPolylineOverlay 26 | switch method { 27 | case NPolylineOverlay.coordsName: setCoords(polylineOverlay, rawCoords: args!) 28 | case NPolylineOverlay.colorName: setColor(polylineOverlay, rawColor: args!) 29 | case NPolylineOverlay.widthName: setWidth(polylineOverlay, rawWidth: args!) 30 | case NPolylineOverlay.lineCapName: setLineCap(polylineOverlay, rawLineCap: args!) 31 | case NPolylineOverlay.lineJoinName: setLineJoin(polylineOverlay, rawLineJoin: args!) 32 | case NPolylineOverlay.patternName: setPattern(polylineOverlay, patternList: args!) 33 | case getterName(NPolylineOverlay.boundsName): getBounds(polylineOverlay, success: result) 34 | default: result(FlutterMethodNotImplemented) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/src/type/map/overlay/overlay/addable/ground_overlay.dart: -------------------------------------------------------------------------------- 1 | part of "../../../../../../flutter_naver_map.dart"; 2 | 3 | /// [NOverlayImage]를 이용해 특정 영역을 지도에 나타낼 수 있는 오버레이입니다. 4 | /// 5 | /// 지상 오버레이라고 부릅니다. 6 | class NGroundOverlay extends NAddableOverlay { 7 | /// 지상 오버레이의 영역을 나타냅니다. 8 | NLatLngBounds get bounds => _bounds; 9 | NLatLngBounds _bounds; 10 | 11 | /// 지상 오버레이에 사용할 이미지를 나타냅니다. 12 | NOverlayImage get image => _image; 13 | NOverlayImage _image; 14 | 15 | /// 지상 오버레이의 불투명도를 나타냅니다. (0~1) 16 | double get alpha => _alpha; 17 | double _alpha; 18 | 19 | @override 20 | // ignore: prefer_final_fields 21 | int _globalZIndex = -300000; 22 | 23 | NGroundOverlay({ 24 | required super.id, 25 | required NLatLngBounds bounds, 26 | required NOverlayImage image, 27 | double alpha = 1.0, 28 | }) : _bounds = bounds, 29 | _image = image, 30 | _alpha = alpha, 31 | super(type: NOverlayType.groundOverlay); 32 | 33 | /// 지상 오버레이의 영역을 지정합니다. 34 | void setBounds(NLatLngBounds bounds) { 35 | _bounds = bounds; 36 | _set(_boundsName, bounds); 37 | } 38 | 39 | /// 지상 오버레이에 사용할 이미지를 지정합니다. 40 | void setImage(NOverlayImage image) { 41 | _image = image; 42 | _set(_imageName, image); 43 | } 44 | 45 | /// 지상 오버레이의 불투명도를 지정합니다. (0~1) 46 | /// 47 | /// 기본값은 불투명함을 나타내는 `1` 48 | void setAlpha(double alpha) { 49 | _alpha = alpha; 50 | _set(_alphaName, alpha); 51 | } 52 | 53 | @override 54 | NPayload toNPayload() => NPayload.make({ 55 | _infoName: info, 56 | _boundsName: _bounds, 57 | _imageName: _image, 58 | _alphaName: _alpha, 59 | ..._commonMap, 60 | }); 61 | 62 | /* 63 | --- Messaging Name Define --- 64 | */ 65 | 66 | static const _infoName = "info"; 67 | static const _boundsName = "bounds"; 68 | static const _imageName = "image"; 69 | static const _alphaName = "alpha"; 70 | } 71 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/controller/overlay/handler/PolygonOverlayHandler.swift: -------------------------------------------------------------------------------- 1 | import NMapsMap 2 | import Flutter 3 | 4 | internal protocol PolygonOverlayHandler: OverlayHandler { 5 | func setCoords(_ polygonOverlay: NMFPolygonOverlay, rawCoords: Any) 6 | 7 | func setColor(_ polygonOverlay: NMFPolygonOverlay, rawColor: Any) 8 | 9 | func setHoles(_ polygonOverlay: NMFPolygonOverlay, rawHoles: Any) 10 | 11 | func setOutlineColor(_ polygonOverlay: NMFPolygonOverlay, rawColor: Any) 12 | 13 | func setOutlineWidth(_ polygonOverlay: NMFPolygonOverlay, rawWidthDp: Any) 14 | 15 | func setOutlinePattern(_ polygonOverlay: NMFPolygonOverlay, patternList: Any) 16 | 17 | func getBounds(_ polygonOverlay: NMFPolygonOverlay, success: (_ bounds: Dictionary) -> Void) 18 | } 19 | 20 | internal extension PolygonOverlayHandler { 21 | func handlePolygonOverlay(polygonOverlay: NMFOverlay, 22 | method: String, 23 | args: Any?, 24 | result: @escaping FlutterResult) { 25 | let polygonOverlay = polygonOverlay as! NMFPolygonOverlay 26 | switch method { 27 | case NPolygonOverlay.coordsName: setCoords(polygonOverlay, rawCoords: args!) 28 | case NPolygonOverlay.colorName: setColor(polygonOverlay, rawColor: args!) 29 | case NPolygonOverlay.holesName: setHoles(polygonOverlay, rawHoles: args!) 30 | case NPolygonOverlay.outlineColorName: setOutlineColor(polygonOverlay, rawColor: args!) 31 | case NPolygonOverlay.outlineWidthName: setOutlineWidth(polygonOverlay, rawWidthDp: args!) 32 | case NPolygonOverlay.outlinePatternName: 33 | setOutlinePattern(polygonOverlay, patternList: args!) 34 | case getterName(NPolygonOverlay.boundsName): getBounds(polygonOverlay, success: result) 35 | default: result(FlutterMethodNotImplemented) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | id "dev.flutter.flutter-gradle-plugin" 5 | } 6 | 7 | def localProperties = new Properties() 8 | def localPropertiesFile = rootProject.file('local.properties') 9 | if (localPropertiesFile.exists()) { 10 | localPropertiesFile.withReader('UTF-8') { reader -> 11 | localProperties.load(reader) 12 | } 13 | } 14 | 15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 16 | if (flutterVersionCode == null) { 17 | flutterVersionCode = '1' 18 | } 19 | 20 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 21 | if (flutterVersionName == null) { 22 | flutterVersionName = '1.0' 23 | } 24 | 25 | android { 26 | compileSdk 36 27 | ndkVersion flutter.ndkVersion 28 | 29 | compileOptions { 30 | sourceCompatibility JavaVersion.VERSION_1_8 31 | targetCompatibility JavaVersion.VERSION_1_8 32 | } 33 | 34 | kotlinOptions { 35 | jvmTarget = '1.8' 36 | } 37 | 38 | sourceSets { 39 | main.java.srcDirs += 'src/main/kotlin' 40 | } 41 | 42 | defaultConfig { 43 | applicationId "dev.note11.flutter_naver_map.flutter_naver_map_example" 44 | minSdkVersion flutter.minSdkVersion 45 | targetSdkVersion 35 46 | versionCode flutterVersionCode.toInteger() 47 | versionName flutterVersionName 48 | } 49 | 50 | buildTypes { 51 | release { 52 | // Add your own signing config for the release build. 53 | // Signing with the debug keys for now, so `flutter run --release` works. 54 | signingConfig signingConfigs.debug 55 | } 56 | } 57 | namespace 'dev.note11.flutter_naver_map.flutter_naver_map_example' 58 | } 59 | 60 | flutter { 61 | source '../..' 62 | } 63 | 64 | dependencies { 65 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 66 | } 67 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/model/flutter_default_custom/NRange.swift: -------------------------------------------------------------------------------- 1 | internal struct NRange : Hashable { 2 | let min: T? 3 | let max: T? 4 | let includeMin: Bool 5 | let includeMax: Bool 6 | 7 | func isInRange(_ value: T) -> Bool { 8 | let minCheck: Bool 9 | switch min { 10 | case .none: 11 | minCheck = true 12 | case .some(let minVal): 13 | minCheck = includeMin ? value.toDouble() >= minVal.toDouble() : value.toDouble() > minVal.toDouble() 14 | } 15 | 16 | let maxCheck: Bool 17 | switch max { 18 | case .none: 19 | maxCheck = true 20 | case .some(let maxVal): 21 | maxCheck = includeMax ? value.toDouble() <= maxVal.toDouble() : value.toDouble() < maxVal.toDouble() 22 | } 23 | 24 | return minCheck && maxCheck 25 | } 26 | 27 | static func fromMessageableWithExactType(args: Any) -> NRange { 28 | guard let map = args as? [String: Any] else { 29 | fatalError("Invalid NRange type") 30 | } 31 | 32 | let rawMin = map["min"] as? T 33 | let rawMax = map["max"] as? T 34 | 35 | guard rawMin != nil && rawMax != nil else { 36 | fatalError("Invalid NRange type") 37 | } 38 | 39 | return NRange( 40 | min: rawMin, 41 | max: rawMax, 42 | includeMin: map["inMin"] as! Bool, 43 | includeMax: map["inMax"] as! Bool 44 | ) 45 | } 46 | } 47 | 48 | extension Numeric { 49 | func toDouble() -> Double { 50 | switch self { 51 | case let int as Int: 52 | return Double(int) 53 | case let double as Double: 54 | return double 55 | case let float as Float: 56 | return Double(float) 57 | default: 58 | fatalError("Unsupported Numeric type") 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/view/NaverMapViewFactory.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.view 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import dev.note11.flutter_naver_map.flutter_naver_map.FlutterNaverMapPlugin 6 | import dev.note11.flutter_naver_map.flutter_naver_map.controller.overlay.OverlayController 7 | import dev.note11.flutter_naver_map.flutter_naver_map.converter.DefaultTypeConverter.asNullableMap 8 | import dev.note11.flutter_naver_map.flutter_naver_map.model.map.NaverMapViewOptions 9 | import io.flutter.plugin.common.BinaryMessenger 10 | import io.flutter.plugin.common.MethodChannel 11 | import io.flutter.plugin.common.StandardMessageCodec 12 | import io.flutter.plugin.platform.PlatformView 13 | import io.flutter.plugin.platform.PlatformViewFactory 14 | 15 | internal class NaverMapViewFactory( 16 | private val activity: Activity, 17 | private val messenger: BinaryMessenger, 18 | ) : PlatformViewFactory(StandardMessageCodec.INSTANCE) { 19 | 20 | override fun create(context: Context, viewId: Int, args: Any?): PlatformView { 21 | val channel = 22 | MethodChannel(messenger, FlutterNaverMapPlugin.createViewMethodChannelName(viewId)) 23 | val overlayChannel = 24 | MethodChannel(messenger, FlutterNaverMapPlugin.createOverlayMethodChannelName(viewId)) 25 | val overlayController = OverlayController(overlayChannel, context) 26 | 27 | val convertedArgs = args!!.asNullableMap() 28 | val options = NaverMapViewOptions.fromMessageable(convertedArgs) 29 | val usingGLSurfaceView = convertedArgs["glsurface"] as? Boolean? 30 | 31 | return NaverMapView( 32 | activity = activity, 33 | flutterProvidedContext = context, 34 | naverMapViewOptions = options, 35 | channel = channel, 36 | overlayController = overlayController, 37 | usingGLSurfaceView = usingGLSurfaceView, 38 | ) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 20 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/controller/overlay/handler/PolylineOverlayHandler.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.controller.overlay.handler 2 | 3 | import com.naver.maps.map.overlay.Overlay 4 | import com.naver.maps.map.overlay.PolylineOverlay 5 | import dev.note11.flutter_naver_map.flutter_naver_map.controller.overlay.OverlayHandler 6 | import dev.note11.flutter_naver_map.flutter_naver_map.controller.overlay.OverlayHandler.Companion.getterName 7 | import dev.note11.flutter_naver_map.flutter_naver_map.model.map.overlay.overlay.NPolylineOverlay 8 | import io.flutter.plugin.common.MethodChannel 9 | 10 | internal interface PolylineOverlayHandler : OverlayHandler { 11 | fun handlePolylineOverlay( 12 | polylineOverlay: Overlay, method: String, arg: Any?, result: MethodChannel.Result, 13 | ) = (polylineOverlay as PolylineOverlay).let { p -> 14 | when (method) { 15 | NPolylineOverlay.coordsName -> setCoords(p, arg!!) 16 | NPolylineOverlay.colorName -> setColor(p, arg!!) 17 | NPolylineOverlay.widthName -> setWidth(p, arg!!) 18 | NPolylineOverlay.lineCapName -> setLineCap(p, arg!!) 19 | NPolylineOverlay.lineJoinName -> setLineJoin(p, arg!!) 20 | NPolylineOverlay.patternName -> setPattern(p, arg!!) 21 | getterName(NPolylineOverlay.boundsName) -> getBounds(p, result::success) 22 | else -> result.notImplemented() 23 | } 24 | } 25 | 26 | fun setCoords(polylineOverlay: PolylineOverlay, rawCoords: Any) 27 | 28 | fun setColor(polylineOverlay: PolylineOverlay, rawColor: Any) 29 | 30 | fun setWidth(polylineOverlay: PolylineOverlay, rawWidthDp: Any) 31 | 32 | fun setLineCap(polylineOverlay: PolylineOverlay, rawLineCap: Any) 33 | 34 | fun setLineJoin(polylineOverlay: PolylineOverlay, rawLineJoin: Any) 35 | 36 | fun setPattern(polylineOverlay: PolylineOverlay, patternDpList: Any) 37 | 38 | fun getBounds(polylineOverlay: PolylineOverlay, success: (bounds: Map) -> Unit) 39 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/controller/overlay/handler/PolygonOverlayHandler.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.controller.overlay.handler 2 | 3 | import com.naver.maps.map.overlay.Overlay 4 | import com.naver.maps.map.overlay.PolygonOverlay 5 | import dev.note11.flutter_naver_map.flutter_naver_map.controller.overlay.OverlayHandler 6 | import dev.note11.flutter_naver_map.flutter_naver_map.controller.overlay.OverlayHandler.Companion.getterName 7 | import dev.note11.flutter_naver_map.flutter_naver_map.model.map.overlay.overlay.NPolygonOverlay 8 | import io.flutter.plugin.common.MethodChannel 9 | 10 | internal interface PolygonOverlayHandler : OverlayHandler { 11 | fun handlePolygonOverlay( 12 | polygonOverlay: Overlay, method: String, arg: Any?, result: MethodChannel.Result, 13 | ) = (polygonOverlay as PolygonOverlay).let { p -> 14 | when (method) { 15 | NPolygonOverlay.coordsName -> setCoords(p, arg!!) 16 | NPolygonOverlay.colorName -> setColor(p, arg!!) 17 | NPolygonOverlay.holesName -> setHoles(p, arg!!) 18 | NPolygonOverlay.outlineColorName -> setOutlineColor(p, arg!!) 19 | NPolygonOverlay.outlineWidthName -> setOutlineWidth(p, arg!!) 20 | NPolygonOverlay.outlinePatternName -> setOutlinePattern(p, arg!!) 21 | getterName(NPolygonOverlay.boundsName) -> getBounds(p, result::success) 22 | else -> result.notImplemented() 23 | } 24 | } 25 | 26 | fun setCoords(polygonOverlay: PolygonOverlay, rawCoords: Any) 27 | 28 | fun setColor(polygonOverlay: PolygonOverlay, rawColor: Any) 29 | 30 | fun setHoles(polygonOverlay: PolygonOverlay, rawHoles: Any) 31 | 32 | fun setOutlineColor(polygonOverlay: PolygonOverlay, rawColor: Any) 33 | 34 | fun setOutlineWidth(polygonOverlay: PolygonOverlay, rawWidthDp: Any) 35 | 36 | fun setOutlinePattern(polygonOverlay: PolygonOverlay, rawPatternDpList: Any) 37 | 38 | fun getBounds(polygonOverlay: PolygonOverlay, success: (bounds: Map) -> Unit) 39 | } -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CADisableMinimumFrameDurationOnPhone 6 | 7 | CFBundleDevelopmentRegion 8 | $(DEVELOPMENT_LANGUAGE) 9 | CFBundleDisplayName 10 | Flutter Naver Map 11 | CFBundleExecutable 12 | $(EXECUTABLE_NAME) 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | flutter_naver_map_example 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | $(FLUTTER_BUILD_NAME) 23 | CFBundleSignature 24 | ???? 25 | CFBundleVersion 26 | $(FLUTTER_BUILD_NUMBER) 27 | LSRequiresIPhoneOS 28 | 29 | NSLocationUsageDescription 30 | need location permission to show user's currnet location in map 31 | NSLocationWhenInUseUsageDescription 32 | need location permission to show user's currnet location in map 33 | UIApplicationSupportsIndirectInputEvents 34 | 35 | UILaunchStoryboardName 36 | LaunchScreen 37 | UIMainStoryboardFile 38 | Main 39 | UISupportedInterfaceOrientations 40 | 41 | UIInterfaceOrientationPortrait 42 | 43 | UISupportedInterfaceOrientations~ipad 44 | 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | UIInterfaceOrientationPortrait 48 | UIInterfaceOrientationPortraitUpsideDown 49 | 50 | UIViewControllerBasedStatusBarAppearance 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/model/map/overlay/overlay/NGroundOverlay.kt: -------------------------------------------------------------------------------- 1 | package dev.note11.flutter_naver_map.flutter_naver_map.model.map.overlay.overlay 2 | 3 | import com.naver.maps.geometry.LatLngBounds 4 | import com.naver.maps.map.overlay.GroundOverlay 5 | import dev.note11.flutter_naver_map.flutter_naver_map.model.map.overlay.overlay.AddableOverlay 6 | import dev.note11.flutter_naver_map.flutter_naver_map.converter.DefaultTypeConverter.asDouble 7 | import dev.note11.flutter_naver_map.flutter_naver_map.converter.DefaultTypeConverter.asMap 8 | import dev.note11.flutter_naver_map.flutter_naver_map.converter.MapTypeConverter.asLatLngBounds 9 | import dev.note11.flutter_naver_map.flutter_naver_map.model.map.info.NOverlayInfo 10 | import dev.note11.flutter_naver_map.flutter_naver_map.model.map.overlay.NOverlayImage 11 | 12 | internal data class NGroundOverlay( 13 | override val info: NOverlayInfo, 14 | val bounds: LatLngBounds, 15 | val image: NOverlayImage, 16 | val alpha: Double, 17 | ) : AddableOverlay() { 18 | 19 | override fun createMapOverlay(): GroundOverlay = applyAtRawOverlay(GroundOverlay()) 20 | 21 | override fun applyAtRawOverlay(overlay: GroundOverlay) = overlay.also { g -> 22 | g.bounds = bounds 23 | g.alpha = alpha.toFloat() 24 | image.applyToOverlay(g::setImage) 25 | } 26 | 27 | companion object { 28 | fun fromMessageable(rawMap: Any): NGroundOverlay = rawMap.asMap().let { 29 | NGroundOverlay( 30 | info = NOverlayInfo.fromMessageable(it[infoName]!!), 31 | bounds = it[boundsName]!!.asLatLngBounds(), 32 | image = NOverlayImage.fromMessageable(it[imageName]!!), 33 | alpha = it[alphaName]!!.asDouble(), 34 | ) 35 | } 36 | 37 | /* 38 | --- Messaging Name Define --- 39 | */ 40 | 41 | private const val infoName = "info" 42 | const val boundsName = "bounds" 43 | const val imageName = "image" 44 | const val alphaName = "alpha" 45 | } 46 | } -------------------------------------------------------------------------------- /lib/src/initializer/flutter_naver_map_initializer.dart: -------------------------------------------------------------------------------- 1 | import "dart:developer"; 2 | import "dart:io"; 3 | 4 | import "package:flutter/services.dart"; 5 | import "package:flutter_naver_map/flutter_naver_map.dart"; 6 | import "package:flutter_naver_map/src/messaging/messaging.dart"; 7 | import "package:meta/meta.dart"; 8 | 9 | class FlutterNaverMap { 10 | @internal 11 | static bool isInitialized = false; 12 | 13 | @internal 14 | static int? androidSdkVersion; 15 | 16 | Function(NAuthFailedException ex)? onAuthFailed; 17 | 18 | /// 지도 사용 전에 호출해야 하는 초기화 메서드입니다. 19 | /// 20 | /// Naver Cloud Platform의 새로운 인증을 지원합니다. 21 | /// 22 | /// NCP 콘솔 좌측 사이드바의 23 | /// 24 | /// [Services > Application Services > Maps](https://console.ncloud.com/maps/application)에서 25 | /// [Application 등록](https://console.ncloud.com/maps/application/create)을 클릭 후, 26 | /// 27 | /// API 선택에서 "Dynamic Map"을 체크합니다. 28 | /// 29 | /// 이후, 인증정보에서 Client ID 값을 확인하실 수 있습니다. 30 | Future init({ 31 | String? clientId, 32 | Function(NAuthFailedException ex)? onAuthFailed, 33 | }) async { 34 | if (!isInitialized) { 35 | NChannel.sdkChannel.setMethodCallHandler(_handler); 36 | } 37 | 38 | this.onAuthFailed = onAuthFailed; 39 | 40 | final result = await NChannel.sdkChannel.invokeMethod("initializeNcp", 41 | {"clientId": clientId, "setAuthFailedListener": onAuthFailed != null}); 42 | 43 | if (result != null) androidSdkVersion = result["androidSdkVersion"]; 44 | isInitialized = true; 45 | 46 | log("SDK Initialized! (${Platform.operatingSystem}${Platform.isAndroid ? ", SDK $androidSdkVersion" : ""})", 47 | name: "FlutterNaverMap"); 48 | } 49 | 50 | @internal 51 | Future getNativeMapSdkVersion() async { 52 | final version = await NChannel.sdkChannel.invokeMethod( 53 | "getNativeMapSdkVersion"); 54 | return version; 55 | } 56 | 57 | Future _handler(MethodCall call) async { 58 | if (call.method == "onAuthFailed" && onAuthFailed != null) { 59 | onAuthFailed!.call(NAuthFailedException.fromMessageable(call.arguments)); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/model/map/overlay/overlay/NPolylineOverlay.swift: -------------------------------------------------------------------------------- 1 | import NMapsMap 2 | 3 | internal struct NPolylineOverlay: AddableOverlay { 4 | typealias OverlayType = NMFPolylineOverlay 5 | var overlayPayload: Dictionary = [:] 6 | 7 | let info: NOverlayInfo 8 | let coords: Array 9 | let color: UIColor 10 | let width: Double 11 | let lineCap: NMFOverlayLineCap 12 | let lineJoin: NMFOverlayLineJoin 13 | let pattern: Array 14 | 15 | func createMapOverlay() -> OverlayType { 16 | let polyline = NMFPolylineOverlay() 17 | return applyAtRawOverlay(polyline) 18 | } 19 | 20 | func applyAtRawOverlay(_ polyline: NMFPolylineOverlay) -> NMFPolylineOverlay { 21 | polyline.line = NMGLineString(points: coords) 22 | polyline.color = color 23 | polyline.width = CGFloat(width) 24 | polyline.capType = lineCap 25 | polyline.joinType = lineJoin 26 | polyline.pattern = pattern 27 | return polyline 28 | } 29 | 30 | static func fromMessageable(_ v: Any) -> NPolylineOverlay { 31 | let d = asDict(v) 32 | return NPolylineOverlay( 33 | info: NOverlayInfo.fromMessageable(d[infoName]!), 34 | coords: asArr(d[coordsName]!, elementCaster: asLatLng), 35 | color: asUIColor(d[colorName]!), 36 | width: asDouble(d[widthName]!), 37 | lineCap: asLineCap(d[lineCapName]!), 38 | lineJoin: asLineJoin(d[lineJoinName]!), 39 | pattern: asArr(d[patternName]!) { 40 | NSNumber(value: asDouble($0)) 41 | } 42 | ) 43 | } 44 | 45 | /* 46 | --- Messaging Name Define --- 47 | */ 48 | private static let infoName = "info" 49 | static let coordsName = "coords"; 50 | static let colorName = "color"; 51 | static let widthName = "width"; 52 | static let lineCapName = "lineCap"; 53 | static let lineJoinName = "lineJoin"; 54 | static let patternName = "pattern"; 55 | static let boundsName = "bounds"; 56 | } 57 | -------------------------------------------------------------------------------- /ios/flutter_naver_map/Sources/flutter_naver_map/converter/DefaultTypeConverter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import CoreGraphics 3 | 4 | internal func asBool(_ v: Any) -> Bool { 5 | v as! Bool 6 | } 7 | 8 | internal func asFloat(_ v: Any) -> Float { 9 | Float(asDouble(v)) 10 | } 11 | 12 | internal func asCGFloat(_ v: Any) -> CGFloat { 13 | CGFloat(asDouble(v)) 14 | } 15 | 16 | internal func asDouble(_ v: Any) -> Double { 17 | v as! Double 18 | } 19 | 20 | internal func asRoundInt(rawFloat: Any) -> Int { 21 | Int(round(asDouble(rawFloat))) 22 | } 23 | 24 | internal func asInt(_ v: Any) -> Int { 25 | v as! Int 26 | } 27 | 28 | internal func asString(_ v: Any) -> String { 29 | v as! String 30 | } 31 | 32 | internal func asDict(_ v: Any) -> Dictionary { 33 | v as! Dictionary 34 | } 35 | 36 | internal func asNullableDict(_ v: Any) -> Dictionary { 37 | v as! Dictionary 38 | } 39 | 40 | internal func asDict(_ v: Any, valueCaster: (Any) throws -> T) -> Dictionary { 41 | let dict = asDict(v) 42 | var newDict: Dictionary = [:] 43 | for (k, v) in dict { 44 | newDict[k] = try! valueCaster(v) 45 | } 46 | return newDict 47 | } 48 | 49 | internal func asStringDict(_ v: Any) -> Dictionary { 50 | v as! Dictionary 51 | } 52 | 53 | internal func asDictWithObjectKey(_ v: Any, keyCaster: (Dictionary) throws -> K, valueCaster: (Any) throws -> V) -> Dictionary { 54 | let dict = v as! Dictionary 55 | var newDict: Dictionary = [:] 56 | for (rawK, v) in dict { 57 | let k = try! keyCaster(rawK as! Dictionary) 58 | newDict[k] = try! valueCaster(v) 59 | } 60 | return newDict 61 | } 62 | 63 | internal func asArr(_ v: Any, elementCaster: (Any) throws -> T) -> Array { 64 | let list = v as! Array 65 | return try! list.map(elementCaster) 66 | } 67 | 68 | internal func castOrNull(_ v: Any?, caster: (Any) throws -> T) -> T? { 69 | if v == nil || v is NSNull { 70 | return nil 71 | } else { 72 | return try! caster(v!) 73 | } 74 | } 75 | --------------------------------------------------------------------------------