# 画面設計書 10-検索画面

## 概要

本ドキュメントは、Bagisto eコマースモバイルアプリの検索画面に関する設計書です。キーワードで商品を検索し、結果を一覧表示する画面です。テキスト入力、音声検索、カメラ検索の3つの検索方法をサポートします。

### 本画面の処理概要

**業務上の目的・背景**：検索画面は、ユーザーが目的の商品を素早く見つけるための重要な機能を提供します。カテゴリナビゲーションでは見つけにくい商品や、特定のキーワードで検索したい場合に使用されます。テキスト入力に加えて、音声検索（SpeechToText）やカメラ検索（画像認識/テキスト認識）をサポートすることで、多様なユーザーニーズに対応しています。

**画面へのアクセス方法**：
- ホーム画面のAppBarの検索アイコンをタップ
- ボトムナビゲーションの検索タブをタップ
- ドロワーメニューの検索項目をタップ

**主要な操作・処理内容**：
1. キーワード入力による商品検索
2. 音声入力（マイク）による検索
3. カメラによる画像検索/テキスト認識検索
4. カテゴリ一覧の表示（検索画面初期表示）
5. 検索結果の商品一覧表示
6. 商品詳細画面への遷移

**画面遷移**：
- 遷移元：ホーム画面、ドロワーメニュー、ボトムナビゲーション
- 遷移先：商品詳細画面

**権限による表示制御**：
- カメラ機能：カメラ権限の許可が必要
- マイク機能：マイク権限の許可が必要

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 12 | 商品検索 | 主機能 | キーワードによる商品検索処理と結果一覧表示 |

## 画面種別

検索画面

## URL/ルーティング

- ルート名: `searchScreen`
- パス: `/search`

## 入出力項目

| 項目名 | 入出力 | データ型 | 必須 | 説明 |
|--------|--------|----------|------|------|
| 検索キーワード | 入力 | String | No | 商品検索用キーワード |
| 音声入力 | 入力 | String | No | 音声認識結果 |
| カメラ入力 | 入力 | String | No | 画像認識/テキスト認識結果 |

## 表示項目

| 項目名 | データ型 | 説明 |
|--------|----------|------|
| 検索入力フィールド | TextField | 検索キーワード入力欄 |
| クリアボタン | IconButton | 検索キーワードクリア |
| 音声検索アイコン | IconButton | 音声検索開始ボタン |
| カメラ検索アイコン | IconButton | カメラ検索開始ボタン |
| Listening表示 | Text | 音声認識中の表示 |
| ローディングバー | LinearProgressIndicator | 検索処理中の表示 |
| カテゴリ一覧 | List<Category> | 初期表示時のカテゴリリスト |
| 検索結果一覧 | List<NewProducts> | 検索結果の商品リスト |
| 空検索結果表示 | Widget | 検索結果がない場合の表示 |

## イベント仕様

### 1-画面初期化イベント（initState）

**トリガー**: 画面がマウントされたタイミング

**処理フロー**:
1. `activateSpeechRecognizer()`で音声認識を初期化
2. `AnimationController`を初期化（音声認識アニメーション用）
3. `SpeechToText`を初期化
4. `SearchBloc`をcontext.readで取得
5. `FetchCategoryPageEvent`を送信してカテゴリ一覧取得
   - フィルタ条件：status=1, locale=現在のロケール, parent_id=1

### 2-テキスト入力検索イベント

**トリガー**: 検索フィールドのonChanged（3文字以上入力時）

**処理フロー**:
1. 入力文字数が3文字以上か確認
2. `SearchBarTextEvent`を送信（入力テキスト保持）
3. `CircularBarEvent`を送信（ローディング表示）
4. `FetchSearchEvent`を送信（nameフィルタで検索）

### 3-音声検索イベント

**トリガー**: マイクアイコンタップ

**処理フロー**:
1. 音声認識中でない場合：`start()`を呼び出し
   - `SpeechToText.listen()`開始
   - `_isListening = true`
2. 音声認識中の場合：`stop()`を呼び出し
   - `SpeechToText.stop()`
   - `_isListening = false`
