- Schedule Recorderは、Flutterをベースにした音声を録音し、予定管理に活用できるアプリケーションです。
- 予定管理といいつつ、現状は音声録音と再生のみです。
- iOSだけに最適化された設計と機能を備えています。Androidでは正しく動作しません。
- 音声録音時のデモ動画
- 通話中に録音を一時停止と自動再開したデモ動画
- iOSのAVFoundationによる高度な音声処理
- スピーカー出力とBluetooth機器のサポート
- カスタマイズされた音声セッション管理
- iOSのCallKitとの完全な統合
- iOSのシステムイベント(着信)にも対応し、録音を中断・再開する機能を提供
- 着信・発信時の自動録音制御
- 通話状態のリアルタイム監視
- 通話終了後の録音自動再開
- FlutterとNative(Swift)間の最適化された通信
- 双方向のMethodChannel実装
- 詳細なデバッグログシステム
- 状態管理の完全な同期
- iOSのバックグラウンドタスク対応
- 録音中でもバックグラウンドで動作可能な設計を採用
- ファイル共有機能
- ファイル共有に対応
- 他のアプリへの音声ファイルの共有が可能
- 他のアプリからの共有ファイル受信には現状未対応
- 音声録音
- ボタン一つで録音を開始・停止可能。
- 音声再生
- 録音した音声データを再生。
- エラーハンドリング
- 不明な操作やエラーを適切に通知。
- ログ機能
- アプリケーションの動作状況を詳細に記録。
- デバッグやトラブルシューティングに活用可能。
- 状態管理
- Riverpodを使用した効率的な状態管理。
- 予測可能で安全な状態の更新。
-
マイク使用権限
- アプリがマイクを使用する際のユーザー許可を取得可能にする
<key>NSMicrophoneUsageDescription</key> <string>Microphone access is required to record audio.</string>
-
バックグラウンド実行の設定
- アプリがバックグラウンドでも録音を継続可能にする
- オーディオ処理をバックグラウンドで実行可能にする
<key>UIBackgroundModes</key> <array> <string>audio</string> <string>processing</string> </array>
-
ファイル共有の設定
- ファイル共有が可能な他のアプリに対して音声ファイルを共有可能にする
<key>LSSupportsOpeningDocumentsInPlace</key> <true/> <key>UIFileSharingEnabled</key> <true/>
-
ファイルタイプの設定
- このアプリの共有メニューから音声ファイル(.m4a, .mp3)を共有可能にする
<key>CFBundleDocumentTypes</key> <array> <dict> <key>CFBundleTypeName</key> <string>Audio</string> <key>LSHandlerRank</key> <string>Alternate</string> <key>LSItemContentTypes</key> <array> <string>public.audio</string> <string>public.mp3</string> <string>public.mpeg-4-audio</string> <string>com.apple.m4a-audio</string> </array> </dict> </array>
-
録音機能
- マイクアイコンのタップで録音を開始。
- 録音中に一時停止アイコンを表示。
- 録音一時停止後は、再び録音を開始可能。
-
再生機能
- 再生アイコンのタップで録音データを再生。
- 再生中に停止アイコンを表示。
-
システムイベントの処理
- 録音中に着電があり、応答した場合のみ、自動的に停止。
- 応答後に電話を切ると、自動的に録音を再開し、前回の録音の継続を行う。
-
録音中に電話が鳴った場合
- 録音は継続される(状態は変化しない)
- 「録音中...」のテキストが表示されたまま
-
録音中に電話に出た場合
- 自動的に一時停止する(pauseメソッドが呼ばれる)
- 「録音一時停止中...」のテキストが表示される
- 再開アイコンが表示される
-
電話を自分から切った場合
- 自動的に録音を再開する(resumeメソッドが呼ばれる)
- 「録音中...」のテキストが表示される
- 一時停止アイコンが表示される
-
電話を相手が切った場合
- 自動的に録音を再開する(resumeメソッドが呼ばれる)
- 「録音中...」のテキストが表示される
- 一時停止アイコンが表示される
-
音声ファイルの統一性
- 一時停止と再開で同じファイルを使用する(stopメソッドは一度だけ呼ばれる)
以下のデシジョンテーブルは、電話着信時の録音動作の仕様を示しています。
条件 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
録音中である | Y | Y | Y | Y | N |
電話が鳴る | Y | Y | Y | Y | - |
電話に出る | N | N | Y | Y | - |
自分から電話を切る | Y | N | Y | N | - |
相手が電話を切る | N | Y | N | Y | - |
アクション | |||||
録音を継続する | Y | Y | N | N | - |
録音を一時停止する | N | N | Y | Y | - |
録音を再開する | N | N | Y | Y | - |
一時停止前のデータと再開後のデータを1ファイルにする | Y | Y | Y | Y | - |
-
録音中に電話が鳴って、電話に出ずに自分で切る
- 録音は継続される
- 状態は変化しない
-
録音中に電話が鳴って、電話に出ずに相手が切る
- 録音は継続される
- 状態は変化しない
-
録音中に電話が鳴って、電話に出て、自分が切る
- 録音は自動的に一時停止する
- 通話終了後、自動的に録音を再開する
- 一時停止前のデータと再開後のデータは1つのファイルに保存される
-
録音中に電話が鳴って、電話に出て、相手が切る
- 録音は自動的に一時停止する
- 通話終了後、自動的に録音を再開する
- 一時停止前のデータと再開後のデータは1つのファイルに保存される
-
録音中でない場合
- 電話着信による影響を受けない
- 録音の一時停止と再開は、電話に出た場合のみ発生します
- 電話に出なかった場合は、録音は継続されます
- 録音再開時は、一時停止前のデータと再開後のデータが1つのファイルに保存されます
- 録音中でない場合は、電話着信による影響を受けません
- MVCパターン
- UIとロジックを分離して、テスト可能性と保守性を向上。
- サービスレイヤー
- AudioServiceを用いて、音声録音・再生機能を統一的に管理。
- FileSharingServiceを用いて、ファイル共有機能を管理。
- 状態管理
- Riverpodを採用し、アプリケーション全体の状態を効率的に管理。
- Provider、StateNotifier、StateProviderを使用した適切な状態管理の実装。
- NativeとFlutterの連携
- MethodChannelを活用して、FlutterとiOSのネイティブコードを効率的に接続。
- swiftのコードは
ios/Runner/AppDelegate.swift
を参照してください。
SchedulePage
: ユーザーインターフェースを管理。AudioService
: 録音・再生のロジックを提供。FileSharingService
: ファイル共有機能を管理。RecordingButtons
: 録音関連のUIコンポーネントを提供。AudioFileList
: 録音ファイルの一覧表示を管理。
主要な依存パッケージとその用途:
- MethodChannelによるNative APIの呼び出し
- 録音イベント(中断・再開)をハンドリング。
- バックグラウンドオーディオセッション
- iOS特有のバックグラウンド処理を適用。
-
ウィジェットテスト
RecordingButtons
のUI操作テストAudioFileList
の表示テスト- 状態変更時のUI更新テスト
-
ユニットテスト
AudioService
のメソッドテストFileSharingService
の機能テスト- Riverpodプロバイダーのテスト
- 録音と再生のボタン操作
- UI操作をモックして、各状態が正しく遷移するかを確認。
- AudioServiceのメソッドテスト
- iOSのネイティブイベント(例:
RecordingInterrupted
)が正しくハンドリングされることを確認。
- iOSのネイティブイベント(例:
# ユニットテストに必要なMockを作成する。なお、riverpod_annotationのためのg.dartファイルもこのコマンドで生成する。
dart run build_runner build --delete-conflicting-outputs
# ユニットテストを実行する
flutter test
# 特定のテストファイルを実行
flutter test test/widgets/schedule_page/recording_buttons_test.dart
flutter test test/widgets/schedule_page/audio_file_list_test.dart
-
Flutterのインストール
- Flutterがインストールされていない場合は、公式サイトを参照してセットアップしてください。
- Flutter自体のバージョン管理には
fvm
を使用しています。
-
lefthookのセットアップ
- コード品質を維持するため、git commit時に自動的にコードの整形とチェックを行います
# lefthookのインストール brew install lefthook # プロジェクトへのlefthookの設定 lefthook install
-
プロジェクトのセットアップ
# 依存パッケージのインストール flutter pub get # アプリの実行 flutter run
以下の処理がgit commit時に自動的に実行されます
- コードの自動修正
dart fix --apply lib
による自動修正の適用
- インポート文の整理
import_sorter
によるインポート文の整理
- コードフォーマット
dart format
によるコードスタイルの統一
- 注意事項
- git commitに
--no-verify
オプションを使用すると、これらのチェックがスキップされます - チェックをスキップすると、CIでエラーとなる可能性があります
- 問題が発生した場合は
lefthook run pre-commit -d
でデバッグモードで実行できます
- git commitに
-
Xcodeを用いてiOS向けにビルドする場合:
open ios/Runner.xcworkspace Xcodeでターゲットを選択し、ビルドを実行。
-
iOSデバイスでの信頼設定
flutter install
でアプリをインストールした後、アプリを起動する前に以下の設定が必要です- iOSデバイスの「設定」アプリを開く
- 「一般」>「VPNとデバイス管理」を選択
- 開発者のメールアドレスを選択
- 「このデベロッパを信頼」を選択
- この設定後、アプリが正常に起動できるようになります
-
マイク権限の問題
- 症状: 録音が開始されない
- 解決: iOSの設定でマイク権限がこのアプリに許可されているかを確認
- 解決: ios/Runner/Info.plistのマイク権限を確認
<key>NSMicrophoneUsageDescription</key> <string>Microphone access is required to record audio.</string>
- 解決: ios/Podfileのマイク権限を確認
post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) target.build_configurations.each do |config| config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [ '$(inherited)', ## dart: PermissionGroup.microphone 'PERMISSION_MICROPHONE=1', ] end end end
-
バックグラウンド録音
- 症状: バックグラウンドで録音が停止
- 解決: ios/Runner/Info.plistのバックグラウンド設定を確認
<key>UIBackgroundModes</key> <array> <string>audio</string> <string>processing</string> </array>
-
署名とプロビジョニングの問題
-
症状: ビルド時に
No profiles for 'com.example.scheduleRecorder' were found
というエラーが発生 -
解決手順:
- Xcodeで
ios/Runner.xcworkspace
を開く - RunnerプロジェクトのSigningセクションで以下を確認
- Teamが選択されているか
- Personal Teamでもビルド/ビルド後のインストールはできます
- Bundle Identifierが正しく設定されているか
- Automatically manage signingが有効になっているか
- Teamが選択されているか
- Product > Clean Build Folderを実行
- Product > Buildを実行
- Xcodeで
-
代替解決策:
- コマンドラインでビルドする場合は
-allowProvisioningUpdates
オプションを追加:
flutter build ios --allow-provisioning-updates
- コマンドラインでビルドする場合は
-
-
AVAudioSessionの管理
setupAudioSession()
メソッドで初期設定- カテゴリ:
.playAndRecord
- オプション:
.defaultToSpeaker
,.allowBluetooth
- 録音中断時の自動ハンドリング
-
通話イベントの処理
CXCallObserverDelegate
による通話状態の監視AVAudioSession.isOtherAudioPlaying
による音声通話の監視- 通話開始時の録音一時停止と終了時の再開
-
録音状態の同期
GetRecordState
イベント: 録音状態の取得RecordingInterrupted
イベント: 録音の一時停止RecordingResumed
イベント: 録音の再開- 状態変更時のMethodChannelを通じたFlutter側への通知
-
状態管理
RecordingStateNotifier
による録音状態の管理- 状態遷移: 停止 → 録音中 → 一時停止
- 各状態でのUI更新とユーザー操作の制御
-
録音処理
record
パッケージによる録音機能の実装- 録音設定:
- エンコーダー:
AudioEncoder.aacLc
- ビットレート: 128000
- サンプルレート: 44100
- エンコーダー:
-
イベントハンドリング
- iOS側からのイベントをMethodChannelで受信
- 録音状態の自動更新
- エラー発生時のログ出力と状態リセット
-
録音の一時停止と再開
- 同一ファイルでの録音継続
- 状態遷移の正確な管理
- UIの適切な更新
-
エラーハンドリング
- 録音失敗時の適切な状態リセット
- ユーザーへのエラー通知
- ログ出力による問題追跡
-
リソース管理
- 録音セッションの適切な開放
- メモリリークの防止
- バックグラウンド処理の最適化
-
ログ出力
- 状態変更時のログ
- エラー発生時の詳細情報
- 録音操作の実行順序
-
一般的な問題と解決策
- 録音が再開されない: AVAudioSessionの状態を確認
- 音量が低い: オーディオセッションの設定を確認
- 状態が不整合: ログを確認し状態遷移を追跡
-
テスト項目
- 手動での一時停止/再開
- 通話による中断/再開
- バックグラウンド録音
- エラー発生時の動作
- 文字列定数の管理
- 文字列はすべて
lib/constants/strings.dart
で管理する。 - ログ文字列は現状ではこのファイルで管理していません。
- 文字列はすべて
- クラウド連携
- 録音データをクラウドストレージに保存する機能。
- 音声認識
- 音声データを文字起こしするAPIとの統合。