🔰基礎技術

【Flutter実践】Androidアプリにログイン機能をつける|Firebase Auth(Googleログイン)の実装手順を解説

前回の記事で、リストを表示できる「習慣トラッカー」の見た目が完成しました。
しかし今のままでは、アプリを再起動するとデータが全部消えてしまいます。

データをクラウド(次回解説するFirestore)に保存するためには、まず「これ誰のデータ?」を特定する必要があります。
つまり、ログイン機能(Authentication)の実装です。

今回は、以前Webアプリ編で解説したFirebase Authenticationを使って、AndroidアプリにGoogleログイン機能を組み込みます。

1. Webとモバイル、認証の仕組みはどう違う?

基本原理は同じですが、実装のアプローチが少し違います。

  • Web (Next.js): APIキーを裏側(環境変数)に隠して使いました。
  • モバイル (Android): アプリ自体がユーザーの端末にダウンロードされるため、APIキーを完全に隠すことは不可能です。

「えっ、キーがバレたら危険じゃない?」
その通りです。だからこそモバイル開発では、「キーがバレても、そのアプリ(あなたの署名が入ったアプリ)からしか使えないようにする」という制限をかけます。

この認証に使うのが「SHA-1(シャーワン)フィンガープリント」という証明書情報です。
今回はここが一番の山場になります。

2. Android特有の「設定ファイル」たち

作業に入る前に、今回触るファイルの役割を整理しておきましょう。

ファイル名役割
android/app/google-services.json【最重要】Firebaseの接続情報(APIキーなど)が書かれた設定ファイル。パスポートのようなもの。
android/build.gradleプロジェクト全体のビルド設定。「Googleのサービスを使うよ」と宣言する場所。
android/app/build.gradleアプリ単体のビルド設定。「Googleの設定ファイルを読み込んでね」と指示する場所。

3. 【準備】Firebaseコンソールでの設定

Step 1: Androidアプリを追加する

  1. Web編で作ったFirebaseプロジェクトを開きます。
  2. 「アプリを追加」ボタンを押し、Androidアイコンをクリックします。
  3. Android パッケージ名を入力します。
    ※前回 flutter create した時の名前(android/app/build.gradleapplicationId に書いてあります。例: tech.simplekits.habit_tracker)。

Step 2: SHA-1フィンガープリントを登録する

Googleログインを使う場合、ここが必須です。
VS Codeのターミナルで以下のコマンドを実行してください。

cd android
./gradlew signingReport

ずらっと文字が出ますが、Task :app:signingReport の下にある SHA1: XX:XX:XX... という文字列を探してコピーし、Firebaseコンソールの「デバッグ用の署名証明書 SHA-1」の欄に貼り付けて「アプリを登録」を押します。

Step 3: 設定ファイルを配置する

  1. Firebaseコンソールから google-services.json をダウンロードします。
  2. VS Codeに戻り、ダウンロードしたファイルを android/app/ フォルダの中に置きます。

💡 Gitコミットについて:
Web開発(Next.jsなど)ではAPIキーを含むファイル(.envなど)をGitに上げるのはタブーですが、モバイルアプリ開発の google-services.json は、プライベートリポジトリにコミットするケースもあります。

基本的にモバイルアプリでは「キーを隠す」だけはなく「キーにロックを掛ける」という対策もセットで行います。

⚠️ 重要:APIキーは「隠せない」前提で守る

この google-services.json はアプリ内部に埋め込まれて配布されるため、詳しい人が解析すれば中のAPIキーを取り出すことは可能です(隠せません)。

そのため、本番リリース時には必ず以下の対策が必要になります。

  • Firebaseコンソールでの設定(さっきやった作業):
    「Googleログイン」を使えるようにするための許可証です。APIキー自体は守られていません。
  • Google Cloud Consoleでの設定:
    APIキーの設定画面で「このキーは、私のアプリ(登録したSHA-1)からしか使えない」という制限(Application Restriction)をかけます。

この「Google Cloud側での制限」をかけることで、万が一キーが漏れても、他人のアプリからは勝手に使われない(APIを蹴れない)状態になります。
※今回は開発段階なので一旦このまま進めますが、ストア公開前には必ず「APIキーの制限」を行いましょう。

4. 【実装】Flutterコードの修正

