androidkotlinandroid-jetpack-composeandroid-viewmodelandroid-mvvm

Jetpack Compose: How good is having a separate ViewModel for every interactive component on Screen


I have chat like interface in Jetpack compose. Every message can either be Text, Audio, Video, or Image.

Each Audio message is displayed with the help of a AudioPlayer composable, and an attached controller to it. The screen ViewModel has a single instance of AudioPlayerController, and each component collects its state from that controller's StateFlow<UiState>. UiState contains states like audioFilePath, fileName, duration, progress, etc. I'm determining which player of the state is playing based on the filePath.


@Composable
fun AudioPlayerUi(audioFilePath:String, ...) {
  val uiState by controller.uiState.collectAsState()
  val isPlaying by remember { derivedStateOf { audioFilePath == uiState.audioFilePath } }
  ...
}

And there are many more components on the screen which have similar logic.

My question is how good is it if my controller extends ViewModel and I create an instance of that controller in each component?

@Composable
fun AudioPlayerUi(audioFilePath:String, ...) {
  val controller by viewModel<AudioPlayerController>()
  val uiState by controller.uiState.collectAsState()
  val isPlaying = uiState.isPlaying
  ...
}

Now there are more questions following it.

My Scrren is something like:

ReceivedText
ReceivedAudio
             SentText
             SentAudio
             SentImage
ReceivedImage
              SentText
ReceivedAudio
ReceivedImage
             SentAudio
             SentImage

##### INPUT WIDGETS ##### 
##### Can be any one ####
TestInput, AudioRecorder, 
Camera, etc
#########################

Each sent component has editing feature as well.

I read most articles and documentations available online, but none of them mentions about having each component having it's vieeModel. Also I can't have all the logic in screen ViewModel because each controller is big in itself.


Solution

  • This is an opinion-based question.
    (So the answer is aligned with the opinions of Android team recommendations)

    To give an answer from Android Docs, Reference - https://developer.android.com/jetpack/compose/state-hoisting#screen-ui-state

    Use ViewModel as a screen UI state holder.

    Using ViewModel for each component does not have any additional benefits and it is an additional overhead.

    The following issues can happen,

    1. Issues in inter-component data sharing.
    2. More memory usage.
    3. Lifecycle issues. (As already mentioned in the question, what UI is visible, what is hidden, init can happen way earlier for some components, etc).

    For individual components, you can use a simple class as a StateHolder or move to a screen-level ViewModel if required to use business logic.

    How to choose State Holder in a simple way.

    1. Screen level state involving business logic - ViewModel
    2. Component level state involving business logic - Hoist to ViewModel
    3. Any UI-specific state - Simple class as state holder or within Composables.

    To add on, as mentioned in the starting, this is an opinionated question and hence an opinionated answer. You can use ViewModel for the Component level if you see it is required and you are careful to handle all the issues related to it.