Flutter: マルチプラットフォーム公開とストア審査

📋 前提条件: 以下を先にマスターしている必要があります

1. このレッスンで学ぶこと


2. 審査却下のリアルなストーリー


3. マルチプラットフォーム公開プロセス


4. Android公開


5. iOS公開


6. Web公開


7. プライバシー準拠


8. 完全な例:ShopApp公開チェックリスト


9. ShopAppリリースチェックリスト v1.2.3

Bobはチェックリストで準備した後、App StoreとGoogle Playの両方が初回提出で通過し、Web版は1時間以内にローンチされました。

100%
graph TD
    REL[Release Process] --> ANDROID2[Android AAB]
    REL --> IOS2[iOS IPA]
    REL --> WEB2[Web PWA]
    ANDROID2 --> PC[Play Console]
    IOS2 --> ASC[App Store Connect]
    WEB2 --> FH[Firebase Hosting / Vercel]
    PC --> REVIEW1[Google Review]
    ASC --> REVIEW2[Apple Review]
    REVIEW1 --> LIVE1[Live on Google Play]
    REVIEW2 --> LIVE2[Live on App Store]
    FH --> LIVE3[Live on Web]

(1) 署名とビルド

▶ サンプル:キーストアの生成

BASH
# Generate signing keystore
keytool -genkey -v -keystore shopapp-release.jks \
  -keyalg RSA -keysize 2048 -validity 10000 \
  -alias shopapp

# Verify keystore
keytool -list -v -keystore shopapp-release.jks -alias shopapp

build.gradle署名設定

GROOVY
// android/app/build.gradle
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}

android {
    defaultConfig {
        applicationId "com.shopapp.app"
        minSdkVersion 21
        targetSdkVersion 34
        versionCode 42
        versionName "1.2.3"
    }
    signingConfigs {
        release {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile keystoreProperties['keyFile'] ? file(keystoreProperties['keyFile']) : null
            storePassword keystoreProperties['keyPassword']
        }
    }
    buildTypes {
        release {
            signingConfig signingConfigs.release
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

ビルドと公開

BASH
# Build release AAB (Android App Bundle)
flutter build appbundle --release
# Output: build/app/outputs/bundle/release/app-release.aab

# Build release APK (for testing)
flutter build apk --release

# Upload to Play Console manually or via CI/CD

(2) Play Consoleローンチチェックリスト

項目 要件
アプリケーションID 一意のパッケージ名 com.shopapp.app
AABファイル 署名済みリリースバンドル
アイコン 512x512 PNG
スクリーンショット スマホ + 7インチタブレット各2-8枚
プライバシーポリシーURL アクセス可能であること
コンテンツレーティング IARCアンケート完了
ターゲット層 年齢層を選択

(1) Apple Developer設定

ステップ 作業
1. 登録 Apple Developer Program($99/年)
2. 証明書 Distribution Certificateを作成
3. プロファイル App Store Provisioning Profileを作成
4. App ID Bundle Identifierを登録
5. アプリレコード App Store Connectでアプリを作成

Xcodeビルド設定

XML
<!-- ios/Runner/Info.plist -->
`<key>`CFBundleShortVersionString</key>
`<string>`1.2.3</string>
`<key>`CFBundleVersion</key>
`<string>`42</string>
`<key>`NSAppTransportSecurity</key>
`<dict>`
    `<key>`NSAllowsArbitraryLoads</key>
    `<false/>`
`</dict>`
`<key>`NSUserTrackingUsageDescription</key>
`<string>`We use tracking to personalize your shopping experience.</string>

ビルドとアップロード

BASH
# Build IPA
flutter build ipa --release \
  --export-options-plist=ios/ExportOptions.plist
# Output: build/ios/ipa/shopapp.ipa

# Upload via Xcode or command line
xcrun altool --upload-app \
  --type ios \
  --file build/ios/ipa/shopapp.ipa \
  --apiKey YOUR_API_KEY \
  --apiIssuer YOUR_ISSUER_ID
# Or via Transporter app

(2) App Store Connectローンチチェックリスト

| Bundle ID | Xcodeプロジェクトと一致すること | | IPAファイル | リリースビルド | | スクリーンショット | 6.7"/6.5"/5.5"の3サイズ | | プレビュー動画 | 任意、30秒以内 | | 説明文 | 4000文字以内 |

▶ サンプル:Appleプライバシーリスト準拠

DART
import 'package:flutter/material.dart';
import 'package:app_tracking_transparency/app_tracking_transparency.dart';
import 'package:firebase_analytics/firebase_analytics.dart';

// Dependencies: app_tracking_transparency: ^0.2.0, firebase_analytics: ^10.0.0 (add to dependencies)

Future<void> requestTrackingPermission() async {
  final status = await AppTrackingTransparency.requestTrackingAuthorization();
  if (status == TrackingStatus.authorized) {
    await FirebaseAnalytics.instance.setAnalyticsCollectionEnabled(true);
  } else {
    await FirebaseAnalytics.instance.setAnalyticsCollectionEnabled(false);
  }
}

出力:

TEXT
App Tracking Transparency dialog displayed
Status: authorized (or denied)
Analytics collection enabled/disabled accordingly

(1) 準拠要件比較

プラットフォーム 必須項目
Google Play プライバシーポリシー / データセーフティフォーム / コンテンツレーティング
App Store プライバシーポリシー / ATT / 暗号化準拠宣言
Web (PWA) GDPR Cookie同意 / プライバシーポリシー

(2) プライバシー準拠チェックリスト

リリース前チェック

3プラットフォーム公開チェックリスト

(1) Android

(2) iOS

(3) Web

(4) リリース後

❓ よくある質問

Q AABとAPKの違いは何ですか?
A AAB(Android App Bundle)はアップロード形式 — Google Playが自動的に最適化されたAPKを生成します。AABはデバイスに必要なリソースのみを含むためサイズが小さくなります。
Q App Store審査は通常どのくらいかかりますか?
A 初回提出は24-48時間、更新は通常12-24時間。却下後の再提出は再度待ち時間が必要。
Q versionCodeとversionNameはどう管理すべきですか?
A versionCodeは増分する整数(リリースごとに+1)、versionNameはユーザーに表示される値(例:1.2.3)。SemVer規約を使用してください。
Q Web版にICP届け出は必要ですか?
A 中国本土のサーバーはICP届け出が必要。海外サーバー(Firebase/Vercel)は不要。
Q GDPR準拠には何が必要ですか?
A 1) 初回起動時にプライバシー同意を表示、2) データ削除オプションの提供、3) プライバシーポリシーリンク、4) データ収集の最小化、5) ユーザーが同意を取り消し可能。
Q App Store審査で却下された場合は?
A 却下理由を慎重に読み、問題を修正して再提出。一般的な却下理由:プライバシーポリシー未記載、ATTポップアップ未表示、クラッシュ、コンテンツポリシー違反。