必要なパッケージ(拡張機能)をインストールします。
ターミナルで android フォルダから戻るのを忘れずに(cd ..)。

flutter pub add firebase_core firebase_auth google_sign_in

次に、lib/main.dart を大幅に書き換えます。
Web開発者がやりがちな「トークンを手動で保存する」処理は不要です。Firebase SDKが勝手にやってくれます。

import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';

// メイン関数を非同期(async)にしてFirebaseを初期化
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(); // これがないと動きません!
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Habit Tracker',
      theme: ThemeData(
        primarySwatch: Colors.indigo,
        useMaterial3: true,
      ),
      // ログイン状態を監視して画面を切り替える
      home: StreamBuilder<User?>(
        stream: FirebaseAuth.instance.authStateChanges(),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(child: CircularProgressIndicator());
          }
          if (snapshot.hasData) {
            // ログイン済みならリスト画面へ
            return const HabitListScreen();
          }
          // 未ログインならログイン画面へ
          return const LoginScreen();
        },
      ),
    );
  }
}

// ▼ ログイン画面
class LoginScreen extends StatelessWidget {
  const LoginScreen({super.key});

  Future<void> _signInWithGoogle() async {
    try {
      // 1. Googleでログインを開始
      final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();
      if (googleUser == null) return; // キャンセルされた場合

      // 2. 認証情報を取得
      final GoogleSignInAuthentication googleAuth = await googleUser.authentication;

      // 3. Firebase用のクレデンシャルを作成
      final credential = GoogleAuthProvider.credential(
        accessToken: googleAuth.accessToken,
        idToken: googleAuth.idToken,
      );

      // 4. Firebaseにサインイン(これで完了!)
      await FirebaseAuth.instance.signInWithCredential(credential);
    } catch (e) {
      print(e); // エラー処理は本来ちゃんとやるべき
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('習慣トラッカーへようこそ', style: TextStyle(fontSize: 20)),
            const SizedBox(height: 30),
            ElevatedButton.icon(
              icon: const Icon(Icons.login),
              label: const Text('Googleでログイン'),
              onPressed: _signInWithGoogle,
            ),
          ],
        ),
      ),
    );
  }
}

// ▼ 習慣リスト画面(前回作成したもの+ログアウト機能)
class HabitListScreen extends StatefulWidget {
  const HabitListScreen({super.key});
  @override
  State<HabitListScreen> createState() => _HabitListScreenState();
}

class _HabitListScreenState extends State<HabitListScreen> {
  final List<String> _habits = []; // ※まだローカル変数です

  @override
  Widget build(BuildContext context) {
    final user = FirebaseAuth.instance.currentUser; // 現在のユーザー情報

    return Scaffold(
      appBar: AppBar(
        title: Text('${user?.displayName ?? "ゲスト"}の習慣'),
        actions: [
          IconButton(
            icon: const Icon(Icons.logout),
            onPressed: () async {
              await FirebaseAuth.instance.signOut(); // これだけでログアウト完了
            },
          )
        ],
      ),
      body: const Center(child: Text('リストは次回DB連携で実装します')),
      // ... その他のUIコードは前回と同じなので省略 ...
    );
  }
}

5. 動作確認

設定ファイルをいじったので、一度アプリを完全に停止してから、再度デバッグ実行(F5)してください。
ビルドに少し時間がかかります。

  1. アプリが起動すると「ログイン画面」が表示されます。
  2. 「Googleでログイン」を押すと、エミュレーター内のGoogleアカウント選択画面が出ます。
  3. アカウントを選ぶと、画面が「習慣リスト」に切り替わります。
  4. 【ここが重要】 アプリを一度終了(タスクキル)して、もう一度起動してみてください。
    ログイン画面には戻らず、いきなり「習慣リスト」が表示されるはずです。

まとめ:認証状態はSDKに任せよう

お疲れ様でした!これでアプリに「鍵」がかかりました。
ポイントは以下の通りです。

  • APIキー: 隠すのではなく、SHA-1で利用元を制限する(のが本番の流儀)。
  • ログイン維持: SharedPreferences などに自力で保存しない。FirebaseAuth.instance.authStateChanges() を信じる。

ユーザーを特定できたので、次はいよいよ「ユーザーごとのデータ」をクラウド(Firestore)に保存します。
スマホを変えてもデータが消えないアプリまで、あと一歩です!

-🔰基礎技術