> ## 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.

# はじめに

> OneS1ght SDK for Flutter（プレビュー）

<Warning>
  **プレビュー版です — 正式リリースではありません。** Flutter SDK は評価用に提供しており、API が変わる可能性があります。
  正式サポートの SDK は [iOS](/ja/sdk/integration/ios) · [Android](/ja/sdk/integration/android) SDK です。
</Warning>

<Info>
  Flutter SDK は OneS1ght iOS・Android SDK をラップしたプラグインです。測位、ゾーンの入退場・滞在判定、送信・再試行はすべて
  ネイティブ SDK が行い、Dart は呼び出しを渡して結果を Dart の型に変換するだけです。そのため、アプリがバックグラウンドに移って
  Dart が止まっても、SDK の動作はネイティブアプリと同じです。<br />
  Dart API の名前は iOS SDK に合わせており、ネイティブのコールバック(`onZoneEnter`・`onPosition` など)は同じ名前の `Stream` です。
</Info>

## 要件

| 項目 | 要件 |
| - | - |
| Flutter | **3.44 以上** — iOS は Swift Package Manager でのみ組み込めます(3.44 から既定で有効) |
| iOS | パッケージの追加: **iOS 18.0 以上** · 測位の動作: **iOS 27.0 以上**、UWB チップ搭載の iPhone |
| Android | パッケージの追加: `minSdk 26` · `compileSdk 37` · 測位の動作: **Android 17 (API 37) 以上**、UWB DL-TDoA 対応端末 |
| 同梱のネイティブ SDK | iOS `0.2.2` · Android `0.0.9`(バージョン固定) |

測位できない端末でもアプリは正常に動作し、測位のみ無効になります。

