Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Auth UI composables #256

Merged
merged 30 commits into from
Aug 23, 2023
Merged

Add Auth UI composables #256

merged 30 commits into from
Aug 23, 2023

Conversation

jan-tennert
Copy link
Collaborator

@jan-tennert jan-tennert commented Aug 8, 2023

What kind of change does this PR introduce?

Feature

What is the current behavior?

You had to make common Auth UI composables yourself.

What is the new behavior?

There is a new module providing Auth UI composables and Form validator for Material 3.

It currently has Email & Password fields in all variants. There are also icons in the front by default.

PasswordField

You can set rules for the password like this:

OutlinedPasswordField(
    value = password,
    onValueChange = { password = it },
    label = { Text("Password") },
    rules = rememberPasswordRuleList(PasswordRule.minLength(6), PasswordRule.containsSpecialCharacter(), PasswordRule.containsDigit(), PasswordRule.containsLowercase(), PasswordRule.containsUppercase())
)

Once set, the field will automatically display an error supporting text if the password doesn't match the criteria.
Note that the built-in roles also accept a custom description as a parameter.
You can also easily create your own rules:

val myRule = PasswordRule("description") { password ->
    password.contains("-")
}

EmailField

The email field has a validator parameter which defaults to a regex solution.
Example:

OutlinedEmailField(
    value = email,
    onValueChange = { email = it },
    label = { Text("E-Mail") },
    validator = EmailValidator { 
        //validate email
    }
)

Phone Field

The phone field also has a validator parameter, which just checks whether the phone number only consists of digits.
On top of that you can provide a mask parameter, which changes how the phone number gets displayed. The value is just the raw number.
Example with the default mask:

OutlinedPhoneField(
    value = phone,
    onValueChange = { phone = it },
    label = { Text("Phone") }
)

image
Example with custom mask: (you can also set it to null, if you don't want a mask)

OutlinedPhoneField(
    value = phone,
    onValueChange = { phone = it },
    label = { Text("Phone") },
    mask = "(###) ###-####"
)

image

Note: You can customize the fields completely, they just all have default values. They also have a `mandatory´ option, you can use that to have optional fields. It is also possible to make fields only mandatory (=validation enabled) once the field is not empty. (See example)

Provider Button

This module provides a function to generate the button content independently of the variation:

OutlinedButton(
    onClick = {}, //Login with Google,
    content = { ProviderButtonContent(Google) }
)
Button(
    onClick = {}, //Login with Twitch,
    content = { ProviderButtonContent(Twitch) }
)

image
You can also only use the icon if you want a custom layout:

Button(
    onClick = {},
) {
    ProviderIcon(Google)
    Text("Login with Google")
}

Custom Form Components

You can also easily make your own form components:

FormComponent(formKey = "accept_terms", mandatory = true) { valid ->
    Row(
        verticalAlignment = Alignment.CenterVertically,
    ) {
        Checkbox(
            checked = valid.value,
            onCheckedChange = { valid.value = it },
        )
        Text("Accept Terms", color = MaterialTheme.colorScheme.onBackground)
    }
}

Mandatory changes whether the AuthState#validForm property is affected by this component.

Auth Form

If the composables are in a AuthForm scope, they automatically change whether the form is valid & complete or not. That way you can easily check whether something is missing and e.g. disable the login button. (See example)

Note: There are still more things coming here.

Here is an example using Compose for Desktop (Windows)
singleWindowApplication {
    MaterialTheme(
        darkColorScheme()
    ) {
        Box(Modifier.fillMaxSize().background(MaterialTheme.colorScheme.background), contentAlignment = Alignment.Center) {
            AuthForm {
                var password by remember { mutableStateOf("") }
                var email by remember { mutableStateOf("") }
                var phone by remember { mutableStateOf("") }
                val state = LocalAuthState.current
                Column(
                    horizontalAlignment = Alignment.CenterHorizontally,
                ) {
                    OutlinedEmailField(
                        value = email,
                        onValueChange = { email = it },
                        label = { Text("E-Mail") },
                        mandatory = email.isNotBlank() //once an email is entered, it is mandatory. (which enable validation)
                    )
                    OutlinedPhoneField(
                        value = phone,
                        onValueChange = { phone = it },
                        label = { Text("Phone Number") }
                    )
                    OutlinedPasswordField(
                        value = password,
                        onValueChange = { password = it },
                        label = { Text("Password") },
                        rules = rememberPasswordRuleList(PasswordRule.minLength(6), PasswordRule.containsSpecialCharacter(), PasswordRule.containsDigit(), PasswordRule.containsLowercase(), PasswordRule.containsUppercase())
                    )
                    FormComponent("accept_terms") { valid ->
                        Row(
                            verticalAlignment = Alignment.CenterVertically,
                        ) {
                            Checkbox(
                                checked = valid.value,
                                onCheckedChange = { valid.value = it },
                            )
                            Text("Accept Terms", color = MaterialTheme.colorScheme.onBackground)
                        }
                    }
                    Button(
                        onClick = {}, //Login with email and password,
                        enabled = state.validForm,
                    ) {
                        Text("Login")
                    }
                    OutlinedButton(
                        onClick = {}, //Login with Google,
                        content = { ProviderButtonContent(Google) }
                    )
                    Button(
                        onClick = {}, //Login with Twitch,
                        content = { ProviderButtonContent(Twitch) }
                    )
                }
            }
        }
    }
}
java_2023-08-22_16-48-18.mp4

Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
@jan-tennert jan-tennert added the enhancement New feature or request label Aug 8, 2023
@jan-tennert jan-tennert self-assigned this Aug 8, 2023
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
@jan-tennert jan-tennert marked this pull request as ready for review August 22, 2023 14:44
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
# Conflicts:
#	gradle/libs.versions.toml
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
Signed-off-by: TheRealJan <jan.m.tennert@gmail.com>
@jan-tennert jan-tennert merged commit e9882f4 into development Aug 23, 2023
9 checks passed
@jan-tennert jan-tennert deleted the compose-ui branch September 3, 2023 11:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant