https://kotlinlang.org logo
#compose
Title
# compose
c

Colton Idle

08/06/2021, 11:31 PM
My design system is calling for 3 types of input fields. 1. Plain text. You can enter anything 2. Email field. The input field should do basic validation to make sure it's an email (include @ sign, etc) 3. Phone number field. The input field should "validate" that it's a phone number (requires numbers, etc) I don't want to get into the topic of what is/isn't a valid phone/email. The topic to discuss is whether or not to bake these validation INTO the composable themselves. So we have something like this.
Copy code
Column{
BasicInput()
EmailInput()
PhoneInput()
}
A few team members have argued that the validation shouldn't be baked into a composable, so we originally were doing all checks in a ViewModel and not in the composable, but the issue is, someone builds a new screen, the add EmailInput() but forgets to add the validation in the VM for the new screen and now we're back at square 1. What would you do?
h

hfhbd

08/06/2021, 11:42 PM
I would put them into each composable, because this function will only handle 1 variable, and adding a view model for 1 variable is overkill. This allows you to reuse one composable, which executes 1 single tasks (return a valid email address) Eg:
Copy code
EmailInput(initial, placeholder, modifier etc) { validEmailAtChangeOrSubmitOrFocusLost ->
}
t

Tash

08/06/2021, 11:59 PM
If each Composable is defined by a set of requirements that call out its existence in the first place, then it should come with its own corresponding State class that implements all the smarts. So for example, the very reason one might want an EmailInput() is because they want “email specific text” and other related behaviors to be packaged along with the UI element in some way. So, for an EmailInput() Composable, you could create an EmailInputState
Copy code
interface EmailInputState { /** handles validations, internal text highlighting, etc **/ }
and then your component could do something like this:
Copy code
@Composable fun EmailInput(state: EmailInputState, ....)
add default param value of
state = remember { EmailInputState() }
if the component defines some default policies. this way you’d be hoisting the validation info as well. check out this for more info
5
e

eygraber

08/08/2021, 2:02 AM
It depends on how maintainable you'll need things to be going forward. For smallish or one off apps, it's probably fine to do it in the composable. If it's something that will be maintained long term, it probably makes sense to keep things like that out of compose. I've found the best strategy is to keep all business logic out of the view layer (in any UI context not just compose or Android). It can sometimes feel cumbersome, but down the road it's always been worth it (and we always get bitten eventually when we don't adhere to that)
👍 1
c

Colton Idle

08/09/2021, 12:58 AM
@Tash thank you! That was really helpful. This line isn't in the docs anywhere right? It was quite enlightening. 😅
If each Composable is defined by a set of requirements that call out its existence in the first place, then it should come with its own corresponding State class that implements all the smarts.
I guess I didn't really see the validation logic as being state, but now I see that the fact that it can be validated or not, is the state that I'm looking for. 👍
t

Tash

08/09/2021, 8:27 AM
This line isn’t in the docs anywhere right?
@Colton Idle that was my overall takeaway from the concepts of state hoisting and the various sections that API guidelines doc, but yeah 😅
validation logic as being state
right, the logic isn’t the state, the result of the logic could be, though. at the end of the day you have one model essentially that is responsible for obfuscating the “logic” and surfacing the result of that logic as state. thus the state, when hoisted could allow the caller of the composable to access information like validity, reason for invalidity if any, etc. whatever you want the EmailInput() to figure out for you
👍 1