0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Flutter × Google スプレッドシートでアプリを作って App Store / Android に公開するまで

0
Last updated at Posted at 2026-04-25

実際に躓いたポイント全部まとめ

個人開発で「自分だけの単語帳アプリ」を Flutter で作り、iOS / Android 両対応で公開しました。
Google スプレッドシートをデータソースとして使うという構成で、思ったより多くの壁にぶつかったので、同じところで詰まる人が減るように記録しておきます。

作ったもの

Google スプレッドシートで管理した単語データをスマホアプリから参照・学習できる単語帳アプリです。

  • PC のスプレッドシートで単語を追加・編集
  • アプリからインポートしてインクリメンタルサーチ・フラッシュカード学習
  • 手動登録にも対応

バックエンドサーバーを持たず、Google スプレッドシートをデータストアとして使うことで、個人開発の規模感に合ったシンプルな構成にしました。

1. Google スプレッドシート連携の実装

必要な準備

Google スプレッドシートをアプリから読み書きするには、Google Cloud Console での設定が必要です。

  1. console.cloud.google.com でプロジェクトを作成
  2. Google Sheets API を有効化
  3. OAuth 2.0 クライアント ID を作成

ここで重要なのが「OAuth クライアント ID はプラットフォームごとに別々に作る必要がある」という点です。最初これを知らずに Android 用のものを iOS でも使おうとして、ログインエラーに悩まされました。

Android の設定

Android では「ウェブアプリケーション」タイプのクライアント ID を使います(Credential Manager の仕様です)。

作成したクライアント ID を android/app/src/main/res/values/sign_in_config.xml に設定します。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="default_web_client_id">
        YOUR_WEB_CLIENT_ID.apps.googleusercontent.com
    </string>
</resources>

Flutter 側では --dart-define でビルド時に値を注入する方法もあります。

// lib/app/app_config.dart
class AppConfig {
  static const googleServerClientId = String.fromEnvironment(
    'GOOGLE_SERVER_CLIENT_ID',
    defaultValue: 'YOUR_DEFAULT_CLIENT_ID.apps.googleusercontent.com',
  );
}

iOS の設定(ここが一番躓く)

Android で動いたからといって iOS でそのまま動くと思ったら大間違いでした。iOS には iOS 専用のクライアント ID が別途必要です。

Google Cloud Console で「iOS」タイプのクライアント ID を作成すると、設定用の .plist ファイルがダウンロードできます。中身はこんな感じです。

<key>CLIENT_ID</key>
<string>XXXXXX-YYYYYYYY.apps.googleusercontent.com</string>
<key>REVERSED_CLIENT_ID</key>
<string>com.googleusercontent.apps.XXXXXX-YYYYYYYY</string>
<key>BUNDLE_ID</key>
<string>jp.your.app</string>

この値を ios/Runner/Info.plist に追加します。

<key>GIDClientID</key>
<string>XXXXXX-YYYYYYYY.apps.googleusercontent.com</string>
<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleTypeRole</key>
        <string>Editor</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>com.googleusercontent.apps.XXXXXX-YYYYYYYY</string>
        </array>
    </dict>
</array>

CFBundleURLSchemes に設定する値は「リバースクライアント ID」と呼ばれる、クライアント ID を逆にした文字列です。
これを忘れると Google からのコールバックが受け取れずサインインが完了しません。

よくあるエラー

iOS でサインインボタンを押したときにこのエラーが出た場合:

PlatformException(google_sign_in, No active configuration.
Make sure GIDClientID is set in Info.plist., NSInvalidArgumentException, null)

Info.plistGIDClientID が設定されていません。上記の手順で追加してください。

2. Android APK の配布

ビルド

flutter build apk --release

これだけです。build/app/outputs/flutter-apk/app-release.apk が生成されます。
iOS と違い、証明書まわりの複雑な設定が不要なのが Android の楽なところです。

配布方法

方法 特徴
Google Play Store 公式配布。審査があるが信頼性が高い
APK 直接配布 審査なし・即日配布可。「提供元不明のアプリ」として警告が出る
Firebase App Distribution テスター向け配布に最適。無料で使える

躓きポイント

Bundle ID がデフォルトのまま

