# 画面設計書 10-アプリ設定画面

## 概要

本ドキュメントは、InvenTree Flutterアプリケーションのアプリ設定画面（InvenTreeAppSettingsWidget）の画面設計を定義するものである。

### 本画面の処理概要

アプリ設定画面はアプリケーション全体の動作設定を変更する機能を提供する。

**業務上の目的・背景**：ユーザーの好みや利用環境に合わせてアプリケーションの動作をカスタマイズする必要がある。本画面ではダークモード、言語設定、サウンド設定、エラーレポート設定などの各種設定を変更できる。

**画面へのアクセス方法**：設定画面のアプリ設定項目をタップして遷移する。

**主要な操作・処理内容**：
1. ダークモードの有効化/無効化
2. 画面の向きの設定（システム/縦/横）
3. ラベル印刷機能の有効化/無効化
4. 厳格なHTTPS設定の有効化/無効化
5. 言語の選択
6. エラーレポートの有効化/無効化
7. サーバーエラー時のサウンドの有効化/無効化
8. バーコードスキャン時のサウンドの有効化/無効化

**画面遷移**：
- 遷移元：設定画面
- 遷移先：なし（設定変更は即時反映）

**権限による表示制御**：本画面は全ユーザーがアクセス可能。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 57 | アプリ設定 | 主機能 | ダークモード、言語、エラーレポートなどの設定変更 |
| 67 | 多言語対応 | 補助機能 | 言語設定の変更時に多言語対応機能と連携 |
| 68 | エラー報告 | 補助機能 | エラー報告設定の有効化・無効化 |

## 画面種別

設定画面（スイッチ/選択形式）

## URL/ルーティング

- Flutter Widget：`InvenTreeAppSettingsWidget`（StatefulWidget）
- 状態クラス：`_InvenTreeAppSettingsState`

## 入出力項目

| 項目名 | 入力/出力 | データ型 | 必須 | 説明 |
|--------|----------|----------|------|------|
| darkMode | 入出力 | bool | - | ダークモードの有効/無効 |
| screenOrientation | 入出力 | int | - | 画面の向き（0:システム, 1:縦, 2:横） |
| enableLabelPrinting | 入出力 | bool | - | ラベル印刷機能の有効/無効 |
| strictHttps | 入出力 | bool | - | 厳格なHTTPSの有効/無効 |
| locale | 入出力 | Locale? | - | 選択された言語ロケール |
| reportErrors | 入出力 | bool | - | エラーレポートの有効/無効 |
| serverSounds | 入出力 | bool | - | サーバーエラー時サウンドの有効/無効 |
| barcodeSounds | 入出力 | bool | - | バーコードスキャン時サウンドの有効/無効 |

## 表示項目

| 項目名 | データ型 | 説明 | 取得元 |
|--------|----------|------|--------|
| AppBarタイトル | String | 「アプリ設定」 | L10().appSettings |
| アプリ設定セクションヘッダー | ListTile | セクションタイトル | L10().appSettings |
| ダークモード | ListTile + Switch | ダークモード設定 | darkMode変数 |
| 画面の向き | ListTile | 画面の向き設定 | screenOrientation変数 |
| ラベル印刷 | ListTile + Switch | ラベル印刷設定 | enableLabelPrinting変数 |
| 厳格なHTTPS | ListTile + Switch | HTTPS設定 | strictHttps変数 |
| 言語 | ListTile | 言語設定 | languageName変数 |
| エラーレポート | ListTile + Switch | エラーレポート設定 | reportErrors変数 |
| サウンドセクションヘッダー | ListTile | セクションタイトル | L10().sounds |
| サーバーエラー | ListTile + Switch | サーバーエラーサウンド設定 | serverSounds変数 |
| バーコードサウンド | ListTile + Switch | バーコードサウンド設定 | barcodeSounds変数 |

## イベント仕様

### 1-ダークモード切替

ダークモードスイッチを切り替えてテーマを変更する。

