2

I'm trying to follow Google's clean architecture guidelines.

I have a navigation subgraph with multiple pages corresponding to stages of filling a form.

Each page has it's own viewModel.

On the last screen I need to send data from the form to app's data layer which sends it to server.

How should I share the form object between viewModels?

I see such options here:

  • Create Form model and FormRepository in app's data layer and inject it to viewModels. I don't like it because knowledge about form is not exclusive for feature module anymore and appears in data layer, though it is basically a UI state
  • Use one viewModel for all stages. I don't like it because some stages do some data retrieval and input validation that is not relevant for others.
  • Create FormHolder singleton in feature module, that is injected to all VMs. I'm not sure about it because I'm trying to follow architecture guidelines.

What should I do?

1 Answer 1

3

Use a ViewModel scoped to the Navigation Graph (per subgraph) AND delegate logic

How to do it:

  1. Create a FormViewModel scoped to the navigation subgraph (not individual screens).

    • This acts as the shared state holder.
  2. Each screen still uses its own ScreenViewModel, but it reads from/writes to the FormViewModel.

  3. Validation and retrieval logic stay in screen ViewModels.

  4. Only FormViewModel talks to domain layer when submitting the final form.

// Shared ViewModel scoped to the subgraph
class FormViewModel @Inject constructor() : ViewModel() {
    var formState = mutableStateOf(FormData())
    fun updatePart1(data: Part1) { formState.value = formState.value.copy(part1 = data) }
    fun submitForm() { /* call useCase to submit */ }
}

// Each screen has its own logic
class Step1ViewModel @Inject constructor(
    private val formViewModel: FormViewModel // Injected via navGraphViewModels()
) : ViewModel() {
    fun onNext(input: Input) {
        // validate and update shared state
        formViewModel.updatePart1(convert(input))
    }
}

Navigation-Scoped ViewModel:

val formViewModel: FormViewModel by navGraphViewModels(R.id.formGraph) {
    defaultViewModelProviderFactory
}
Sign up to request clarification or add additional context in comments.

2 Comments

Why did this answer get so many upvotes, and why is it even accepted? It entirely misses the point that the question is about Compose. Using navGraphViewModels to scope view models to the nav graph only works with the old Views system, when in a Fragment. Since Compose has no Fragments, this solution won't work.
Yea, it doesn't work with exact code, but creating separate VM on NavHost level works. Tho, I haven't found any way to make it via Koin. Looks like it will be implemented as sharedKoinViewModel() but it is marked experimental on docs and the name is unable to be resolved in my project.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.