Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
## 1.4.3

### Add (New Features)
- [All Platform] Add `NCompassWidget`, `NaverMapOptions.compassEnable`, `NaverMapOptions.compassHideWhenUnrotated`
- [All Platform] Add New API `NaverMapController.nowCameraPositionStream`

### Fix
- [All Platform] Fixed an issue where the stored image cache wasn’t being cleared.

### Update
- [All Platform] Support custom `cacheKey` on `NOverlayImage.fromByteArray`
- [All Platform] improve accuracy of `NaverMapController.nowCameraPosition`


## 1.4.2

### Update
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,10 @@ internal class NaverMapController(
}

override fun onCameraIdle() {
channel.invokeMethod("onCameraIdle", null)
val cameraPosition = naverMap.cameraPosition
channel.invokeMethod("onCameraIdle", mapOf(
"position" to cameraPosition.toMessageable()
))
}

override fun onSelectedIndoorChanged(selectedIndoor: IndoorSelection?) {
Expand Down
40 changes: 40 additions & 0 deletions example/integration_test/camera_update_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,44 @@ void cameraUpdateTests() {
expect(lastMovedData?.$2, false);
onCameraChangeStreamSubscription.cancel();
});

testNaverMap("nowCameraPositionStream emits latest camera data",
(controller, tester) async {
const expectedPosition = NCameraPosition(
target: NLatLng(37.5651, 126.9783), zoom: 13, tilt: 0, bearing: 0);
const expectedReason = NCameraUpdateReason.control;

final movingEventFuture = controller.nowCameraPositionStream.firstWhere(
(event) => event.reason == expectedReason && !event.isIdle);

final idleEventFuture = controller.nowCameraPositionStream.firstWhere(
(event) =>
event.reason == expectedReason &&
event.isIdle &&
_isSameTarget(event.position.target, expectedPosition.target));

final update = NCameraUpdate.withParams(
target: expectedPosition.target, zoom: expectedPosition.zoom)
..setReason(expectedReason);

await controller.updateCamera(update);

final movingEvent =
await movingEventFuture.timeout(const Duration(seconds: 5));
final idleEvent =
await idleEventFuture.timeout(const Duration(seconds: 5));

expect(movingEvent.isIdle, isFalse);
expectCameraPosition(idleEvent.position, expectedPosition);
expect(idleEvent.reason, expectedReason);
expectCameraPosition(controller.nowCameraPosition, expectedPosition);
final fetchedPosition = await controller.getCameraPosition();
expectCameraPosition(fetchedPosition, expectedPosition);
});
}

bool _isSameTarget(NLatLng actual, NLatLng expected) {
const threshold = 0.00001;
return (actual.latitude - expected.latitude).abs() < threshold &&
(actual.longitude - expected.longitude).abs() < threshold;
}
2 changes: 1 addition & 1 deletion example/ios/Flutter/AppFrameworkInfo.plist
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>12.0</string>
<string>13.0</string>
</dict>
</plist>
2 changes: 1 addition & 1 deletion example/ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
platform :ios, '12.0'
platform :ios, '13.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
Expand Down
4 changes: 2 additions & 2 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/permission_handler_apple/ios"

SPEC CHECKSUMS:
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
lat_compass: eadcaf7fa6194ac03e557b3c0ee7168b5415ff43
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d

PODFILE CHECKSUM: 865bfa77219454a712543ad6494f6458c48be43e
PODFILE CHECKSUM: 2ae0fe099ea5e0085ba4e9d891687ccd904e990d

COCOAPODS: 1.16.2
8 changes: 5 additions & 3 deletions example/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
9FE980E828B8365171E1ED2B /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
ADE31C1C5831608538915FDD /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
C4E8888C28E8AC2A7518F9F3 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand All @@ -68,6 +69,7 @@
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */,
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
Expand Down Expand Up @@ -372,7 +374,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
Expand Down Expand Up @@ -450,7 +452,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
Expand Down Expand Up @@ -499,7 +501,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
Expand All @@ -51,6 +52,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
Expand Down
6 changes: 6 additions & 0 deletions example/lib/pages/examples/options_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ class _NaverMapViewOptionsExampleState
requestLocationPermission(context,
onGranted: () => buttonEnable(true));
}),
TextSwitcher(
title: "나침반",
description: ".compassEnable",
value: options.compassEnable,
onChanged: (v) =>
options = options.copyWith(compassEnable: v)),
if (indoorAvailable)
TextSwitcher(
title: "실내 지도",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,10 @@ internal class NaverMapController: NaverMapControlSender, NaverMapControlHandler
}

