Compose имеет большое преимущество, но я чувствую, что читаемость и сложность кода увеличиваются с увеличением поля.
Вот пример, не стесняйтесь поделиться тем, чего мне не хватает.
data class UserForm(
val firstName: String,
val lastName: String,
val age: Int?,
val gender: String,
val phone: String,
val email: String,
val address: String,
val city: String,
val state: String,
val zip: String,
val country: String,
val occupation: String,
val company: String,
val maritalStatus: String,
val notes: String )
sealed interface FormIntent {
data class FirstNameChanged(val value: String) : FormIntent
data class LastNameChanged(val value: String) : FormIntent
data class AgeChanged(val value: String) : FormIntent
data class GenderChanged(val value: String) : FormIntent
data class PhoneChanged(val value: String) : FormIntent
data class EmailChanged(val value: String) : FormIntent
data class AddressChanged(val value: String) : FormIntent
data class CityChanged(val value: String) : FormIntent
data class StateChanged(val value: String) : FormIntent
data class ZipChanged(val value: String) : FormIntent
data class CountryChanged(val value: String) : FormIntent
data class OccupationChanged(val value: String) : FormIntent
data class CompanyChanged(val value: String) : FormIntent
data class MaritalStatusChanged(val value: String) : FormIntent
data class NotesChanged(val value: String) : FormIntent
object Submit : FormIntent }
data class FormState(
val firstName: String = "",
val lastName: String = "",
val age: String = "",
val gender: String = "",
val phone: String = "",
val email: String = "",
val address: String = "",
val city: String = "",
val state: String = "",
val zip: String = "",
val country: String = "",
val occupation: String = "",
val company: String = "",
val maritalStatus: String = "",
val notes: String = "",
val isSubmitting: Boolean = false,
val successMessage: String? = null,
val errorMessage: String? = null,
val fieldErrors: Map = emptyMap()
)
class FormViewModel(
private val repository: UserFormRepository
) : ViewModel() {
private val _state = MutableStateFlow(FormState())
val state = _state.asStateFlow()
fun onIntent(intent: FormIntent) {
when (intent) {
is FormIntent.FirstNameChanged -> update { copy(firstName = intent.value) }
is FormIntent.LastNameChanged -> update { copy(lastName = intent.value) }
is FormIntent.AgeChanged -> update { copy(age = intent.value) }
is FormIntent.GenderChanged -> update { copy(gender = intent.value) }
is FormIntent.PhoneChanged -> update { copy(phone = intent.value) }
is FormIntent.EmailChanged -> update { copy(email = intent.value) }
is FormIntent.AddressChanged -> update { copy(address = intent.value) }
is FormIntent.CityChanged -> update { copy(city = intent.value) }
is FormIntent.StateChanged -> update { copy(state = intent.value) }
is FormIntent.ZipChanged -> update { copy(zip = intent.value) }
is FormIntent.CountryChanged -> update { copy(country = intent.value) }
is FormIntent.OccupationChanged -> update { copy(occupation = intent.value) }
is FormIntent.CompanyChanged -> update { copy(company = intent.value) }
is FormIntent.MaritalStatusChanged -> update { copy(maritalStatus = intent.value) }
is FormIntent.NotesChanged -> update { copy(notes = intent.value) }
FormIntent.Submit -> submit()
}
}
private fun update(block: FormState.() -> FormState) {
_state.value = _state.value.block()
}
private fun submit() {
val current = _state.value
val errors = validate(current)
if (errors.isNotEmpty()) {
update { copy(fieldErrors = errors) }
return
}
viewModelScope.launch {
update { copy(isSubmitting = true, errorMessage = null, successMessage = null) }
val form = UserForm(
firstName = current.firstName,
lastName = current.lastName,
age = current.age.toIntOrNull(),
gender = current.gender,
phone = current.phone,
email = current.email,
address = current.address,
city = current.city,
state = current.state,
zip = current.zip,
country = current.country,
occupation = current.occupation,
company = current.company,
maritalStatus = current.maritalStatus,
notes = current.notes
)
val result = repository.submitForm(form)
if (result.isSuccess) {
update { copy(isSubmitting = false, successMessage = "Form submitted!") }
} else {
update { copy(isSubmitting = false, errorMessage = result.exceptionOrNull()?.message ?: "Failed") }
}
}
}
//Sample validation code - real time project will be much more bigger than this
private fun validate(s: FormState): Map {
val errors = mutableMapOf()
if (s.firstName.isBlank()) errors["firstName"] = "Required"
if (s.lastName.isBlank()) errors["lastName"] = "Required"
if (s.email.isBlank() || !android.util.Patterns.EMAIL_ADDRESS.matcher(s.email).matches())
errors["email"] = "Invalid email"
if (s.age.toIntOrNull() == null) errors["age"] = "Enter valid age"
return errors
}
}
@Composable
fun FormScreen(viewModel: FormViewModel = /* DI or hiltViewModel() */) {
val state by viewModel.state.collectAsState()
Column(
modifier = Modifier
.padding(16.dp)
.verticalScroll(rememberScrollState())
) {
OutlinedTextField(
value = state.firstName,
onValueChange = { viewModel.onIntent(FormIntent.FirstNameChanged(it)) },
label = { Text("First Name") },
modifier = Modifier.fillMaxWidth(),
isError = state.fieldErrors["firstName"] != null
)
ErrorText(state.fieldErrors["firstName"])
OutlinedTextField(
value = state.lastName,
onValueChange = { viewModel.onIntent(FormIntent.LastNameChanged(it)) },
label = { Text("Last Name") },
modifier = Modifier.fillMaxWidth(),
isError = state.fieldErrors["lastName"] != null
)
ErrorText(state.fieldErrors["lastName"])
OutlinedTextField(
value = state.age,
onValueChange = { viewModel.onIntent(FormIntent.AgeChanged(it)) },
label = { Text("Age") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
modifier = Modifier.fillMaxWidth(),
isError = state.fieldErrors["age"] != null
)
ErrorText(state.fieldErrors["age"])
// ... repeat for other fields ...
Spacer(Modifier.height(16.dp))
Button(
onClick = { viewModel.onIntent(FormIntent.Submit) },
enabled = !state.isSubmitting,
modifier = Modifier.fillMaxWidth()
) {
Text(if (state.isSubmitting) "Submitting…" else "Submit")
}
state.successMessage?.let { Text(it, color = Color.Green, modifier = Modifier.padding(top = 8.dp)) }
state.errorMessage?.let { Text(it, color = Color.Red, modifier = Modifier.padding(top = 8.dp)) }
}
}
@Composable
fun ErrorText(message: String?) {
if (message != null) {
Text(
text = message,
color = Color.Red,
fontSize = 12.sp,
modifier = Modifier.padding(start = 4.dp, bottom = 8.dp)
)
}
}
Это пример кода для простых наборов полей, реализация которого становится раздутой. Поделитесь своими знаниями о том, как сократить стандартный код для проекта среднего размера.
Compose имеет большое преимущество, но я чувствую, что читаемость и сложность кода увеличиваются с увеличением поля. Вот пример, не стесняйтесь поделиться тем, чего мне не хватает. [code] data class UserForm( val firstName: String, val lastName: String, val age: Int?, val gender: String, val phone: String, val email: String, val address: String, val city: String, val state: String, val zip: String, val country: String, val occupation: String, val company: String, val maritalStatus: String, val notes: String )
sealed interface FormIntent { data class FirstNameChanged(val value: String) : FormIntent data class LastNameChanged(val value: String) : FormIntent data class AgeChanged(val value: String) : FormIntent data class GenderChanged(val value: String) : FormIntent data class PhoneChanged(val value: String) : FormIntent data class EmailChanged(val value: String) : FormIntent data class AddressChanged(val value: String) : FormIntent data class CityChanged(val value: String) : FormIntent data class StateChanged(val value: String) : FormIntent data class ZipChanged(val value: String) : FormIntent data class CountryChanged(val value: String) : FormIntent data class OccupationChanged(val value: String) : FormIntent data class CompanyChanged(val value: String) : FormIntent data class MaritalStatusChanged(val value: String) : FormIntent data class NotesChanged(val value: String) : FormIntent
object Submit : FormIntent }
data class FormState( val firstName: String = "", val lastName: String = "", val age: String = "", val gender: String = "", val phone: String = "", val email: String = "", val address: String = "", val city: String = "", val state: String = "", val zip: String = "", val country: String = "", val occupation: String = "", val company: String = "", val maritalStatus: String = "", val notes: String = "",
val isSubmitting: Boolean = false, val successMessage: String? = null, val errorMessage: String? = null, val fieldErrors: Map = emptyMap() )
class FormViewModel( private val repository: UserFormRepository ) : ViewModel() {
private val _state = MutableStateFlow(FormState()) val state = _state.asStateFlow()
fun onIntent(intent: FormIntent) { when (intent) { is FormIntent.FirstNameChanged -> update { copy(firstName = intent.value) } is FormIntent.LastNameChanged -> update { copy(lastName = intent.value) } is FormIntent.AgeChanged -> update { copy(age = intent.value) } is FormIntent.GenderChanged -> update { copy(gender = intent.value) } is FormIntent.PhoneChanged -> update { copy(phone = intent.value) } is FormIntent.EmailChanged -> update { copy(email = intent.value) } is FormIntent.AddressChanged -> update { copy(address = intent.value) } is FormIntent.CityChanged -> update { copy(city = intent.value) } is FormIntent.StateChanged -> update { copy(state = intent.value) } is FormIntent.ZipChanged -> update { copy(zip = intent.value) } is FormIntent.CountryChanged -> update { copy(country = intent.value) } is FormIntent.OccupationChanged -> update { copy(occupation = intent.value) } is FormIntent.CompanyChanged -> update { copy(company = intent.value) } is FormIntent.MaritalStatusChanged -> update { copy(maritalStatus = intent.value) } is FormIntent.NotesChanged -> update { copy(notes = intent.value) } FormIntent.Submit -> submit() } }
private fun submit() { val current = _state.value val errors = validate(current) if (errors.isNotEmpty()) { update { copy(fieldErrors = errors) } return }
viewModelScope.launch { update { copy(isSubmitting = true, errorMessage = null, successMessage = null) } val form = UserForm( firstName = current.firstName, lastName = current.lastName, age = current.age.toIntOrNull(), gender = current.gender, phone = current.phone, email = current.email, address = current.address, city = current.city, state = current.state, zip = current.zip, country = current.country, occupation = current.occupation, company = current.company, maritalStatus = current.maritalStatus, notes = current.notes )
//Sample validation code - real time project will be much more bigger than this private fun validate(s: FormState): Map { val errors = mutableMapOf() if (s.firstName.isBlank()) errors["firstName"] = "Required" if (s.lastName.isBlank()) errors["lastName"] = "Required" if (s.email.isBlank() || !android.util.Patterns.EMAIL_ADDRESS.matcher(s.email).matches()) errors["email"] = "Invalid email" if (s.age.toIntOrNull() == null) errors["age"] = "Enter valid age" return errors } }
@Composable fun FormScreen(viewModel: FormViewModel = /* DI or hiltViewModel() */) { val state by viewModel.state.collectAsState()
@Composable fun ErrorText(message: String?) { if (message != null) { Text( text = message, color = Color.Red, fontSize = 12.sp, modifier = Modifier.padding(start = 4.dp, bottom = 8.dp) ) } }
[/code] Это пример кода для простых наборов полей, реализация которого становится раздутой. Поделитесь своими знаниями о том, как сократить стандартный код для проекта среднего размера.