1+ package com.firebase.ui.auth.compose.ui.components
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+ * val emailTextValue = remember { mutableStateOf("") }
43+ *
44+ * val emailValidator = remember {
45+ * EmailValidator(stringProvider = DefaultAuthUIStringProvider(context))
46+ * }
47+ *
48+ * AuthTextField(
49+ * value = emailTextValue,
50+ * onValueChange = { emailTextValue.value = it },
51+ * label = {
52+ * Text("Email")
53+ * },
54+ * validator = emailValidator
55+ * )
56+ * ```
57+ *
58+ * @param modifier A modifier for the field.
59+ * @param value The current value of the text field.
60+ * @param onValueChange A callback when the value changes.
61+ * @param label The label for the text field.
62+ * @param enabled If the field is enabled.
63+ * @param isError Manually set the error state.
64+ * @param errorMessage A custom error message to display.
65+ * @param validator A validator to automatically handle error state and messages.
66+ * @param keyboardOptions Keyboard options for the field.
67+ * @param keyboardActions Keyboard actions for the field.
68+ * @param visualTransformation Visual transformation for the input (e.g., password).
69+ * @param leadingIcon An optional icon to display at the start of the field.
70+ * @param trailingIcon An optional icon to display at the start of the field.
71+ */
72+ @Composable
73+ fun AuthTextField (
74+ modifier : Modifier = Modifier ,
75+ value : String ,
76+ onValueChange : (String ) -> Unit ,
77+ label : @Composable (() -> Unit )? = null,
78+ enabled : Boolean = true,
79+ isError : Boolean? = null,
80+ errorMessage : String? = null,
81+ validator : FieldValidator ? = null,
82+ keyboardOptions : KeyboardOptions = KeyboardOptions .Default ,
83+ keyboardActions : KeyboardActions = KeyboardActions .Default ,
84+ visualTransformation : VisualTransformation = VisualTransformation .None ,
85+ leadingIcon : @Composable (() -> Unit )? = null,
86+ trailingIcon : @Composable (() -> Unit )? = null,
87+ ) {
88+ val isSecureTextField = validator is PasswordValidator
89+ var passwordVisible by remember { mutableStateOf(false ) }
90+
91+ TextField (
92+ modifier = modifier,
93+ value = value,
94+ onValueChange = { newValue ->
95+ onValueChange(newValue)
96+ validator?.validate(newValue)
97+ },
98+ label = label,
99+ singleLine = true ,
100+ enabled = enabled,
101+ isError = isError ? : validator?.hasError ? : false ,
102+ supportingText = {
103+ if (validator?.hasError ? : false ) {
104+ Text (text = errorMessage ? : validator.errorMessage)
105+ }
106+ },
107+ keyboardOptions = keyboardOptions,
108+ keyboardActions = keyboardActions,
109+ visualTransformation = if (isSecureTextField && ! passwordVisible)
110+ PasswordVisualTransformation () else visualTransformation,
111+ leadingIcon = leadingIcon,
112+ trailingIcon = trailingIcon ? : {
113+ if (isSecureTextField) {
114+ IconButton (
115+ onClick = {
116+ passwordVisible = ! passwordVisible
117+ }
118+ ) {
119+ Icon (
120+ imageVector = if (passwordVisible)
121+ Icons .Filled .VisibilityOff else Icons .Filled .Visibility ,
122+ contentDescription = if (passwordVisible) " Hide password" else " Show password"
123+ )
124+ }
125+ }
126+ },
127+ )
128+ }
129+
130+ @Preview(showBackground = true )
131+ @Composable
132+ internal fun PreviewAuthTextField () {
133+ val context = LocalContext .current
134+ val nameTextValue = remember { mutableStateOf(" " ) }
135+ val emailTextValue = remember { mutableStateOf(" " ) }
136+ val passwordTextValue = remember { mutableStateOf(" " ) }
137+ val emailValidator = remember {
138+ EmailValidator (stringProvider = DefaultAuthUIStringProvider (context))
139+ }
140+ val passwordValidator = remember {
141+ PasswordValidator (
142+ stringProvider = DefaultAuthUIStringProvider (context),
143+ rules = listOf (
144+ PasswordRule .MinimumLength (8 ),
145+ PasswordRule .RequireUppercase ,
146+ PasswordRule .RequireLowercase ,
147+ )
148+ )
149+ }
150+
151+ Column (
152+ modifier = Modifier
153+ .fillMaxSize(),
154+ verticalArrangement = Arrangement .Center ,
155+ horizontalAlignment = Alignment .CenterHorizontally ,
156+ ) {
157+ AuthTextField (
158+ value = nameTextValue.value,
159+ label = {
160+ Text (" Name" )
161+ },
162+ onValueChange = { text ->
163+ nameTextValue.value = text
164+ },
165+ )
166+ Spacer (modifier = Modifier .height(16 .dp))
167+ AuthTextField (
168+ value = emailTextValue.value,
169+ validator = emailValidator,
170+ label = {
171+ Text (" Email" )
172+ },
173+ onValueChange = { text ->
174+ emailTextValue.value = text
175+ },
176+ leadingIcon = {
177+ Icon (
178+ imageVector = Icons .Default .Email ,
179+ contentDescription = " "
180+ )
181+ }
182+ )
183+ Spacer (modifier = Modifier .height(16 .dp))
184+ AuthTextField (
185+ value = passwordTextValue.value,
186+ validator = passwordValidator,
187+ label = {
188+ Text (" Password" )
189+ },
190+ onValueChange = { text ->
191+ passwordTextValue.value = text
192+ },
193+ leadingIcon = {
194+ Icon (
195+ imageVector = Icons .Default .Lock ,
196+ contentDescription = " "
197+ )
198+ }
199+ )
200+ }
201+ }
0 commit comments