Newer
Older
MiniTias / docs / 04_SPEC / SPEC_01_画面機能仕様書.md

画面機能仕様書 (Screen & Function Specification)

本ドキュメントでは,MiniTIAS アプリの画面設計,画面遷移,カメラ制御,ファイル管理,およびアーキテクチャを定義する.

画面一覧 (Screen List)

ID画面名概要
S-01撮影画面インカメラのライブプレビューを表示し,撮影を行う
S-02一覧画面撮影済み画像のサムネイル一覧を表示し,確認・削除を行う

※ 初期バージョンでは 2 画面構成とする.

画面遷移 (Screen Transition)

[アプリ起動]
    │
    ▼
┌──────────┐       BottomNavigationBar       ┌──────────┐
│  S-01    │ ◄──────────────────────────────► │  S-02    │
│ 撮影画面  │         タブ切り替え              │ 一覧画面  │
└──────────┘                                  └──────────┘
                                                   │
                                                   │ サムネイルタップ
                                                   ▼
                                              ┌──────────┐
                                              │ 拡大表示   │
                                              │ (ダイアログ) │
                                              └──────────┘
  • アプリ起動時のデフォルト画面は 撮影画面(S-01) とする
  • 画面切り替えには BottomNavigationBar を使用する(タブ 2 つ: 撮影 / 一覧)
  • 画像の拡大表示はダイアログ(showDialog)で実装し,独立画面としない

撮影画面 (S-01: Capture Screen)

レイアウト

UI 全体を Transform.rotate(angle: pi) で 180° 回転する.以下はコード上の配置順(回転前)を示す.操作者が逆さに置いた端末を見ると,上下が反転して表示される.

操作者の視点(回転後)            コード上の配置(回転前)
┌─────────────────────┐      ┌─────────────────────┐
│ (黒い空白)          │      │  BottomNavBar        │
│  アタッチメントで隠れる │      │  明るさスライダー      │
├─────────────────────┤      │                     │
│                     │      │  カメラプレビュー      │
│  カメラプレビュー      │      │  (中央クロップ)      │
│  (中央クロップ)      │      │                     │
│                     │      ├─────────────────────┤
│  明るさスライダー      │      │ (黒い空白)          │
│  BottomNavBar        │      │  アタッチメントで隠れる │
└─────────────────────┘      └─────────────────────┘

※ アタッチメント(SmTIAS)により画面の約 1/3 が隠れるため,その領域は黒い空白とし,UI 要素は見える領域に配置する.

UI 要素

要素仕様
カメラプレビューインカメラのライブ映像.鏡像(左右反転)+上下反転で表示.画面いっぱいに拡大し中央をクロップ
シャッターボタン丸型ボタン.タップで撮影実行.連続タップ可(連写対応).※ Step 3 で実装
明るさスライダーナビバーの直下に配置.画面の輝度を手動で調整する(デフォルト: 0.8)
BottomNavigationBar撮影タブ(アクティブ)/ 一覧タブ.背景色は黒系で統一

動作仕様

  • 起動時: カメラの初期化を行い,プレビューを開始する
  • 撮影: シャッターボタンタップで静止画をキャプチャし,PNG 形式で保存する
  • 連続撮影: 前回の保存完了を待たずに次の撮影が可能.撮影中はシャッターボタンの操作を受け付ける
  • フィードバック: 撮影成功時にスナックバーで「保存しました: ファイル名」を表示する
  • エラー時: カメラ初期化失敗やストレージ書き込み失敗時は,エラーメッセージをスナックバーで表示する

レイアウト

┌─────────────────────────┐
│     BottomNavBar         │
├─────────────────────────┤
│ ┌─────┐ ┌─────┐ ┌─────┐│
│ │     │ │     │ │     ││
│ │ img │ │ img │ │ img ││
│ │     │ │     │ │     ││
│ └─────┘ └─────┘ └─────┘│
│ ┌─────┐ ┌─────┐ ┌─────┐│
│ │     │ │     │ │     ││
│ │ img │ │ img │ │ img ││
│ │     │ │     │ │     ││
│ └─────┘ └─────┘ └─────┘│
│          ...             │
├─────────────────────────┤
│   [画面上部エリア]        │  ← 逆さ配置のため上部が操作側
└─────────────────────────┘

