Android quickstart

In order to use FlowForms you just need to make 3 steps :

1 : Declare a Form, its fields, and their validations using our FlowForms DSL in your ViewModel :


class SignUpViewModel : ViewModel() {

    var userName: String = "",
    var password: String = ""

    val form = flowForm {
        field(USERNAME, Required { userName })
        field(PASSWORD,
            Required { password },
            MinLength(MIN_PASSWORD_LENGTH) { password }
        )
    }

    companion object {
        const val USERNAME = "username"
        const val PASSWORD = "password"
        const val MIN_PASSWORD_LENGTH = 8
    }
}

In the above snippet we are declaring a form with two fields (Username & Password). Both fields are required and the password field also requires to have at least 8 characters.

2 : Listen to the fields status on your Fragment or Activity using our repeatOnLifeCycleScope extension :


class SignUpFormFragment : Fragment() {
    ...
    private var binding : SignUpFormBindingFragment? = null
    private lateinit var viewModel : SignUpViewModel

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        viewModel = ViewModelProvider(requireActivity())[SignUpViewModel::class.java] // or use you favorite DI tool
        ...
        viewModel.form.apply {
            repeatOnLifeCycleScope(
                { field(SignUpViewModel.USERNAME)?.status?.collect(::onUserNameStatusChange) },
                { field(SignUpViewModel.PASSWORD)?.status?.collect(::onPasswordStatusChange) },
                { status.collect(::onFormStatusChange) }
            )
        }
    }

    private fun onUserNameStatusChange(status: FieldStatus) {
        when (status.code) {
            REQUIRED_UNSATISFIED -> binding?.usernameInputLayout.error = "this field is required"
            else -> binding?.usernameInputLayout.error = null
        }
    }

    private fun onPasswordStatusChange(status: FieldStatus) {
        when (status.code) {
            REQUIRED_UNSATISFIED -> binding?.passwordInputLayout.error = "this field is required"
            MIN_LENGTH_UNSATISFIED -> binding?.passwordInputLayout.error = "Should contain at least 8 characters"
            else -> binding?.passwordInputLayout.error = null
        }
    }

    private fun onFormStatusChange(status: FormStatus) {
        when (status.code) {
            CORRECT -> binding?.continueButton.isEnabled = true
            else -> binding?.continueButton.isEnabled = false
        }
    }
}

In the above snippet we are getting each field using the form.field(...) function, and then collecting (observing) their status and displaying an error message in their input layouts when the field is incorrect (i.e. a field's validation failed). Both REQUIRED_UNSATISFIED and MIN_LENGTH_UNSATISFIED are specific status codes of the defined fields' validations in the form declaration (on the VM). Additionally, we are collecting the general form status, enabling a "continue" button when the form is correct (i.e. all its fields are correct) and disabling it when any of its fields are incorrect (at least one validation failed on some field).

3 : Bind your input views to the form fields using our bind form’s extension function :


class SignUpFormFragment : Fragment() {
    ...
    // call this method after collecting the fields and form status
    private fun bindFields() {
        binding?.apply {
            viewModel.form.bind(lifecycleScope,
                usernameInputEditText to SignUpFormModel.USERNAME,
                passwordInputEditText to SignUpFormModel.PASSWORD
            )
        }
    }
}

In the above snippet we are binding the userName input to the username field in the form, and the password input to the password field. This automatically calls the respective field's validations when any of the inputs change, in our case it is when the user types something in them.

And that’s one of the easiest forms we can create using FlowForms, we don’t need to care about managing the field’s validation triggering nor making any complex logic to enable or disable the continue button. This quickstart example is also meant to be used with two-way databinding, setting and using our fields’ values directly in our xml layouts as the following :


android:text="@={viewModel.userName}"

For further information about two-way databiding, refer to this official documentation. However, the snippets above can be easily adapted to not use two-way databinding.

FlowForms’s full potential is better appreciated when making more complex forms, you can review the android example app ViewModel included in the project. Which makes use of asynchronous validations and many other capabilities. You will see that the implementation steps doesn’t change at all. BTW, there is an example using Activity and another one using Fragment.

However, the example app and this guide does not cover all FlowForms’s features (but a lot of them), so for a detailed list of all the available features please refer to the documentation index