使用“TextField”和提交“Button”创建一个简单的表单?

问题描述 投票:0回答:1

查看大量代码行来完成基本任务:

首先是进口:

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.text.KeyboardOptions

import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable

import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation

import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextField

import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.ui.tooling.preview.Preview

然后

TextField

@Composable
fun EmailTextField() {
    var email by rememberSaveable { mutableStateOf("") }

    TextField(
        value = email,
        onValueChange = { email = it },
        label = { Text("Enter email") }
    )
}

@Composable
fun PasswordTextField() {
    var password by rememberSaveable { mutableStateOf("") }

    TextField(
        value = password,
        onValueChange = { password = it },
        label = { Text("Enter password") },
        visualTransformation = PasswordVisualTransformation(),
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password)
    )
}

最后是

App
本身:

@OptIn(ExperimentalResourceApi::class)
@Composable
@Preview
fun App() {
    MaterialTheme {
        var showContent by remember { mutableStateOf(false) }
        Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
            EmailTextField()
            PasswordTextField()
            Button(onClick = { showContent = !showContent }) {
                Text("Auth")
            }
        }
    }
}

尝试获取

email
password
,将其传递给执行 HTTP 请求的函数,并根据输出导航到欢迎屏幕或显示错误。

官方 compose 示例项目显示了 14 个文件来解决此问题(它甚至不执行 HTTP 请求/响应):https://github.com/android/compose-samples/tree/309d4b9/Jetsurvey /app/src/main/java/com/example/compose/jetsurvey/signinsignup

是否有一种简单的方法来获取电子邮件+密码并导航到表单提交的下一步?

android kotlin android-jetpack-compose androidx kotlin-multiplatform
1个回答
0
投票

是否有一种简单的方法来获取电子邮件+密码并导航到表单提交的下一步?

您链接的存储库所做的不仅仅是一次登录。它包含两个不同的登录和注册屏幕,以及一个允许在登录/注册之间进行选择的欢迎页面。
此外,该存储库旨在作为仅使用 Compose 库构建更复杂表单的基础。如果您的表单始终只有两个

TextField
,那么您可以简化整个过程。

我们可以先总结一下哪些文件在做什么:

  • UI 定义及其 ViewModel:
    • WelcomeScreen.kt
      +
      WelcomeViewModel.kt
    • SignInScreen.kt
      +
      SignInViewModel.kt
    • SignUpScreen.kt
      +
      WelcomeViewModel.kt
    • SignInSignUpScreen.kt
  • 表单验证:
    • EmailState.kt
    • PasswordState.kt
    • TextFieldState.kt
  • 用于创建 ViewModel 的包装器可组合项
    • WelcomeRoute.kt
    • SignInRoute.kt
    • SignUpRoute.kt
  • 存储库
    • UserRepository.kt

现在让我们分析一下降低复杂性的潜力:

UI 定义:没有潜力
Jetpack Compose 中的常见设计是每个主屏幕都有自己的 ViewModel。

表单验证:潜力巨大
如果您的

TextField
只会出现一种错误状态(密码或电子邮件错误),那么您可以将该逻辑移至 ViewModel 类。

Wrapper Composables:潜力巨大
这些 Composables 的目的只是生成带有

UserRepository
实例的 ViewModel。这个逻辑可以向上移动到
NavHost
,或者在使用像 Hilt 或 Koin 这样的依赖注入时完全删除。

存储库:没有潜力
这是必需的。


因此,当您通常以简单性为目标来实现这一点时,您最终会得到大约 8 个文件,这可能更容易接受。如果您只实现一个登录页面,您最终会得到三个文件:

  • LoginScreen.kt
  • LoginViewModel.kt
  • UserRepository.kt

关于您提到的代码冗长主题:
Jetpack Compose 将 UI 和布局定义统一为单个可组合项。虽然代码乍一看可能很冗长,但请记住,与经典的 Android 视图框架相比,您将逻辑和布局定义合并到一个位置,最终总共会减少行数。

不过,我确实同意,在 Compose 中将可组合项拆分为更小的单元尤其重要。


最后,登录屏幕的简单实现如下所示:

@Composable
LoginComposable(loginViewModel: LoginViewModel = viewModel(), onNavigateToContent: () -> Unit) {

    var email by rememberSaveable { mutableStateOf("") }
    var password by rememberSaveable { mutableStateOf("") }

    LaunchedEffect(loginViewModel.successfulLogin) {
        if(loginViewModel.successfulLogin) {
            onNavigateToContent()
        }
    }

    Column(modifier = Modifier.fillMaxSize()) {

        TextField(
            value = email,
            onValueChange = { email = it },
            label = { Text("Enter email") },
            isError = loginViewModel.emailError,
            supportingText = if(loginViewModel.invalidEmail) {
                Text(text = "Login failed")
            }
        )

        TextField(
            value = password,
            onValueChange = { password = it },
            label = { Text("Enter password") },
            visualTransformation = PasswordVisualTransformation(),
            keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
            isError = loginViewModel.passwordError,
            supportingText = if(loginViewModel.invalidPassword) {
                Text(text = "Login failed")
            }
        )
    }
}

ViewModel 看起来像这样(userRepository 由依赖注入框架注入):

class LoginViewModel(
    private val userRepository: UserRepository  // using dependency injection
) : ViewModel() {

    var successfulLogin by mutableStateOf(false)
    var emailError by mutableStateOf(false)
    var passwordError = mutableStateOf(false)

    fun login(username: String, password: String) {
        usernameError = false
        passwordError = false

        viewModelScope.launch {
            try {
                userRepository.login(username, password)  // call networking suspend function
                successfulLogin = true
            } catch (e: Exception) {
                // TODO add more sophisticated error handling here
                usernameError = true
                passwordError = true
            }
        }
    }
}

这应该会给你一个简单实现的想法。

© www.soinside.com 2019 - 2024. All rights reserved.