※ 撮影画面と同様に UI 全体を 180° 回転して表示する.

UI 要素

要素仕様
サムネイルグリッド3 列のグリッド表示.新しい画像が先頭(降順)
サムネイル正方形クロップ.タップで拡大表示
ファイル名ラベル各サムネイル下部にファイル名を表示
BottomNavigationBar撮影タブ / 一覧タブ(アクティブ)

拡大表示ダイアログ

要素仕様
画像元画像をフル解像度で表示.ピンチ操作でズーム可能
ファイル名ダイアログ上部にファイル名を表示
削除ボタンダイアログ内に配置.タップで削除確認ダイアログを表示
閉じるボタンダイアログ外タップまたは閉じるボタンで閉じる

動作仕様

  • 画面表示時: Pictures/MiniTIAS/ ディレクトリ内の PNG ファイルを走査し,一覧を構築する
  • リフレッシュ: 撮影画面から戻った際に一覧を再取得する
  • 削除: 削除確認ダイアログで「削除」を選択すると,ファイルをストレージから削除し,一覧から除去する
  • 空状態: 画像が 0 件の場合は「撮影した画像がありません」のメッセージを表示する

カメラ制御仕様 (Camera Control)

カメラ設定

項目
使用カメラフロントカメラ(インカメラ)
解像度ResolutionPreset.max(カメラが対応する最大解像度)
フォーカスオートフォーカス(デフォルト動作)
フラッシュOFF(LED ライトはアタッチメント側で制御)
画像フォーマットCamera2 API で YUV_420_888 生データを取得し,PNG に直接変換して保存(JPEG 非経由)

プレビュー表示

  • camera パッケージの CameraPreview ウィジェットを使用する
  • UI 全体を Transform.rotate(angle: pi) で 180° 回転する
  • カメラ映像に Matrix4.diagonal3Values(-scale, -scale, 1.0) を適用する
    • X 軸反転: 鏡像表示(操作者が自然に見える向き)
    • Y 軸反転: 端末が逆さのため上下を補正
  • プレビューは画面いっぱいに拡大し,はみ出す部分はクロップする(ClipRect + スケール計算)

撮影フロー

シャッターボタンタップ
    │
    ▼
プレビュー停止(CameraController.dispose())
    │
    ▼
Camera2 API でフロントカメラを開く(YUV_420_888,フル解像度)
    │
    ▼
AE/AF 安定のため 1 秒間プレビューフレームを流す
    │
    ▼
STILL_CAPTURE リクエストで YUV フレームを 1 枚取得
    │
    ▼
YUV → RGB 変換(BT.601 係数,isolate で実行)
    │
    ▼
PNG エンコード(image パッケージ,ロスレス)
    │
    ▼
ファイル名生成(命名規則に従う)
    │
    ▼
Pictures/MiniTIAS/ に保存
    │
    ▼
MediaStore 通知(MediaScannerConnection でスキャン)
    │
    ▼
Camera2 を閉じ,プレビューを再開
    │
    ▼
スナックバーで保存完了を通知

ライフサイクル管理

  • WidgetsBindingObserver を使用し,アプリのライフサイクルを監視する
  • バックグラウンド遷移時paused): カメラリソースを解放する(CameraController.dispose()
  • フォアグラウンド復帰時resumed): カメラを再初期化する
  • 画面遷移でカメラを使用しない画面に移動した場合もカメラリソースを解放する

ファイル管理仕様 (File Management)

保存先

  • パス: Pictures/MiniTIAS/(Android 共有ストレージ)
  • Android 10 以上では MediaStore API を使用する(saver_gallery パッケージで抽象化)
  • ディレクトリが存在しない場合は自動作成する

ファイル命名規則

  • 基本形式: MiniTIAS_YYYYMMDD_HHmmss.png
  • 同秒重複時: MiniTIAS_YYYYMMDD_HHmmss_1.pngMiniTIAS_YYYYMMDD_HHmmss_2.png,...
  • タイムスタンプはデバイスのローカル時刻を使用する

重複回避ロジック

ファイル名候補 = "MiniTIAS_{timestamp}.png"

IF ファイル名候補が存在しない:
    RETURN ファイル名候補

suffix = 1
WHILE "MiniTIAS_{timestamp}_{suffix}.png" が存在する:
    suffix += 1

RETURN "MiniTIAS_{timestamp}_{suffix}.png"

削除

  • 一覧画面の拡大表示ダイアログから削除を実行する
  • 削除前に確認ダイアログを表示する(「この画像を削除しますか?」)
  • ファイルシステムから物理削除する(ゴミ箱機能なし)
  • 削除後,MediaStore からも当該エントリを除去する

パーミッション管理 (Permission Handling)

必要なパーミッション

パーミッション用途対象 API レベル
CAMERAカメラプレビュー・撮影全バージョン
WRITE_EXTERNAL_STORAGE画像保存API 28 以下
READ_EXTERNAL_STORAGE画像一覧取得API 28 以下
READ_MEDIA_IMAGES画像一覧取得API 33 以上

※ API 29〜32 では Scoped Storage により,アプリが MediaStore 経由で保存したファイルは追加権限なしでアクセス可能.

パーミッション要求フロー

アプリ起動
    │
    ▼
カメラ権限を確認
    │
    ├── 許可済み → カメラプレビュー開始
    │
    └── 未許可 → 権限リクエストダイアログ表示
              │
              ├── 許可 → カメラプレビュー開始
              │
              └── 拒否 → 「カメラの権限が必要です」メッセージ表示
                         + 設定画面への誘導ボタン
  • ストレージ権限は撮影時・一覧表示時にそれぞれ確認する
  • permission_handler パッケージを使用する

アーキテクチャ (Architecture)

状態管理

  • provider パッケージを使用する
  • 画面ごとに ChangeNotifier を作成し,ビジネスロジックを UI から分離する

Provider 構成

Provider責務
CameraProviderカメラの初期化・プレビュー制御・撮影実行・ライフサイクル管理
GalleryProvider画像一覧の取得・キャッシュ・削除操作

ディレクトリ構成

lib/
├── main.dart                  # アプリエントリポイント,Provider 登録
├── app.dart                   # MaterialApp 定義,テーマ設定
├── providers/
│   ├── camera_provider.dart   # カメラ制御の状態管理
│   └── gallery_provider.dart  # 画像一覧の状態管理
├── screens/
│   ├── capture_screen.dart    # 撮影画面 (S-01)
│   └── gallery_screen.dart    # 一覧画面 (S-02)
├── widgets/
│   ├── camera_preview.dart    # カメラプレビューウィジェット
│   ├── shutter_button.dart    # シャッターボタン
│   ├── image_grid.dart        # サムネイルグリッド
│   └── image_detail_dialog.dart # 拡大表示ダイアログ
└── services/
    ├── file_service.dart      # ファイル保存・命名・削除
    └── permission_service.dart # パーミッション管理

レイヤー構成

┌─────────────────────────────────┐
│  Screens(画面)                  │  UI 層: ウィジェット構築のみ
├─────────────────────────────────┤
│  Widgets(部品)                  │  再利用可能な UI コンポーネント
├─────────────────────────────────┤
│  Providers(状態管理)            │  ビジネスロジック・状態保持
├─────────────────────────────────┤
│  Services(サービス)             │  外部リソースとのインタフェース
└─────────────────────────────────┘
  • Screens は Provider を context.watch / context.read で参照し,直接 Service を呼ばない
  • Providers は Service を呼び出し,結果を状態として保持する
  • Services はプラットフォーム API やファイルシステムとの橋渡しを行う

非機能要件 (Non-functional Requirements)

画面輝度

  • アプリ起動時に画面の輝度をデフォルト値(0.8)に設定する
  • ユーザーが明るさスライダーで任意に調整できる
  • アプリ終了時にシステムの輝度設定に復帰する
  • アタッチメント装着時に光センサーで画面が暗くなることへの対策

パフォーマンス

  • 撮影から保存完了まで 2 秒以内を目標とする
  • 一覧画面のサムネイル読み込みは非同期で行い,画面表示をブロックしない

データ保全

  • 撮影データは端末ローカルにのみ保存する(クラウド同期なし)
  • PNG(非圧縮)形式で保存し,画質劣化を防ぐ

対応端末

  • 試験端末: AQUOS sense3(SH-02M,Android 9)
  • minSdkVersion: 21(Android 5.0)
  • targetSdkVersion: Flutter デフォルト(最新安定版に追従)