3. 音声認識結果を`onRecognitionResult()`で受信
4. 結果が3文字以上の場合、検索イベントを送信

### 4-カメラ検索イベント

**トリガー**: カメラアイコンタップ

**処理フロー**:
1. `DialogHelper.searchDialog()`で検索方法選択ダイアログ表示
   - 画像検索（imageSearch）
   - テキスト検索（textSearch）
2. カメラ権限を確認（`Permission.camera.request()`）
3. 権限許可時：MethodChannelで native コードを呼び出し
4. 認識結果を検索フィールドにセット
5. `onImageSearch()`で検索実行

### 5-カテゴリタップイベント

**トリガー**: カテゴリ一覧のアイテムタップ

**処理フロー**:
1. カテゴリ商品一覧画面またはサブカテゴリ画面へ遷移

### 6-検索結果クリアイベント

**トリガー**: クリアボタン（×）タップ

**処理フロー**:
1. `SearchBarTextEvent`を送信（空文字）
2. 検索結果をクリア

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 検索実行 | なし | SELECT | サーバーサイドで商品検索 |

本画面ではデータベースの更新処理は行いません。

## メッセージ仕様

| メッセージID | メッセージ種別 | 表示条件 | メッセージ内容 |
|-------------|--------------|----------|--------------|
| MSG-001 | 情報 | 音声認識中 | 「Listening...」 |
| MSG-002 | 情報 | 検索結果なし | 「No products found」（EmptyDataView） |
| MSG-003 | 警告 | 権限エラー | プラットフォーム固有のエラーメッセージ |

## 例外処理

| 例外ケース | 処理内容 | ユーザーへの影響 |
|-----------|---------|----------------|
| カテゴリ取得失敗 | SkeletonLoaderを継続表示 | カテゴリ一覧が表示されない |
| 検索失敗 | EmptyDataViewを表示 | 「No products found」メッセージ表示 |
| カメラ権限拒否 | 再度権限リクエストまたは設定画面へ誘導 | カメラ検索が使用できない |
| 音声認識失敗 | 状態をリセット | 通常のテキスト入力に切り替え |
| ネットワークエラー | networkErrorDialogを表示 | 再試行を促す |

## 備考

- 検索は3文字以上入力時に実行される
- 音声認識はspeech_to_textパッケージを使用
- カメラ検索はMethodChannelでネイティブコード（Android/iOS）を呼び出し
- 音声認識中はパルスアニメーションを表示
- InternetConnectionCheckerでネットワーク状態を確認
- カテゴリ一覧は検索前の初期表示として表示

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | new_product_data.dart | `lib/screens/home_page/data_model/new_product_data.dart` | 商品データモデル |
| 1-2 | get_categories_drawer_data_model.dart | `lib/screens/home_page/data_model/get_categories_drawer_data_model.dart` | カテゴリデータモデル |

**読解のコツ**: NewProductsModelは検索結果として返却される。HomeCategoriesは初期表示のカテゴリ一覧。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | search_screen.dart | `lib/screens/search_screen/view/search_screen.dart` | 検索画面のメインウィジェット |

**主要処理フロー**:
1. **39-48行目**: `initState()`で音声認識初期化、FetchCategoryPageEvent送信
2. **50-58行目**: `activateSpeechRecognizer()`でSpeechToText初期化
3. **60-73行目**: `onRecognitionResult()`で音声認識結果処理
4. **76-150行目**: `build()`でBlocConsumer構築
5. **153-222行目**: `_setAppBarView()`でAppBar構築（検索入力、マイク、カメラ）
6. **233-260行目**: `_buildVoiceInput()`で音声検索ウィジェット
7. **288-324行目**: `_checkPermission()`でカメラ権限処理

#### Step 3: Blocレイヤーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | search_bloc.dart | `lib/screens/search_screen/bloc/search_bloc.dart` | 状態管理Bloc |
| 3-2 | search_event.dart | `lib/screens/search_screen/bloc/search_event.dart` | イベント定義 |