Flutter でプロジェクトを新規作成すると Bundle ID が com.example.アプリ名 になっています。これを公開前に必ず変更してください。android/app/build.gradleapplicationIdios/Runner.xcodeproj/project.pbxprojPRODUCT_BUNDLE_IDENTIFIER の両方を変更します。

特に iOS 側は Google Cloud Console でクライアント ID を作成したときの Bundle ID と完全一致している必要があります。不一致だとサインインが失敗します。

Keystore のバックアップを忘れずに

Google Play でアプリを公開したあと、Keystore ファイルを紛失するとアップデートが一切できなくなります。必ず安全な場所にバックアップしてください。

3. App Store への公開

Android に比べると、iOS は準備が多いです。
順番に進めれば詰まらないので、手順通りにやることが大切です。

前提

  • Apple Developer Program(年額 $99 / 約15,000円)への登録が必要
  • Xcode が動く Mac が必要

手順の全体像

① Developer Portal で Bundle ID を登録
        ↓
② App Store Connect でアプリを登録
        ↓
③ Xcode で署名設定
        ↓
④ flutter build ipa でビルド
        ↓
⑤ Xcode Organizer でアップロード
        ↓
⑥ TestFlight でテスト → 審査提出

① Developer Portal で Bundle ID を登録

App Store Connect でアプリを登録しようとすると、Bundle ID のドロップダウンに何も出てこないことがあります。
これは先に Bundle ID を Apple Developer Portal に登録する必要があるためです。

  1. developer.apple.com/account/resources/identifiers/list を開く
  2. 右上の「+」をクリック
  3. 「App IDs」→「App」を選択
  4. Bundle ID:Explicit を選択 → jp.your.app を入力
  5. 「Register」で完了

② App Store Connect でアプリを登録

appstoreconnect.apple.com → 「マイ App」→「+」→「新規 App」

Bundle ID のドロップダウンを開くと、先ほど登録した ID が選択肢に現れます。

③ Xcode で署名設定

Xcode でプロジェクトを開くときは必ず .xcworkspace を開いてください。.xcodeproj を開くと CocoaPods の依存関係が読み込まれません。

open ios/Runner.xcworkspace

左のファイルツリー最上部にある「Runner」(青いアイコン)をクリック → ターゲット「Runner」を選択 → 「Signing & Capabilities」タブ

  • Automatically manage signing にチェック
  • Team で自分の Apple Developer アカウントを選択
  • Bundle Identifier が正しいことを確認

④ IPA ファイルをビルド

flutter build ipa --release

⑤ Xcode Organizer でアップロード

Xcode メニュー → Product → Archive

アーカイブが完了すると Organizer ウィンドウが開きます。
「Distribute App」→「App Store Connect」→「Upload」で App Store Connect にアップロードされます。

⑥ TestFlight → 審査提出

アップロード後、App Store Connect でビルドの処理が完了するまで10〜30分かかります。処理完了後は TestFlight で実機テストができます。問題なければ「審査へ提出」。

4. 開発中に遭遇したその他の問題

Android ANR(アプリが応答しません)が頻発

jsonEncode / jsonDecode を Flutter のメインスレッド(UI スレッド)で実行していたことが原因でした。
単語データが増えるにつれて処理時間が長くなり、Android の ANR 閾値(約5秒)を超えてしまっていました。

解決策は compute() を使ってバックグラウンド Isolate で処理することです。

import 'package:flutter/foundation.dart' show compute;

// トップレベル関数として定義(compute() に渡す関数はトップレベル必須)
List<Entry> _decodeEntries(String raw) {
  final list = jsonDecode(raw) as List<dynamic>;
  return list.map((e) => Entry.fromJson(e as Map<String, dynamic>)).toList();
}

// 呼び出し側
final entries = await compute(_decodeEntries, rawJsonString);

また、複数シートのインポート時に1シートごとにディスク書き込みしていたのも原因の一つでした。全シートの処理が終わったあと、1回だけ書き込むように変更して解決しました。

iOS シミュレータでペーストができない

シミュレータの Edit メニューに「Automatically Sync Pasteboard」という項目があり、チェックを入れればMacのクリップボードと共有されるはずなのですが、特定の Xcode バージョンでは機能しないバグがあります。

