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