0

I have an app with two activities. Activity1 changes a State stored in a ViewModel, then starts Activity2. But somehow, the state change is only reflected in Activity1, not in Activity2.

ViewModel

class MyViewModel : ViewModel() {
    var hasChanged: Boolean by mutableStateOf(false)
}

Composable of Activity1

@Composable
fun Screen1() {

    val context = LocalContext.current
    val viewModel: MyViewModel = viewModel()

    Column {
        Text("State changed: " + viewModel.hasChanged.toString())
        Button(
            onClick = {
                viewModel.hasChanged = true
                startActivity(context, Intent(context, Activity2::class.java), null)
            }
        ) {
            Text("Change State!")
        }
    }
}

Composable of Activity2

@Composable
fun Screen2() {
    val viewModel: MyViewModel = viewModel()
    Text("State changed: " + viewModel.hasChanged.toString())  # stays 'false'
}

Behavior of the app

  • Activity1 correctly shows the state to be false, initially
  • After button is pressed, Activity1 correctly displays the state to be true
  • Activity2 opens but still shows the state to be false

Question

Why is the state change not reflected in Activity2 and can this be fixed?

Björn
  • 1,575
  • 13
  • 9

1 Answers1

2

ViewModels are unique to classes that implement ViewModelStoreOwner

@Composable
public inline fun <reified VM : ViewModel> viewModel(
    viewModelStoreOwner: ViewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) {
        "No ViewModelStoreOwner was provided via LocalViewModelStoreOwner"
    },
    key: String? = null,
    factory: ViewModelProvider.Factory? = null,
    extras: CreationExtras = if (viewModelStoreOwner is HasDefaultViewModelProviderFactory) {
        viewModelStoreOwner.defaultViewModelCreationExtras
    } else {
        CreationExtras.Empty
    }

ComponentActivity implements ViewModelStoreOwner, so it's a ViewModelStoreOwner that contains its own ViewModel instances and uses a HashMap to get same ViewModel for key provided as String.

val vm: MyViewModel = ViewModelProvider(owner = this)[MyViewModel::class.java]

This is how they are created under the hood. This here is Activity or Fragment depending on where ViewModelProvider's owner param is set at. In Compose ViewModelStore owner is accessed by LocalViewModelStoreOwner.current

You need to pass your hasChanged between Activities using Bundle, or have a Singleton repository or UseCase class that you can inject to both ViewModels or use local db or another methods to pass data between Activities.

Thracian
  • 43,021
  • 16
  • 133
  • 222
  • can you please help me on this [issue](https://stackoverflow.com/q/73844235/11560810) – Kotlin Learner Sep 25 '22 at 12:49
  • Thanks @Thracian, that sent me off in the right direction! I didn't realize that every activity would get its own ViewModel. Also, it seems that Jetpack Compose is designed to rather use a single activity. So I'm thinking about getting rid of the second one altogether. – Björn Sep 25 '22 at 15:13