**処理フロー**：
1. ユーザーがスイッチをタップ
2. `value`がtrueの場合：
   - `AdaptiveTheme.of(context).setDark()`でダークテーマを設定
3. `value`がfalseの場合：
   - `AdaptiveTheme.of(context).setLight()`でライトテーマを設定
4. `setState()`で`darkMode = value`を更新

### 2-画面の向き設定

画面の向き項目をタップして向きを選択する。

**処理フロー**：
1. ユーザーが画面の向き項目をタップ
2. `choiceDialog()`で選択ダイアログを表示
3. 選択肢：
   - システム（SCREEN_ORIENTATION_SYSTEM = 0）
   - 縦向き（SCREEN_ORIENTATION_PORTRAIT = 1）
   - 横向き（SCREEN_ORIENTATION_LANDSCAPE = 2）
4. `onSelected`で`screenOrientation = idx`を設定
5. `InvenTreeSettingsManager().setValue(INV_SCREEN_ORIENTATION, screenOrientation)`で保存
6. `setState()`でUIを更新

### 3-ラベル印刷切替

ラベル印刷スイッチを切り替えて機能を有効化/無効化する。

**処理フロー**：
1. ユーザーがスイッチをタップ
2. `InvenTreeSettingsManager().setValue(INV_ENABLE_LABEL_PRINTING, value)`で保存
3. `setState()`で`enableLabelPrinting = value`を更新

### 4-厳格なHTTPS切替

厳格なHTTPSスイッチを切り替えて設定を変更する。

**処理フロー**：
1. ユーザーがスイッチをタップ
2. `InvenTreeSettingsManager().setValue(INV_STRICT_HTTPS, value)`で保存
3. `setState()`で`strictHttps = value`を更新

### 5-言語選択

言語項目をタップして言語を選択する。

**処理フロー**：
1. ユーザーが言語項目をタップ
2. `_selectLocale(context)`を呼び出し
3. `supported_locales`から選択肢を構築（デフォルト言語を含む）
4. `launchApiForm()`で選択フォームを表示
5. 選択された言語で`InvenTreeSettingsManager().setSelectedLocale(selected_locale)`を呼び出し
6. `setState()`で`locale = selected_locale`を更新
7. `InvenTreeApp.of(context)?.setLocale(locale)`でアプリ全体のロケールを更新
8. `InvenTreeAPI().clearStatusCodeData()`でキャッシュされたステータスラベルをクリア

### 6-エラーレポート切替

エラーレポートスイッチを切り替えて設定を変更する。

**処理フロー**：
1. ユーザーがスイッチをタップ
2. `InvenTreeSettingsManager().setValue(INV_REPORT_ERRORS, value)`で保存
3. `setState()`で`reportErrors = value`を更新

### 7-サーバーエラーサウンド切替

サーバーエラーサウンドスイッチを切り替えて設定を変更する。

**処理フロー**：
1. ユーザーがスイッチをタップ
2. `InvenTreeSettingsManager().setValue(INV_SOUNDS_SERVER, value)`で保存
3. `setState()`で`serverSounds = value`を更新

### 8-バーコードサウンド切替

バーコードサウンドスイッチを切り替えて設定を変更する。

**処理フロー**：
1. ユーザーがスイッチをタップ
2. `InvenTreeSettingsManager().setValue(INV_SOUNDS_BARCODE, value)`で保存
3. `setState()`で`barcodeSounds = value`を更新

## データベース更新仕様

### 操作別データベース影響一覧

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面の向き設定 | settings（ローカル） | UPDATE | INV_SCREEN_ORIENTATIONを更新 |
| ラベル印刷切替 | settings（ローカル） | UPDATE | INV_ENABLE_LABEL_PRINTINGを更新 |
| 厳格なHTTPS切替 | settings（ローカル） | UPDATE | INV_STRICT_HTTPSを更新 |
| 言語選択 | settings（ローカル） | UPDATE | ロケール設定を更新 |
| エラーレポート切替 | settings（ローカル） | UPDATE | INV_REPORT_ERRORSを更新 |
| サーバーエラーサウンド切替 | settings（ローカル） | UPDATE | INV_SOUNDS_SERVERを更新 |
| バーコードサウンド切替 | settings（ローカル） | UPDATE | INV_SOUNDS_BARCODEを更新 |

