> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ones1ght.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Flutter 설정 가이드

## 시스템 요구사항

| 항목 | 요구사항 |
| - | - |
| Flutter | 3.44 이상 (iOS 는 Swift Package Manager 로만 설치) |
| iOS | iOS 27 이상 · UWB 지원 iPhone (앱 최소 타깃은 iOS 18) |
| Android | Android 17 (API 37) 이상 · UWB DL-TDoA 지원 기기 (앱 최소 타깃은 API 26) |
| 네이티브 SDK | iOS `0.2.2` · Android `0.0.9` (플러그인에 고정) |
| 언어 | Dart |

측위정보 수신 미지원 기기인 경우 SDK 동작을 중지하며 운영중인 앱에 영향을 미치지 않습니다.

## SDK 설치

**Step 1**

[pub.dev](https://pub.dev/packages/ones1ght_sdk) 에서 패키지를 추가합니다.

```bash theme={null}
flutter pub add ones1ght_sdk
```

수동으로 추가시 `pubspec.yaml` 의 dependencies 항목에 추가합니다.

```yaml pubspec.yaml theme={null}
dependencies:
  ones1ght_sdk: ^0.0.4
```

**Step 2**

Dart 코드에서 가져옵니다.

```dart Dart icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/dart.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=02dee5dd767b4c76ef0e07bc8663c542" theme={null}
import 'package:ones1ght_sdk/ones1ght_sdk.dart';
```

## iOS 설정

**1. Swift Package Manager를 설정합니다.**

```bash theme={null}
flutter config --enable-swift-package-manager
```

**2. 배포 타깃 올리기**

`ios/Runner.xcodeproj` 의 `IPHONEOS_DEPLOYMENT_TARGET` 을 **18.0** 으로 올립니다.

**3. 권한 문구 넣기**

`ios/Runner/Info.plist` 에 아래 다섯 가지 권한 문구를 하나도 빠짐없이 추가합니다. 추가 방법은 [\[iOS 설정 가이드\]](/developers/sdk/integration/ios#ios-권한-설정)를 참고하세요.

| 키 | 권한 | 용도 |
| - | - | - |
| `NSBluetoothAlwaysUsageDescription` | 블루투스 권한 | 로케이터 BLE 수신용 |
| `NSLocationWhenInUseUsageDescription` | 위치 권한 | 로케이터의 위치 확인용도 |
| `NSLocationAlwaysAndWhenInUseUsageDescription` | 위치 권한 (항상) | 로케이터의 위치 확인용도 (iOS 27.2 beta 이상) |
| `NSLocationTemporaryUsageDescriptionDictionary` | 정밀 위치 권한 | 정확한 실내 위치 계산용 안의 키는 반드시 `Positioning` |
| `NSNearbyInteractionUsageDescription` | Nearby Interaction | 사용자의 정밀 측위 용도 |

```xml Info.plist theme={null}
<key>NSBluetoothAlwaysUsageDescription</key>
<string>주변 측위 장비를 찾는 데 사용합니다.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>실내 위치 확인을 위해 사용합니다.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>iOS 27.2 이상에서 건물의 층을 찾는 데 사용합니다.</string>
<key>NSLocationTemporaryUsageDescriptionDictionary</key>
<dict>
    <key>Positioning</key>
    <string>정확한 실내 위치를 계산하는 데 사용합니다.</string>
</dict>
<key>NSNearbyInteractionUsageDescription</key>
<string>UWB 정밀 측위를 위해 사용합니다.</string>
```

iOS 27.2 이상에서는 위치 권한이 **'항상'** 이어야 층을 찾습니다. SDK 는 '항상' 을 요청하지 않으므로 '앱 사용 중' 허용 뒤 앱이 직접 요청하세요

```dart theme={null}
// permission_handler 는 접두사를 붙여 가져온다 — PermissionStatus 이름이 SDK 와 겹친다.
import 'package:permission_handler/permission_handler.dart' as ph;

await ph.Permission.locationAlways.request();
```

## Android 설정

**1. MainActivity 바꾸기**

SDK 의 권한 요청에는 `ComponentActivity` 가 필요하므로 `MainActivity` 를 **`FlutterFragmentActivity`** 로 바꿉니다.

```kotlin MainActivity.kt icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/kotlin.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=3721cc48a6f44aef50ea7a0151e369f2" theme={null}
import io.flutter.embedding.android.FlutterFragmentActivity

class MainActivity : FlutterFragmentActivity()
```

**2. 빌드 버전 맞추기**

```kotlin android/app/build.gradle.kts icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/kotlin.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=3721cc48a6f44aef50ea7a0151e369f2" theme={null}
android {
    compileSdk = 37
    defaultConfig {
        minSdk = 26
    }
}
```

측위 권한은 Android SDK 매니페스트에서 자동으로 병합됩니다.

## SDK 초기화

측위 엔진을 가동하기 위한 초기화 작업을 시작합니다.

| 함수 · 속성 | 설명 | 반환값 |
| - | - | - |
| `initialize(sdkKey:)` | 키 검증과 설정 수신. 앱 시작 시 1회 | `Future<void>` |
| `isInitialized()` | 초기화 성공 여부 | `Future<bool>` |
| `sdkVersion()` | SDK 버전 | `Future<String>` |
| `pluginVersion` | 플러그인 버전 | `String` (예: `'0.0.4'`) |

모든 호출은 `Future` 이고, 이벤트는 `Stream` 입니다.

```dart Dart icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/dart.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=02dee5dd767b4c76ef0e07bc8663c542" theme={null}
const sdkKey = String.fromEnvironment('ONES1GHT_SDK_KEY');   // flutter run --dart-define=ONES1GHT_SDK_KEY=…
await OneS1ght.initialize(sdkKey: sdkKey);
```

## 프로필

사용자(계정 또는 기기) 한 명의 동선은 **프로필** 단위로 관리됩니다. \

프로필 ID 는 고유하며, 분실하면 다시 찾을 수 없으니 보관에 주의해 주세요.

| 함수 · 속성 | 설명 | 반환값 |
| - | - | - |
| `createProfile(attributes)` | 속성으로 프로필 생성 | `Future<String>` (프로필 ID) |
| `identify(profileId:)` | 이 기기의 측위를 프로필에 연결.  프로필 해제 시 `null` | `Future<void>` |
| `fetchProfile(profileId)` | 프로필 ID 로 속성 조회 | `Future<Map<String, String>>` |
| `replaceProfile(profileId, attributes:)` | 속성 전체 교체 | `Future<void>` |
| `deleteProfile(profileId)` | 프로필 삭제 | `Future<void>` |

### 프로필 생성

새 프로필을 만들면 프로필 ID 를 돌려받습니다. 속성은 레포트 · 지표의 세그먼트로 쓰이며 Key-Value 에 제약이 없습니다.\

발급받은 ID 는 앱(또는 고객사 서버)에 보관해 두고, 매 실행마다 측위 전에 `identify` 로 연결합니다.

> [!WARNING]
>
> 프로필 속성에는 이름 · 전화번호 · 회원 ID 같은 민감한 정보를 넣지 마세요.\\
>
> 연령대(`"20s"`)처럼 식별되지 않는 값만 담습니다.

```dart Dart icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/dart.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=02dee5dd767b4c76ef0e07bc8663c542" theme={null}
final profileId = await OneS1ght.createProfile({'gender': 'F', 'ageBand': '20s'});   // 새 프로필 생성 → 프로필 ID 반환
save(profileId);                                                                    // 앱 또는 고객사 서버에 보관

await OneS1ght.identify(profileId: savedProfileId);   // 매 실행, 측위 전에 연결. 로그아웃 시 null
```

### 프로필 정보 조회

프로필 ID를 통해 저장된 개인화 속성을 조회합니다.

```dart Dart icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/dart.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=02dee5dd767b4c76ef0e07bc8663c542" theme={null}
final attributes = await OneS1ght.fetchProfile(profileId);   // {gender: F, ageBand: 20s}
```

### 프로필 정보 수정

개인화 프로필 속성을 **통째로 교체**됩니다. 수정 후 기존 값은 삭제되며 되돌릴 수 없습니다.

```dart Dart icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/dart.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=02dee5dd767b4c76ef0e07bc8663c542" theme={null}
await OneS1ght.replaceProfile(profileId, attributes: {'gender': 'F', 'ageBand': '30s'});   // 속성 전체 교체
```

### 프로필 삭제

개인화 프로필 속성을 삭제합니다. 삭제된 속성은 되돌릴 수 없습니다.

```dart Dart icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/dart.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=02dee5dd767b4c76ef0e07bc8663c542" theme={null}
await OneS1ght.deleteProfile(profileId);
```

## 건물, 층 선택

측위좌표를 수신하려면 로케이터가 있는 건물과 층을 선택해야 합니다.

| 함수 · 속성 | 설명 | 반환값 |
| - | - | - |
| `floorSession()` | 측위 세션 | `Future<FloorSession>` |
| `session.onFloorDetected` | 엔진이 층을 찾으면 흘러옴.  층을 잃으면 `null` | `Stream<String?>` (층 ID) |
| `buildings()` | 콘솔에 등록된 건물 목록 | `Future<List<Building>>`  `id` : 건물 ID  `name` : 건물명  `floorCount` : 층 개수 |
| `floors(buildingId:)` | 건물의 층 목록 (도면 이미지 없음) | `Future<List<Floor>>` |
| `floor(buildingId:, floorId:)` | 층 하나 (도면 이미지 포함) | `Future<Floor>` |
| `setFloorMap(floor, buildingId:)` | 층 지정  해제 시 `null` | `Future<void>` |
| `session.floor()` | 지금 지정된 층 | `Future<Floor?>` |

### BLE 수신으로 선택

SDK 의 측위 엔진이 로케이터의 BLE 신호로 층을 찾으면 `onFloorDetected` 로 층 ID 가 옵니다. \

층을 잃으면 `null` 이 옵니다.

```dart Dart icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/dart.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=02dee5dd767b4c76ef0e07bc8663c542" theme={null}
final session = await OneS1ght.floorSession();                                   // 측위 세션 획득
session.onFloorDetected.listen((floorId) async {                                  // 엔진이 층을 찾으면 층 ID 전달
  if (floorId == null) return;
  final floor = await OneS1ght.floor(buildingId: buildingId, floorId: floorId);   // 층 정보 조회
  await OneS1ght.setFloorMap(floor, buildingId: buildingId);                      // 층 지정
});
```

### 수동으로 선택

사람이 층을 고르게 하거나 측위 전에 지도를 먼저 그릴 때는 건물과 층을 직접 지정합니다.

```dart Dart icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/dart.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=02dee5dd767b4c76ef0e07bc8663c542" theme={null}
final buildings = await OneS1ght.buildings();                               // 콘솔에 등록된 건물 목록
final floors    = await OneS1ght.floors(buildingId: buildings.first.id);    // 건물에 속한 층 목록

await OneS1ght.setFloorMap(floors.first, buildingId: buildings.first.id);   // 층 지정
await OneS1ght.setFloorMap(null);                                           // 층 지정 해제
```

## 측위정보 실시간 수신

`floorSession()` 으로 측위 세션을 얻고, `begin()` 으로 시작해 `end()` 로 종료합니다. \

위치는 `onPosition` 으로 받으며, 좌표(미터)가 최대 초당 4회 옵니다.

| 상황 | 할 일 |
| - | - |
| 앱이 백그라운드로 감 | 없음 — 네이티브 SDK 가 측위를 멈추고, 포그라운드로 돌아오면 자동으로 다시 시작합니다. Dart 가 멈춰도 같습니다. |
| 화면을 잠시 벗어날 때 | `pause()` 로 중지, `resume()` 으로 재개합니다. 엔진은 켜져 있어 바로 이어집니다. |
| 측위를 완전히 끝낼 때 | `end()`. 엔진까지 꺼지므로 다음 `begin()` 은 로케이터를 처음부터 다시 찾습니다. |

| 함수 · 속성 | 설명 | 반환값 |
| - | - | - |
| `session.onPosition` | 실시간 좌표 | `Stream<Coordinates>`  `x` · `y` : 도면 기준 미터  `z` : 높이 |
| `session.begin()` | 측위 시작 | `Future<void>` |
| `session.end()` | 측위 종료 + 남은 좌표 전송 | `Future<void>` |
| `session.pause()` / `resume()` | 일시정지 / 재개 | `Future<void>` |
| `session.isRunning()` / `isPaused()` | 측위 가동 / 일시정지 여부 | `Future<bool>` |

```dart Dart icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/dart.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=02dee5dd767b4c76ef0e07bc8663c542" theme={null}
final session = await OneS1ght.floorSession();
session.onPosition.listen((coord) => updateMyLocationMarker(coord.x, coord.y));   // coord.z: 높이

await session.begin();   // 측위 시작
await session.end();     // 측위 종료 + 남은 좌표 전송
```

## 구역 이벤트

콘솔 [공간 관리](/console/areas)에서 그린 구역의 진입 · 이탈 · 체류 이벤트를 받습니다. 판정은 기기 안에서 이루어져 바로 옵니다. \

구역에 걸린 시책(쿠폰 · 사이니지 등)은 서버가 매칭해 `onTriggers` 로 전달됩니다.\

이벤트는 모두 `Stream` 이라 여러 곳에서 동시에 들어도 됩니다.

| 함수 · 속성 | 설명 | 반환값 |
| - | - | - |
| `session.onZoneEnter` / `onZoneExit` | 구역 진입 / 이탈 | `Stream<Zone>`  `id` : 구역 ID  `name` : 구역명  `polygon` : 구역 도형(꼭짓점 목록)  `dwellSeconds` : 체류 판정 기준(초) |
| `session.onZoneDwell` | 구역 체류 시  `dwellSeconds` 도달 시 방문당 1회 | `Stream<ZoneDwell>`  `zone` : 구역  `seconds` : 체류 시간(초) |
| `session.onTriggers` | 서버가 매칭한 개인화 액션 | `Stream`  `zoneId` : 구역 ID  `triggers` : 트리거 목록  (`triggerId` : 트리거 ID  `type` : 유형  `payload` : 유형별 부가 정보) |
| `session.onConfigChanged` | 콘솔의 구역 설정 변경 시 | `Stream<ConfigChange>`  `ZonesChanged` : 구역 변경  `PlanChanged` : 도면 변경  `RulesChanged` : 시책 변경  `SdkConfigChanged` : SDK 원격 설정 변경  `ResyncNeeded` : 전체 재조회 (연결이 다시 맺어졌거나 이벤트를 놓칠 경우) |
| `refreshZones()` | 구역정보 최신화 | `Future<List<Zone>>` |

```dart Dart icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/dart.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=02dee5dd767b4c76ef0e07bc8663c542" theme={null}
session.onZoneEnter.listen((zone) => showBanner('진입: ${zone.name}'));
session.onZoneExit.listen((zone) => hideBanner());
session.onZoneDwell.listen((d) => showBanner('체류: ${d.zone.name} — ${d.seconds.toInt()}초'));
session.onTriggers.listen((t) => t.triggers.forEach(handle));   // type: signage · coupon · tracking · merch · generic
```

### 구역정보 업데이트

콘솔에서 구역 정보가 바뀔 때 `onConfigChanged` 가 흘러옵니다. 연달아 오면 1초쯤 모아서 `refreshZones()` 를 한 번만 부릅니다.

```dart Dart icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/dart.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=02dee5dd767b4c76ef0e07bc8663c542" theme={null}
session.onConfigChanged.listen((change) {
  switch (change) {
    case ZonesChanged() || ResyncNeeded():
      OneS1ght.refreshZones();
    case PlanChanged():
      reloadFloorPlan();   // 도면이 바뀜
    default:
      break;
  }
});
```

## 도면 표시

로케이터와 관련된 도면과 사용자의 현재 위치를 표시할 수 있습니다. \

도면 설정을 하지 않을 경우 배경은 격자로 표시됩니다.

| 함수 · 속성 | 설명 | 반환값 |
| - | - | - |
| `floor(buildingId:, floorId:)` | 층 상세 정보 | `Future<Floor>` `image: Uint8List?` : 도면 이미지 `hasPlan` : 도면 등록 여부 `originX` : 도면 원점 X (미터) `originY` : 도면 원점 Y (미터) `widthM` : 가로 길이 (미터) `heightM` : 세로 길이 (미터) `minX` : 최소 X값 `minY` : 최소 Y값 `maxX` : 최대 X값 `maxY` : 최대 Y값 |
| `locators(buildingId:, floorId:)` | 층의 로케이터 정보 | `Future<FloorLocators>` `locators` : 로케이터 정보 (`address` : UWB MAC 뒤 2바이트 (`int`), `x` · `y` · `z` : 로케이터 좌표) `sessionId` : UWB 세션 ID (층마다 다름) `positioningReady` : 측위 가능 여부 |

```dart Dart icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/dart.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=02dee5dd767b4c76ef0e07bc8663c542" theme={null}
final floor = await OneS1ght.floor(buildingId: buildingId, floorId: floorId);
if (floor.image != null) planWidget = Image.memory(floor.image!);
setBounds(floor.minX, floor.minY, floor.maxX, floor.maxY);
```

## 기타 옵션

| 함수 · 속성 | 설명 | 반환값 |
| - | - | - |
| `uploadPendingPositions()` | 앱 내부 저장 좌표 즉시 전송 | `Future<void>` |
| `discardPendingPositions()` | 앱 내부 저장 좌표 버림 | `Future<void>` |
| `reset()` | 세션 폐기 (새 세션은 만들지 않으며, 다시 쓰려면 `initialize` 를 호출합니다) | `Future<void>` |
| `session.onStopped` | 세션 중단 | `Stream<StopReason>` : 세션 중단 사유  `ended` : 세션 종료(정상)  `engineFailed` : 엔진 이상(실패) |
| `deviceAvailability()` | 이 기기에서 측위가 되는지와 안 되는 이유 | `Future<DeviceAvailability>` — `available` · `osVersionTooLow` · `deviceNotSupported` |
| `isDeviceAvailable()` | 위의 요약 | `Future<bool>` |
| `OneS1ght.onDebugLog` | SDK 디버깅 도구 | `Stream<DebugLog>` — `level` · `message` 로그 반환 |

### 데이터 전송 제어

좌표는 **300건이 채워지거나 전송 후 60초 경과 시** 자동 전송됩니다. 단 앱이 강제 종료되면 좌표는 폐기됩니다.

```dart Dart icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/dart.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=02dee5dd767b4c76ef0e07bc8663c542" theme={null}
await OneS1ght.uploadPendingPositions();    // 쌓인 좌표를 지금 전송
await OneS1ght.discardPendingPositions();   // 보내지 않고 버림
```

### 세션 재설정

현재 세션을 폐기합니다. 새 세션은 만들지 않으니, 다시 쓰려면 `initialize` 를 호출하세요(다른 키로도 가능합니다).

```dart Dart icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/dart.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=02dee5dd767b4c76ef0e07bc8663c542" theme={null}
await OneS1ght.reset();
```

### 측위 종료 처리

측위 엔진이 종료된 경우 `onStopped` 이벤트가 실행되며 측위 가능 상태로 전환 가능합니다.

```dart Dart icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/dart.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=02dee5dd767b4c76ef0e07bc8663c542" theme={null}
session.onStopped.listen((reason) {
  if (reason == StopReason.engineFailed) showRetrySearch();   // 원인을 풀고 begin() 다시
});
```

### 지원 기기 확인

`deviceAvailability` 는 네트워크(인터넷) 연결 없이 바로 확인 가능하며\

측위를 지원하지 않는 기기라도 앱의 동작에 영향을 미치지 않습니다.

```dart Dart icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/dart.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=02dee5dd767b4c76ef0e07bc8663c542" theme={null}
switch (await OneS1ght.deviceAvailability()) {
  case DeviceAvailability.available:
    break;
  case DeviceAvailability.osVersionTooLow:
    showNotice('OS 업데이트 후 사용할 수 있습니다');
  case DeviceAvailability.deviceNotSupported:
    hidePositioningFeatures();   // UWB 미지원 기기
}
```

## 부록

### 권한 요청

`requestPermission()` 은 SDK 사용에 필요한 모든 권한을 한 번에 요청합니다. \

사용자가 한 번이라도 권한요청을 거절 할 시 앱에서 재요청 할 수 없으니 설정 앱으로 이동하도록 유도하세요.

| 함수 · 속성 | 설명 | 반환값 |
| - | - | - |
| `requestPermission()` | 측위 권한 요청 | `Future<PermissionStatus>`  `authorized` : 측위 가능  `denied` : 거부  `unsupported` : 측위 미지원 기기 (창을 띄우지 않음) |

```dart Dart icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/dart.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=02dee5dd767b4c76ef0e07bc8663c542" theme={null}
switch (await OneS1ght.requestPermission()) {
  case PermissionStatus.authorized:
    break;
  case PermissionStatus.denied:
    showSettingsGuide();        // 앱에서 재요청 불가 — 설정 앱으로
  case PermissionStatus.unsupported:
    showUnsupportedNotice();    // 측위 미지원 기기
}
```

### 디버그 로그

디버그 로그 확인시 SDK 초기화문 선언(`initialize`) **직전에** 등록해 주세요. \

로그는 `flutter run` 을 실행한 터미널이나 IDE 의 디버그 콘솔에 찍힙니다. \

기기에서 직접 수집하는 방법은 [iOS](/developers/sdk/integration/ios#디버그-로그) · [Android](/developers/sdk/integration/android#디버그-로그) 가이드를 참고하세요.\

사용자 측위정보는 시뮬레이터 · 에뮬레이터에서 확인할 수 없습니다.

```dart Dart icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/dart.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=02dee5dd767b4c76ef0e07bc8663c542" theme={null}
import 'package:flutter/foundation.dart';   // kDebugMode · debugPrint

if (kDebugMode) {
  OneS1ght.onDebugLog.listen((log) => debugPrint('[OneS1ght] ${log.level} ${log.message}'));
}
```

SDK 로그 · 안내 문구의 언어는 `setLanguage` 로 정합니다 — `'ko'` · `'ja'` · `'en'`, `null` 이면 기기 언어를 따릅니다.

```dart Dart icon="https://mintcdn.com/onecheck/wv66PbaB8dD4xd9I/images/icons/dart.svg?fit=max&auto=format&n=wv66PbaB8dD4xd9I&q=85&s=02dee5dd767b4c76ef0e07bc8663c542" theme={null}
await OneS1ght.setLanguage('ko');
```

### 네이티브 SDK 와 다른 점

| 항목 | 네이티브 SDK | Flutter |
| - | - | - |
| 이벤트 | 콜백 속성(`session.onZoneEnter = …`) | 같은 이름의 `Stream`(`session.onZoneEnter.listen(…)`) |
| 상태 값 | 속성(`OneS1ght.isInitialized`) | `Future` 를 돌려주는 함수(`await OneS1ght.isInitialized()`) |
| 초기화 | Android 는 `Context` 를 함께 넘김 | 플러그인이 넘겨 줌 — `initialize(sdkKey:)` 만 |
| 권한 | Android 는 `requestPermission(activity)` | 플러그인이 Activity 를 넘겨 줌 — `requestPermission()` 만 |
| 지원하지 않는 것 | — | 커스텀 측위 provider(`begin(provider:)`) |

## 문제 해결하기

| 증상 | 원인과 해결 |
| - | - |
| `pod install` 이 OneS1ght 안내 문구와 함께 멈춤 | Swift Package Manager 가 꺼져 있습니다.  `flutter config --enable-swift-package-manager` 후 다시 빌드 |
| Android 에서 `requestPermission()` 이 오류로 실패 | `MainActivity` 를 `FlutterFragmentActivity` 로 변경 |
| Android 에서 지원 기기인데 `deviceNotSupported` | `initialize()` 전에 읽었습니다. 초기화 후 다시 읽어 주세요 |

> **[에러 코드](/developers/sdk/troubleshooting/error-codes)**
>
> 개발 중 발생하는 오류 내용을 안내합니다.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.