📖 まとめ


(1) ▶ サンプル:バージョン番号リリースサイクル

TEXT
1.0.0  →  MVP launch (first release)
1.0.1  →  Urgent bug fix (patch)
1.1.0  →  New feature (minor)
1.2.3  →  ShopApp release (minor + patch)
2.0.0  →  Major refactoring (major)

出力:

TEXT
SemVer: MAJOR.MINOR.PATCH
- MAJOR breaking changes
- MINOR compatible additions
- PATCH compatible fixes

(2) ▶ サンプル:Firebase Crashlytics連携

DART
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'dart:ui' show PlatformDispatcher;

// Dependencies: firebase_core: ^2.0.0, firebase_crashlytics: ^3.0.0 (add to dependencies)
// ShopApp comes from project lib/app.dart

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  FlutterError.onError = (details) {
    FirebaseCrashlytics.instance.recordFlutterFatalError(details);
  };

  PlatformDispatcher.instance.onError = (error, stack) {
    FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
    return true;
  };

  runApp(const ShopApp());
}

出力:

TEXT
Crashlytics initialized
Fatal errors auto-reported
Non-fatal errors auto-reported

(3) ▶ サンプル:リリース監視ダッシュボード

BASH
# Firebase Crashlytics
firebase functions:log --only publishMonitor
# Expected output:
# {"status":"ok","version":"1.2.3","crashRate":"0.05%"}

出力:

TEXT
Crash rate after rollout:
- 1h: 0.05% (baseline 0.10%)
- 24h: 0.04% (healthy)
- ANR rate: 0.01% (acceptable)

📝 練習問題

  1. 基本 (⭐):ShopApp用のAndroidキーストアを生成し、build.gradle署名を設定し、リリースAABのビルドに成功してください。
  2. 中級 (⭐⭐):ShopAppのデュアルプラットフォーム公開準備を完了してください — Android AAB署名 + iOS証明書設定、マルチプラットフォーム公開チェックリストを作成。
  3. チャレンジ (⭐⭐⭐):ShopAppの完全なクロスプラットフォーム公開プロセス(Android + iOS + Web)を実装してください。プライバシー準拠宣言、マルチチャンネルスクリーンショット生成、App Store説明文の作成を含む。

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%