### テーブル別更新項目詳細

#### settings（ローカルDB - Sembast）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | appScreenOrientation | 0, 1, 2 | 画面の向き |
| UPDATE | enableLabelPrinting | true/false | ラベル印刷 |
| UPDATE | strictHttps | true/false | 厳格なHTTPS |
| UPDATE | reportErrors | true/false | エラーレポート |
| UPDATE | serverSounds | true/false | サーバーサウンド |
| UPDATE | barcodeSounds | true/false | バーコードサウンド |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|--------------|---------|
| MSG-001 | タイトル | L10().appSettings | AppBarタイトル |
| MSG-002 | セクション | L10().appSettings | アプリ設定セクションヘッダー |
| MSG-003 | ラベル | L10().darkMode | ダークモード項目タイトル |
| MSG-004 | 説明 | L10().darkModeEnable | ダークモード項目説明 |
| MSG-005 | ラベル | L10().orientation | 画面の向き項目タイトル |
| MSG-006 | 説明 | L10().orientationDetail | 画面の向き項目説明 |
| MSG-007 | 選択肢 | L10().orientationSystem | システム向き選択肢 |
| MSG-008 | 選択肢 | L10().orientationPortrait | 縦向き選択肢 |
| MSG-009 | 選択肢 | L10().orientationLandscape | 横向き選択肢 |
| MSG-010 | ラベル | L10().labelPrinting | ラベル印刷項目タイトル |
| MSG-011 | 説明 | L10().labelPrintingDetail | ラベル印刷項目説明 |
| MSG-012 | ラベル | L10().strictHttps | 厳格なHTTPS項目タイトル |
| MSG-013 | 説明 | L10().strictHttpsDetails | 厳格なHTTPS項目説明 |
| MSG-014 | ラベル | L10().language | 言語項目タイトル |
| MSG-015 | ダイアログ | L10().languageSelect | 言語選択ダイアログタイトル |
| MSG-016 | 選択肢 | L10().languageDefault | デフォルト言語選択肢 |
| MSG-017 | ラベル | L10().errorReportUpload | エラーレポート項目タイトル |
| MSG-018 | 説明 | L10().errorReportUploadDetails | エラーレポート項目説明 |
| MSG-019 | セクション | L10().sounds | サウンドセクションヘッダー |
| MSG-020 | ラベル | L10().serverError | サーバーエラーサウンド項目タイトル |
| MSG-021 | 説明 | L10().soundOnServerError | サーバーエラーサウンド項目説明 |
| MSG-022 | ラベル | L10().barcodeTones | バーコードサウンド項目タイトル |
| MSG-023 | 説明 | L10().soundOnBarcodeAction | バーコードサウンド項目説明 |

## 例外処理

| 例外種別 | 発生条件 | 対応処理 |
|---------|---------|---------|
| 設定値取得失敗 | ローカルDBからの読み込み失敗 | デフォルト値を使用 |
| ロケール取得失敗 | LocaleNames.nameOf()がnullを返す | languageDefaultを表示 |
| ローディング中のUI操作 | mountedがfalse | setState()をスキップ |

## 備考

- initState()でloadSettings()を呼び出し、すべての設定を非同期で読み込む
- ダークモードはAdaptiveThemeパッケージを使用して管理
- 言語設定はsupported_localesリストから選択
- 言語変更時はInvenTreeApp.setLocale()でアプリ全体に反映
- 画面の向きはSCREEN_ORIENTATION_SYSTEM(0), SCREEN_ORIENTATION_PORTRAIT(1), SCREEN_ORIENTATION_LANDSCAPE(2)の3種類
- 設定変更は即時保存され、画面遷移なしで反映される

---

## コードリーディングガイド

