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