SDK を実際に動作させるには、以下の準備が必要です。詳しくは
[SDK 導入前のチェックリスト](/ja/sdk/overview#sdk-導入前のチェックリスト)をご覧ください。

| 事前準備 | 場所 |
| - | - |
| SDK API キー (`ock_sdk_…`) | OneS1ght コンソール → **モバイル SDK** |
| 建物・フロア・ロケーターの設置 | 測位インフラの設置時にあわせて実施 |
| ゾーン | OneS1ght コンソール → [空間管理](/ja/locator/areas) |

## 導入

`pubspec.yaml` に git 依存として追加します。プレビュー版のため pub.dev には公開していません。

```yaml theme={null}
dependencies:
  ones1ght_sdk:
    git:
      url: https://github.com/onecheck-inc/OneS1ght-Flutter-SDK
      ref: v0.0.1
```

```dart theme={null}
import 'package:ones1ght_sdk/ones1ght.dart';
```

<Warning>
  `ref` には **タグ(`v0.0.1`)** を指定してください。`main` を指定すると、リポジトリが更新されるたびにアプリのビルドが知らないうちに変わります。
</Warning>

### iOS の設定

**Swift Package Manager でのみ組み込めます。** CocoaPods には対応していません — 測位エンジンが動的フレームワークのため、
CocoaPods ではアプリに含まれず、起動直後にアプリが終了します。Swift Package Manager が無効な場合、`pod install` は次の
コマンドを案内して停止します。

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

iOS のデプロイメントターゲットを **18.0** に上げます(`ios/Runner.xcodeproj` → `IPHONEOS_DEPLOYMENT_TARGET`)。

`ios/Runner/Info.plist` に次のキーを追加します。最初の 3 つがないと、権限をリクエストした時点でアプリが終了します。

```xml theme={null}
<key>NSLocationWhenInUseUsageDescription</key>
<string>店内での位置を把握するために使用します。</string>
<key>NSNearbyInteractionUsageDescription</key>
<string>UWB による高精度測位に使用します。</string>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>周辺の測位機器を探すために使用します。</string>
<key>NSLocationTemporaryUsageDescriptionDictionary</key>
<dict>
    <key>Positioning</key>
    <string>正確な屋内位置を計算するために使用します。</string>
</dict>
```

<Warning>
  `NSLocationTemporaryUsageDescriptionDictionary` 内のキーは **`Positioning`** にしてください。異なると iOS はエラーもログも
  出さずにリクエストを無視します。
</Warning>

**iOS 27.2 以降では、位置情報の権限が「常に」でないとフロアを検出できません。** `NSLocationAlwaysAndWhenInUseUsageDescription`
を追加し、「使用中のみ」が許可された後にアプリから「常に」をリクエストしてください(例:
[`permission_handler`](https://pub.dev/packages/permission_handler) の `Permission.locationAlways.request()`)。SDK は「常に」を
リクエストしません。理由は [iOS 連携ガイド](/ja/sdk/integration/ios) をご覧ください。

<Warning>
  `UIBackgroundModes` に `bluetooth-central` は追加しないでください。SDK はバックグラウンドで測位を止めるためこのモードを使わず、
  App Store 審査で未使用のバックグラウンドモード(2.5.4)としてリジェクトされる可能性があります。
</Warning>

### Android の設定

`MainActivity` を **`FlutterFragmentActivity`** に変更します。SDK の権限リクエストには `ComponentActivity` が必要ですが、
Flutter 既定の `FlutterActivity` はそうではありません。

```kotlin theme={null}
// android/app/src/main/kotlin/.../MainActivity.kt
import io.flutter.embedding.android.FlutterFragmentActivity

class MainActivity : FlutterFragmentActivity()
```

`android/app/build.gradle.kts` でビルドバージョンを合わせます。

```kotlin theme={null}
android {
    compileSdk = 37
    defaultConfig {
        minSdk = 26
    }
}
```

測位の権限(`RANGING`・`ACCESS_FINE_LOCATION`・`ACCESS_COARSE_LOCATION`・`BLUETOOTH_SCAN`)と `INTERNET` は Android SDK の
マニフェストから自動でマージされます。別途追加する必要はありません。

## SDK の初期化

アプリ起動時に 1 回呼び出します。キーの検証とテナント設定の受信をあわせて行います。

```dart theme={null}
await OneS1ght.initialize(sdkKey: 'YOUR_API_KEY');
```

* 失敗すると `OneS1ghtException` を投げます — もう一度呼び出すと再試行になります。
* 成功後に再度呼び出しても無視され、別のキーで呼び出すとセッションを作り直します。
* 端末が対応しているかはここでは確認しません。測位できない端末でも建物・フロア・ゾーンは取得できます。

### 環境別 API キーの使い分け

OneS1ght コンソールでは API キーを開発用(Development)と本番用(Production)に分けて発行できます。キーはコードに書かず、
ビルド時に渡すことをおすすめします。

```dart theme={null}
const sdkKey = String.fromEnvironment('ONES1GHT_SDK_KEY');
await OneS1ght.initialize(sdkKey: sdkKey);
```

```bash theme={null}
flutter run --dart-define=ONES1GHT_SDK_KEY=ock_sdk_…
```

## 端末対応の確認

```dart theme={null}
switch (await OneS1ght.deviceAvailability()) {
  case DeviceAvailability.available:
    break;
  case DeviceAvailability.osVersionTooLow:
    // 「OS をアップデートするとご利用いただけます」
    break;
  case DeviceAvailability.deviceNotSupported:
    // 「この端末は屋内測位に対応していません」
    break;
}
```

<Note>
  Android では `deviceAvailability()` を **`initialize()` の後に** 読んでください。それより前は UWB チップを確認できないため
  `deviceNotSupported` が返り、デバッグログに警告が残ります。iOS はいつ読んでも構いません。
</Note>

## 権限のリクエスト

```dart theme={null}
final status = await OneS1ght.requestPermission();   // システムの権限ダイアログが表示されます
```

| 結果 | 意味 |
| - | - |
| `PermissionStatus.authorized` | 許可 |
| `PermissionStatus.denied` | 拒否 — アプリから再度リクエストできないため、設定アプリへ案内してください |
| `PermissionStatus.unsupported` | 測位できない端末 — ダイアログなしですぐに返ります |

* すでに回答済みの権限であれば、ダイアログなしですぐに返ります。`initialize` の前でも呼び出せます。
* iOS は Nearby Interaction の権限を求め、位置情報の権限は測位開始時に SDK が求めます。Android は UWB・正確な位置情報・
  付近のデバイスの権限をまとめて求めます。
* Android で `MainActivity` が `FlutterFragmentActivity` でない場合、コード `unsupportedActivity` のエラーになります。

## プロフィールの作成と連携

```dart theme={null}
final profileId = await OneS1ght.createProfile({'gender': 'F', 'ageGroup': '20s'});
// profileId はアプリで保存し、次回以降の起動でも使います。

await OneS1ght.identify(profileId: profileId);   // 測位開始の前に必ず呼び出す
```

* 属性は自由に決められます。年齢は正確な値ではなく年代(`"20s"`)をおすすめします。
* ログアウトなどで連携を解除するときは `identify(profileId: null)` を呼び出します。
* 取得・置換・削除: `fetchProfile(id)` · `replaceProfile(id, attributes: …)`(全体置換) · `deleteProfile(id)`。

## 建物・フロアの選択（任意）

```dart 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);
```

`setFloorMap` を呼び出さない場合、測位エンジンが BLE でフロアを探します。最初の指定時は `buildingId` もあわせて渡してください
(ないと `E3001`)。`setFloorMap(null)` はフロアの指定を解除します。

### エンジンが見つけたフロアに追従する

```dart theme={null}
final session = await OneS1ght.floorSession();
session.onFloorDetected.listen((floorId) async {
  if (floorId == null) return;   // フロアを見失った
  final floor = await OneS1ght.floor(buildingId: buildingId, floorId: floorId);
  await OneS1ght.setFloorMap(floor, buildingId: buildingId);
});
```

### 図面の表示

`floors()` の一覧では図面画像が空です。地図を描くフロアだけ `floor(buildingId:, floorId:)` で取得すると、`image`(元のバイト列)
が入ります。座標は図面ローカルのメートルで、`originX`・`originY`・`widthM`・`heightM` で画面座標に変換します。

```dart theme={null}
final floor = await OneS1ght.floor(buildingId: buildingId, floorId: floorId);
if (floor.image != null) Image.memory(floor.image!);
```

## 測位の開始と終了

```dart theme={null}
final session = await OneS1ght.floorSession();   // 常に同じインスタンス
await session.begin();   // 来店時
// ...
await session.end();     // 終了 + 残りの座標を送信。初期化・フロア設定は維持
```

`begin()` が失敗すると `OneS1ghtException` を投げます — `E1001`(未初期化) · `E1004`(`identify` 未呼び出し) ·
`E2001`(OS が古い) · `E2002`(非対応端末)。

### 一時停止と再開

```dart theme={null}
await session.pause();    // 座標の表示・収集・判定のみ停止、エンジンはフロアを保持
await session.resume();   // すぐに再開
```

### 測位が閉じたとき

```dart theme={null}
session.onStopped.listen((reason) {
  if (reason == StopReason.engineFailed) {
    // 権限・Bluetooth などの原因を解消してから begin() で開き直します
  }
});
```

## イベントの受信

イベントはすべて `Stream` です。複数の場所で同時に受信しても構いません。

```dart theme={null}
session.onZoneEnter.listen((zone) => debugPrint('IN  ${zone.name}'));
session.onZoneExit.listen((zone) => debugPrint('OUT ${zone.name}'));
session.onZoneDwell.listen((d) => debugPrint('DWELL ${d.zone.name} ${d.seconds}s'));
session.onPosition.listen((c) => debugPrint('${c.x}, ${c.y}'));   // 図面ローカルのメートル
session.onTriggers.listen((t) {
  for (final trigger in t.triggers) {
    // trigger.type: signage · coupon · tracking · merch · generic(新しい値が増える可能性があります)
  }
});
```

### ゾーン情報の更新

```dart theme={null}
final zones = await OneS1ght.refreshZones();   // 現在のフロアのゾーンだけ再取得(図面の再ダウンロードなし)
```

### コンソール変更の受信

```dart theme={null}
session.onConfigChanged.listen((change) {
  switch (change) {
    case ZonesChanged() || ResyncNeeded():
      scheduleZoneRefresh();      // 1 秒ほどまとめてから OneS1ght.refreshZones() を 1 回
    case PlanChanged(:final floorId):
      redrawFloor(floorId);       // floor() で取り直して地図を描き直す
    case RulesChanged(:final zoneId):
      recheckZoneEvents(zoneId);  // そのゾーンに入っていれば、そのゾーンのイベントをもう一度照会
    default:
      break;
  }
});
```

<Warning>
  SDK はこの通知を受けても何もしません。ゾーンを取り直すたびに入退場判定が最初からやり直しになるため、続けて届く
  `ZonesChanged` はまとめて 1 回だけ処理してください。
</Warning>

## データ送信の制御

```dart theme={null}
await OneS1ght.uploadPendingPositions();    // たまった座標を今すぐ送信
await OneS1ght.discardPendingPositions();   // たまった座標を送信せずに破棄
```

## セッションのリセット

```dart theme={null}
await OneS1ght.reset();   // セッションを破棄 — 別のキーで initialize できます
```

## エラー処理

失敗はすべて `OneS1ghtException` です。`code` はネイティブ SDK の [エラーコード](/ja/sdk/faq/error-code) で、iOS・Android で
同じです。

```dart theme={null}
try {
  await session.begin();
} on OneS1ghtException catch (e) {
  switch (e.code) {
    case 'E1004':
      await OneS1ght.identify(profileId: savedProfileId);   // プロフィールを連携してから再開
    case 'E2001' || 'E2002':
      showUnsupportedNotice();                              // 測位できない端末の案内
    default:
      debugPrint('${e.code} ${e.message}');
  }
}
```

| フィールド | 内容 |
| - | - |
| `code` | `E1001` などの SDK エラーコード。Flutter プラグイン自体のエラーは `unsupportedActivity`(Android の Activity 設定) · `internal` |
| `kind` | `sdk`(SDK の状態) · `api`(サーバー応答) · `plugin`(プラグイン) |
| `status` · `detail` | サーバーエラー時の HTTP ステータスと詳細 |

## デバッグログ

```dart theme={null}
import 'package:flutter/foundation.dart';   // kDebugMode · debugPrint

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

ログレベルと読み方は [ログの使い方](/ja/sdk/faq/logging) をご覧ください。本番ビルドでは購読しないことをおすすめします。
SDK のログ・案内文の言語は `OneS1ght.setLanguage('ja')` で指定できます(`'ko'`・`'ja'`・`'en'`、`null` は端末の言語)。

## バックグラウンドでの動作

<Warning>
  UWB 測位は iOS・Android ともに **フォアグラウンド専用** です。アプリがバックグラウンドに移ると、ネイティブ SDK が測位を止めて
  残りの座標を送信します。これは Dart ではなくネイティブ SDK が行うため、Flutter アプリでも同じです。
</Warning>

<Note>
  実際の測位はシミュレーター・エミュレーターでは確認できません。UWB 対応の実機にインストールしてテストしてください。
</Note>

## ネイティブ 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` の後に再ビルド |
| `requestPermission()` が `unsupportedActivity` | Android の `MainActivity` を `FlutterFragmentActivity` に変更 |
| 対応端末の Android で `deviceNotSupported` | `initialize()` の前に読んでいます。初期化後にもう一度読んでください |
| iOS 27.2 以降でフロアが見つからない(`E3007`) | 位置情報の権限「常に」が必要です([iOS の設定](#ios-の設定)) |

<CardGroup cols={2}>
  <Card title="ログの使い方" icon="wrench" href="/ja/sdk/faq/logging">
    開発中のログの使い方をご案内します。
  </Card>

  <Card title="エラーコード" icon="triangle-exclamation" href="/ja/sdk/faq/error-code">
    開発中に発生するエラーの内容をご案内します。
  </Card>
</CardGroup>


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