func onCameraIdle() {
channel.invokeMethod("onCameraIdle", arguments: nil)
let cameraPosition = mapView.cameraPosition
channel.invokeMethod("onCameraIdle", arguments: [
"position": cameraPosition.toMessageable()
])
}

func onSelectedIndoorChanged(selectedIndoor: NMFIndoorSelection?) {
Expand Down
4 changes: 4 additions & 0 deletions lib/flutter_naver_map.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ part 'src/type/map/camera/camera_position.dart';

part 'src/type/map/camera/camera_update.dart';

part 'src/type/map/camera/on_camera_changed_params.dart';

part 'src/type/map/enums.dart';

part 'src/type/map/geo/latlng.dart';
Expand Down Expand Up @@ -158,6 +160,8 @@ part 'src/widget/elements/zoom_control_widget.dart';

part 'src/widget/elements/scale_bar_widget.dart';

part 'src/widget/elements/compass_widget.dart';

part 'src/widget/elements/my_location_button_widget.dart';

/*
Expand Down
26 changes: 12 additions & 14 deletions lib/src/controller/map/controller.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
part of "../../../flutter_naver_map.dart";

typedef _OnCameraChangedParams = ({
NCameraPosition position,
NCameraUpdateReason reason
});

abstract class NaverMapController implements _NaverMapControlSender {
static NaverMapController _createController(MethodChannel controllerChannel,
{required int viewId, required NCameraPosition initialCameraPosition}) {
Expand All @@ -25,12 +20,12 @@ abstract class NaverMapController implements _NaverMapControlSender {
@experimental
NCameraPosition get nowCameraPosition;

Stream<_OnCameraChangedParams> get _nowCameraPositionStream;
Stream<OnCameraChangedParams> get nowCameraPositionStream;

Stream<NLocationTrackingMode> get _locationTrackingModeStream;

void _updateNowCameraPositionData(
NCameraPosition position, NCameraUpdateReason reason);
NCameraPosition position, NCameraUpdateReason? reason, bool isIdle);
}

class _NaverMapControllerImpl
Expand All @@ -46,18 +41,19 @@ class _NaverMapControllerImpl
_nowCameraPositionStreamController.currentData.position;

@override
Stream<_OnCameraChangedParams> get _nowCameraPositionStream =>
Stream<OnCameraChangedParams> get nowCameraPositionStream =>
_nowCameraPositionStreamController.stream;

final NValueHoldHotStreamController<_OnCameraChangedParams>
final NValueHoldHotStreamController<OnCameraChangedParams>
_nowCameraPositionStreamController;

_NaverMapControllerImpl(this.channel, this.overlayController,
NCameraPosition initialCameraPosition)
: _nowCameraPositionStreamController =
NValueHoldHotStreamController<_OnCameraChangedParams>((
NValueHoldHotStreamController(OnCameraChangedParams(
position: initialCameraPosition,
reason: NCameraUpdateReason.developer
reason: NCameraUpdateReason.developer,
isIdle: true,
));

@override
Expand Down Expand Up @@ -255,9 +251,11 @@ class _NaverMapControllerImpl

@override
void _updateNowCameraPositionData(
NCameraPosition position, NCameraUpdateReason reason) {
_nowCameraPositionStreamController
.add((position: position, reason: reason));
NCameraPosition position, NCameraUpdateReason? reason, bool isIdle) {
_nowCameraPositionStreamController.add(OnCameraChangedParams(
position: position,
reason: reason ?? _nowCameraPositionStreamController.currentData.reason,
isIdle: isIdle));
}

/*
Expand Down
8 changes: 3 additions & 5 deletions lib/src/controller/map/handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,10 @@ mixin _NaverMapControlHandler {

void onSymbolTapped(NSymbolInfo symbol);

@Deprecated("use `onCameraChangeWithCameraPosition` instead")
void onCameraChange(NCameraUpdateReason reason, bool animated);

void onCameraChangeWithCameraPosition(
NCameraUpdateReason reason, bool animated, NCameraPosition position);

void onCameraIdle();
void onCameraIdle(NCameraPosition position);

void onSelectedIndoorChanged(NSelectedIndoor? selectedIndoor);

Expand Down Expand Up @@ -60,7 +57,8 @@ mixin _NaverMapControlHandler {
NCameraPosition._fromMessageable(args["position"]));
break;
case "onCameraIdle":
onCameraIdle();
final args = call.arguments;
onCameraIdle(NCameraPosition._fromMessageable(args["position"]));
break;
case "onSelectedIndoorChanged":
final selectedIndoor = call.arguments != null
Expand Down
27 changes: 27 additions & 0 deletions lib/src/type/map/camera/on_camera_changed_params.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
part of "../../../../flutter_naver_map.dart";

class OnCameraChangedParams {
final NCameraPosition position;
final NCameraUpdateReason reason;
final bool isIdle;

const OnCameraChangedParams(
{required this.position, required this.reason, required this.isIdle});

@override
bool operator ==(Object other) =>
identical(this, other) ||
other is OnCameraChangedParams &&
runtimeType == other.runtimeType &&
position == other.position &&
reason == other.reason &&
isIdle == other.isIdle;

@override
int get hashCode => position.hashCode ^ reason.hashCode ^ isIdle.hashCode;

@override
String toString() {
return "OnCameraChangedParams{position: $position, reason: $reason, isIdle: $isIdle}";
}
}
21 changes: 21 additions & 0 deletions lib/src/type/map/map_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,16 @@ class NaverMapViewOptions with NMessageableWithMap {
/// 기본값은 `false`
final bool locationButtonEnable;

/// 나침반을 활성화할지 여부를 지정합니다.
///
/// 기본값은 `false`
final bool compassEnable;

/// 나침반을 활성화했다면, 항상 보여주지 않고 회전되었을 때만 보여줄지 여부를 지정합니다.
///
/// 기본값은 `true`
final bool compassHideWhenUnrotated;

/// 네이버 로고 클릭을 활성화할지 여부를 지정합니다.
///
/// 기본값은 `true`
Expand Down Expand Up @@ -206,6 +216,8 @@ class NaverMapViewOptions with NMessageableWithMap {
this.scaleBarEnable = true,
this.indoorLevelPickerEnable = true,
this.locationButtonEnable = false,
this.compassEnable = false,
this.compassHideWhenUnrotated = true,
this.logoClickEnable = true,
this.logoAlign = NLogoAlign.leftBottom,
this.logoMargin = defaultLogoMargin,
Expand Down Expand Up @@ -284,6 +296,8 @@ class NaverMapViewOptions with NMessageableWithMap {
bool? scaleBarEnable,
bool? indoorLevelPickerEnable,
bool? locationButtonEnable,
bool? compassEnable,
bool? compassHideWhenUnrotated,
bool? logoClickEnable,
NLogoAlign? logoAlign,
EdgeInsets? logoMargin,
Expand Down Expand Up @@ -327,6 +341,9 @@ class NaverMapViewOptions with NMessageableWithMap {
indoorLevelPickerEnable:
indoorLevelPickerEnable ?? this.indoorLevelPickerEnable,
locationButtonEnable: locationButtonEnable ?? this.locationButtonEnable,
compassEnable: compassEnable ?? this.compassEnable,
compassHideWhenUnrotated:
compassHideWhenUnrotated ?? this.compassHideWhenUnrotated,
logoClickEnable: logoClickEnable ?? this.logoClickEnable,
logoAlign: logoAlign ?? this.logoAlign,
logoMargin: logoMargin ?? this.logoMargin,
Expand Down Expand Up @@ -388,6 +405,8 @@ class NaverMapViewOptions with NMessageableWithMap {
scaleBarEnable == other.scaleBarEnable &&
indoorLevelPickerEnable == other.indoorLevelPickerEnable &&
locationButtonEnable == other.locationButtonEnable &&
compassEnable == other.compassEnable &&
compassHideWhenUnrotated == other.compassHideWhenUnrotated &&
logoClickEnable == other.logoClickEnable &&
logoAlign == other.logoAlign &&
logoMargin == other.logoMargin &&
Expand Down Expand Up @@ -425,6 +444,8 @@ class NaverMapViewOptions with NMessageableWithMap {
scaleBarEnable.hashCode ^
indoorLevelPickerEnable.hashCode ^
locationButtonEnable.hashCode ^
compassEnable.hashCode ^
compassHideWhenUnrotated.hashCode ^
logoClickEnable.hashCode ^
logoAlign.hashCode ^
logoMargin.hashCode ^
Expand Down
6 changes: 4 additions & 2 deletions lib/src/type/map/overlay/overlay_image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ class NOverlayImage with NMessageableWithMap {
_mode = _NOverlayImageMode.file;

/// ByteArray로 지도에서 사용할 이미지를 생성합니다. (캐시를 사용합니다)
static Future<NOverlayImage> fromByteArray(Uint8List imageBytes) async {
final path = await ImageUtil.saveImage(imageBytes);
///
/// 기본적으로 image byte를 통해 hash를 생성하여 cache key로 사용하나, 해시 충돌 가능성이 있으므로 [cacheKey] 사용을 권장합니다.
static Future<NOverlayImage> fromByteArray(Uint8List imageBytes, {String? cacheKey}) async {
final path = await ImageUtil.saveImage(imageBytes, cacheKey);
return NOverlayImage._(path: path, mode: _NOverlayImageMode.temp);
}

Expand Down
Loading
Loading