Skip to content

feat: 設定画面にプライバシーポリシー・利用規約・サポート導線を追加#53

Open
haino357 wants to merge 4 commits into
developfrom
feature/settings-legal-pages
Open

feat: 設定画面にプライバシーポリシー・利用規約・サポート導線を追加#53
haino357 wants to merge 4 commits into
developfrom
feature/settings-legal-pages

Conversation

@haino357

@haino357 haino357 commented Mar 2, 2026

Copy link
Copy Markdown
Owner

Summary

  • 設定画面を3セクション・6メニュー項目に拡充(アプリ情報 / 法的情報 / サポート)
  • url_launcher / in_app_review パッケージを追加
  • 定数ファイル・URL起動ユーティリティを新規作成

変更内容

アプリ情報セクション

  • バージョン情報 — trailing に v1.0.0 形式で表示
  • ライセンス情報showLicensePage() でFlutter標準ライセンス画面を表示

法的情報セクション

  • プライバシーポリシー — 外部ブラウザで開く(Icons.open_in_new
  • 利用規約 — 外部ブラウザで開く(Icons.open_in_new

サポートセクション

  • お問い合わせ — mailto: リンク(アプリバージョン・OS情報を自動付与)
  • レビューを書くin_app_review → フォールバックでストアURL

その他

  • lib/constants/app_constants.dart — URL・メールアドレスの定数を一元管理
  • lib/utils/url_launcher_helper.dart — URL起動・メール起動ヘルパー(エラー時SnackBar表示)
  • テストに全セクション・メニュー項目の表示確認を追加

Test plan

  • fvm flutter analyze — lint違反なし
  • fvm flutter test — 全7テスト通過
  • 実機/シミュレータで各メニュー項目のタップ動作を確認
  • プライバシーポリシー・利用規約リンクが外部ブラウザで開くことを確認
  • お問い合わせからメールアプリが起動し、バージョン・OS情報が本文に含まれることを確認

Closes #40

🤖 Generated with Claude Code

- url_launcher / in_app_review パッケージを追加
- 定数ファイル(app_constants.dart)でURL・メールアドレスを一元管理
- URL起動ユーティリティ(url_launcher_helper.dart)を追加
- 設定画面を3セクション6メニュー項目に拡充
  - アプリ情報: バージョン情報・ライセンス情報
  - 法的情報: プライバシーポリシー・利用規約
  - サポート: お問い合わせ(端末情報自動付与)・レビューを書く
- テストにセクション・メニュー項目の表示確認を追加

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

設定画面(Settings)に「法的情報(プライバシーポリシー/利用規約)」と「サポート(問い合わせ/レビュー)」の導線を追加し、外部URL起動・アプリ内レビュー依頼を行えるようにするPRです。book_manager の設定タブを拡充して、ストア公開に必要な情報導線を整備する目的に合致しています。

Changes:

  • 設定画面を3セクション(アプリ情報/法的情報/サポート)構成に拡充し、外部リンク・メール・レビュー導線を追加
  • URL/メール起動ヘルパーと、URL/メール/ストアURL等の定数ファイルを新規追加
  • url_launcher / in_app_review 追加に伴う依存関係更新と、表示テスト追加

Reviewed changes

Copilot reviewed 10 out of 11 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
lib/screens/settings_screen.dart 設定画面に法的情報/サポートのメニューを追加し、URL起動・メール起動・レビュー依頼を実装
lib/utils/url_launcher_helper.dart url_launcher を使った外部URL/メール起動の共通ヘルパーを追加
lib/constants/app_constants.dart プライバシーポリシー/利用規約/サポートメール/ストアURLの定数を追加
test/widget_test.dart 設定画面のセクション/メニュー項目表示のテストを追加・更新
pubspec.yaml url_launcher / in_app_review をdependenciesに追加
pubspec.lock 依存関係解決結果の更新(関連パッケージ追加、SDK制約行の更新含む)
macos/Flutter/GeneratedPluginRegistrant.swift 追加プラグインの自動登録更新
linux/flutter/generated_plugins.cmake 追加プラグインの自動登録更新
linux/flutter/generated_plugin_registrant.cc 追加プラグインの自動登録更新
windows/flutter/generated_plugins.cmake 追加プラグインの自動登録更新
windows/flutter/generated_plugin_registrant.cc 追加プラグインの自動登録更新

Comment thread lib/screens/settings_screen.dart Outdated
Comment on lines +1 to +2
import 'dart:async';
import 'dart:io';

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dart:io の import と Platform.* の参照が入っているため、Web ターゲット(web/ がある構成)ではコンパイルエラーになります。Web もサポートする場合は kIsWeb / defaultTargetPlatform を使う、もしくは条件付き import で Web では Platform を参照しない実装に切り替えてください。

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

修正しました(cafd7e1)。dart:ioPlatformpackage:flutter/foundation.dartdefaultTargetPlatform / kIsWeb に変更し、Webターゲットでもコンパイルエラーが発生しないようにしました。

Comment thread lib/screens/settings_screen.dart Outdated
Comment on lines +139 to +144
if (context.mounted) {
final storeUrl = Platform.isIOS
? AppConstants.appStoreUrl
: AppConstants.googlePlayUrl;
await launchExternalUrl(context, storeUrl);
}

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ストアURLのフォールバックが Platform.isIOS 以外はすべて Google Play URL になっており、Windows/macOS/Linux/Web では誤った遷移になります。Android/iOS 以外は「未対応」のSnackBar表示にするか、プラットフォームごとの適切な遷移先(例: 公式サイト)を用意して分岐してください。

Suggested change
if (context.mounted) {
final storeUrl = Platform.isIOS
? AppConstants.appStoreUrl
: AppConstants.googlePlayUrl;
await launchExternalUrl(context, storeUrl);
}
if (!context.mounted) {
return;
}
String? storeUrl;
if (Platform.isIOS) {
storeUrl = AppConstants.appStoreUrl;
} else if (Platform.isAndroid) {
storeUrl = AppConstants.googlePlayUrl;
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('このプラットフォームではレビュー機能は未対応です。'),
),
);
return;
}
await launchExternalUrl(context, storeUrl);

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

