Skip to content

Commit a840716

Browse files
committed
feat: AuthTextField with validation
1 parent 4c3a59b commit a840716

File tree

1 file changed

+193
-0
lines changed

1 file changed

+193
-0
lines changed
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
package com.firebase.ui.auth.compose
2+
3+
import androidx.compose.foundation.layout.Arrangement
4+
import androidx.compose.foundation.layout.Column
5+
import androidx.compose.foundation.layout.Spacer
6+
import androidx.compose.foundation.layout.fillMaxSize
7+
import androidx.compose.foundation.layout.height
8+
import androidx.compose.foundation.text.KeyboardActions
9+
import androidx.compose.foundation.text.KeyboardOptions
10+
import androidx.compose.material.icons.Icons
11+
import androidx.compose.material.icons.filled.Email
12+
import androidx.compose.material.icons.filled.Lock
13+
import androidx.compose.material.icons.filled.Visibility
14+
import androidx.compose.material.icons.filled.VisibilityOff
15+
import androidx.compose.material3.Icon
16+
import androidx.compose.material3.IconButton
17+
import androidx.compose.material3.Text
18+
import androidx.compose.material3.TextField
19+
import androidx.compose.runtime.Composable
20+
import androidx.compose.runtime.getValue
21+
import androidx.compose.runtime.mutableStateOf
22+
import androidx.compose.runtime.remember
23+
import androidx.compose.runtime.setValue
24+
import androidx.compose.ui.Alignment
25+
import androidx.compose.ui.Modifier
26+
import androidx.compose.ui.platform.LocalContext
27+
import androidx.compose.ui.text.input.PasswordVisualTransformation
28+
import androidx.compose.ui.text.input.VisualTransformation
29+
import androidx.compose.ui.tooling.preview.Preview
30+
import androidx.compose.ui.unit.dp
31+
import com.firebase.ui.auth.compose.configuration.PasswordRule
32+
import com.firebase.ui.auth.compose.configuration.string_provider.DefaultAuthUIStringProvider
33+
import com.firebase.ui.auth.compose.configuration.validators.EmailValidator
34+
import com.firebase.ui.auth.compose.configuration.validators.FieldValidator
35+
import com.firebase.ui.auth.compose.configuration.validators.PasswordValidator
36+
37+
/**
38+
* A customizable input field with built-in validation display.
39+
*
40+
* **Example usage:**
41+
* ```kotlin
42+
* AuthTextField(
43+
* value = email,
44+
* onValueChange = { email = it },
45+
* label = "Email",
46+
* validator = EmailValidator()
47+
* )
48+
* ```
49+
*
50+
* @param modifier A modifier for the field.
51+
* @param value The current value of the text field.
52+
* @param onValueChange A callback when the value changes.
53+
* @param label The label for the text field.
54+
* @param enabled If the field is enabled.
55+
* @param isError Manually set the error state.
56+
* @param errorMessage A custom error message to display.
57+
* @param validator A validator to automatically handle error state and messages.
58+
* @param keyboardOptions Keyboard options for the field.
59+
* @param keyboardActions Keyboard actions for the field.
60+
* @param visualTransformation Visual transformation for the input (e.g., password).
61+
* @param leadingIcon An optional icon to display at the start of the field.
62+
* @param trailingIcon An optional icon to display at the start of the field.
63+
*/
64+
@Composable
65+
fun AuthTextField(
66+
modifier: Modifier = Modifier,
67+
value: String,
68+
onValueChange: (String) -> Unit,
69+
label: @Composable (() -> Unit)? = null,
70+
enabled: Boolean = true,
71+
isError: Boolean? = null,
72+
errorMessage: String? = null,
73+
validator: FieldValidator? = null,
74+
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
75+
keyboardActions: KeyboardActions = KeyboardActions.Default,
76+
visualTransformation: VisualTransformation = VisualTransformation.None,
77+
leadingIcon: @Composable (() -> Unit)? = null,
78+
trailingIcon: @Composable (() -> Unit)? = null,
79+
) {
80+
val isSecureTextField = validator is PasswordValidator
81+
var passwordVisible by remember { mutableStateOf(false) }
82+
83+
TextField(
84+
modifier = modifier,
85+
value = value,
86+
onValueChange = { newValue ->
87+
onValueChange(newValue)
88+
validator?.validate(newValue)
89+
},
90+
label = label,
91+
singleLine = true,
92+
enabled = enabled,
93+
isError = isError ?: validator?.hasError ?: false,
94+
supportingText = {
95+
if (validator?.hasError ?: false) {
96+
Text(text = errorMessage ?: validator.errorMessage)
97+
}
98+
},
99+
keyboardOptions = keyboardOptions,
100+
keyboardActions = keyboardActions,
101+
visualTransformation = if (isSecureTextField && !passwordVisible)
102+
PasswordVisualTransformation() else visualTransformation,
103+
leadingIcon = leadingIcon,
104+
trailingIcon = trailingIcon ?: {
105+
if (isSecureTextField) {
106+
IconButton(
107+
onClick = {
108+
passwordVisible = !passwordVisible
109+
}
110+
) {
111+
Icon(
112+
imageVector = if (passwordVisible)
113+
Icons.Filled.VisibilityOff else Icons.Filled.Visibility,
114+
contentDescription = if (passwordVisible) "Hide password" else "Show password"
115+
)
116+
}
117+
}
118+
},
119+
)
120+
}
121+
122+
@Preview(showBackground = true)
123+
@Composable
124+
internal fun PreviewAuthTextField() {
125+
val context = LocalContext.current
126+
val nameTextValue = remember { mutableStateOf("") }
127+
val emailTextValue = remember { mutableStateOf("") }
128+
val passwordTextValue = remember { mutableStateOf("") }
129+
val emailValidator = remember {
130+
EmailValidator(stringProvider = DefaultAuthUIStringProvider(context),)
131+
}
132+
val passwordValidator = remember {
133+
PasswordValidator(
134+
stringProvider = DefaultAuthUIStringProvider(context),
135+
rules = listOf(
136+
PasswordRule.MinimumLength(8),
137+
PasswordRule.RequireUppercase,
138+
PasswordRule.RequireLowercase,
139+
)
140+
)
141+
}
142+
143+
Column(
144+
modifier = Modifier
145+
.fillMaxSize(),
146+
verticalArrangement = Arrangement.Center,
147+
horizontalAlignment = Alignment.CenterHorizontally,
148+
) {
149+
AuthTextField(
150+
value = nameTextValue.value,
151+
label = {
152+
Text("Name")
153+
},
154+
onValueChange = { text ->
155+
nameTextValue.value = text
156+
},
157+
)
158+
Spacer(modifier = Modifier.height(16.dp))
159+
AuthTextField(
160+
value = emailTextValue.value,
161+
validator = emailValidator,
162+
label = {
163+
Text("Email")
164+
},
165+
onValueChange = { text ->
166+
emailTextValue.value = text
167+
},
168+
leadingIcon = {
169+
Icon(
170+
imageVector = Icons.Default.Email,
171+
contentDescription = ""
172+
)
173+
}
174+
)
175+
Spacer(modifier = Modifier.height(16.dp))
176+
AuthTextField(
177+
value = passwordTextValue.value,
178+
validator = passwordValidator,
179+
label = {
180+
Text("Password")
181+
},
182+
onValueChange = { text ->
183+
passwordTextValue.value = text
184+
},
185+
leadingIcon = {
186+
Icon(
187+
imageVector = Icons.Default.Lock,
188+
contentDescription = ""
189+
)
190+
}
191+
)
192+
}
193+
}

0 commit comments

Comments
 (0)