├── .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 "";
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 |
--------------------------------------------------------------------------------