修正しました(cafd7e1)。defaultTargetPlatform で Android/iOS のみストアURLに分岐し、それ以外のプラットフォームでは「このプラットフォームではレビュー機能は未対応です」の SnackBar を表示するようにしました。

Comment thread lib/screens/settings_screen.dart Outdated
to: AppConstants.supportEmail,
subject: '【読書管理】お問い合わせ',
body: '\n\n---\n'
'アプリバージョン: ${version ?? "不明"}\n'

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefer_single_quotes: true のlint設定に反して、文字列リテラルにダブルクォート("不明")が使われています。シングルクォートに統一してください(flutter analyze で警告/エラーになります)。

Suggested change
'アプリバージョン: ${version ?? "不明"}\n'
'アプリバージョン: ${version ?? '不明'}\n'

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

修正しました(cafd7e1)。シングルクォートに統一しました。

Comment thread lib/utils/url_launcher_helper.dart Outdated
Comment on lines +7 to +11
Future<void> launchExternalUrl(BuildContext context, String url) async {
final uri = Uri.parse(url);
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
} else {

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

canLaunchUrl を前提にしているため、Android 11+ の package visibility 制約下では Manifest の <queries> 設定が無いと canLaunchUrl が false になり、実際は起動できるのに SnackBar 側に倒れる可能性があります。launchUrl の戻り値(bool)を確認して失敗時に SnackBar を出す方式に寄せるか、canLaunchUrl を使い続けるなら必要な <queries> を追加してください。

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

修正しました(cafd7e1)。canLaunchUrl による事前チェックを廃止し、launchUrl の戻り値(bool)で成否を判定する方式に変更しました。これにより Android 11+ の package visibility 制約下でも正しく動作します。

Comment thread lib/utils/url_launcher_helper.dart Outdated
///
/// 失敗時は [context] を使って SnackBar でエラーを表示する。
Future<void> launchExternalUrl(BuildContext context, String url) async {
final uri = Uri.parse(url);

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uri.parse(url) は不正なURL文字列で FormatException を投げてクラッシュする可能性があります(定数差し替えミス等)。パース失敗を try/catch で吸収して SnackBar を出すなど、例外経路もユーザー向けにハンドリングしてください。

Suggested change
final uri = Uri.parse(url);
late final Uri uri;
try {
uri = Uri.parse(url);
} on FormatException {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('不正なURLです')),
);
}
return;
}

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

