🔒

Jetpack Composeでログイン状態を維持する

2024/05/01に公開

🤔やってみたいこと

Jetpack Comopseで、FirebaseAuthを使用して、ログインした後ログイン状態を維持する機能を実装したい。

こちらの記事の内容を改造する
https://zenn.dev/jboy_blog/articles/811bb8941d13e9

公式によると、ユーザーの情報を取得すればトークンを取得することができ、ログインの維持をするのを実現することができます。
https://firebase.google.com/docs/auth/android/manage-users?hl=ja#get_the_currently_signed-in_user

現在ログインしているユーザーを取得するには、getCurrentUser メソッドを呼び出すことをおすすめします。ユーザーがログインしていない場合、getCurrentUser は null を返します。

val user = Firebase.auth.currentUser
if (user != null) {
    // User is signed in
} else {
    // No user is signed in
}

状態を管理する変数を用意して、認証状態を判定するのに使う。

var isLoggedIn by remember { mutableStateOf(FirebaseAuth.getInstance().currentUser != null) }

🚀やってみたこと

build.gradle.ktsに対応した設定をして機能実装するとログインの維持ができた。

プロジェクトレベルのbuild.gradle.kts

// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
    id("com.android.application") version "8.2.2" apply false
    id("org.jetbrains.kotlin.android") version "1.9.0" apply false
    // add firebase crashlytics
    id("com.google.gms.google-services") version "4.4.1" apply false
    id("com.google.firebase.crashlytics") version "2.9.9" apply false
}

Firebaseのライブラリが複数入ってますが、FirebaseAuthに関係したものだけで大丈夫です。

モジュールレベルのbuild.gradle.kts
plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("com.google.gms.google-services")
    id("com.google.firebase.crashlytics")
}

android {
    namespace = "com.junichi.yourbalance"
    compileSdk = 34

    defaultConfig {
        applicationId = "com.junichi.yourbalance"
        minSdk = 24
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables {
            useSupportLibrary = true
        }
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = "1.8"
    }
    buildFeatures {
        compose = true
    }
    composeOptions {
        kotlinCompilerExtensionVersion = "1.5.1"
    }
    packaging {
        resources {
            excludes += "/META-INF/{AL2.0,LGPL2.1}"
        }
    }
}

dependencies {

    implementation("androidx.core:core-ktx:1.13.0")
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
    implementation("androidx.activity:activity-compose:1.9.0")
    implementation(platform("androidx.compose:compose-bom:2023.08.00"))
    implementation("androidx.compose.ui:ui")
    implementation("androidx.compose.ui:ui-graphics")
    implementation("androidx.compose.ui:ui-tooling-preview")
    implementation("androidx.compose.material3:material3")
    testImplementation("junit:junit:4.13.2")
    androidTestImplementation("androidx.test.ext:junit:1.1.5")
    androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
    androidTestImplementation(platform("androidx.compose:compose-bom:2023.08.00"))
    androidTestImplementation("androidx.compose.ui:ui-test-junit4")
    debugImplementation("androidx.compose.ui:ui-tooling")
    debugImplementation("androidx.compose.ui:ui-test-manifest")
    // navigation library
    val nav_version = "2.7.7"

    implementation("androidx.navigation:navigation-compose:$nav_version")
    // Firebase libraries
    implementation(platform("com.google.firebase:firebase-bom:32.3.1"))

    implementation("com.google.firebase:firebase-auth-ktx")
    implementation("com.google.firebase:firebase-firestore")
    implementation("com.google.firebase:firebase-analytics-ktx")
    implementation("com.google.firebase:firebase-crashlytics")
}

こちらがアプリのコード

サンプルコード
package com.junichi.yourbalance

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import com.google.firebase.FirebaseApp
import com.google.firebase.auth.FirebaseAuth
import com.junichi.yourbalance.ui.theme.YourBalanceTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Firebaseの初期化
        FirebaseApp.initializeApp(this)

        setContent {
            YourBalanceTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    var isLoggedIn by remember { mutableStateOf(FirebaseAuth.getInstance().currentUser != null) }

                    if (isLoggedIn) {
                        WelcomePage(onLogout = {
                            isLoggedIn = false
                        })
                    } else {
                        LoginPage(onLoginSuccess = {
                            isLoggedIn = true
                        })
                    }
                }
            }
        }
    }
}

@Composable
fun LoginPage(onLoginSuccess: () -> Unit) {
    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Button(onClick = { performAnonymousLogin(onLoginSuccess) }) {
            Text("あなたの選択肢を選ぶ")
        }
    }
}

private fun performAnonymousLogin(onLoginSuccess: () -> Unit) {
    FirebaseAuth.getInstance().signInAnonymously()
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                onLoginSuccess()
            } else {
                // ログインできなかった場合の処理。今回はログを出す
                println("ログインできませんでした")
            }
        }
}

@Composable
fun WelcomePage(onLogout: () -> Unit) {
    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text("ようこそ!")
        Button(onClick = { onLogout() }) {
            Text("ログアウト")
        }
    }
}

🙂最後に

今回は、Jetpack Composeで、FirebaseAuthのログインを維持する機能を実装してみました。途中でエラー連発して詰まりましたが、なんとか動くものを作れました。
気になったのは、匿名認証したユーザーのアカウントは1個しか作れなくて、もし削除するとまたアカウントを作れないようです?

Discussion