「Get Pasteboard」もグレーアウトしていて使えないことがあり、原因の特定に時間を取られました。結論:シミュレータのクリップボード問題はシミュレータのバグなので、実機で確認するのが正解です。
テキストフィールドの長押し → ネイティブペーストは動くことが多いので、開発中はそれで代替できます。

RenderFlex Overflow が繰り返し発生する

Column の中に高さが可変のウィジェットを並べると、画面サイズや他のウィジェットの高さが変わるたびにオーバーフローエラーが出ました。

根本的な原因は「兄弟ウィジェットの高さを手で推定して最大高さを計算していた」ことです。
ウィジェットを追加するたびに推定値がずれてオーバーフローする、という悪循環でした。

解決策は LayoutBuilder を使って実際の残り高さを取得し、オーバーレイパネルを Stack + Positioned で描画することです。

Expanded(
  child: LayoutBuilder(
    builder: (context, constraints) => Stack(
      children: [
        Positioned.fill(child: メインコンテンツ),
        if (パネルを表示したい)
          Positioned(
            top: 0, left: 0, right: 0,
            child: ConstrainedBox(
              // LayoutBuilder から取得した実際の高さを使う
              constraints: BoxConstraints(maxHeight: constraints.maxHeight),
              child: オーバーレイパネル,
            ),
          ),
      ],
    ),
  ),
)

こうすることで、兄弟ウィジェットの高さを推定する必要がなくなり、構造的にオーバーフローが発生しなくなります。

5. Google Search Console でのドメイン所有権確認

Google Cloud Console でドメイン名を含む設定(OAuth の承認済みドメインなど)を行う際、そのドメインの所有権を Google に証明する必要があります。
Search Console でのドメイン確認が済んでいないと Google Cloud Console 側でエラーが表示されることがあります。

Search Console でドメインを追加する

まず Google Search Console 側で操作します。

  1. search.google.com/search-console を開く
  2. 「プロパティを追加」→ 「ドメイン」 を選択(「URL プレフィックス」ではないので注意)
  3. ドメイン名(例:hoge.jp)を入力して「続行」
  4. 表示された TXT レコードの値(google-site-verification=XXXXXXXXXX)をコピーしておく

お名前.com で DNS TXT レコードを追加する

DNS レコードの追加は取得元のレジストラで行います。お名前.com の場合は以下の手順です。

  1. お名前.com Navi にログイン

  2. 「ドメイン」→「ドメイン機能一覧」→「DNS 関連機能の設定」

  3. 対象ドメインを選択 →「次へ」

  4. 「DNS レコード設定を利用する」→「設定する」

  5. 以下の内容を入力して「追加」

    項目
    ホスト名 (空白)
    TYPE TXT
    TTL 3600
    VALUE google-site-verification=XXXXXXXXXX(Search Console でコピーした値)
  6. 「確認画面へ」→「設定する」で保存

DNS の反映には最大 48 時間かかる場合があります(通常は数分〜1 時間程度)。
反映後に Search Console の「確認」ボタンを押すと所有権が証明され、Google Cloud Console のエラーも解消されます。

まとめ

問題 原因 解決策
iOS Google サインイン失敗 iOS 専用クライアント ID が未設定 Info.plist に GIDClientID を追加
Bundle ID が App Store Connect に出ない Developer Portal への登録が先 Identifiers で App ID を登録
Android ANR JSON 処理をメインスレッドで実行 compute() でバックグラウンド処理
Overflow が繰り返し出る 高さを手で推定していた LayoutBuilder で実測値を使う
シミュレータでペーストできない Xcode のバグ 実機でテストする
Google Cloud Console のドメインエラー Search Console での所有権確認が未完了 DNS TXT レコードを追加して確認

Google スプレッドシート連携自体は便利で、PC でデータ管理・スマホで参照という使い方がシームレスにできます。ただし OAuth の設定はプラットフォームごとに異なるため、Android で動いたからといって iOS でそのまま動くとは限りません。

App Store の公開は手順が多いですが、一度やると流れがわかります。
Bundle ID の事前登録さえ忘れなければ、あとは Xcode の自動署名に任せるだけです。

この記事は実際の個人開発プロジェクト の開発過程をもとに書きました。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?