修正しました(cafd7e1)。Uri.parsetry/catch で囲み、FormatException 発生時は「不正なURLです」の SnackBar を表示して早期リターンするようにしました。

Comment thread lib/constants/app_constants.dart Outdated
Comment on lines +5 to +23
// TODO: リリース前に実際のURLに差し替えること
/// プライバシーポリシーURL
static const privacyPolicyUrl =
'https://example.com/privacy-policy';

/// 利用規約URL
static const termsOfServiceUrl =
'https://example.com/terms-of-service';

/// お問い合わせメールアドレス
static const supportEmail = 'support@example.com';

/// App Store URL(iOS)
static const appStoreUrl =
'https://apps.apple.com/app/idXXXXXXXXXX';

/// Google Play URL(Android)
static const googlePlayUrl =
'https://play.google.com/store/apps/details?id=com.example.book_manager';

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

プレースホルダーのURL/メール(example.com や idXXXXXXXXXX)がそのままアプリUIから到達可能になっています。リリース前差し替え前提なら、少なくとも本番向けビルドでは実URLが必須になるようにビルドフレーバー/環境変数で注入するか、未設定時はメニューを無効化して「準備中」表示にするなど、誤誘導を防いでください。

Suggested change
// TODO: リリース前に実際のURLに差し替えること
/// プライバシーポリシーURL
static const privacyPolicyUrl =
'https://example.com/privacy-policy';
/// 利用規約URL
static const termsOfServiceUrl =
'https://example.com/terms-of-service';
/// お問い合わせメールアドレス
static const supportEmail = 'support@example.com';
/// App Store URL(iOS)
static const appStoreUrl =
'https://apps.apple.com/app/idXXXXXXXXXX';
/// Google Play URL(Android)
static const googlePlayUrl =
'https://play.google.com/store/apps/details?id=com.example.book_manager';
/// プライバシーポリシーURL
///
/// - 本番ビルドでは `--dart-define=PRIVACY_POLICY_URL=...` で必ず設定すること
/// - 未設定(空文字)の場合は、画面遷移やリンク表示を無効化し「準備中」などを表示すること
static const String privacyPolicyUrl = String.fromEnvironment(
'PRIVACY_POLICY_URL',
defaultValue: '',
);
/// 利用規約URL
///
/// - 本番ビルドでは `--dart-define=TERMS_OF_SERVICE_URL=...` で必ず設定すること
/// - 未設定(空文字)の場合は、画面遷移やリンク表示を無効化し「準備中」などを表示すること
static const String termsOfServiceUrl = String.fromEnvironment(
'TERMS_OF_SERVICE_URL',
defaultValue: '',
);
/// お問い合わせメールアドレス
///
/// - 本番ビルドでは `--dart-define=SUPPORT_EMAIL=...` で必ず設定すること
/// - 未設定(空文字)の場合は、問い合わせメニューを無効化し「準備中」などを表示すること
static const String supportEmail = String.fromEnvironment(
'SUPPORT_EMAIL',
defaultValue: '',
);
/// App Store URL(iOS)
///
/// - 本番ビルドでは `--dart-define=APP_STORE_URL=...` で必ず設定すること
/// - 未設定(空文字)の場合は、ストア遷移ボタンを非表示または無効化すること
static const String appStoreUrl = String.fromEnvironment(
'APP_STORE_URL',
defaultValue: '',
);
/// Google Play URL(Android)
///
/// - 本番ビルドでは `--dart-define=GOOGLE_PLAY_URL=...` で必ず設定すること
/// - 未設定(空文字)の場合は、ストア遷移ボタンを非表示または無効化すること
static const String googlePlayUrl = String.fromEnvironment(
'GOOGLE_PLAY_URL',
defaultValue: '',
);

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

