Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
227 changes: 118 additions & 109 deletions app/src/main/java/com/firebaseui/android/demo/HighLevelApiDemoActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import androidx.compose.material3.TextButton
import androidx.compose.material3.TooltipAnchorPosition
import androidx.compose.material3.TooltipBox
import androidx.compose.material3.TooltipDefaults
import androidx.compose.material3.TopAppBarColors
import androidx.compose.material3.rememberTooltipState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
Expand Down Expand Up @@ -70,10 +71,6 @@ class HighLevelApiDemoActivity : ComponentActivity() {
val authUI = FirebaseAuthUI.getInstance()
val emailLink = intent.getStringExtra(EmailLinkConstants.EXTRA_EMAIL_LINK)

val customTheme = AuthUITheme.Default.copy(
providerButtonShape = ShapeDefaults.ExtraLarge
)

class CustomAuthUIStringProvider(
private val defaultProvider: AuthUIStringProvider
) : AuthUIStringProvider by defaultProvider {
Expand All @@ -85,124 +82,136 @@ class HighLevelApiDemoActivity : ComponentActivity() {
val customStringProvider =
CustomAuthUIStringProvider(DefaultAuthUIStringProvider(applicationContext))

val configuration = authUIConfiguration {
context = applicationContext
theme = customTheme
logo = AuthUIAsset.Resource(R.drawable.firebase_auth)
tosUrl = "https://policies.google.com/terms"
privacyPolicyUrl = "https://policies.google.com/privacy"
isAnonymousUpgradeEnabled = false
isMfaEnabled = false
stringProvider = customStringProvider
transitions = AuthUITransitions(
enterTransition = { slideInHorizontally { it } },
exitTransition = { slideOutHorizontally { -it } },
popEnterTransition = { slideInHorizontally { -it } },
popExitTransition = { slideOutHorizontally { it } }
setContent {
val customTheme = AuthUITheme.Adaptive.copy(
providerButtonShape = ShapeDefaults.ExtraLarge,
topAppBarColors = TopAppBarColors(
containerColor = Color(0xFFFFA000),
scrolledContainerColor = Color(0xFFFFA000),
navigationIconContentColor = Color.Black,
titleContentColor = Color.Black,
actionIconContentColor = Color.Black,
subtitleContentColor = Color.Black,
)
)
providers {
provider(AuthProvider.Anonymous)
provider(
AuthProvider.Google(
scopes = listOf("email"),
serverClientId = "406099696497-a12gakvts4epfk5pkio7dphc1anjiggc.apps.googleusercontent.com",
)

val configuration = authUIConfiguration {
context = applicationContext
theme = customTheme
logo = AuthUIAsset.Resource(R.drawable.firebase_auth)
tosUrl = "https://policies.google.com/terms"
privacyPolicyUrl = "https://policies.google.com/privacy"
isAnonymousUpgradeEnabled = false
isMfaEnabled = false
stringProvider = customStringProvider
transitions = AuthUITransitions(
enterTransition = { slideInHorizontally { it } },
exitTransition = { slideOutHorizontally { -it } },
popEnterTransition = { slideInHorizontally { -it } },
popExitTransition = { slideOutHorizontally { it } }
)
provider(
AuthProvider.Email(
isDisplayNameRequired = true,
isEmailLinkForceSameDeviceEnabled = false,
isEmailLinkSignInEnabled = true,
emailLinkActionCodeSettings = actionCodeSettings {
url = "https://flutterfire-e2e-tests.firebaseapp.com"
handleCodeInApp = true
setAndroidPackageName(
"com.firebaseui.android.demo",
true,
null
)
},
isNewAccountsAllowed = true,
minimumPasswordLength = 8,
passwordValidationRules = listOf(
PasswordRule.MinimumLength(8),
PasswordRule.RequireLowercase,
PasswordRule.RequireUppercase,
),
providers {
provider(AuthProvider.Anonymous)
provider(
AuthProvider.Google(
scopes = listOf("email"),
serverClientId = "406099696497-a12gakvts4epfk5pkio7dphc1anjiggc.apps.googleusercontent.com",
)
)
)
provider(
AuthProvider.Phone(
defaultNumber = null,
defaultCountryCode = null,
allowedCountries = emptyList(),
smsCodeLength = 6,
timeout = 120L,
isInstantVerificationEnabled = true
provider(
AuthProvider.Email(
isDisplayNameRequired = true,
isEmailLinkForceSameDeviceEnabled = false,
isEmailLinkSignInEnabled = true,
emailLinkActionCodeSettings = actionCodeSettings {
url = "https://flutterfire-e2e-tests.firebaseapp.com"
handleCodeInApp = true
setAndroidPackageName(
"com.firebaseui.android.demo",
true,
null
)
},
isNewAccountsAllowed = true,
minimumPasswordLength = 8,
passwordValidationRules = listOf(
PasswordRule.MinimumLength(8),
PasswordRule.RequireLowercase,
PasswordRule.RequireUppercase,
),
)
)
)
provider(
AuthProvider.Facebook()
)
provider(
AuthProvider.Twitter(
customParameters = emptyMap()
provider(
AuthProvider.Phone(
defaultNumber = null,
defaultCountryCode = null,
allowedCountries = emptyList(),
smsCodeLength = 6,
timeout = 120L,
isInstantVerificationEnabled = true
)
)
)
provider(
AuthProvider.Apple(
customParameters = emptyMap(),
locale = null
provider(
AuthProvider.Facebook()
)
)
provider(
AuthProvider.Microsoft(
scopes = emptyList(),
tenant = "",
customParameters = emptyMap(),
provider(
AuthProvider.Twitter(
customParameters = emptyMap()
)
)
)
provider(
AuthProvider.Github(
scopes = emptyList(),
customParameters = emptyMap(),
provider(
AuthProvider.Apple(
customParameters = emptyMap(),
locale = null
)
)
)
provider(
AuthProvider.Yahoo(
scopes = emptyList(),
customParameters = emptyMap(),
provider(
AuthProvider.Microsoft(
scopes = emptyList(),
tenant = "",
customParameters = emptyMap(),
)
)
)
provider(
AuthProvider.GenericOAuth(
providerName = "LINE",
providerId = "oidc.line",
scopes = emptyList(),
customParameters = emptyMap(),
buttonLabel = "Sign in with LINE",
buttonIcon = AuthUIAsset.Resource(R.drawable.ic_line_logo_24dp),
buttonColor = Color(0xFF06C755),
contentColor = Color.White
provider(
AuthProvider.Github(
scopes = emptyList(),
customParameters = emptyMap(),
)
)
)
provider(
AuthProvider.GenericOAuth(
providerName = "Discord",
providerId = "oidc.discord",
scopes = emptyList(),
customParameters = emptyMap(),
buttonLabel = "Sign in with Discord",
buttonIcon = AuthUIAsset.Resource(R.drawable.ic_discord_24dp),
buttonColor = Color(0xFF5865F2),
contentColor = Color.White
provider(
AuthProvider.Yahoo(
scopes = emptyList(),
customParameters = emptyMap(),
)
)
)
provider(
AuthProvider.GenericOAuth(
providerName = "LINE",
providerId = "oidc.line",
scopes = emptyList(),
customParameters = emptyMap(),
buttonLabel = "Sign in with LINE",
buttonIcon = AuthUIAsset.Resource(R.drawable.ic_line_logo_24dp),
buttonColor = Color(0xFF06C755),
contentColor = Color.White
)
)
provider(
AuthProvider.GenericOAuth(
providerName = "Discord",
providerId = "oidc.discord",
scopes = emptyList(),
customParameters = emptyMap(),
buttonLabel = "Sign in with Discord",
buttonIcon = AuthUIAsset.Resource(R.drawable.ic_discord_24dp),
buttonColor = Color(0xFF5865F2),
contentColor = Color.White
)
)
}
}
}

setContent {
AuthUITheme {
AuthUITheme(theme = customTheme) {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
Expand Down
24 changes: 24 additions & 0 deletions auth/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1546,6 +1546,30 @@ val configuration = authUIConfiguration {
}
```

### Customizing the Top App Bar

Override the colors used by the top app bar shown on auth screens:

```kotlin
val customTheme = AuthUITheme.Default.copy(
topAppBarColors = TopAppBarColors(
containerColor = Color(0xFF2E7D32),
scrolledContainerColor = Color(0xFF2E7D32),
navigationIconContentColor = Color.White,
titleContentColor = Color.White,
actionIconContentColor = Color.White,
subtitleContentColor = Color.White,
)
)

val configuration = authUIConfiguration {
providers { provider(AuthProvider.Email()) }
theme = customTheme
}
```

If left unset (`null`), the top app bar falls back to colors derived from `colorScheme`'s `primary`/`onPrimary`.

### Screen Transitions

Customize the animations when navigating between screens using the `AuthUITransitions` object:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import androidx.compose.material3.ColorScheme
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Shapes
import androidx.compose.material3.TopAppBarColors
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.Typography
import androidx.compose.material3.darkColorScheme
Expand Down Expand Up @@ -81,6 +82,12 @@ class AuthUITheme(
* ```
*/
val providerButtonShape: Shape? = null,

/**
* Custom colors for the top app bar shown on auth screens. If null, falls back to
* colors derived from [colorScheme] (see [AuthUITheme.topAppBarColors]).
*/
val topAppBarColors: TopAppBarColors? = null,
Comment thread
demolaf marked this conversation as resolved.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LocalAuthUITheme.current.topAppBarColors ?: AuthUITheme.topAppBarColors is copied into all 6 screen files - can we pull that into one resolved property on AuthUITheme instead? Saves updating 6 files next time this logic changes.

) {

/**
Expand All @@ -91,6 +98,7 @@ class AuthUITheme(
* @param shapes The shapes to use. Defaults to this theme's shapes.
* @param providerStyles Custom styling for individual providers. Defaults to this theme's provider styles.
* @param providerButtonShape Default shape for provider buttons. Defaults to this theme's provider button shape.
* @param topAppBarColors Custom top app bar colors. Defaults to this theme's top app bar colors.
* @return A new AuthUITheme instance with the specified properties.
*/
fun copy(
Expand All @@ -99,13 +107,15 @@ class AuthUITheme(
shapes: Shapes = this.shapes,
providerStyles: Map<String, ProviderStyle> = this.providerStyles,
providerButtonShape: Shape? = this.providerButtonShape,
topAppBarColors: TopAppBarColors? = this.topAppBarColors,
): AuthUITheme {
return AuthUITheme(
colorScheme = colorScheme,
typography = typography,
shapes = shapes,
providerStyles = providerStyles,
providerButtonShape = providerButtonShape
providerButtonShape = providerButtonShape,
topAppBarColors = topAppBarColors
)
}

Expand All @@ -118,6 +128,7 @@ class AuthUITheme(
if (shapes != other.shapes) return false
if (providerStyles != other.providerStyles) return false
if (providerButtonShape != other.providerButtonShape) return false
if (topAppBarColors != other.topAppBarColors) return false

return true
}
Expand All @@ -128,12 +139,14 @@ class AuthUITheme(
result = 31 * result + shapes.hashCode()
result = 31 * result + providerStyles.hashCode()
result = 31 * result + (providerButtonShape?.hashCode() ?: 0)
result = 31 * result + (topAppBarColors?.hashCode() ?: 0)
return result
}

override fun toString(): String {
return "AuthUITheme(colorScheme=$colorScheme, typography=$typography, shapes=$shapes, " +
"providerStyles=$providerStyles, providerButtonShape=$providerButtonShape)"
"providerStyles=$providerStyles, providerButtonShape=$providerButtonShape, " +
"topAppBarColors=$topAppBarColors)"
}

/**
Expand Down Expand Up @@ -236,6 +249,15 @@ class AuthUITheme(
titleContentColor = MaterialTheme.colorScheme.onPrimary,
navigationIconContentColor = MaterialTheme.colorScheme.onPrimary,
)

/**
* Resolves the top app bar colors to use for the current [LocalAuthUITheme], falling back
* to [topAppBarColors] when the current theme doesn't specify its own.
*/
@OptIn(ExperimentalMaterial3Api::class)
@get:Composable
val resolvedTopAppBarColors: TopAppBarColors
get() = LocalAuthUITheme.current.topAppBarColors ?: topAppBarColors
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import com.firebase.ui.auth.configuration.AuthUIConfiguration
import com.firebase.ui.auth.configuration.MfaFactor
import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider
import com.firebase.ui.auth.configuration.string_provider.LocalAuthUIStringProvider
import com.firebase.ui.auth.configuration.theme.AuthUITheme
import com.firebase.ui.auth.mfa.MfaEnrollmentContentState
import com.firebase.ui.auth.mfa.MfaEnrollmentStep
import com.firebase.ui.auth.mfa.toMfaErrorMessage
Expand Down Expand Up @@ -253,7 +254,8 @@ private fun SelectFactorUI(
Scaffold(
topBar = {
TopAppBar(
title = { Text(stringProvider.mfaManageFactorsTitle) }
title = { Text(stringProvider.mfaManageFactorsTitle) },
colors = AuthUITheme.resolvedTopAppBarColors
)
}
) { innerPadding ->
Expand Down
Loading