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