本画面を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、設定管理の構造と定数を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | preferences.dart | `lib/preferences.dart` | 設定キー定数（INV_SOUNDS_BARCODE等）と画面向き定数を理解する |
| 1-2 | preferences.dart | `lib/preferences.dart` | InvenTreeSettingsManagerクラスのgetValue/setValueメソッドを理解する |

**読解のコツ**: SCREEN_ORIENTATION_SYSTEM(0), SCREEN_ORIENTATION_PORTRAIT(1), SCREEN_ORIENTATION_LANDSCAPE(2)の3種類の定数に注目する。

#### Step 2: エントリーポイントを理解する

処理の起点となるファイル・関数を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | app_settings.dart | `lib/settings/app_settings.dart` | InvenTreeAppSettingsWidgetと_InvenTreeAppSettingsStateを理解する（行1-351） |

**主要処理フロー**:
1. **行19-22**: InvenTreeAppSettingsWidgetクラスの定義
2. **行24-41**: _InvenTreeAppSettingsStateクラスの定義、各設定変数の宣言
3. **行43-48**: initState()でloadSettings()を呼び出し
4. **行50-87**: loadSettings()で全設定を非同期で読み込み
5. **行89-140**: _selectLocale()で言語選択ダイアログを表示
6. **行142-349**: build()メソッドでScaffoldとListViewを構築

#### Step 3: 各設定項目の処理を理解する

設定項目ごとの処理フローを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | app_settings.dart | `lib/settings/app_settings.dart` | ダークモード切替（行182-199） |
| 3-2 | app_settings.dart | `lib/settings/app_settings.dart` | 画面の向き設定（行200-251） |
| 3-3 | app_settings.dart | `lib/settings/app_settings.dart` | 言語選択（行283-290） |
| 3-4 | app_settings.dart | `lib/settings/app_settings.dart` | サウンド設定（行313-343） |

**主要処理フロー**:
- **行186-198**: ダークモードスイッチのonChangedでAdaptiveTheme.setDark/setLightを呼び出し
- **行207-250**: 画面の向きのonTapでchoiceDialog()を表示し、選択結果をINV_SCREEN_ORIENTATIONに保存
- **行287-289**: 言語のonTapで_selectLocale()を呼び出し
- **行319-324, 333-341**: サウンドスイッチのonChangedでINV_SOUNDS_SERVER/INV_SOUNDS_BARCODEを保存

#### Step 4: 言語選択の詳細を理解する

言語選択ダイアログの実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | app_settings.dart | `lib/settings/app_settings.dart` | _selectLocale()メソッド（行89-140） |
| 4-2 | supported_locales.dart | `lib/l10n/supported_locales.dart` | サポートされているロケールリスト |

**主要処理フロー**:
- **行90-100**: optionsリストにデフォルト言語とsupported_localesを追加
- **行111-140**: launchApiForm()で選択ダイアログを表示
- **行127**: InvenTreeSettingsManager().setSelectedLocale()で選択言語を保存
- **行134**: InvenTreeApp.of(context)?.setLocale()でアプリ全体のロケールを更新

### プログラム呼び出し階層図