**主要処理フロー**:
- `FetchCategoryPageEvent`: 初期カテゴリ一覧取得
- `FetchSearchEvent`: 商品検索
- `SearchBarTextEvent`: 検索テキスト状態管理
- `CircularBarEvent`: ローディング状態管理

#### Step 4: ビューレイヤーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | categories_view.dart | `lib/screens/search_screen/view/widget/categories_view.dart` | カテゴリ一覧表示 |
| 4-2 | product_list.dart | `lib/screens/search_screen/view/widget/product_list.dart` | 検索結果表示 |

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

```
SearchScreen (StatefulWidget)
    │
    ├─ initState()
    │      ├─ activateSpeechRecognizer()
    │      │      ├─ AnimationController
    │      │      └─ SpeechToText.initialize()
    │      ├─ SearchBloc (context.read)
    │      └─ SearchBloc.add(FetchCategoryPageEvent)
    │
    ├─ build() → BlocConsumer<SearchBloc>
    │      ├─ _setAppBarView()
    │      │      ├─ TextField (onChanged → FetchSearchEvent)
    │      │      ├─ クリアボタン → SearchBarTextEvent("")
    │      │      ├─ _buildVoiceInput()
    │      │      │      └─ onTap → start()/stop()
    │      │      │             ├─ SpeechToText.listen()
    │      │      │             └─ onRecognitionResult()
    │      │      │                    └─ FetchSearchEvent
    │      │      └─ カメラアイコン
    │      │             └─ DialogHelper.searchDialog()
    │      │                    └─ _checkPermission()
    │      │                           └─ MethodChannel
    │      │                                  └─ onImageSearch()
    │      │
    │      └─ body
    │             ├─ LinearProgressIndicator (isLoading)
    │             ├─ CategoriesView (カテゴリ一覧)
    │             ├─ ProductList (検索結果)
    │             └─ EmptyDataView (結果なし)
    │
    └─ SearchBloc
           ├─ FetchCategoryPageEvent → FetchCategoriesPageDataState
           ├─ FetchSearchEvent → FetchSearchDataState
           ├─ SearchBarTextEvent → AppBarSearchTextState
           └─ CircularBarEvent → CircularBarState
```

### データフロー図

```
[入力]                    [処理]                         [出力]

テキスト入力 ─────────────▶ FetchSearchEvent ─────────────▶ NewProductsModel
      │                         │
      │                         ▼
      │                  ProductList表示
      │
音声入力 ──────────────────▶ SpeechToText.listen()
      │                         │
      │                         ▼
      │                  onRecognitionResult()
      │                         │
      │                         ▼
      └─────────────────▶ FetchSearchEvent ─────────────▶ NewProductsModel

カメラ入力 ─────────────────▶ MethodChannel (native)
      │                         │
      │                         ▼
      │                  認識結果取得
      │                         │
      │                         ▼
      └─────────────────▶ FetchSearchEvent ─────────────▶ NewProductsModel
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| search_screen.dart | `lib/screens/search_screen/view/search_screen.dart` | ソース | 検索画面のメインウィジェット |
| categories_view.dart | `lib/screens/search_screen/view/widget/categories_view.dart` | ソース | カテゴリ一覧表示 |
| product_list.dart | `lib/screens/search_screen/view/widget/product_list.dart` | ソース | 検索結果表示 |
| search_bloc.dart | `lib/screens/search_screen/bloc/search_bloc.dart` | ソース | 状態管理Bloc |
| search_event.dart | `lib/screens/search_screen/bloc/search_event.dart` | ソース | イベント定義 |
| search_state.dart | `lib/screens/search_screen/bloc/search_state.dart` | ソース | 状態定義 |
| search_repository.dart | `lib/screens/search_screen/bloc/search_repository.dart` | ソース | API通信処理 |
| new_product_data.dart | `lib/screens/home_page/data_model/new_product_data.dart` | ソース | 商品データモデル |
| index.dart | `lib/screens/search_screen/utils/index.dart` | ソース | エクスポート集約 |
| route_constants.dart | `lib/utils/route_constants.dart` | ソース | ルーティング定数 |
