12 KiB
Phase 4: ユーザビリティ向上 - 実装計画
Version: 1.0 Date: 2026-01-16 Based on: Antigravity's proposal + Claude's improvements Status: Ready to implement
Overview
Phase 4では以下2つの機能を実装します:
- 🌙 ダークモード自動切替 - 時間帯に応じた自動テーマ変更
- 🖋️ 和風フォント対応 - 日本酒アプリにふさわしいフォント選択
Feature 1: ダークモード自動切替 🌙
要件定義
- 既存: ライト/ダーク/システム連動の3モード
- 追加: 時間連動モード(夜間自動ダーク化)
- 切替時刻: 20:00〜06:00 = ダークモード
- ユーザー設定: 設定画面で選択可能
技術仕様
A. ThemeMode拡張
enum AppThemeMode {
light, // 常にライト
dark, // 常にダーク
system, // OSに従う
autoTime, // 時間連動(新規)
}
B. 時刻判定ロジック
class ThemeProvider extends ChangeNotifier {
AppThemeMode _themeMode = AppThemeMode.autoTime;
// 現在適用すべきThemeModeを計算
ThemeMode get effectiveThemeMode {
if (_themeMode == AppThemeMode.autoTime) {
final hour = DateTime.now().hour;
// 20:00〜06:00 = ダークモード
return (hour >= 20 || hour < 6) ? ThemeMode.dark : ThemeMode.light;
}
// その他のモードは既存ロジック
return _themeModeToFlutterThemeMode(_themeMode);
}
}
C. 自動更新メカニズム
方式1: WidgetsBindingObserver(推奨)
- アプリがバックグラウンドから復帰した時に再計算
- 電池消費が少ない
- リアルタイム性は低い(アプリを開いた時のみ)
方式2: Timer
- 1分ごとに時刻をチェック
- リアルタイム性が高い
- 電池消費がやや増える
採用: 方式1(推奨) - 日本酒アプリでは厳密なリアルタイム性は不要
class ThemeProvider extends ChangeNotifier with WidgetsBindingObserver {
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
// アプリ復帰時にテーマを再計算
notifyListeners();
}
}
}
D. UI実装
設定画面の選択肢:
○ ライトモード
○ ダークモード
○ システム設定に従う
● 自動(夜間ダーク) ← NEW
現在の状態表示:
テーマ: 自動(夜間ダーク)
現在: ダークモード(20:00〜06:00適用中)
実装ファイル
| ファイル | 変更内容 |
|---|---|
lib/providers/theme_provider.dart |
AppThemeMode enum追加、autoTime実装 |
lib/screens/settings_screen.dart |
4つ目の選択肢追加、状態表示追加 |
lib/services/hive_service.dart |
autoTimeモードの永続化対応 |
テスト計画
1. 時刻判定テスト
// テスト用時刻注入
class TimeService {
static DateTime Function() now = () => DateTime.now();
}
// テストケース:
// 05:59 → ダーク
// 06:00 → ライト
// 19:59 → ライト
// 20:00 → ダーク
2. UI動作テスト
- 設定で「自動(夜間ダーク)」を選択できる
- 現在の適用状態が表示される
- アプリ再起動後も設定が保持される
- 時刻をまたいでアプリを開くとテーマが変わる
3. パフォーマンステスト
- 電池消費が増えていない
- アプリ復帰時の遅延がない
Feature 2: 和風フォント対応 🖋️
要件定義
- 既存: システムフォント(Noto Sans JP相当)
- 追加: 日本酒らしい和風フォント
- 実装方式: Google Fonts(動的読み込み)
- ユーザー設定: 設定画面で選択可能
フォント候補
Option 1: Potta One(推奨 - Antigravity提案)
- 特徴: 丸みがあり親しみやすい、読みやすい
- サイズ: ~2MB
- 用途: メイン表示、カジュアルな雰囲気
- プレビュー: ぽんしゅルーム / 純米大吟醸
Option 2: Yuji Syuku
- 特徴: 筆文字風、書道感が強い
- サイズ: ~1.5MB
- 用途: タイトル、高級感演出
- プレビュー: ぽんしゅルーム / 純米大吟醸
Option 3: Shippori Mincho
- 特徴: 明朝体、洗練された印象
- サイズ: ~3MB
- 用途: フォーマルな表示、詳細画面
- プレビュー: ぽんしゅルーム / 純米大吟醸
Option 4: Zen Maru Gothic
- 特徴: ゴシック体、モダン和風
- サイズ: ~2MB
- 用途: 全般的な表示、バランス重視
- プレビュー: ぽんしゅルーム / 純米大吟醸
初回実装: Potta One(1種類のみ) 将来的に複数選択肢を追加可能な設計にする。
技術仕様
A. フォント定義
enum AppFontStyle {
standard, // システムフォント
pottaOne, // 丸文字(和風カジュアル)
// 将来の拡張:
// yujiSyuku, // 筆文字
// shipporiMincho, // 明朝体
}
B. テーマ適用
// app_theme.dart
class AppTheme {
static ThemeData buildTheme({
required Brightness brightness,
required AppFontStyle fontStyle,
}) {
final TextTheme textTheme;
switch (fontStyle) {
case AppFontStyle.pottaOne:
textTheme = GoogleFonts.pottaOneTextTheme();
break;
case AppFontStyle.standard:
default:
textTheme = const TextTheme(); // システムデフォルト
}
return ThemeData(
brightness: brightness,
textTheme: textTheme,
// ... 既存の設定
);
}
}
C. Provider統合
class ThemeProvider extends ChangeNotifier {
AppFontStyle _fontStyle = AppFontStyle.standard;
void setFontStyle(AppFontStyle style) {
_fontStyle = style;
notifyListeners();
// Hiveに保存
}
}
D. UI実装
設定画面の選択肢:
フォント設定
○ 標準フォント
● 和風フォント(丸文字)
プレビュー:
┌─────────────────┐
│ ぽんしゅルーム │ ← 実際のフォントで表示
│ 純米大吟醸 │
└─────────────────┘
実装ファイル
| ファイル | 変更内容 |
|---|---|
pubspec.yaml |
google_fonts: ^6.1.0(既存) |
lib/theme/app_theme.dart |
AppFontStyle対応、GoogleFonts統合 |
lib/providers/theme_provider.dart |
フォント設定管理 |
lib/screens/settings_screen.dart |
フォント選択UI追加 |
lib/services/hive_service.dart |
フォント設定の永続化 |
パフォーマンス考慮
初回読み込み:
- Google Fontsは初回のみネットワークからダウンロード
- キャッシュに保存される(2回目以降は即座に表示)
- ダウンロード中はシステムフォントで表示(フォント切替時の一瞬のちらつき)
オフライン対応(オプション):
# pubspec.yaml
fonts:
- family: PottaOne
fonts:
- asset: assets/fonts/PottaOne-Regular.ttf
- 事前にアセットとして含める(アプリサイズ +2MB)
- ネットワーク不要で即座に表示
推奨: Google Fonts動的読み込み(初回実装) アプリサイズを増やさず、将来的に複数フォント追加が容易。
テスト計画
1. フォント適用テスト
- 設定で「和風フォント」を選択できる
- 全画面でフォントが変わる(Home, Detail, Settings)
- PDFでもフォントが反映される ※重要
- アプリ再起動後も設定が保持される
2. 読みやすさテスト
- 短文(銘柄名): 視認性OK
- 長文(説明文): 読みやすさOK
- 小サイズ(10px〜): 潰れない
- ダークモード: コントラストOK
3. パフォーマンステスト
- 初回読み込み時間: 3秒以内
- 2回目以降: 即座に表示
- アプリサイズ増加: 0MB(動的読み込み)
4. PDF出力テスト
- PDF生成時にPotta Oneが使用される
- PDFファイルサイズが適切(フォント埋め込み)
- 印刷時にフォントが正しく表示される
実装スケジュール
Phase 4A: ダークモード自動切替(所要時間: 2-3時間)
Step 1: Provider実装(30分)
AppThemeModeenum追加effectiveThemeModeロジック実装WidgetsBindingObserver統合
Step 2: UI実装(30分)
- 設定画面に「自動(夜間ダーク)」追加
- 現在の状態表示を追加
Step 3: 永続化(30分)
- Hiveに
autoTimeモード保存 - 起動時の復元処理
Step 4: テスト(1時間)
- 時刻判定テスト(モック使用)
- UI動作確認
- 実機で夜間動作確認
Phase 4B: 和風フォント対応(所要時間: 2-3時間)
Step 1: テーマ実装(30分)
AppFontStyleenum追加AppTheme.buildTheme()にフォント統合- GoogleFonts.pottaOne() 適用
Step 2: Provider実装(30分)
- ThemeProviderにフォント設定追加
- setFontStyle() 実装
Step 3: UI実装(30分)
- 設定画面にフォント選択追加
- プレビュー表示
Step 4: PDF対応(30分)
- PdfService でフォント設定を参照
- Potta One をPDFに埋め込み
Step 5: テスト(1時間)
- 全画面でフォント確認
- PDF出力確認
- パフォーマンス計測
リスク管理
リスク1: Google Fonts読み込み失敗
シナリオ: ネットワーク不安定、サーバーダウン 影響: フォントがシステムフォントにフォールバック 対策:
- エラー時にユーザーに通知
- 次回起動時に再試行
- 最終的にはアセット埋め込みも検討
リスク2: PDFフォント埋め込み失敗
シナリオ: pdf パッケージがGoogle Fontsをサポートしない
影響: PDFは標準フォントで生成される
対策:
- 事前に検証(PDF生成テスト)
- 必要ならTTFファイルを手動ダウンロードして埋め込み
リスク3: ダークモード切替のタイミングずれ
シナリオ: アプリをバックグラウンドで長時間起動 影響: 20:00になってもライトモードのまま 対策:
WidgetsBindingObserverで確実に検知- 設定画面を開いた時も強制再計算
成果物
コード変更
lib/providers/theme_provider.dart(+50 lines)lib/theme/app_theme.dart(+30 lines)lib/screens/settings_screen.dart(+80 lines)lib/services/hive_service.dart(+20 lines)
ドキュメント
- この実装計画書
- ユーザー向けリリースノート(後ほど作成)
テスト結果レポート
- ダークモード自動切替の動作確認
- フォント適用の全画面スクリーンショット
次フェーズへの展望
Phase 4C: フォント拡張(将来)
- 複数フォント選択肢(Yuji Syuku, Shippori Mincho)
- フォントサイズ調整(大/中/小)
- 部分的フォント適用(タイトルのみ和風、本文は標準)
Phase 4D: テーマカスタマイズ(将来)
- カラーテーマ選択(青/緑/赤など)
- アクセントカラー変更
- カスタムテーマ保存
承認
この計画で実装を開始してよろしいでしょうか?
承認いただければ、Phase 4A(ダークモード)から実装を開始します。
End of Plan