プライバシーポリシー・利用規約のURLは GitHub Pages(https://haino357.github.io/book_manager/...)に更新済みです(b3fc960)。ストアURL・メールアドレスについてはリリース前に差し替えが必要なため、TODOコメントを残しています。--dart-define による環境変数注入はリリースビルド整備時に別Issueで対応する方針とします。

haino357 and others added 2 commits March 2, 2026 18:27
- docs/にプライバシーポリシー・利用規約のHTMLページを作成
- app_constants.dartのURLをGitHub PagesのURLに更新
- アプリから外部ブラウザで法的文書を表示する方式に統一

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- dart:ioのPlatformをfoundationのdefaultTargetPlatform/kIsWebに変更しWeb対応
- レビュー機能のストアURL分岐をAndroid/iOSのみに限定し他プラットフォームは未対応表示
- ダブルクォートをシングルクォートに統一
- canLaunchUrlをlaunchUrlの戻り値判定に変更しAndroid 11+の誤判定を回避
- Uri.parseにtry/catchを追加し不正URL時のクラッシュを防止

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 3 comments.

Comment thread lib/screens/settings_screen.dart Outdated
Comment on lines +156 to +180
final inAppReview = InAppReview.instance;
if (await inAppReview.isAvailable()) {
await inAppReview.requestReview();
} else {
if (!context.mounted) {
return;
}

String? storeUrl;
if (defaultTargetPlatform == TargetPlatform.iOS) {
storeUrl = AppConstants.appStoreUrl;
} else if (defaultTargetPlatform == TargetPlatform.android) {
storeUrl = AppConstants.googlePlayUrl;
}

if (storeUrl != null) {
await launchExternalUrl(context, storeUrl);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('このプラットフォームではレビュー機能は未対応です'),
),
);
}
}

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in_app_review は Windows/Linux/Web ではプラグインが登録されていないため、isAvailable() 呼び出しが MissingPluginException 等で落ちる可能性があります(このファイルではプラットフォーム判定より前に呼んでいます)。defaultTargetPlatform で Android/iOS のときだけ InAppReview を呼ぶ、もしくは isAvailable/requestReviewtry/catch して失敗時にストアURLフォールバック/未対応SnackBarに倒すようにしてください。

Suggested change
final inAppReview = InAppReview.instance;
if (await inAppReview.isAvailable()) {
await inAppReview.requestReview();
} else {
if (!context.mounted) {
return;
}
String? storeUrl;
if (defaultTargetPlatform == TargetPlatform.iOS) {
storeUrl = AppConstants.appStoreUrl;
} else if (defaultTargetPlatform == TargetPlatform.android) {
storeUrl = AppConstants.googlePlayUrl;
}
if (storeUrl != null) {
await launchExternalUrl(context, storeUrl);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('このプラットフォームではレビュー機能は未対応です'),
),
);
}
}
// Web/非対応プラットフォームではプラグインが登録されていない可能性があるため、先に判定
if (kIsWeb ||
(defaultTargetPlatform != TargetPlatform.android &&
defaultTargetPlatform != TargetPlatform.iOS)) {
if (!context.mounted) {
return;
}
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('このプラットフォームではレビュー機能は未対応です'),
),
);
return;
}
try {
final inAppReview = InAppReview.instance;
if (await inAppReview.isAvailable()) {
await inAppReview.requestReview();
return;
}
} catch (_) {
// プラグイン未登録やその他の例外時はストアURLフォールバックへ
}
if (!context.mounted) {
return;
}
String? storeUrl;
if (defaultTargetPlatform == TargetPlatform.iOS) {
storeUrl = AppConstants.appStoreUrl;
} else if (defaultTargetPlatform == TargetPlatform.android) {
storeUrl = AppConstants.googlePlayUrl;
}
if (storeUrl != null) {
await launchExternalUrl(context, storeUrl);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('このプラットフォームではレビュー機能は未対応です'),
),
);
}

Copilot uses AI. Check for mistakes.
Comment thread pubspec.yaml
Comment on lines 51 to +53
package_info_plus: ^9.0.0
url_launcher: ^6.3.1
in_app_review: ^2.0.10

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pubspec.lock 側の Flutter SDK 制約が >=3.35.0 に上がっていますが、pubspec.yamlenvironment:flutter>=3.32.0 のままです。このままだと 3.32 系で pub get が再現できず依存解決が壊れるので、pubspec.yaml の Flutter 制約も >=3.35.0 に合わせるか、依存バージョンを調整して 3.32 を維持するかをどちらかに統一してください。

Copilot uses AI. Check for mistakes.
Comment thread lib/constants/app_constants.dart Outdated
Comment on lines +18 to +26
static const supportEmail = 'support@example.com';

/// App Store URL(iOS)
static const appStoreUrl =
'https://apps.apple.com/app/idXXXXXXXXXX';

/// Google Play URL(Android)
static const googlePlayUrl =
'https://play.google.com/store/apps/details?id=com.example.book_manager';

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

supportEmail / appStoreUrl / googlePlayUrl がプレースホルダーのままなので、ユーザーが到達すると誤誘導(example.com へのメール作成や無効なストアURL)になります。少なくとも未設定・プレースホルダー時は該当メニューを無効化して「準備中」表示にする、もしくは --dart-define 等で本番ビルド時に必須注入する仕組みにして、実値が入るまで起動しない/導線が出ないようにしてください。

Suggested change
static const supportEmail = 'support@example.com';
/// App Store URL(iOS)
static const appStoreUrl =
'https://apps.apple.com/app/idXXXXXXXXXX';
/// Google Play URL(Android)
static const googlePlayUrl =
'https://play.google.com/store/apps/details?id=com.example.book_manager';
/// 空文字の場合は「準備中」として扱い、問い合わせ導線を表示しないこと
static const supportEmail = '';
/// App Store URL(iOS)
/// 空文字の場合は「準備中」として扱い、ストア導線を表示しないこと
static const appStoreUrl = '';
/// Google Play URL(Android)
/// 空文字の場合は「準備中」として扱い、ストア導線を表示しないこと
static const googlePlayUrl = '';

Copilot uses AI. Check for mistakes.
- in_app_review 呼び出しを Web/非対応プラットフォーム判定 + try/catch で囲み、MissingPluginException を吸収
- pubspec.yaml の Flutter SDK 制約を pubspec.lock に合わせて >=3.35.0 に統一
- supportEmail/appStoreUrl/googlePlayUrl をプレースホルダーから空文字に変更し、
  お問い合わせメニューは空文字時に「準備中」表示で無効化、
  レビュー導線は空文字時にストアURLフォールバックを行わずSnackBar表示

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 4 comments.

Comment thread docs/privacy-policy.html
<p>本プライバシーポリシーは、必要に応じて更新されることがあります。変更があった場合は、アプリのアップデートを通じてお知らせします。</p>

<h2>お問い合わせ</h2>
<p>本プライバシーポリシーに関するお問い合わせは、アプリ内の「お問い合わせ」からご連絡ください。</p>
<p>開発者は、必要に応じて本規約を変更することがあります。変更後の利用規約は、アプリのアップデートを通じて公開されます。アップデート後のアプリの利用をもって、変更後の規約に同意したものとみなします。</p>

<h2>お問い合わせ</h2>
<p>本規約に関するお問い合わせは、アプリ内の「お問い合わせ」からご連絡ください。</p>
Comment on lines +192 to +202
final storeUrl = defaultTargetPlatform == TargetPlatform.iOS
? AppConstants.appStoreUrl
: AppConstants.googlePlayUrl;

if (storeUrl.isNotEmpty) {
await launchExternalUrl(context, storeUrl);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('このプラットフォームではレビュー機能は未対応です'),
),
Comment on lines +121 to +122
onTap: () {
unawaited(_requestReview(context));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

プライバシーポリシーなどの画面を作る

2 participants