Firebaseui-android: [Feature Request] Jetpack Compose Support for AuthUI

Created on 28 Feb 2021  路  9Comments  路  Source: firebase/FirebaseUI-Android

I haven't worked with other FirebaseUI libraries, but I reckon that it'll be a feature request for most libraries :) I'm thinking that AuthUI could be a good place to start. Now that Compose is in beta, it'd be super awesome to provide experimental APIs for that.

Something like this:

@Composable
fun AppLoginScreen() {
    val navController = rememberNavController()
    FirebaseLoginScreen(
        onAuthenticated = { navController.navigate(...) }
    ) { authenticationMethods ->
        Column {
            Image(painterResource(R.drawable.bg_onboarding), contentDescription = null)
            Spacer(Modifier.height(32.dp))
            Text(stringResource(R.string.onboarding_title), style = titleTextStyle)
            Text(stringResource(R.string.onboarding_subtitle))
            authenticationMethods.forEach { method ->
                LoginButton(stringResource(method.name)) { requestAuthentication(method) }
            }
        }
    }
}

// API

@Composable
fun FirebaseLoginScreen(
    onAuthenticated: (response: IdpResponse) -> Unit,
    content: @Composable FirebaseLoginScreenScope.(
        authenticationMethods: List<AuthUI.IdpConfig>,
    ) -> Unit,
) {
    val authenticationMethods = listOf<AuthUI.IdpConfig>()
    FirebaseLoginScreenScope().content(authenticationMethods)
}

class FirebaseLoginScreenScope {

    fun requestAuthentication(method: AuthUI.IdpConfig) {
        ...
    }
}

I'd be super happy to contribute here if that's something you'd accept contributions for.

auth feature request

Most helpful comment

I think a separate module sounds like a good idea for this, if you can make it work without making the build too complex! I have a feeling you two can figure this out ... I don't know half of the gradle magic that you do.

It's probably easiest if you two use a shared fork of this project for your experiments but if that turns out to be a pain I could add you both as collaborators here and create a new experimental branch with no restrictions for you two to use as a playground. Let me know if you need that!

All 9 comments

@jossiwolf this is super interesting! So would this primarily be a replacement for the "custom XML layout" feature we have now? Or would it be something deeper.

I have only recently started playing with Compose and I have not at all begun to consider how FirebaseUI should work with compose.

It would be something deeper!

Let's consider two cases:

  1. Authentication with default layout
  2. Authentication with custom layout

Case 1 works with Jetpack Compose when using the Activity Result APIs (there's a registerActivityForResult composable introduced in androidx.activity:activity-ktx 1.3.0-alpha03: https://android-review.googlesource.com/c/platform/frameworks/support/+/1569321) which is pretty cool. I think it already works today when using ActivityResultContracts.StartActivityForResult as contract type, but a bit more manual work is required.
Here's what it could look like using Compose once we added official support for the Activity Result APIs:

@Composable
fun LoginScreen(navController: NavController) {
    val firebaseLogin = registerForActivityResult(FirebaseAuthUIActivityResult()) { result ->
        if (result.resultCode == Activity.RESULT_OK) {
            navController.navigate(NavDestinations.Home)
        }
    }
    // Run this every composition (which should be once in our case)
    SideEffect {
        firebaseLogin.launch(...)
    }
}

So custom layouts are the primary motivation for this, but given the prototype mentioned above we could provide a default value for the content:

@Composable
fun FirebaseLoginScreen(
    onAuthenticated: (response: IdpResponse) -> Unit,
    content: @Composable FirebaseLoginScreenScope.(
        authenticationMethods: List<AuthUI.IdpConfig>,
    ) -> Unit = { DefaultLoginScreenContent(...) },
) { ... }

@Composable
private fun FirebaseLoginScreenScope.DefaultLoginScreenContent(
    authenticationMethods: List<AuthUI.IdpConfig>
) {
   // Our default UI implementation
}

With that, the FirebaseLoginScreen composable could be used as the main entry point for auth from the AuthUI library for both custom and default layout. (I think that's pretty dope!)

@Composable
fun MyLoginScreen(navController: NavController) {
    FirebaseLoginScreen(onAuthenticated = { navController.navigate(...) })
}

Obvs this is all quite early stage and requires a bit of infra, but could be pretty cool to experiment with.

@jossiwolf that all sounds super cool! Almost definitely an 8.0.0 feature because I think we'd be moving the codebase to Kotlin but I am not opposed to making that sort of breaking change later in the year, specifically once Jetpack Compose is GA

Perfect! Sounds like syncing that would be great. What do you think of adding an AuthUI-Compose module with support for Compose?

@jossiwolf no strong feelings either way on making a separate module. It will probably make our lives a bit harder because we'd need to change the visibility of some FirebaseUI internals to make them available across modules but it may be the right thing to do as far as separating dependencies.

@jossiwolf Thank you so much for opening this!! I had already thought of a way to write firestore-compose and database-compose modules for FirebaseUI but the auth module was a big question mark in my mind, so seeing this makes me really happy!

I actually went ahead and wrote a library to show what I had in mind for firestore: https://github.com/rosariopfernandes/firebase-compose. In short, I created a custom State (named it CollectionState) that takes a Firestore Query as parameter and the state's value is a sealed class containing either the list or the error returned from that query in realtime. You can see its usage example here.

Would love to hear your thoughts on it

@rosariopfernandes Whoa, that looks super super cool! I think this is incredibly useful and the API is nice. Once I get to play with infra here a bit, let's sync and see if we can join forces for this. I'm pretty sure we'll have to make some internal adjustments and open up some APIs as Sam mentioned so I'm not sure if I can work in a fork of your repo, but I'm sure we'll find something cool!

@samtstern Talking about separate module vs using the same module: Using a separate module would enable us to publish it as an "independent" artefact and not force the main module to AGP 7 before Compose is stable. I think it would be great to provide early access to the Compose version of this to get feedback, the best way is probably by making it easy to try out. I'll experiment starting with a separate module!

@jossiwolf Instead of forking my repo, we could simply create a separate module here for firestore (if @samtstern agrees) and start doing experiments the same way you'll do with auth.

I'm not thinking about RTDB yet so that we can focus on the ones we're kickstarting with (auth and firestore), but once we get to it the approach should be similar to firestore.

I think a separate module sounds like a good idea for this, if you can make it work without making the build too complex! I have a feeling you two can figure this out ... I don't know half of the gradle magic that you do.

It's probably easiest if you two use a shared fork of this project for your experiments but if that turns out to be a pain I could add you both as collaborators here and create a new experimental branch with no restrictions for you two to use as a playground. Let me know if you need that!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ghost picture ghost  路  5Comments

judeebene picture judeebene  路  4Comments

ozican picture ozican  路  6Comments

s-p-a-r-k picture s-p-a-r-k  路  5Comments

RedCider picture RedCider  路  5Comments