From d3e5d257ed696f1cc5c09a3db047a199f91ecac6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 5 Mar 2026 17:46:28 +0900 Subject: [PATCH 1/2] =?UTF-8?q?[fix]=20Android=20=ED=96=85=ED=8B=B1=20?= =?UTF-8?q?=ED=94=BC=EB=93=9C=EB=B0=B1=20=EA=B0=95=EB=8F=84=20=EB=B3=B4?= =?UTF-8?q?=EC=A0=95=20(vibration=20=ED=8C=A8=ED=82=A4=EC=A7=80=20?= =?UTF-8?q?=EB=8F=84=EC=9E=85)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Android에서 Flutter HapticFeedback API의 light/medium/heavy가 실제 체감 강도와 불일치하는 문제를 vibration 패키지의 amplitude 직접 제어로 해결. Co-Authored-By: Claude Opus 4.6 --- .../handlers/device_bridge_handler.dart | 64 +++++++++++++------ pubspec.lock | 40 ++++++++++++ pubspec.yaml | 1 + 3 files changed, 87 insertions(+), 18 deletions(-) diff --git a/lib/bridge/handlers/device_bridge_handler.dart b/lib/bridge/handlers/device_bridge_handler.dart index 750a1c5..69cd4a3 100644 --- a/lib/bridge/handlers/device_bridge_handler.dart +++ b/lib/bridge/handlers/device_bridge_handler.dart @@ -5,6 +5,7 @@ import 'package:app/permissions/FirebaseConfig.dart'; import 'package:app/utils/env/env.dart'; import 'package:flutter/services.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; +import 'package:vibration/vibration.dart'; /// 디바이스 토큰, 햅틱, 환경 전환 관련 브릿지 핸들러 mixin DeviceBridgeHandler on BridgeHandlerBase { @@ -43,24 +44,51 @@ mixin DeviceBridgeHandler on BridgeHandlerBase { return; } - switch (hapticType) { - case 'light': - await HapticFeedback.lightImpact(); - break; - case 'medium': - await HapticFeedback.mediumImpact(); - break; - case 'heavy': - await HapticFeedback.heavyImpact(); - break; - case 'selection': - await HapticFeedback.selectionClick(); - break; - case 'vibrate': - await HapticFeedback.vibrate(); - break; - default: - logger.w('Unknown haptic feedback type: $hapticType'); + if (Platform.isAndroid && + await Vibration.hasAmplitudeControl() == true) { + // Android: Vibration API로 amplitude 직접 제어 + // amplitude 기준: expo-haptics 프로덕션 값 + Android 공식 디자인 가이드 + // 레벨 간 최소 1.4배 차이 유지 (Android Haptics Design Principles) + switch (hapticType) { + case 'light': + Vibration.vibrate(duration: 50, amplitude: 30); + break; + case 'medium': + Vibration.vibrate(duration: 43, amplitude: 50); + break; + case 'heavy': + Vibration.vibrate(duration: 60, amplitude: 70); + break; + case 'selection': + Vibration.vibrate(duration: 20, amplitude: 20); + break; + case 'vibrate': + Vibration.vibrate(duration: 500, amplitude: 128); + break; + default: + logger.w('Unknown haptic feedback type: $hapticType'); + } + } else { + // iOS 또는 amplitude 미지원 Android: Flutter 기본 API 사용 + switch (hapticType) { + case 'light': + await HapticFeedback.lightImpact(); + break; + case 'medium': + await HapticFeedback.mediumImpact(); + break; + case 'heavy': + await HapticFeedback.heavyImpact(); + break; + case 'selection': + await HapticFeedback.selectionClick(); + break; + case 'vibrate': + await HapticFeedback.vibrate(); + break; + default: + logger.w('Unknown haptic feedback type: $hapticType'); + } } } else { logger.w( diff --git a/pubspec.lock b/pubspec.lock index 1b4b555..5b951e8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -257,6 +257,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.10" + device_info_plus: + dependency: transitive + description: + name: device_info_plus + sha256: "72d146c6d7098689ff5c5f66bcf593ac11efc530095385356e131070333e64da" + url: "https://pub.dev" + source: hosted + version: "11.3.0" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f + url: "https://pub.dev" + source: hosted + version: "7.0.3" dio: dependency: transitive description: @@ -1389,6 +1405,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + vibration: + dependency: "direct main" + description: + name: vibration + sha256: "3b08a0579c2f9c18d5d78cb5c74f1005f731e02eeca6d72561a2e8059bf98ec3" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + vibration_platform_interface: + dependency: transitive + description: + name: vibration_platform_interface + sha256: "6ffeee63547562a6fef53c05a41d4fdcae2c0595b83ef59a4813b0612cd2bc36" + url: "https://pub.dev" + source: hosted + version: "0.0.3" vm_service: dependency: transitive description: @@ -1469,6 +1501,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.13.0" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852" + url: "https://pub.dev" + source: hosted + version: "1.1.5" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index eda00c8..2e16be3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -28,6 +28,7 @@ dependencies: flutter_native_splash: ^2.4.6 shared_preferences: ^2.5.2 share_plus: ^10.1.4 + vibration: ^2.0.0 dev_dependencies: flutter_test: From 2daa22d2d21dec68e029c99f11931000e9a01109 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 5 Mar 2026 18:13:46 +0900 Subject: [PATCH 2/2] fix: add await to Vibration.vibrate() calls for proper error handling Addresses review comment by @copilot-pull-request-reviewer on device_bridge_handler.dart:66. PR: #10 --- lib/bridge/handlers/device_bridge_handler.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/bridge/handlers/device_bridge_handler.dart b/lib/bridge/handlers/device_bridge_handler.dart index 69cd4a3..9e54428 100644 --- a/lib/bridge/handlers/device_bridge_handler.dart +++ b/lib/bridge/handlers/device_bridge_handler.dart @@ -51,19 +51,19 @@ mixin DeviceBridgeHandler on BridgeHandlerBase { // 레벨 간 최소 1.4배 차이 유지 (Android Haptics Design Principles) switch (hapticType) { case 'light': - Vibration.vibrate(duration: 50, amplitude: 30); + await Vibration.vibrate(duration: 50, amplitude: 30); break; case 'medium': - Vibration.vibrate(duration: 43, amplitude: 50); + await Vibration.vibrate(duration: 43, amplitude: 50); break; case 'heavy': - Vibration.vibrate(duration: 60, amplitude: 70); + await Vibration.vibrate(duration: 60, amplitude: 70); break; case 'selection': - Vibration.vibrate(duration: 20, amplitude: 20); + await Vibration.vibrate(duration: 20, amplitude: 20); break; case 'vibrate': - Vibration.vibrate(duration: 500, amplitude: 128); + await Vibration.vibrate(duration: 500, amplitude: 128); break; default: logger.w('Unknown haptic feedback type: $hapticType');