Skip to content

Commit 8e28099

Browse files
committed
feat: AuthProviderButton for AuthMethodPicker
1 parent 29a443f commit 8e28099

File tree

7 files changed

+356
-13
lines changed

7 files changed

+356
-13
lines changed
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
package com.firebase.ui.auth.compose
2+
3+
import androidx.compose.foundation.Image
4+
import androidx.compose.foundation.layout.Arrangement
5+
import androidx.compose.foundation.layout.Column
6+
import androidx.compose.foundation.layout.Row
7+
import androidx.compose.foundation.layout.Spacer
8+
import androidx.compose.foundation.layout.fillMaxSize
9+
import androidx.compose.foundation.layout.width
10+
import androidx.compose.material.icons.Icons
11+
import androidx.compose.material.icons.filled.Star
12+
import androidx.compose.material3.Button
13+
import androidx.compose.material3.ButtonDefaults
14+
import androidx.compose.material3.Text
15+
import androidx.compose.runtime.Composable
16+
import androidx.compose.ui.Alignment
17+
import androidx.compose.ui.Modifier
18+
import androidx.compose.ui.graphics.Color
19+
import androidx.compose.ui.platform.LocalContext
20+
import androidx.compose.ui.tooling.preview.Preview
21+
import androidx.compose.ui.unit.dp
22+
import com.firebase.ui.auth.compose.configuration.AuthProvider
23+
import com.firebase.ui.auth.compose.configuration.Provider
24+
import com.firebase.ui.auth.compose.configuration.stringprovider.AuthUIStringProvider
25+
import com.firebase.ui.auth.compose.configuration.stringprovider.DefaultAuthUIStringProvider
26+
import com.firebase.ui.auth.compose.configuration.theme.AuthUIAsset
27+
import com.firebase.ui.auth.compose.configuration.theme.AuthUITheme
28+
29+
/**
30+
* A customizable button for an authentication provider.
31+
*
32+
* This button displays the icon and name of an authentication provider (e.g., Google, Facebook).
33+
* It is designed to be used within a list of sign-in options. The button's appearance can be
34+
* customized using the [style] parameter, and its text is localized via the [stringProvider].
35+
*
36+
* **Example usage:**
37+
* ```kotlin
38+
* AuthProviderButton(
39+
* provider = AuthProvider.Facebook(),
40+
* onClick = { /* Handle Facebook sign-in */ },
41+
* stringProvider = DefaultAuthUIStringProvider(LocalContext.current)
42+
* )
43+
* ```
44+
*
45+
* @param modifier A modifier for the button
46+
* @param provider The provider to represent.
47+
* @param onClick A callback when the button is clicked
48+
* @param enabled If the button is enabled. Defaults to true.
49+
* @param style Optional custom styling for the button.
50+
* @param stringProvider The [AuthUIStringProvider] for localized strings
51+
*
52+
* @since 10.0.0
53+
*/
54+
@Composable
55+
fun AuthProviderButton(
56+
modifier: Modifier = Modifier,
57+
provider: AuthProvider,
58+
onClick: () -> Unit,
59+
enabled: Boolean = true,
60+
style: AuthUITheme.ProviderStyle? = null,
61+
stringProvider: AuthUIStringProvider,
62+
) {
63+
val providerStyle = resolveProviderStyle(provider, style)
64+
val providerText = resolveProviderLabel(provider, stringProvider)
65+
66+
Button(
67+
modifier = modifier,
68+
colors = ButtonDefaults.buttonColors(
69+
containerColor = providerStyle.backgroundColor,
70+
contentColor = providerStyle.contentColor,
71+
),
72+
onClick = onClick,
73+
enabled = enabled,
74+
) {
75+
Row(
76+
verticalAlignment = Alignment.CenterVertically
77+
) {
78+
Image(
79+
painter = providerStyle.icon.painter,
80+
contentDescription = providerText
81+
)
82+
Spacer(modifier = Modifier.width(8.dp))
83+
Text(
84+
text = providerText
85+
)
86+
}
87+
}
88+
}
89+
90+
private fun resolveProviderStyle(
91+
provider: AuthProvider,
92+
style: AuthUITheme.ProviderStyle?,
93+
): AuthUITheme.ProviderStyle {
94+
if (style != null) return style
95+
96+
val defaultStyle = requireNotNull(
97+
AuthUITheme.Default.providerStyles[provider.providerId]
98+
) { "No default style found for provider: ${provider.providerId}" }
99+
100+
return if (provider is AuthProvider.GenericOAuth) {
101+
AuthUITheme.ProviderStyle(
102+
icon = provider.buttonIcon ?: defaultStyle.icon,
103+
backgroundColor = provider.buttonColor ?: defaultStyle.backgroundColor,
104+
contentColor = provider.contentColor ?: defaultStyle.contentColor,
105+
)
106+
} else {
107+
defaultStyle
108+
}
109+
}
110+
111+
private fun resolveProviderLabel(
112+
provider: AuthProvider,
113+
stringProvider: AuthUIStringProvider
114+
): String = when (provider) {
115+
is AuthProvider.GenericOAuth -> provider.buttonLabel
116+
else -> when (Provider.fromId(provider.providerId)) {
117+
Provider.GOOGLE -> stringProvider.signInWithGoogle
118+
Provider.FACEBOOK -> stringProvider.signInWithFacebook
119+
Provider.TWITTER -> stringProvider.signInWithTwitter
120+
Provider.GITHUB -> stringProvider.signInWithGithub
121+
Provider.EMAIL -> stringProvider.signInWithEmail
122+
Provider.PHONE -> stringProvider.signInWithPhone
123+
Provider.ANONYMOUS -> stringProvider.signInAnonymously
124+
Provider.MICROSOFT -> stringProvider.signInWithMicrosoft
125+
Provider.YAHOO -> stringProvider.signInWithYahoo
126+
Provider.APPLE -> stringProvider.signInWithApple
127+
null -> error("Unknown provider: ${provider.providerId}")
128+
}
129+
}
130+
131+
@Preview(showBackground = true)
132+
@Composable
133+
private fun PreviewAuthProviderButton() {
134+
val context = LocalContext.current
135+
Column(
136+
modifier = Modifier
137+
.fillMaxSize(),
138+
verticalArrangement = Arrangement.Center,
139+
horizontalAlignment = Alignment.CenterHorizontally
140+
) {
141+
AuthProviderButton(
142+
provider = AuthProvider.Email(
143+
actionCodeSettings = null,
144+
passwordValidationRules = emptyList()
145+
),
146+
onClick = {},
147+
stringProvider = DefaultAuthUIStringProvider(context)
148+
)
149+
AuthProviderButton(
150+
provider = AuthProvider.Phone(
151+
defaultNumber = null,
152+
defaultCountryCode = null,
153+
allowedCountries = null,
154+
),
155+
onClick = {},
156+
stringProvider = DefaultAuthUIStringProvider(context)
157+
)
158+
AuthProviderButton(
159+
provider = AuthProvider.Google(
160+
scopes = emptyList(),
161+
serverClientId = null
162+
),
163+
onClick = {},
164+
stringProvider = DefaultAuthUIStringProvider(context)
165+
)
166+
AuthProviderButton(
167+
provider = AuthProvider.Facebook(),
168+
onClick = {},
169+
stringProvider = DefaultAuthUIStringProvider(context)
170+
)
171+
AuthProviderButton(
172+
provider = AuthProvider.Twitter(
173+
customParameters = emptyMap()
174+
),
175+
onClick = {},
176+
stringProvider = DefaultAuthUIStringProvider(context)
177+
)
178+
AuthProviderButton(
179+
provider = AuthProvider.Github(
180+
customParameters = emptyMap()
181+
),
182+
onClick = {},
183+
stringProvider = DefaultAuthUIStringProvider(context)
184+
)
185+
AuthProviderButton(
186+
provider = AuthProvider.Microsoft(
187+
tenant = null,
188+
customParameters = emptyMap()
189+
),
190+
onClick = {},
191+
stringProvider = DefaultAuthUIStringProvider(context)
192+
)
193+
AuthProviderButton(
194+
provider = AuthProvider.Yahoo(
195+
customParameters = emptyMap()
196+
),
197+
onClick = {},
198+
stringProvider = DefaultAuthUIStringProvider(context)
199+
)
200+
AuthProviderButton(
201+
provider = AuthProvider.Apple(
202+
locale = null,
203+
customParameters = emptyMap()
204+
),
205+
onClick = {},
206+
stringProvider = DefaultAuthUIStringProvider(context)
207+
)
208+
AuthProviderButton(
209+
provider = AuthProvider.Anonymous,
210+
onClick = {},
211+
stringProvider = DefaultAuthUIStringProvider(context)
212+
)
213+
AuthProviderButton(
214+
provider = AuthProvider.GenericOAuth(
215+
providerId = "google.com",
216+
scopes = emptyList(),
217+
customParameters = emptyMap(),
218+
buttonLabel = "Sign in with Generic",
219+
buttonIcon = AuthUIAsset.Vector(Icons.Default.Star),
220+
buttonColor = Color.Gray,
221+
contentColor = Color.White
222+
),
223+
onClick = {},
224+
stringProvider = DefaultAuthUIStringProvider(context)
225+
)
226+
AuthProviderButton(
227+
provider = AuthProvider.Google(
228+
scopes = emptyList(),
229+
serverClientId = null
230+
),
231+
onClick = {},
232+
style = AuthUITheme.Default.providerStyles[Provider.MICROSOFT.id],
233+
stringProvider = DefaultAuthUIStringProvider(context)
234+
)
235+
}
236+
}

auth/src/main/java/com/firebase/ui/auth/compose/configuration/AuthProvider.kt

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,10 @@
1515
package com.firebase.ui.auth.compose.configuration
1616

1717
import android.content.Context
18-
import android.graphics.Color
18+
import androidx.compose.ui.graphics.Color
1919
import android.util.Log
20-
import androidx.compose.ui.graphics.vector.ImageVector
21-
import com.firebase.ui.auth.AuthUI
2220
import com.firebase.ui.auth.R
21+
import com.firebase.ui.auth.compose.configuration.theme.AuthUIAsset
2322
import com.firebase.ui.auth.util.Preconditions
2423
import com.firebase.ui.auth.util.data.PhoneNumberUtils
2524
import com.firebase.ui.auth.util.data.ProviderAvailability
@@ -55,7 +54,13 @@ internal enum class Provider(val id: String) {
5554
ANONYMOUS("anonymous"),
5655
MICROSOFT("microsoft.com"),
5756
YAHOO("yahoo.com"),
58-
APPLE("apple.com"),
57+
APPLE("apple.com");
58+
59+
companion object {
60+
fun fromId(id: String): Provider? {
61+
return entries.find { it.id == id }
62+
}
63+
}
5964
}
6065

6166
/**
@@ -446,12 +451,17 @@ abstract class AuthProvider(open val providerId: String) {
446451
/**
447452
* An optional icon for the provider button.
448453
*/
449-
val buttonIcon: ImageVector?,
454+
val buttonIcon: AuthUIAsset?,
450455

451456
/**
452457
* An optional background color for the provider button.
453458
*/
454-
val buttonColor: Color?
459+
val buttonColor: Color?,
460+
461+
/**
462+
* An optional content color for the provider button.
463+
*/
464+
val contentColor: Color?
455465
) : OAuthProvider(
456466
providerId = providerId,
457467
scopes = scopes,
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright 2025 Google Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the
10+
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
* express or implied. See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
15+
package com.firebase.ui.auth.compose.configuration.theme
16+
17+
import androidx.annotation.DrawableRes
18+
import androidx.compose.runtime.Composable
19+
import androidx.compose.ui.graphics.painter.Painter
20+
import androidx.compose.ui.graphics.vector.ImageVector
21+
import androidx.compose.ui.graphics.vector.rememberVectorPainter
22+
import androidx.compose.ui.res.painterResource
23+
24+
/**
25+
* Represents a visual asset used in the authentication UI.
26+
*
27+
* This sealed class allows specifying icons and images from either Android drawable
28+
* resources ([Resource]) or Jetpack Compose [ImageVector]s ([Vector]). The [painter]
29+
* property provides a unified way to get a [Painter] for the asset within a composable.
30+
*
31+
* **Example usage:**
32+
* ```kotlin
33+
* // To use a drawable resource:
34+
* val asset = AuthUIAsset.Resource(R.drawable.my_logo)
35+
*
36+
* // To use a vector asset:
37+
* val vectorAsset = AuthUIAsset.Vector(Icons.Default.Info)
38+
* ```
39+
*/
40+
sealed class AuthUIAsset {
41+
/**
42+
* An asset loaded from a drawable resource.
43+
*
44+
* @param resId The resource ID of the drawable (e.g., `R.drawable.my_icon`).
45+
*/
46+
class Resource(@param:DrawableRes val resId: Int) : AuthUIAsset()
47+
48+
/**
49+
* An asset represented by an [ImageVector].
50+
*
51+
* @param image The [ImageVector] to be displayed.
52+
*/
53+
class Vector(val image: ImageVector) : AuthUIAsset()
54+
55+
/**
56+
* A [Painter] that can be used to draw this asset in a composable.
57+
*
58+
* This property automatically resolves the asset type and returns the appropriate
59+
* [Painter] for rendering.
60+
*/
61+
@get:Composable
62+
internal val painter: Painter
63+
get() = when (this) {
64+
is Resource -> painterResource(resId)
65+
is Vector -> rememberVectorPainter(image)
66+
}
67+
}

auth/src/main/java/com/firebase/ui/auth/compose/configuration/theme/AuthUITheme.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ class AuthUITheme(
5656
* provider button, allowing for per-provider branding and customization.
5757
*/
5858
class ProviderStyle(
59+
/**
60+
* The provider's icon.
61+
*/
62+
val icon: AuthUIAsset,
63+
5964
/**
6065
* The background color of the button.
6166
*/
@@ -112,7 +117,6 @@ class AuthUITheme(
112117
providerStyles = providerStyles
113118
)
114119
}
115-
116120
}
117121
}
118122

0 commit comments

Comments
 (0)