```
InvenTreeAppSettingsWidget (app_settings.dart)
    |
    +-- _InvenTreeAppSettingsState
            |
            +-- initState()
            |       +-- loadSettings()
            |               +-- showLoadingOverlay()
            |               +-- InvenTreeSettingsManager().getValue() x 6
            |               +-- AdaptiveTheme.of(context).mode.isDark
            |               +-- InvenTreeSettingsManager().getSelectedLocale()
            |               +-- hideLoadingOverlay()
            |               +-- setState()
            |
            +-- build()
            |       |
            |       +-- Scaffold
            |               |
            |               +-- AppBar
            |               |
            |               +-- ListView
            |                       |
            |                       +-- ListTile (ダークモード)
            |                       |       +-- Switch.onChanged
            |                       |               +-- AdaptiveTheme.setDark/setLight()
            |                       |
            |                       +-- GestureDetector (画面の向き)
            |                       |       +-- onTap
            |                       |               +-- choiceDialog()
            |                       |                       +-- InvenTreeSettingsManager().setValue()
            |                       |
            |                       +-- ListTile (ラベル印刷)
            |                       |       +-- Switch.onChanged
            |                       |               +-- InvenTreeSettingsManager().setValue()
            |                       |
            |                       +-- ListTile (厳格なHTTPS)
            |                       |       +-- Switch.onChanged
            |                       |               +-- InvenTreeSettingsManager().setValue()
            |                       |
            |                       +-- ListTile (言語)
            |                       |       +-- onTap
            |                       |               +-- _selectLocale()
            |                       |
            |                       +-- ListTile (エラーレポート)
            |                       |       +-- Switch.onChanged
            |                       |               +-- InvenTreeSettingsManager().setValue()
            |                       |
            |                       +-- ListTile (サーバーサウンド)
            |                       |       +-- Switch.onChanged
            |                       |               +-- InvenTreeSettingsManager().setValue()
            |                       |
            |                       +-- ListTile (バーコードサウンド)
            |                               +-- Switch.onChanged
            |                                       +-- InvenTreeSettingsManager().setValue()
            |
            +-- _selectLocale()
                    |
                    +-- supported_locales から選択肢を構築
                    +-- launchApiForm()
                            |
                            +-- onSuccess
                                    +-- InvenTreeSettingsManager().setSelectedLocale()
                                    +-- setState()
                                    +-- InvenTreeApp.setLocale()
                                    +-- InvenTreeAPI().clearStatusCodeData()
```

### データフロー図

```
[初期化]                        [処理]                              [出力]

initState()             -->  loadSettings()                    -->  設定値読み込み
                                |
                                v
                         InvenTreeSettingsManager().getValue()
                                |
                                v
                         setState()                             -->  UI更新

[ユーザー操作]                  [処理]                              [出力]

スイッチ切替            -->  Switch.onChanged                  -->  即時反映
                                |
                                v
                         InvenTreeSettingsManager().setValue()  -->  ローカルDB保存
                                |
                                v
                         setState()                             -->  UI更新

画面の向き選択          -->  choiceDialog()                    -->  ダイアログ表示
                                |
                                v
                         onSelected
                                |
                                v
                         InvenTreeSettingsManager().setValue()  -->  ローカルDB保存
                                |
                                v
                         setState()                             -->  UI更新

言語選択                -->  _selectLocale()                   -->  ダイアログ表示
                                |
                                v
                         launchApiForm()
                                |
                                v
                         onSuccess
                                |
                                +-- setSelectedLocale()         -->  ローカルDB保存
                                +-- setLocale()                 -->  アプリ全体に反映
                                +-- clearStatusCodeData()       -->  キャッシュクリア
                                +-- setState()                  -->  UI更新

ダークモード切替        -->  AdaptiveTheme.setDark/setLight()  -->  テーマ即時反映
                                |
                                v
                         setState()                             -->  UI更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| app_settings.dart | `lib/settings/app_settings.dart` | ソース | InvenTreeAppSettingsWidgetの定義（行1-351） |
| preferences.dart | `lib/preferences.dart` | ソース | 設定キー定数と画面向き定数、InvenTreeSettingsManagerクラス |
| supported_locales.dart | `lib/l10n/supported_locales.dart` | ソース | サポートされているロケールリスト |
| main.dart | `lib/main.dart` | ソース | InvenTreeApp.setLocale()メソッド |
| api.dart | `lib/api.dart` | ソース | InvenTreeAPI.clearStatusCodeData()メソッド |
| api_form.dart | `lib/api_form.dart` | ソース | launchApiForm()関数 |
| dialogs.dart | `lib/widget/dialogs.dart` | ソース | choiceDialog()関数 |
| progress.dart | `lib/widget/progress.dart` | ソース | showLoadingOverlay()/hideLoadingOverlay()関数 |
| app_colors.dart | `lib/app_colors.dart` | ソース | COLOR_APP_BAR, COLOR_ACTIONの定義 |
| l10.dart | `lib/l10.dart` | ソース | 多言語対応 |
