diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8f4d6920..283c51e1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,17 @@
+## 1.1.1
+
+### Improve
+- [All Platform] Improve: NOverlayImage.fromWidget delete Image Widget support with assertion.
+- [Example] Improve: All Examples Improve
+
+### Fix
+- [All Platform] Fix: InfoWindow.onMarker not attached successfully (issue: [#154](https://github.com/note11g/flutter_naver_map/issues/154), PR: [#156](https://github.com/note11g/flutter_naver_map/pull/156))
+- [Android] Fix: black screen caused by flutter 3.16.0~ & android 6.0~9.0 (SDK 23~28) (issue: [#135](https://github.com/note11g/flutter_naver_map/issues/135), PR: [#153](https://github.com/note11g/flutter_naver_map/pull/153))
+- [Android] Fix: MapWidget ignore navigator stack issue android 6.0~13.0 (SDK 23~33) (issue: [#56](https://github.com/note11g/flutter_naver_map/issues/56), Temp Fix PR: [#151](https://github.com/note11g/flutter_naver_map/pull/151))
+
+## 1.1.0+1
+- Update Readme & Apply Dart formatting
+
## 1.1.0
### Improve
- [All Platform] Add method `controller.forceRefresh` & Update Naver Map SDK version to 3.17.0 (issue: [#116](https://github.com/note11g/flutter_naver_map/issues/116), PR: [#139](https://github.com/note11g/flutter_naver_map/pull/139))
diff --git a/README.md b/README.md
index 877fa42c..3fdffb28 100644
--- a/README.md
+++ b/README.md
@@ -3,19 +3,29 @@
[](https://pub.dev/packages/flutter_naver_map)
[](https://github.com/note11g/flutter_naver_map)
-
+
-
-
-
-## Known issues
+

+
+## Version Up Guide
+`1.1.1` 이상으로 업그레이드 할 경우, 해당 코드를 다음과 같이 지워주세요.
(편의상 주석처리로 표시해두었지만, 그냥 지워주세요)
+
+[관련 이슈](https://github.com/note11g/flutter_naver_map/issues/56)가 해결되어, 더 이상 필요하지 않습니다.
-### Android Platform
-- Impeller Engine 지원 안함 (Android 기본 값은 Skia) [#133](https://github.com/note11g/flutter_naver_map/issues/133)
-- **[WIP]** flutter 3.16 버전부터 Android 9.0(SDK 28) 이하 작동 오류 [#135](https://github.com/note11g/flutter_naver_map/issues/135) ([임시 해결 PR #148](https://github.com/note11g/flutter_naver_map/pull/148))
-- **[WIP]** native_splash_screen 패키지 동시 사용 지원 안함 [#56](https://github.com/note11g/flutter_naver_map/issues/56)
+```kotlin
+// android/app/main/.../MainActivity.kt
+class MainActivity : FlutterActivity() // {
+// override fun onCreate(savedInstanceState: Bundle?) {
+// intent.putExtra("background_mode", "transparent")
+// super.onCreate(savedInstanceState)
+// }
+// }
+```
+
+## Known issues
+- Android Impeller Engine 지원 안함 (Android 기본 값은 Skia) [#133](https://github.com/note11g/flutter_naver_map/issues/133)
이슈 제보는 언제나 환영입니다:)
diff --git a/android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/FlutterNaverMapPlugin.kt b/android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/FlutterNaverMapPlugin.kt
index 1067a0d8..45f8aac8 100644
--- a/android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/FlutterNaverMapPlugin.kt
+++ b/android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/FlutterNaverMapPlugin.kt
@@ -1,5 +1,6 @@
package dev.note11.flutter_naver_map.flutter_naver_map
+import android.app.Activity
import android.content.Context
import dev.note11.flutter_naver_map.flutter_naver_map.sdk.SdkInitializer
import dev.note11.flutter_naver_map.flutter_naver_map.view.NaverMapViewFactory
@@ -28,14 +29,19 @@ internal class FlutterNaverMapPlugin : FlutterPlugin, ActivityAware {
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) = Unit
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
+ val activity = binding.activity
val naverMapViewFactory =
- NaverMapViewFactory(binding.activity, pluginBinding.binaryMessenger)
+ NaverMapViewFactory(activity, pluginBinding.binaryMessenger)
+ correctDisplayOnFlutterNavigatorStackWithActivityBackgroundMode(activity)
pluginBinding.platformViewRegistry.registerViewFactory(
- MAP_VIEW_TYPE_ID,
- naverMapViewFactory
+ MAP_VIEW_TYPE_ID, naverMapViewFactory
)
}
+ private fun correctDisplayOnFlutterNavigatorStackWithActivityBackgroundMode(activity: Activity) {
+ activity.intent.putExtra("background_mode", "transparent")
+ }
+
override fun onDetachedFromActivityForConfigChanges() = Unit
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) = Unit
diff --git a/android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/controller/overlay/OverlayController.kt b/android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/controller/overlay/OverlayController.kt
index 1bb214df..98616fb7 100644
--- a/android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/controller/overlay/OverlayController.kt
+++ b/android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/controller/overlay/OverlayController.kt
@@ -254,6 +254,7 @@ internal class OverlayController(
success: (Any?) -> Unit,
) {
val nInfoWindow = NInfoWindow.fromMessageable(rawInfoWindow, context = context)
+ .apply { setCommonProperties(rawInfoWindow.asMap()) }
val infoWindow = saveOverlayWithAddable(creator = nInfoWindow)
val align = rawAlign.asAlign()
diff --git a/android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/converter/AddableOverlay.kt b/android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/converter/AddableOverlay.kt
index 99c201f4..9ce0df2e 100644
--- a/android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/converter/AddableOverlay.kt
+++ b/android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/converter/AddableOverlay.kt
@@ -26,7 +26,7 @@ internal abstract class AddableOverlay {
private lateinit var commonProperties: Map
- private fun setCommonProperties(rawArgs: Map) {
+ fun setCommonProperties(rawArgs: Map) {
commonProperties = rawArgs.filter { OverlayHandler.allPropertyNames.contains(it.key) }
}
diff --git a/android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/sdk/SdkInitializer.kt b/android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/sdk/SdkInitializer.kt
index 351288d1..2b72452e 100644
--- a/android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/sdk/SdkInitializer.kt
+++ b/android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/sdk/SdkInitializer.kt
@@ -36,7 +36,7 @@ internal class SdkInitializer(
try {
if (clientId != null) initializeMapSdk(context, clientId, isGov)
if (setAuthFailedListener) setOnAuthFailedListener()
- val sendPayload = mapOf("androidSdkVersion" to androidSdkVersion)
+ val sendPayload = mapOf("androidSdkVersion" to Build.VERSION.SDK_INT)
onSuccess(sendPayload)
} catch (e: NaverMapSdk.AuthFailedException) {
onFailure(e)
@@ -63,8 +63,4 @@ internal class SdkInitializer(
)
)
}
-
- companion object {
- private val androidSdkVersion: Int get() = Build.VERSION.SDK_INT
- }
}
\ No newline at end of file
diff --git a/android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/view/NaverMapView.kt b/android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/view/NaverMapView.kt
index c365c261..9a077bfd 100644
--- a/android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/view/NaverMapView.kt
+++ b/android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/view/NaverMapView.kt
@@ -3,7 +3,9 @@ package dev.note11.flutter_naver_map.flutter_naver_map.view
import android.app.Activity
import android.app.Application
import android.content.ComponentCallbacks
+import android.content.Context
import android.content.res.Configuration
+import android.os.Build
import android.os.Bundle
import android.view.View
import com.naver.maps.map.MapView
@@ -21,6 +23,7 @@ import io.flutter.plugin.platform.PlatformView
internal class NaverMapView(
private val activity: Activity,
+ private val flutterProvidedContext: Context,
private val naverMapViewOptions: NaverMapViewOptions,
private val channel: MethodChannel,
private val overlayController: OverlayHandler,
@@ -28,13 +31,16 @@ internal class NaverMapView(
private lateinit var naverMap: NaverMap
private lateinit var naverMapControlSender: NaverMapControlSender
- private val mapView = MapView(activity, naverMapViewOptions.naverMapOptions).apply {
- setTempMethodCallHandler()
- getMapAsync { naverMap ->
- this@NaverMapView.naverMap = naverMap
- onMapReady()
+ private val mapView =
+ MapView(flutterProvidedContext, naverMapViewOptions.naverMapOptions.apply {
+ useTextureView(Build.VERSION.SDK_INT <= 29)
+ }).apply {
+ setTempMethodCallHandler()
+ getMapAsync { naverMap ->
+ this@NaverMapView.naverMap = naverMap
+ onMapReady()
+ }
}
- }
private var isListenerRegistered = false
private var rawNaverMapOptionTempCache: Any? = null
@@ -60,7 +66,7 @@ internal class NaverMapView(
private fun initializeMapController() {
naverMapControlSender = NaverMapController(
- naverMap, channel, activity.applicationContext, overlayController
+ naverMap, channel, flutterProvidedContext, overlayController
).apply {
rawNaverMapOptionTempCache?.let { updateOptions(it.asMap()) {} }
}
diff --git a/android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/view/NaverMapViewFactory.kt b/android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/view/NaverMapViewFactory.kt
index 030cf8c0..c26b255e 100644
--- a/android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/view/NaverMapViewFactory.kt
+++ b/android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/view/NaverMapViewFactory.kt
@@ -29,6 +29,7 @@ internal class NaverMapViewFactory(
return NaverMapView(
activity = activity,
+ flutterProvidedContext = context,
naverMapViewOptions = options,
channel = channel,
overlayController = overlayController,
diff --git a/example/android/app/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map_example/MainActivity.kt b/example/android/app/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map_example/MainActivity.kt
index c1054ab4..87bbc437 100644
--- a/example/android/app/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map_example/MainActivity.kt
+++ b/example/android/app/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map_example/MainActivity.kt
@@ -3,9 +3,4 @@ package dev.note11.flutter_naver_map.flutter_naver_map_example
import android.os.Bundle
import io.flutter.embedding.android.FlutterActivity
-class MainActivity : FlutterActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- intent.putExtra("background_mode", "transparent")
- super.onCreate(savedInstanceState)
- }
-}
+class MainActivity : FlutterActivity() {}
diff --git a/example/integration_test/overlay_test.dart b/example/integration_test/overlay_test.dart
index 97012897..81dd65f4 100644
--- a/example/integration_test/overlay_test.dart
+++ b/example/integration_test/overlay_test.dart
@@ -135,5 +135,42 @@ void main() {
marker.setAlpha(0.8);
expect(marker.alpha, 0.8);
});
+
+ /// #154 issue test (https://github.com/note11g/flutter_naver_map/issues/154)
+ testNaverMap("NInfoWindow.onMarker test", (controller, tester) async {
+ final cameraTarget =
+ await controller.getCameraPosition().then((cp) => cp.target);
+ final marker = NMarker(id: "1", position: cameraTarget);
+ await controller.addOverlay(marker);
+ final infoWindow =
+ NInfoWindow.onMarker(id: "2", text: "infowindow", offsetX: 0.1)
+ ..setMinZoom(13);
+ await marker.openInfoWindow(infoWindow);
+ expect(infoWindow.offsetX, 0.1);
+ expect(infoWindow.minZoom, 13);
+
+ await Future.delayed(const Duration(milliseconds: 200));
+ final screenTarget =
+ await controller.latLngToScreenLocation(cameraTarget);
+
+ Future> pickNOverlayInfo() {
+ return controller
+ .pickAll(screenTarget, radius: 800)
+ .then((pickable) => pickable.whereType().toList());
+ }
+
+ final pickedInfoListByDefaultZoom = await pickNOverlayInfo();
+ print(pickedInfoListByDefaultZoom);
+ expect(pickedInfoListByDefaultZoom.contains(infoWindow.info), true);
+
+ await controller.updateCamera(NCameraUpdate.scrollAndZoomTo(zoom: 12)
+ ..setAnimation(duration: Duration.zero));
+
+ await Future.delayed(const Duration(milliseconds: 200));
+
+ final pickedInfoListWhenZoomOut = await pickNOverlayInfo();
+ print(pickedInfoListWhenZoomOut);
+ expect(pickedInfoListWhenZoomOut.contains(infoWindow.info), false);
+ });
});
}
diff --git a/example/lib/design/custom_widget.dart b/example/lib/design/custom_widget.dart
index 33a04796..8d15bc28 100644
--- a/example/lib/design/custom_widget.dart
+++ b/example/lib/design/custom_widget.dart
@@ -452,8 +452,7 @@ class BottomPadding extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
- padding:
- EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom));
+ padding: EdgeInsets.only(bottom: MediaQuery.paddingOf(context).bottom));
}
}
@@ -571,8 +570,9 @@ class _BalloonPainter extends CustomPainter {
}
class HalfActionButton extends StatelessWidget {
- final Function() action;
+ final Function()? action;
final IconData icon;
+ final Color? color;
final String title;
final String description;
@@ -580,6 +580,7 @@ class HalfActionButton extends StatelessWidget {
super.key,
required this.action,
required this.icon,
+ this.color,
required this.title,
required this.description,
});
@@ -587,7 +588,7 @@ class HalfActionButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Material(
- color: getColorTheme(context).outlineVariant,
+ color: color ?? getColorTheme(context).outline,
borderRadius: BorderRadius.circular(12),
clipBehavior: Clip.antiAlias,
child: InkWell(
@@ -596,28 +597,24 @@ class HalfActionButton extends StatelessWidget {
padding:
const EdgeInsets.symmetric(vertical: 12, horizontal: 12),
child: Row(children: [
- Icon(icon,
- color: getColorTheme(context).primary, size: 22),
+ Icon(icon, color: getColorTheme(context).primary, size: 22),
const SizedBox(width: 10),
Expanded(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(title,
- style: getTextTheme(context)
- .labelMedium
- ?.copyWith(color: Colors.black),
- overflow: TextOverflow.fade,
- softWrap: false,
- maxLines: 1),
- const SizedBox(height: 2),
- Text(description,
- style: getTextTheme(context).bodySmall,
- overflow: TextOverflow.fade,
- softWrap: false,
- maxLines: 1),
- ]),
- ),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(title,
+ style: getTextTheme(context).labelMedium,
+ overflow: TextOverflow.fade,
+ softWrap: false,
+ maxLines: 1),
+ const SizedBox(height: 2),
+ Text(description,
+ style: getTextTheme(context).bodySmall,
+ overflow: TextOverflow.fade,
+ softWrap: false,
+ maxLines: 1),
+ ])),
]))));
}
}
@@ -654,3 +651,51 @@ class HalfActionButtonGrid extends StatelessWidget {
return Column(children: rowCellAndGapWidgets);
}
}
+
+class SmallButton extends StatelessWidget {
+ final String text;
+ final VoidCallback onTap;
+ final Color? color;
+ final Color? textColor;
+ final IconData? icon;
+ final double radius;
+
+ const SmallButton(
+ this.text, {
+ super.key,
+ required this.onTap,
+ this.color,
+ this.textColor,
+ this.icon,
+ this.radius = 6,
+ });
+
+ BorderRadius get borderRadius => BorderRadius.circular(radius);
+
+ @override
+ Widget build(BuildContext context) {
+ return Material(
+ color: color ?? getColorTheme(context).secondary,
+ borderRadius: borderRadius,
+ child: InkWell(
+ onTap: onTap,
+ borderRadius: borderRadius,
+ child: Padding(
+ padding: const EdgeInsets.symmetric(vertical: 8.0),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ if (icon != null)
+ Padding(
+ padding: const EdgeInsets.only(right: 4),
+ child: Icon(icon,
+ color: textColor ?? Colors.white, size: 16),
+ ),
+ Text(text,
+ style: getTextTheme(context)
+ .labelSmall
+ ?.copyWith(color: textColor)),
+ ]))));
+ }
+}
diff --git a/example/lib/design/map_function_item.dart b/example/lib/design/map_function_item.dart
index dec7fc21..07aa5cb3 100644
--- a/example/lib/design/map_function_item.dart
+++ b/example/lib/design/map_function_item.dart
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_bottom_drawer/flutter_bottom_drawer.dart';
+import 'package:flutter_naver_map_example/design/custom_widget.dart';
import 'theme.dart';
@@ -8,7 +9,8 @@ typedef MapFunctionItemOnBack = void Function();
class MapFunctionItem {
final String title;
- final String? description;
+ final String description;
+ final IconData icon;
final MapFunctionItemOnTap? onTap;
final Widget Function(bool canScroll)? page;
final MapFunctionItemOnBack? onBack;
@@ -18,7 +20,8 @@ class MapFunctionItem {
MapFunctionItem({
required this.title,
- this.description,
+ required this.description,
+ required this.icon,
this.page,
this.onTap,
this.onBack,
@@ -29,35 +32,13 @@ class MapFunctionItem {
bool get needItemOnTap => onTap == null;
Widget getItemWidget(BuildContext context) {
- return InkWell(
- onTap: onTap != null ? () => onTap!.call(this) : null,
- child: Container(
- padding: EdgeInsets.symmetric(
- horizontal: 24, vertical: description != null ? 12 : 20),
- child:
- Row(crossAxisAlignment: CrossAxisAlignment.center, children: [
- Expanded(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(title, style: Theme.of(context).textTheme.titleSmall),
- if (description != null)
- Padding(
- padding: const EdgeInsets.only(top: 4),
- child: SizedBox(
- width: double.infinity,
- child: Text(description!,
- style: getTextTheme(context)
- .bodyMedium
- ?.copyWith(
- color:
- getColorTheme(context).secondary),
- overflow: TextOverflow.ellipsis,
- maxLines: 1)))
- ])),
- const Icon(Icons.arrow_forward_ios_rounded, size: 20),
- ])));
+ return HalfActionButton(
+ action: onTap != null ? () => onTap!.call(this) : null,
+ title: title,
+ color: getColorTheme(context).onSurface,
+ description: description,
+ icon: icon,
+ );
}
Widget getPageWidget(BuildContext context, {bool canScroll = true}) {
@@ -113,6 +94,7 @@ class MapFunctionItem {
MapFunctionItemOnBack? onBack,
bool? isScrollPage,
DrawerMoveController? drawerController,
+ IconData? icon,
}) {
return MapFunctionItem(
title: title ?? this.title,
@@ -122,6 +104,7 @@ class MapFunctionItem {
onBack: onBack ?? this.onBack,
isScrollPage: isScrollPage ?? this.isScrollPage,
drawerController: drawerController ?? this.drawerController,
+ icon: icon ?? this.icon,
);
}
}
diff --git a/example/lib/design/theme.dart b/example/lib/design/theme.dart
index db63e2c9..2b6c1414 100644
--- a/example/lib/design/theme.dart
+++ b/example/lib/design/theme.dart
@@ -12,6 +12,7 @@ class ExampleAppTheme {
outline: Colors.grey.shade200,
outlineVariant: Colors.grey.shade200,
primaryContainer: const Color(0xFFD2FFB4),
+ onSurface: Colors.grey.shade100,
),
textTheme: const TextTheme(
titleSmall: TextStyle(
@@ -29,7 +30,7 @@ class ExampleAppTheme {
labelMedium: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w700,
- color: Colors.white,
+ color: Colors.black,
letterSpacing: 0),
labelLarge: TextStyle(
fontSize: 16,
@@ -49,6 +50,7 @@ class ExampleAppTheme {
outline: Colors.grey.shade700,
outlineVariant: Colors.white54,
primaryContainer: const Color(0xFF7FA864),
+ onSurface: Colors.grey.shade800,
),
textTheme: const TextTheme(
titleSmall: TextStyle(
diff --git a/example/lib/main.dart b/example/lib/main.dart
index 2d2fe0e8..71db1c25 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -1,12 +1,17 @@
import 'dart:async';
+import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_naver_map/flutter_naver_map.dart';
+import 'package:flutter_naver_map_example/pages/examples/camera_example.dart';
import 'package:flutter_naver_map_example/pages/examples/controller_example.dart';
import 'package:flutter_naver_map_example/pages/examples/overlay_example.dart';
+import 'package:flutter_naver_map_example/pages/examples/pick_example.dart';
+import 'package:flutter_naver_map_example/util/overlay_portal_util.dart';
+import 'package:transparent_pointer/transparent_pointer.dart';
-import 'pages/bottom_drawer.dart';
-import 'pages/new_window_page.dart';
+import 'pages/utils/bottom_drawer.dart';
+import 'pages/utils/new_window_page.dart';
import 'design/map_function_item.dart';
import 'design/theme.dart';
@@ -49,16 +54,26 @@ class _FNMapPageState extends State {
late EdgeInsets safeArea;
double drawerHeight = 0;
+ final nOverlayInfoOverlayPortalController = NInfoOverlayPortalController();
+
@override
Widget build(BuildContext context) {
safeArea = MediaQuery.of(context).padding;
- return WillPopScope(
- onWillPop: () async => drawerTool.processWillPop(),
- child: Stack(children: [
- Positioned.fill(child: mapWidget()),
- drawerTool.bottomDrawer,
- ]),
- );
+ return Scaffold(
+ body: PopScope(
+ onPopInvoked: (didPop) => drawerTool.processWillPop(),
+ child: Stack(children: [
+ GestureDetector(
+ onTapDown: (details) => _onLastTouchStreamController.sink
+ .add(details.globalPosition)),
+ Positioned.fill(child: TransparentPointer(child: mapWidget())),
+ drawerTool.bottomDrawer,
+ OverlayPortal(
+ controller: nOverlayInfoOverlayPortalController,
+ overlayChildBuilder: (context) =>
+ nOverlayInfoOverlayPortalController.builder(
+ context, mapController)),
+ ])));
}
/*
@@ -87,7 +102,7 @@ class _FNMapPageState extends State {
mapController = controller;
}
- void onMapTapped(NPoint point, NLatLng latLng) {
+ void onMapTapped(NPoint point, NLatLng latLng) async {
// ...
}
@@ -108,7 +123,8 @@ class _FNMapPageState extends State {
// ...
}
- final _onCameraChangeStreamController = StreamController.broadcast();
+ final _onCameraChangeStreamController = StreamController.broadcast();
+ final _onLastTouchStreamController = StreamController.broadcast();
/*
--- Bottom Drawer Widget ---
@@ -122,8 +138,9 @@ class _FNMapPageState extends State {
late final List pages = [
MapFunctionItem(
- title: "NaverMapViewOptions 변경",
- description: "지도의 옵션을 변경할 수 있어요",
+ title: "지도 위젯 옵션 변경하기",
+ description: "위젯에 보여지는 걸 바꿔봐요",
+ icon: Icons.map_rounded,
page: (canScroll) => NaverMapViewOptionsExample(
canScroll: canScroll,
options: options,
@@ -132,61 +149,54 @@ class _FNMapPageState extends State {
})),
MapFunctionItem(
title: "오버레이 추가 / 제거",
- description: "마커, 경로 등의 각종 오버레이들을 추가하고 제거할 수 있어요",
+ description: "마커/경로/도형 등을 띄워봐요",
+ icon: Icons.add_location_alt_rounded,
isScrollPage: false,
page: (canScroll) => NOverlayExample(
- canScroll: canScroll, mapController: mapController)),
+ nOverlayInfoOverlayPortalController:
+ nOverlayInfoOverlayPortalController,
+ canScroll: canScroll,
+ mapController: mapController)),
MapFunctionItem(
title: "카메라 이동",
- description: "지도에 보이는 영역을 카메라를 이동하여 바꿀 수 있어요",
- page: (canScroll) => _cameraMoveTestPage(mapController)),
+ isScrollPage: false,
+ icon: Icons.zoom_in_rounded,
+ description: "지도를 요리조리 움직여봐요",
+ page: (canScroll) => CameraUpdateExample(
+ onCameraChangeStream: _onCameraChangeStreamController.stream,
+ canScroll: canScroll,
+ mapController: mapController)),
+ MapFunctionItem(
+ title: "주변 Pickable 보기",
+ description: "주변 심볼, 오버레이를 찾아봐요",
+ icon: Icons.domain_rounded,
+ page: (canScroll) {
+ final screenSize = MediaQuery.sizeOf(context);
+ return NaverMapPickExample(
+ canScroll: canScroll,
+ mapController: mapController,
+ mapEndPoint:
+ Point(screenSize.width, screenSize.height - drawerHeight),
+ onCameraChangeStream: _onCameraChangeStreamController.stream,
+ );
+ }),
MapFunctionItem(
title: "기타 컨트롤러 기능",
- description: "컨트롤러로 지도의 상태를 가져오거나 변경할 수 있습니다.",
+ description: "컨트롤러 기능을 살펴봐요",
isScrollPage: false,
+ icon: Icons.sports_esports_rounded,
page: (canScroll) => NaverMapControllerExample(
canScroll: canScroll,
mapController: mapController,
onCameraChangeStream: _onCameraChangeStreamController.stream,
+ onLastTouchStream: _onLastTouchStreamController.stream,
)),
- MapFunctionItem(
- title: "주변 심볼 및 오버레이 가져오기",
- description: "특정 영역 주변의 심볼 및 오버레이를 가져올 수 있어요",
- page: (canScroll) => _pickTestPage()),
MapFunctionItem(
title: "새 페이지에서 지도 보기",
- description: "새 페이지에서 지도를 봅니다. (메모리 누수 확인용)",
+ icon: Icons.note_add_rounded,
+ description: "테스트용이에요",
onTap: (_) => Navigator.of(context).push(
MaterialPageRoute(builder: (context) => const NewWindowTestPage())),
),
];
-
- Widget _cameraMoveTestPage(NaverMapController mapController) {
- return Padding(
- padding: const EdgeInsets.all(24),
- child: Column(children: [
- // todo
- const Text("_cameraMoveTestPage"),
- const Text("카메라 이동"),
- ElevatedButton(
- onPressed: () {
- mapController.updateCamera(NCameraUpdate.fromCameraPosition(
- const NCameraPosition(
- target: NLatLng(37.56362422812855, 126.96269803941277),
- zoom: 17.00922642853924,
- bearing: 119.62995870263971)));
- },
- child: const Text('카메라 회전')),
- ]));
- }
-
- Widget _pickTestPage() {
- return const Padding(
- padding: EdgeInsets.all(24),
- child: Column(children: [
- // todo
- Text("_pickTestPage"),
- Text("주변 심볼 및 오버레이 가져오기"),
- ]));
- }
}
diff --git a/example/lib/pages/bottom_drawer.dart b/example/lib/pages/bottom_drawer.dart
deleted file mode 100644
index 2114a21e..00000000
--- a/example/lib/pages/bottom_drawer.dart
+++ /dev/null
@@ -1,131 +0,0 @@
-import 'package:flutter/foundation.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_bottom_drawer/flutter_bottom_drawer.dart';
-
-import '../design/map_function_item.dart';
-import '../design/theme.dart';
-
-class ExampleAppBottomDrawer {
- final BuildContext context;
- final Function(double height) onDrawerHeightChanged;
- final Function() rebuild;
- final List pages;
- final void Function()? onPageDispose;
-
- ExampleAppBottomDrawer({
- required this.context,
- required this.onDrawerHeightChanged,
- required this.rebuild,
- required this.pages,
- this.onPageDispose,
- });
-
- ColorScheme get colorTheme => getColorTheme(context);
-
- TextTheme get textTheme => getTextTheme(context);
-
- late DrawerMoveController drawerController;
- late DrawerState drawerState;
- late Function(Function()) drawerSetState;
- final scrollController = ScrollController();
-
- MapFunctionItem? nowItem;
-
- bool get hasPage => nowItem != null;
-
- void go(MapFunctionItem item) {
- nowItem = item;
- rebuildDrawerAndPage();
- }
-
- void back() {
- nowItem = null;
- onPageDispose?.call();
- rebuildDrawerAndPage();
- }
-
- void rebuildDrawerAndPage() {
- drawerSetState(() {}); // drawer rebuild
- rebuild(); // page rebuild (height changed)
- }
-
- BottomDrawer get bottomDrawer => BottomDrawer(
- height: nowItem?.isScrollPage == false ? null : 200,
- expandedHeight: 480,
- handleSectionHeight: 20,
- handleColor: colorTheme.secondary,
- backgroundColor: colorTheme.background,
- onReady: (controller) => drawerController = controller,
- onStateChanged: (state) => drawerState = state,
- onHeightChanged: onDrawerHeightChanged,
- builder: (state, setState, context) {
- drawerSetState = setState;
- return hasPage
- ? selectedPage(state == DrawerState.opened)
- : innerListView(state == DrawerState.opened);
- });
-
- Widget innerListView(bool canScroll) => Column(children: [
- innerListViewHeader(),
- Expanded(
- child: ScrollConfiguration(
- behavior: const ScrollBehavior().copyWith(overscroll: false),
- child: Scrollbar(
- controller: scrollController,
- child: ListView.builder(
- controller: scrollController,
- padding: EdgeInsets.only(
- bottom: MediaQuery.of(context).padding.bottom),
- physics: canScroll
- ? const ClampingScrollPhysics()
- : const NeverScrollableScrollPhysics(),
- itemCount: pages.length,
- itemBuilder: (context, index) {
- MapFunctionItem page = pages[index];
- if (page.needItemOnTap) {
- page = page.copyWith(
- onTap: go,
- onBack: back,
- drawerController: drawerController);
- }
- return page.getItemWidget(context);
- },
- )))),
- ]);
-
- Widget innerListViewHeader() => Container(
- decoration: BoxDecoration(
- border: Border(
- bottom: BorderSide(
- color: colorTheme.onBackground.withOpacity(0.28), width: 0.2)),
- ),
- padding: const EdgeInsets.fromLTRB(24, 12, 24, 16),
- child: Row(crossAxisAlignment: CrossAxisAlignment.center, children: [
- Text("지도 기능 둘러보기", style: textTheme.titleLarge),
- const Spacer(),
- Container(
- padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 4),
- decoration: BoxDecoration(
- color: kReleaseMode ? colorTheme.primary : colorTheme.secondary,
- borderRadius: BorderRadius.circular(4),
- ),
- child: Text(kReleaseMode ? "Release Mode" : "Debug Mode",
- style: textTheme.labelSmall)),
- ]));
-
- Widget selectedPage(bool canScroll) {
- assert(nowItem != null);
- return nowItem!.getPageWidget(context, canScroll: canScroll);
- }
-
- bool processWillPop() {
- if (hasPage) {
- back();
- return false;
- } else if (drawerState != DrawerState.closed) {
- drawerController.close();
- return false;
- }
- return true;
- }
-}
diff --git a/example/lib/pages/examples/camera_example.dart b/example/lib/pages/examples/camera_example.dart
new file mode 100644
index 00000000..e9108d15
--- /dev/null
+++ b/example/lib/pages/examples/camera_example.dart
@@ -0,0 +1,275 @@
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_naver_map/flutter_naver_map.dart';
+import 'package:flutter_naver_map_example/design/theme.dart';
+import 'package:flutter_naver_map_example/pages/utils/example_base.dart';
+import 'package:flutter_naver_map_example/util/string_util.dart';
+
+import '../../design/custom_widget.dart';
+
+class CameraUpdateExample extends ExampleBasePage {
+ final Stream onCameraChangeStream;
+
+ const CameraUpdateExample({
+ Key? key,
+ required super.mapController,
+ required super.canScroll,
+ required this.onCameraChangeStream,
+ }) : super(key: key);
+
+ @override
+ State createState() => _NOverlayExampleState();
+}
+
+class _NOverlayExampleState extends State {
+ /// 현재 카메라 상태
+ NCameraPosition? _nowCameraPosition;
+ int _animationMill = 300;
+ static const dpForMove = 40.0;
+
+ void onCameraChange() async {
+ _nowCameraPosition = await _mapController.getCameraPosition();
+ if (mounted) setState(() {});
+ }
+
+ void updateCamera(NCameraUpdate cameraUpdate) {
+ _mapController.updateCamera(cameraUpdate
+ ..setAnimation(duration: Duration(milliseconds: _animationMill)));
+ }
+
+ void moveCameraCoordWithDp(double dp, Axis axis) async {
+ final meterPerDp = await _mapController.getMeterPerDp();
+ final offsetMeter = meterPerDp * dp;
+ updateCamera(NCameraUpdate.withParams(
+ target: _nowCameraPosition!.target.offsetByMeter(
+ eastMeter: axis == Axis.horizontal ? offsetMeter : 0,
+ northMeter: axis == Axis.vertical ? offsetMeter : 0,
+ )));
+ }
+
+ Widget coordControlWidget() {
+ return Padding(
+ padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 20)
+ .copyWith(right: 20),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Expanded(
+ child: InnerSimpleTitle(
+ title:
+ "좌표 (${_nowCameraPosition?.target.toShortString()})",
+ description:
+ "NCameraUpdate.scrollAndZoomTo, .target,\nNCameraPosition.target")),
+ _arrowButtonController(
+ up: () => moveCameraCoordWithDp(dpForMove, Axis.vertical),
+ down: () => moveCameraCoordWithDp(-dpForMove, Axis.vertical),
+ left: () => moveCameraCoordWithDp(-dpForMove, Axis.horizontal),
+ right: () => moveCameraCoordWithDp(dpForMove, Axis.horizontal),
+ ),
+ ]));
+ }
+
+ Widget _arrowButtonController({
+ required VoidCallback left,
+ required VoidCallback right,
+ required VoidCallback up,
+ required VoidCallback down,
+ }) {
+ Widget buttonSeparator([bool horizontal = false]) => Container(
+ width: horizontal ? null : 1,
+ height: horizontal ? 1 : null,
+ color: getColorTheme(context).onSurface);
+
+ Widget arrowButton(IconData icon, void Function() onTap,
+ {double width = 32, double height = 48}) =>
+ Material(
+ color: getColorTheme(context).outline,
+ child: InkWell(
+ onTap: onTap,
+ child: Container(
+ width: width,
+ height: height,
+ alignment: Alignment.center,
+ child: Icon(icon, size: 20))));
+
+ return ClipRRect(
+ borderRadius: BorderRadius.circular(8),
+ child: IntrinsicHeight(
+ child: Row(children: [
+ arrowButton(Icons.arrow_left_rounded, left),
+ buttonSeparator(),
+ SizedBox(
+ width: 40,
+ height: 48,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ Expanded(
+ child: arrowButton(Icons.arrow_drop_up_rounded, up)),
+ buttonSeparator(true),
+ Expanded(
+ child:
+ arrowButton(Icons.arrow_drop_down_rounded, down)),
+ ])),
+ buttonSeparator(),
+ arrowButton(Icons.arrow_right_rounded, right),
+ ])));
+ }
+
+ Widget zoomControlWidget() {
+ return plusMinusItemWidget(
+ title: "줌 레벨",
+ method:
+ "NCameraUpdate.scrollAndZoomTo, .zoom,\n.zoomBy, NCameraPosition.zoom",
+ value: _nowCameraPosition?.zoom.toStringAsFixed(1),
+ resetFunc: () => updateCamera(NCameraUpdate.withParams(zoom: 14)),
+ minusFunc: () => updateCamera(NCameraUpdate.zoomBy(-0.5)),
+ plusFunc: () => updateCamera(NCameraUpdate.zoomBy(0.5)),
+ );
+ }
+
+ Widget tiltControlWidget() {
+ return plusMinusItemWidget(
+ title: "틸트 레벨",
+ method: "NCameraUpdate.tilt, .tiltBy,\nNCameraPosition.tilt",
+ value: _nowCameraPosition?.tilt.toStringAsFixed(1),
+ resetFunc: () => updateCamera(NCameraUpdate.withParams(tilt: 0)),
+ minusFunc: () => updateCamera(NCameraUpdate.withParams(tiltBy: -9)),
+ plusFunc: () => updateCamera(NCameraUpdate.withParams(tiltBy: 9)),
+ );
+ }
+
+ Widget bearingControlWidget() {
+ return plusMinusItemWidget(
+ title: "방향각(도)",
+ method: "NCameraUpdate.bearing, .bearingBy,\nNCameraPosition.bearing",
+ value: _nowCameraPosition?.bearing.toStringAsFixed(1),
+ resetFunc: () => updateCamera(NCameraUpdate.withParams(bearing: 0)),
+ minusFunc: () => updateCamera(NCameraUpdate.withParams(bearingBy: -20)),
+ plusFunc: () => updateCamera(NCameraUpdate.withParams(bearingBy: 20)),
+ );
+ }
+
+ Widget animationSpeedControlWidget() {
+ return plusMinusItemWidget(
+ title: "이동 애니메이션 시간",
+ method: "NCameraUpdate.setAnimation(\n\tNCameraAnimation?, Duration?)",
+ value: "${_animationMill}ms",
+ resetFunc: () => setState(() => _animationMill = 800),
+ minusFunc: () => setState(() => _animationMill -= 100),
+ plusFunc: () => setState(() => _animationMill += 100),
+ );
+ }
+
+ Widget plusMinusItemWidget({
+ required String title,
+ required String method,
+ required String? value,
+ required void Function() resetFunc,
+ required void Function() plusFunc,
+ required void Function() minusFunc,
+ }) {
+ return Padding(
+ padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 20)
+ .copyWith(right: 8),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Expanded(
+ child: InnerSimpleTitle(
+ title: title,
+ description: method,
+ direction: Axis.vertical)),
+ Row(children: [
+ IconButton(
+ onPressed: minusFunc,
+ icon: const Icon(Icons.remove_circle)),
+ Material(
+ borderRadius: BorderRadius.circular(8),
+ color: getColorTheme(context).outline,
+ child: InkWell(
+ onTap: resetFunc,
+ borderRadius: BorderRadius.circular(8),
+ child: Stack(children: [
+ Container(
+ constraints: const BoxConstraints(minWidth: 60),
+ padding: const EdgeInsets.symmetric(vertical: 10),
+ alignment: Alignment.center,
+ child: Text(value.toString(),
+ style: getTextTheme(context).titleSmall),
+ ),
+ Positioned(
+ top: 2,
+ right: 2,
+ child: Icon(Icons.refresh_rounded,
+ size: 12,
+ color: getColorTheme(context).secondary)),
+ ]))),
+ IconButton(
+ onPressed: plusFunc, icon: const Icon(Icons.add_circle)),
+ ]),
+ ]));
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Column(children: [
+ coordControlWidget(),
+ zoomControlWidget(),
+ if (widget.canScroll)
+ Column(children: [
+ tiltControlWidget(),
+ bearingControlWidget(),
+ animationSpeedControlWidget(),
+ ]),
+ const BottomPadding(),
+ ]);
+ }
+
+ bool _onKeyUp(KeyEvent event) {
+ if (event is KeyDownEvent) return true;
+
+ final _ = switch (event.logicalKey) {
+ LogicalKeyboardKey.arrowUp =>
+ moveCameraCoordWithDp(dpForMove, Axis.vertical),
+ LogicalKeyboardKey.arrowDown =>
+ moveCameraCoordWithDp(-dpForMove, Axis.vertical),
+ LogicalKeyboardKey.arrowLeft =>
+ moveCameraCoordWithDp(-dpForMove, Axis.horizontal),
+ LogicalKeyboardKey.arrowRight =>
+ moveCameraCoordWithDp(dpForMove, Axis.horizontal),
+ LogicalKeyboardKey.minus => updateCamera(NCameraUpdate.zoomBy(-0.5)),
+ LogicalKeyboardKey.add ||
+ LogicalKeyboardKey.equal =>
+ updateCamera(NCameraUpdate.zoomBy(0.5)),
+ _ => null,
+ };
+
+ return false;
+ }
+
+ NaverMapController get _mapController => widget.mapController;
+ StreamSubscription? onCameraChangeStreamSubscription;
+ StreamSubscription? onKeyUpStreamSubscription;
+
+ @override
+ void initState() {
+ super.initState();
+ HardwareKeyboard.instance.addHandler(_onKeyUp);
+ onCameraChange();
+ onCameraChangeStreamSubscription =
+ widget.onCameraChangeStream.listen((_) => onCameraChange());
+ }
+
+ @override
+ void dispose() {
+ HardwareKeyboard.instance.removeHandler(_onKeyUp);
+ onCameraChangeStreamSubscription?.cancel();
+ onCameraChangeStreamSubscription = null;
+ super.dispose();
+ }
+}
diff --git a/example/lib/pages/examples/controller_example.dart b/example/lib/pages/examples/controller_example.dart
index 601e73ae..04f4efab 100644
--- a/example/lib/pages/examples/controller_example.dart
+++ b/example/lib/pages/examples/controller_example.dart
@@ -1,22 +1,26 @@
import 'dart:async';
+import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_naver_map/flutter_naver_map.dart';
import 'package:flutter_naver_map_example/design/custom_widget.dart';
-import 'package:flutter_naver_map_example/pages/example_base.dart';
+import 'package:flutter_naver_map_example/pages/utils/example_base.dart';
import 'package:flutter_naver_map_example/util/alert_util.dart';
+import 'package:flutter_naver_map_example/util/string_util.dart';
import '../../design/theme.dart';
class NaverMapControllerExample extends ExampleBasePage {
final Stream onCameraChangeStream;
+ final Stream onLastTouchStream;
const NaverMapControllerExample({
super.key,
required super.mapController,
required super.canScroll,
required this.onCameraChangeStream,
+ required this.onLastTouchStream,
});
@override
@@ -31,10 +35,25 @@ class _NaverMapControllerExampleState extends State {
/// 현재 m/dp (1dp가 몇 미터인지)
double? _nowMeterPerDp;
+ /// 현재 표출되는 지도 범위
+ NLatLngBounds? _regionBounds;
+
+ /// 마지막 터치 화면 좌표
+ NPoint? _lastTappedScreenPosition;
+ NLatLng? _lastTappedMapPosition;
+
void onCameraChange() async {
_nowCameraPosition = await _mapController.getCameraPosition();
_nowMeterPerDp = await _mapController.getMeterPerDp();
- setState(() {});
+ _regionBounds = await _mapController.getContentBounds();
+ if (mounted) setState(() {});
+ }
+
+ void onLastTouch(Offset offset) {
+ _lastTappedScreenPosition = NPoint(offset.dx, offset.dy);
+ _mapController
+ .screenLocationToLatLng(_lastTappedScreenPosition!)
+ .then((latLng) => _lastTappedMapPosition = latLng);
}
Widget _nowCameraPositionWidget() {
@@ -101,19 +120,54 @@ class _NaverMapControllerExampleState extends State {
]));
}
- Widget _actionButtonSections(bool isUnFold) {
+ Widget _contentsRegionWidget() {
+ return Container(
+ padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 8),
+ child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
+ const InnerSimpleTitle(
+ title: "지도 위젯에 보여지는 범위",
+ description: ".getContentBounds() | .getContentRegion()"),
+ const SizedBox(height: 4),
+ Text(
+ "남서: ${_regionBounds?.southWest.toShortString()}, "
+ "북동: ${_regionBounds?.northEast.toShortString()}",
+ style: getTextTheme(context)
+ .bodyMedium
+ ?.copyWith(fontWeight: FontWeight.w700)),
+ ]));
+ }
+
+ Widget _switchLatLngToScreenLocationWidget() {
+ return Container(
+ padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 8),
+ child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
+ const InnerSimpleTitle(
+ title: "위치 좌표 ↔️ 화면 좌표",
+ description:
+ ".screenLocationToLatLng(NPoint)\n.latLngToScreenLocation(NLatLng)"),
+ const SizedBox(height: 4),
+ Text(
+ _lastTappedScreenPosition != null
+ ? "마지막 드래그 화면 좌표: NPoint(${_lastTappedScreenPosition!.x.toStringAsFixed(5)}, ${_lastTappedScreenPosition!.y.toStringAsFixed(5)})\n"
+ "변환 된 지도 좌표: NLatLng(${_lastTappedMapPosition?.toShortString()})"
+ : "지도를 드래그해보세요 (드래그 시작점을 수집합니다)",
+ style: getTextTheme(context)
+ .bodyMedium
+ ?.copyWith(fontWeight: FontWeight.w700)),
+ ]));
+ }
+
+ Widget _actionButtonSections() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 8),
child: HalfActionButtonGrid(buttons: [
- if (isUnFold)
HalfActionButton(
action: _mapController.forceRefresh,
icon: Icons.refresh,
title: "지도 강제 새로고침",
description: ".forceRefresh"),
HalfActionButton(
- action: () => AlertUtil.openAlert("준비중인 예제입니다.\n함수로는 사용하실 수 있습니다.",
- context: context),
+ action: _takeSnapshot,
icon: Icons.camera_alt,
title: "지도 캡쳐하기",
description: ".takeSnapshot"),
@@ -121,20 +175,61 @@ class _NaverMapControllerExampleState extends State {
);
}
+ void _takeSnapshot() async {
+ final snapshot = await _mapController.takeSnapshot(showControls: false);
+ if (context.mounted) {
+ showDialog(
+ context: context,
+ builder: (context) {
+ return Center(
+ child: Material(
+ color: getColorTheme(context).background,
+ borderRadius: BorderRadius.circular(12),
+ child: Container(
+ padding: const EdgeInsets.all(12),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text("지도 캡쳐하기",
+ style: getTextTheme(context).titleMedium),
+ Container(
+ margin: const EdgeInsets.only(top: 6),
+ height: MediaQuery.sizeOf(context).height * 0.64,
+ child: Image.file(File(snapshot.path))),
+ ])),
+ ));
+ });
+ }
+ }
+
@override
Widget build(BuildContext context) {
- return Column(children: [
- _nowCameraPositionWidget(),
- _meterPerDpWidget(),
- _actionButtonSections(widget.canScroll),
- const BottomPadding(),
- ]);
+ return Expanded(
+ flex: widget.canScroll ? 1 : 0,
+ child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
+ _nowCameraPositionWidget(),
+ if (widget.canScroll)
+ Expanded(
+ child: ListView(
+ padding: EdgeInsets.only(
+ bottom: MediaQuery.paddingOf(context).bottom),
+ children: [
+ _actionButtonSections(),
+ _meterPerDpWidget(),
+ _contentsRegionWidget(),
+ _switchLatLngToScreenLocationWidget(),
+ ])),
+ if (!widget.canScroll) const BottomPadding(),
+ ]),
+ );
}
// --- worker ---
NaverMapController get _mapController => widget.mapController;
- StreamSubscription? onCameraChangeStreamSubscription;
+ StreamSubscription? onCameraChangeStreamSubscription;
+ StreamSubscription? onLastTouchStreamSubscription;
@override
void initState() {
@@ -142,11 +237,14 @@ class _NaverMapControllerExampleState extends State {
onCameraChange();
onCameraChangeStreamSubscription =
widget.onCameraChangeStream.listen((_) => onCameraChange());
+ onLastTouchStreamSubscription =
+ widget.onLastTouchStream.listen(onLastTouch);
}
@override
void dispose() {
onCameraChangeStreamSubscription?.cancel();
+ onLastTouchStreamSubscription?.cancel();
super.dispose();
}
}
diff --git a/example/lib/pages/examples/overlay_example.dart b/example/lib/pages/examples/overlay_example.dart
index 994f377c..921c6d8b 100644
--- a/example/lib/pages/examples/overlay_example.dart
+++ b/example/lib/pages/examples/overlay_example.dart
@@ -3,15 +3,20 @@ import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter_naver_map/flutter_naver_map.dart';
import 'package:flutter_naver_map_example/design/theme.dart';
-import 'package:flutter_naver_map_example/pages/example_base.dart';
+import 'package:flutter_naver_map_example/pages/utils/example_base.dart';
+import 'package:flutter_naver_map_example/util/overlay_portal_util.dart';
+import 'package:flutter_naver_map_example/util/string_util.dart';
import '../../design/custom_widget.dart';
class NOverlayExample extends ExampleBasePage {
+ final NInfoOverlayPortalController nOverlayInfoOverlayPortalController;
+
const NOverlayExample({
Key? key,
required super.mapController,
required super.canScroll,
+ required this.nOverlayInfoOverlayPortalController,
}) : super(key: key);
@override
@@ -30,49 +35,66 @@ class _NOverlayExampleState extends State {
type: willCreateOverlayType, cameraPosition: cameraPosition);
overlay.setOnTapListener((overlay) {
final latLng = cameraPosition.target;
- mapController.latLngToScreenLocation(latLng).then(
- (point) => addFlutterFloatingOverlay(point: point, overlay: overlay));
+ mapController.latLngToScreenLocation(latLng).then((point) =>
+ addFlutterFloatingOverlay(
+ point: point, overlay: overlay, latLng: latLng));
});
mapController.addOverlay(overlay);
}
- OverlayEntry? entry;
-
void addFlutterFloatingOverlay({
required NOverlay overlay,
required NPoint point,
+ required NLatLng latLng,
}) {
- entry = OverlayEntry(builder: (context) {
- final xPosition = (point.x) - 28;
- return Positioned.fill(
- child: GestureDetector(
- behavior: HitTestBehavior.opaque,
- onTap: () => removeFlutterFloatingOverlay(),
- child: Stack(children: [
- Positioned(
- left: xPosition < 0 ? 0 : xPosition,
- top: point.y < 0 ? 0 : point.y,
- child: Balloon(
- size: const Size(200, 200),
- padding: const EdgeInsets.all(8),
- backgroundColor: getColorTheme(context).background,
- child: ListView(children: [
- Text(overlay.toString()),
- SimpleButton(
- text: "지우기",
- action: () {
- mapController.deleteOverlay(overlay.info);
- removeFlutterFloatingOverlay();
- })
- ])))
- ])));
- });
- Overlay.of(context).insert(entry!);
- }
+ widget.nOverlayInfoOverlayPortalController.openWithWidget(
+ builder: (context, mapController, controller) {
+ Widget header() => Padding(
+ padding: const EdgeInsets.fromLTRB(10, 10, 10, 4),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: Text(overlay.runtimeType.toString(),
+ maxLines: 1,
+ softWrap: false,
+ overflow: TextOverflow.fade,
+ style: getTextTheme(context).titleSmall)),
+ const Icon(Icons.close_rounded),
+ ]));
- void removeFlutterFloatingOverlay() {
- entry?.remove();
- entry = null;
+ return Column(children: [
+ header(),
+ Expanded(
+ child: ListView(
+ padding: const EdgeInsets.symmetric(horizontal: 10),
+ children: [
+ Text(
+ overlay.info.id == overlay.info.parseIdAsTimeString()
+ ? "id: ${overlay.info.id}"
+ : "${overlay.info.parseIdAsTimeString().replaceFirst(":", "시 ").replaceFirst(":", "분 ")}초에 생성됨",
+ style: getTextTheme(context).bodySmall),
+ Text("${latLng.toShortString()}에 위치함",
+ style: getTextTheme(context).bodySmall),
+ const SizedBox(height: 4),
+ Text(
+ "zIndex: ${overlay.zIndex} (global: ${overlay.globalZIndex})\n"
+ "${overlay.minZoom} ${overlay.isMinZoomInclusive ? "≤" : "<"}"
+ " [보이는 줌 범위] ${overlay.isMaxZoomInclusive ? "≤" : "<"} ${overlay.maxZoom}\n",
+ style: getTextTheme(context).bodySmall),
+ ])),
+ SmallButton("오버레이 지우기",
+ icon: Icons.delete_forever_outlined,
+ radius: 0,
+ color: Colors.red.shade600, onTap: () {
+ mapController.deleteOverlay(overlay.info);
+ controller.hide();
+ }),
+ ]);
+ },
+ screenPoint: point,
+ overlay: overlay);
}
@override
@@ -99,56 +121,53 @@ class _NOverlayExampleState extends State {
padding: const EdgeInsets.fromLTRB(24, 0, 24, 12),
child: Row(children: [
Expanded(
- child: SimpleButton(
- text: "${willCreateOverlayType.koreanName}만 모두 지우기",
- color: Colors.orange,
- margin: EdgeInsets.zero,
- action: () => mapController.clearOverlays(
- type: willCreateOverlayType)),
- ),
+ child: SimpleButton(
+ text: "${willCreateOverlayType.koreanName}만 모두 지우기",
+ color: Colors.orange,
+ margin: EdgeInsets.zero,
+ action: () => mapController.clearOverlays(
+ type: willCreateOverlayType))),
const SizedBox(width: 12),
Expanded(
- child: SimpleButton(
- text: "모두 지우기",
- color: Colors.red,
- margin: EdgeInsets.zero,
- action: () => mapController.clearOverlays()),
- ),
+ child: SimpleButton(
+ text: "모두 지우기",
+ color: Colors.red,
+ margin: EdgeInsets.zero,
+ action: () => mapController.clearOverlays())),
])),
const BottomPadding(),
]);
}
+}
- @override
- void dispose() {
- removeFlutterFloatingOverlay();
- super.dispose();
+extension NOverlayTypeExtension on NOverlayType {
+ String get koreanName {
+ return switch (this) {
+ NOverlayType.marker => "마커",
+ NOverlayType.infoWindow => "정보창",
+ NOverlayType.circleOverlay => "원 오버레이",
+ NOverlayType.groundOverlay => "지상 오버레이",
+ NOverlayType.polygonOverlay => "다각형 오버레이",
+ NOverlayType.polylineOverlay => "선 오버레이",
+ NOverlayType.pathOverlay => "경로 오버레이",
+ NOverlayType.multipartPathOverlay => "경로(멀티파트) 오버레이",
+ NOverlayType.arrowheadPathOverlay => "경로(화살표) 오버레이",
+ NOverlayType.locationOverlay => "위치 오버레이",
+ };
}
}
-extension NOverlayExtension on NOverlayType {
- String get koreanName {
- switch (this) {
- case NOverlayType.marker:
- return "마커";
- case NOverlayType.infoWindow:
- return "정보창";
- case NOverlayType.circleOverlay:
- return "원 오버레이";
- case NOverlayType.groundOverlay:
- return "지상 오버레이";
- case NOverlayType.polygonOverlay:
- return "다각형 오버레이";
- case NOverlayType.polylineOverlay:
- return "선 오버레이";
- case NOverlayType.pathOverlay:
- return "경로 오버레이";
- case NOverlayType.multipartPathOverlay:
- return "경로(멀티파트) 오버레이";
- case NOverlayType.arrowheadPathOverlay:
- return "경로(화살표) 오버레이";
- case NOverlayType.locationOverlay:
- return "위치 오버레이";
+extension NOverlayInfoExtension on NOverlayInfo {
+ String parseIdAsTimeString() {
+ final idForCreatedAt = int.tryParse(id);
+ if (idForCreatedAt == null) return id;
+ try {
+ return DateTime.fromMillisecondsSinceEpoch(idForCreatedAt)
+ .toIso8601String()
+ .split("T")
+ .last;
+ } catch (_) {
+ return id;
}
}
}
diff --git a/example/lib/pages/examples/pick_example.dart b/example/lib/pages/examples/pick_example.dart
new file mode 100644
index 00000000..574429b4
--- /dev/null
+++ b/example/lib/pages/examples/pick_example.dart
@@ -0,0 +1,122 @@
+import 'dart:async';
+import 'dart:math';
+
+import 'package:flutter/material.dart';
+import 'package:flutter_naver_map/flutter_naver_map.dart';
+import 'package:flutter_naver_map_example/design/custom_widget.dart';
+import 'package:flutter_naver_map_example/pages/utils/example_base.dart';
+import 'package:flutter_naver_map_example/pages/examples/overlay_example.dart';
+
+class NaverMapPickExample extends ExampleBasePage {
+ final Stream onCameraChangeStream;
+ final Point mapEndPoint;
+
+ const NaverMapPickExample({
+ super.key,
+ required super.mapController,
+ required super.canScroll,
+ required this.mapEndPoint,
+ required this.onCameraChangeStream,
+ });
+
+ @override
+ State createState() => _NaverMapControllerExampleState();
+}
+
+class _NaverMapControllerExampleState extends State {
+ final List pickables = [];
+
+ void onCameraChange() async {
+ final p = widget.mapEndPoint;
+ final center = NPoint(p.x / 2, p.y / 2);
+ final radius = max(p.x, p.y);
+ final pickables = await _mapController.pickAll(center, radius: radius);
+ this.pickables
+ ..clear()
+ ..addAll(pickables);
+ setState(() {});
+ }
+
+ Widget _nowCameraPositionWidget() {
+ return Container(
+ padding: const EdgeInsets.symmetric(horizontal: 24),
+ child:
+ Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
+ const InnerSimpleTitle(
+ title: "지도에 표시되는 심볼 및 오버레이들",
+ description: "controller.pickAll(NPoint, radius)",
+ direction: Axis.vertical),
+ const SizedBox(height: 8),
+ Expanded(
+ child: SingleChildScrollView(
+ padding: EdgeInsets.only(
+ bottom: 8 + MediaQuery.paddingOf(context).bottom),
+ child: HalfActionButtonGrid(
+ buttons: pickables.map((e) => _pickableItem(e)).toList()),
+ )),
+ ]));
+ }
+
+ Widget _pickableItem(NPickableInfo info) {
+ final String title;
+ final String description;
+ final IconData icon;
+ final void Function() action;
+
+ if (info is NOverlayInfo) {
+ title = info.type.koreanName;
+ description = info.parseIdAsTimeString();
+
+ icon = switch (info.type) {
+ NOverlayType.marker => Icons.place_rounded,
+ NOverlayType.infoWindow => Icons.chat_bubble_rounded,
+ NOverlayType.circleOverlay => Icons.circle_outlined,
+ NOverlayType.groundOverlay => Icons.square,
+ NOverlayType.polygonOverlay => Icons.star,
+ NOverlayType.polylineOverlay => Icons.polyline_rounded,
+ NOverlayType.pathOverlay => Icons.route_rounded,
+ NOverlayType.multipartPathOverlay => Icons.route_sharp,
+ NOverlayType.arrowheadPathOverlay => Icons.arrow_right_alt_rounded,
+ NOverlayType.locationOverlay => Icons.my_location_rounded,
+ };
+ action = () {};
+ } else if (info is NSymbolInfo) {
+ title = info.caption.replaceAll("\n", " ");
+ description =
+ "${info.position.latitude.toStringAsFixed(5)}, ${info.position.longitude.toStringAsFixed(5)}";
+ icon = Icons.apartment_rounded;
+ action = () => _mapController
+ .updateCamera(NCameraUpdate.scrollAndZoomTo(target: info.position));
+ } else {
+ return const SizedBox();
+ }
+
+ return HalfActionButton(
+ action: action, icon: icon, title: title, description: description);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Expanded(child: _nowCameraPositionWidget());
+ }
+
+ // --- worker ---
+
+ NaverMapController get _mapController => widget.mapController;
+ StreamSubscription? onCameraChangeStreamSubscription;
+
+ @override
+ void initState() {
+ super.initState();
+ onCameraChange();
+ onCameraChangeStreamSubscription =
+ widget.onCameraChangeStream.listen((_) => onCameraChange());
+ }
+
+ @override
+ void dispose() {
+ onCameraChangeStreamSubscription?.cancel();
+ onCameraChangeStreamSubscription = null;
+ super.dispose();
+ }
+}
diff --git a/example/lib/pages/utils/bottom_drawer.dart b/example/lib/pages/utils/bottom_drawer.dart
new file mode 100644
index 00000000..06c2f10d
--- /dev/null
+++ b/example/lib/pages/utils/bottom_drawer.dart
@@ -0,0 +1,162 @@
+import 'dart:io';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bottom_drawer/flutter_bottom_drawer.dart';
+import 'package:flutter_naver_map_example/design/custom_widget.dart';
+
+import '../../design/map_function_item.dart';
+import '../../design/theme.dart';
+
+class ExampleAppBottomDrawer {
+ final BuildContext context;
+ final Function(double height) onDrawerHeightChanged;
+ final Function() rebuild;
+ final List pages;
+ final void Function()? onPageDispose;
+
+ ExampleAppBottomDrawer({
+ required this.context,
+ required this.onDrawerHeightChanged,
+ required this.rebuild,
+ required this.pages,
+ this.onPageDispose,
+ });
+
+ ColorScheme get colorTheme => getColorTheme(context);
+
+ TextTheme get textTheme => getTextTheme(context);
+
+ late DrawerMoveController drawerController;
+ late DrawerState drawerState;
+ late Function(Function()) drawerSetState;
+
+ MapFunctionItem? nowItem;
+
+ bool get hasPage => nowItem != null;
+
+ void go(MapFunctionItem item) {
+ nowItem = item;
+ if (drawerController.nowState == DrawerState.closed) {
+ drawerController.open();
+ Future.delayed(_drawerAnimationDuration)
+ .then((_) => rebuildDrawerAndPage());
+ } else {
+ rebuildDrawerAndPage();
+ }
+ }
+
+ void back() {
+ nowItem = null;
+ onPageDispose?.call();
+ rebuildDrawerAndPage();
+ if (drawerController.nowState == DrawerState.opened) {
+ WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
+ drawerController.close();
+ Future.delayed(_drawerAnimationDuration)
+ .then((_) => rebuildDrawerAndPage());
+ });
+ }
+ }
+
+ void rebuildDrawerAndPage() {
+ drawerSetState(() {}); // drawer rebuild
+ rebuild(); // page rebuild (height changed)
+ }
+
+ static const _drawerAnimationDuration = Duration(milliseconds: 300);
+
+ BottomDrawer get bottomDrawer => BottomDrawer(
+ height: nowItem?.isScrollPage == true
+ ? MediaQuery.sizeOf(context).height / 3.6
+ : null,
+ expandedHeight: 480,
+ handleSectionHeight: 20,
+ handleColor: colorTheme.secondary,
+ backgroundColor: colorTheme.background,
+ onReady: (controller) => drawerController = controller,
+ onStateChanged: (state) => drawerState = state,
+ onHeightChanged: onDrawerHeightChanged,
+ resizeAnimationDuration: _drawerAnimationDuration,
+ builder: (state, setState, context) {
+ drawerSetState = setState;
+ return hasPage
+ ? selectedPage(state == DrawerState.opened)
+ : innerListView(state == DrawerState.closed);
+ });
+
+ Widget innerListView(bool isClosed) => Column(children: [
+ innerListViewHeader(),
+ Expanded(
+ flex: isClosed ? 0 : 1,
+ child: Padding(
+ padding: const EdgeInsets.all(8)
+ .copyWith(bottom: 8 + MediaQuery.of(context).padding.bottom),
+ child: HalfActionButtonGrid(
+ buttons: pages.map((page) {
+ if (page.needItemOnTap) {
+ page = page.copyWith(
+ onTap: go,
+ onBack: back,
+ drawerController: drawerController);
+ }
+ return page.getItemWidget(context);
+ }).toList()),
+ ),
+ ),
+ ]);
+
+ Widget innerListViewHeader() => Container(
+ decoration: BoxDecoration(
+ border: Border(
+ bottom: BorderSide(
+ color: colorTheme.onBackground.withOpacity(0.28), width: 0.2)),
+ ),
+ padding: const EdgeInsets.fromLTRB(24, 12, 24, 16),
+ child: Row(crossAxisAlignment: CrossAxisAlignment.center, children: [
+ Text("지도 기능 둘러보기", style: textTheme.titleLarge),
+ Expanded(
+ child: LayoutBuilder(
+ builder: (context, constraints) =>
+ Row(mainAxisAlignment: MainAxisAlignment.end, children: [
+ Container(
+ padding:
+ const EdgeInsets.symmetric(vertical: 2, horizontal: 4),
+ decoration: BoxDecoration(
+ color: Platform.isAndroid ? Colors.green : Colors.black,
+ borderRadius: BorderRadius.circular(4)),
+ child: Row(children: [
+ Icon(Platform.isAndroid ? Icons.android : Icons.apple,
+ color: Colors.white, size: 14),
+ const SizedBox(width: 2),
+ ConstrainedBox(
+ constraints:
+ BoxConstraints(maxWidth: constraints.maxWidth - 32),
+ child: Text(Platform.operatingSystemVersion,
+ softWrap: false,
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ style: textTheme.labelSmall),
+ ),
+ ])),
+ ]),
+ ),
+ ),
+ ]));
+
+ Widget selectedPage(bool canScroll) {
+ assert(nowItem != null);
+ return nowItem!.getPageWidget(context, canScroll: canScroll);
+ }
+
+ bool processWillPop() {
+ if (hasPage) {
+ back();
+ return false;
+ } else if (drawerState != DrawerState.closed) {
+ drawerController.close();
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/example/lib/pages/example_base.dart b/example/lib/pages/utils/example_base.dart
similarity index 99%
rename from example/lib/pages/example_base.dart
rename to example/lib/pages/utils/example_base.dart
index 7bc7c08d..73a7c47b 100644
--- a/example/lib/pages/example_base.dart
+++ b/example/lib/pages/utils/example_base.dart
@@ -11,4 +11,3 @@ abstract class ExampleBasePage extends StatefulWidget {
required this.canScroll,
});
}
-
diff --git a/example/lib/pages/new_window_page.dart b/example/lib/pages/utils/new_window_page.dart
similarity index 100%
rename from example/lib/pages/new_window_page.dart
rename to example/lib/pages/utils/new_window_page.dart
diff --git a/example/lib/util/overlay_portal_util.dart b/example/lib/util/overlay_portal_util.dart
new file mode 100644
index 00000000..16a5a658
--- /dev/null
+++ b/example/lib/util/overlay_portal_util.dart
@@ -0,0 +1,54 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_naver_map/flutter_naver_map.dart';
+import 'package:flutter_naver_map_example/design/custom_widget.dart';
+import 'package:flutter_naver_map_example/design/theme.dart';
+
+typedef NInfoOverlayBuilderCreateCallback = Widget Function(
+ BuildContext context,
+ NaverMapController mapController,
+ NInfoOverlayPortalController controller);
+
+typedef NInfoOverlayBuilderCallback = Widget Function(
+ BuildContext context, NaverMapController mapController);
+
+class NInfoOverlayPortalController extends OverlayPortalController {
+ NInfoOverlayPortalController({super.debugLabel});
+
+ NInfoOverlayBuilderCallback _builder =
+ (BuildContext _, NaverMapController __) => const SizedBox();
+
+ NInfoOverlayBuilderCallback get builder => _builder;
+
+ void openWithWidget({
+ required NInfoOverlayBuilderCreateCallback builder,
+ required NPoint screenPoint,
+ required NOverlay overlay,
+ }) {
+ _builder = (BuildContext context, NaverMapController mapController) =>
+ _nOverlayInfoOverlayWithTouchScope(context,
+ point: screenPoint,
+ child: builder.call(context, mapController, this));
+ show();
+ }
+
+ Widget _nOverlayInfoOverlayWithTouchScope(BuildContext context,
+ {required NPoint point, required Widget child}) {
+ final xPosition = (point.x) - 28;
+ return Positioned.fill(
+ child: GestureDetector(
+ behavior: HitTestBehavior.opaque,
+ onTap: () => hide(),
+ child: Container(
+ color: Colors.black12,
+ child: Stack(children: [
+ Positioned(
+ left: xPosition < 0 ? 0 : xPosition,
+ top: point.y < 0 ? 0 : point.y,
+ child: Balloon(
+ size: const Size(200, 160),
+ padding: EdgeInsets.zero,
+ backgroundColor: getColorTheme(context).background,
+ child: child)),
+ ]))));
+ }
+}
diff --git a/example/lib/util/string_util.dart b/example/lib/util/string_util.dart
new file mode 100644
index 00000000..e21538fb
--- /dev/null
+++ b/example/lib/util/string_util.dart
@@ -0,0 +1,7 @@
+import 'package:flutter_naver_map/flutter_naver_map.dart';
+
+extension NLatLngExtension on NLatLng {
+ String toShortString([int length = 5]) {
+ return "${latitude.toStringAsFixed(length)}, ${longitude.toStringAsFixed(length)}";
+ }
+}
diff --git a/example/pubspec.lock b/example/pubspec.lock
index 57d1b8a5..92472df0 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -82,10 +82,10 @@ packages:
dependency: "direct main"
description:
name: flutter_bottom_drawer
- sha256: d84e9bd2a99ec7879ab238e1886a9e0f723a402942ea9ce9e1cf60c602f2a9dd
+ sha256: "3d832e1819210144e9a0ce30a574acecda1a2ad6a61fcf6898f4fb1a82024693"
url: "https://pub.dev"
source: hosted
- version: "0.0.8"
+ version: "0.1.0"
flutter_driver:
dependency: transitive
description: flutter
@@ -110,7 +110,7 @@ packages:
path: ".."
relative: true
source: path
- version: "1.1.0"
+ version: "1.1.1"
flutter_styled_toast:
dependency: "direct main"
description:
@@ -124,6 +124,11 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
+ flutter_web_plugins:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
fuchsia_remote_debug_protocol:
dependency: transitive
description: flutter
@@ -234,42 +239,50 @@ packages:
dependency: "direct main"
description:
name: permission_handler
- sha256: "284a66179cabdf942f838543e10413246f06424d960c92ba95c84439154fcac8"
+ sha256: "860c6b871c94c78e202dc69546d4d8fd84bd59faeb36f8fb9888668a53ff4f78"
url: "https://pub.dev"
source: hosted
- version: "11.0.1"
+ version: "11.1.0"
permission_handler_android:
dependency: transitive
description:
name: permission_handler_android
- sha256: f9fddd3b46109bd69ff3f9efa5006d2d309b7aec0f3c1c5637a60a2d5659e76e
+ sha256: "2f1bec180ee2f5665c22faada971a8f024761f632e93ddc23310487df52dcfa6"
url: "https://pub.dev"
source: hosted
- version: "11.1.0"
+ version: "12.0.1"
permission_handler_apple:
dependency: transitive
description:
name: permission_handler_apple
- sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5"
+ sha256: "1a816084338ada8d574b1cb48390e6e8b19305d5120fe3a37c98825bacc78306"
url: "https://pub.dev"
source: hosted
- version: "9.1.4"
+ version: "9.2.0"
+ permission_handler_html:
+ dependency: transitive
+ description:
+ name: permission_handler_html
+ sha256: "11b762a8c123dced6461933a88ea1edbbe036078c3f9f41b08886e678e7864df"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.1.0+2"
permission_handler_platform_interface:
dependency: transitive
description:
name: permission_handler_platform_interface
- sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4"
+ sha256: d87349312f7eaf6ce0adaf668daf700ac5b06af84338bd8b8574dfbd93ffe1a1
url: "https://pub.dev"
source: hosted
- version: "3.12.0"
+ version: "4.0.2"
permission_handler_windows:
dependency: transitive
description:
name: permission_handler_windows
- sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098
+ sha256: "1e8640c1e39121128da6b816d236e714d2cf17fac5a105dd6acdd3403a628004"
url: "https://pub.dev"
source: hosted
- version: "0.1.3"
+ version: "0.2.0"
platform:
dependency: transitive
description:
@@ -363,6 +376,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.6.1"
+ transparent_pointer:
+ dependency: "direct main"
+ description:
+ name: transparent_pointer
+ sha256: "27f5a7a63e517b6a56962bd473bbfcdcacce13fc996a264d6665da9a24650eb9"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.0"
typed_data:
dependency: transitive
description:
@@ -421,4 +442,4 @@ packages:
version: "1.0.3"
sdks:
dart: ">=3.2.1 <4.0.0"
- flutter: ">=3.10.0"
+ flutter: ">=3.16.0"
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index 538a9386..00dfadac 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -15,10 +15,11 @@ dependencies:
flutter_naver_map:
path: ../
- flutter_bottom_drawer: 0.0.8
- permission_handler: 11.0.1
+ flutter_bottom_drawer: 0.1.0
+ permission_handler: 11.1.0
flutter_styled_toast: 2.2.1
pull_to_refresh_flutter3: 2.0.2
+ transparent_pointer: ^1.0.0
dev_dependencies:
flutter_test:
diff --git a/ios/Classes/controller/overlay/OverlayController.swift b/ios/Classes/controller/overlay/OverlayController.swift
index cc3915a5..7eb8ec8b 100644
--- a/ios/Classes/controller/overlay/OverlayController.swift
+++ b/ios/Classes/controller/overlay/OverlayController.swift
@@ -233,7 +233,8 @@ internal class OverlayController: OverlaySender, OverlayHandler, ArrowheadPathOv
}
func openInfoWindow(_ marker: NMFMarker, rawInfoWindow: Any, rawAlign: Any, success: (Any?) -> ()) {
- let nInfoWindow = NInfoWindow.fromMessageable(rawInfoWindow)
+ var nInfoWindow = NInfoWindow.fromMessageable(rawInfoWindow)
+ nInfoWindow.setCommonProperties(rawArgs: asDict(rawInfoWindow))
let infoWindow = saveOverlayWithAddable(creator: nInfoWindow) as! NMFInfoWindow
let align = try! asAlign(rawAlign)
diff --git a/lib/src/controller/map/sender.dart b/lib/src/controller/map/sender.dart
index 61f4317b..eb6d12da 100644
--- a/lib/src/controller/map/sender.dart
+++ b/lib/src/controller/map/sender.dart
@@ -25,10 +25,8 @@ abstract class _NaverMapControlSender {
/// using parameter is deprecated. use [getMeterPerDpAtLatitude] instead.
/// please getMeterPerDp() without parameter.
Future getMeterPerDp({
- @Deprecated("use getMeterPerDpAtLatitude() instead")
- double? latitude,
- @Deprecated("use getMeterPerDpAtLatitude() instead")
- double? zoom,
+ @Deprecated("use getMeterPerDpAtLatitude() instead") double? latitude,
+ @Deprecated("use getMeterPerDpAtLatitude() instead") double? zoom,
});
/// meter / dp at latitude (required zoomLevel)
diff --git a/lib/src/type/default/padding.dart b/lib/src/type/default/padding.dart
index de6bc495..5d2965e9 100644
--- a/lib/src/type/default/padding.dart
+++ b/lib/src/type/default/padding.dart
@@ -1,8 +1,7 @@
part of flutter_naver_map;
class NEdgeInsets extends EdgeInsets with NMessageableWithMap {
- const NEdgeInsets.fromLTRB(
- super.left, super.top, super.right, super.bottom)
+ const NEdgeInsets.fromLTRB(super.left, super.top, super.right, super.bottom)
: super.fromLTRB();
NEdgeInsets.fromEdgeInsets(EdgeInsets insets)
diff --git a/lib/src/type/map/overlay/overlay_image.dart b/lib/src/type/map/overlay/overlay_image.dart
index 03dbe88e..21d524f2 100644
--- a/lib/src/type/map/overlay/overlay_image.dart
+++ b/lib/src/type/map/overlay/overlay_image.dart
@@ -28,6 +28,10 @@ class NOverlayImage with NMessageableWithMap {
required Size size,
required BuildContext context,
}) async {
+ assert(
+ widget.runtimeType != Image,
+ "Do not use Image widget.\n"
+ "Instead, using `NOverlayImage.fromAssetImage` or `.fromFile` or `.fromByteArray` Constructor.");
final imageBytes = await WidgetToImageUtil.widgetToImageByte(widget,
size: size, context: context);
final path = await ImageUtil.saveImage(imageBytes);
diff --git a/lib/src/widget/platform_view.dart b/lib/src/widget/platform_view.dart
index 6901f977..c76a28e4 100644
--- a/lib/src/widget/platform_view.dart
+++ b/lib/src/widget/platform_view.dart
@@ -21,20 +21,13 @@ class _PlatformViewCreator {
hitTestBehavior: hitTestBehavior,
),
onCreatePlatformView: (params) {
- // issues
- // 1. android 5.0 (API 21) ~ android 7.1 (API 25) render issue (GLSurfaceView rendered under flutter view)
- // -> ExpensiveAndroidView, TextureSurfaceComposition (SurfaceAndroidView).
- // 2. "Unexpected platform view context for view ID 0;" issue. (+ minimum API 23)
- // -> AndroidView (virtual display)
+ // RenderView(Impl Android Side), Display Mode
+ // API 23 ~ 29 : TextureView, Texture Layer Hybrid Composition.
+ // API 30 ~ : GLSurfaceView, Hybrid Composition (TLHC cause issue: flutter#98865)
- // initAndroidView : auto detect.
- // but Android 10 or higher, use TextureSurfaceComposition.
-
- const hybridView = PlatformViewsService.initExpensiveAndroidView;
- // const autoView = PlatformViewsService.initAndroidView;
- const autoView = PlatformViewsService.initSurfaceAndroidView;
-
- final usingView = androidSdkVersion! >= 26 ? hybridView : autoView;
+ final usingView = androidSdkVersion! <= 29
+ ? PlatformViewsService.initAndroidView
+ : PlatformViewsService.initExpensiveAndroidView;
final view = usingView.call(
id: params.id,
diff --git a/pubspec.yaml b/pubspec.yaml
index 79d6d337..797be04d 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,6 @@
name: flutter_naver_map
description: Naver Map plugin for Flutter, which provides map service of Korea.
-version: 1.1.0
+version: 1.1.1
homepage: https://github.com/note11g/flutter_naver_map
documentation: https://note11.dev/flutter_naver_map