API de champ d'application ViewModel   Inclus dans Android Jetpack.

Le champ d'application est essentiel pour utiliser efficacement des ViewModels. Chaque ViewModel s'applique à un objet qui implémente l'interface ViewModelStoreOwner. Plusieurs API vous permettent de gérer plus facilement le champ d'application de vos ViewModels. Ce document décrit certaines des techniques clés que vous devez connaître.

La méthode ViewModelProvider.get() vous permet d'obtenir une instance d'un ViewModel limité à n'importe quel ViewModelStoreOwner. Pour les utilisateurs de Kotlin, différentes fonctions d'extension sont disponibles pour les cas d'utilisation les plus courants. Toutes les implémentations de fonctions d'extension Kotlin utilisent l'API ViewModelProvider.

ViewModels limités au ViewModelStoreOwner le plus proche

Vous pouvez limiter le champ d'application d'un ViewModel à un composable, à une activité ou à la destination d'un graphique de navigation. La fonction viewModel() de Compose vous permet d'obtenir une instance de ViewModel limitée au ViewModelStoreOwner le plus proche.

import androidx.lifecycle.viewmodel.compose.viewModel

@Composable
fun MyScreen(
    modifier: Modifier = Modifier,
    // ViewModel API available in lifecycle.lifecycle-viewmodel-compose
    // The ViewModel is scoped to the closest ViewModelStoreOwner provided
    // via the LocalViewModelStoreOwner CompositionLocal. In order of proximity,
    // this could be the destination of a Navigation graph
    // or the host Activity.
    viewModel: MyViewModel = viewModel()
) { /* ... */ }

ViewModels limités à un ViewModelStoreOwner

La fonction viewModel() accepte le paramètre facultatif viewModelStoreOwner, que vous pouvez utiliser pour spécifier le ViewModelStoreOwner auquel l'instance du ViewModel s'applique.

import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.lifecycle.ViewModelStoreOwner

@Composable
fun MyScreen(
    // A custom owner passed in, such as a parent NavBackStackEntry
    customOwner: ViewModelStoreOwner,
    // The ViewModel is now scoped to the provided customOwner
    viewModel: MyViewModel = viewModel(viewModelStoreOwner = customOwner)
) {
    /* ... */
}

ViewModels limités à un composable

Vous pouvez utiliser rememberViewModelStoreOwner() pour limiter le champ d'application d'un ViewModel directement au site d'appel d'un composable. Cela est particulièrement utile pour les composants d'interface utilisateur qui sont ajoutés ou supprimés dynamiquement de l'écran en fonction de l'état, comme les éléments d'une page ou d'une liste paresseuse. Lorsque le composable propriétaire du ViewModelStoreOwner quitte la composition, le ViewModelStore associé est effacé et le ViewModel est détruit.

Utilisez rememberViewModelStoreOwner() pour créer un magasin tenant compte du cycle de vie et qui survit aux modifications de configuration.

@Composable
fun RememberViewModelStoreOwnerSample() {
    // Create a ViewModelStoreOwner scoped to this specific call site.
    // When this composable leaves the composition,
    // the associated ViewModelStore will be cleared.
    val scopedOwner = rememberViewModelStoreOwner()

    CompositionLocalProvider(LocalViewModelStoreOwner provides scopedOwner) {
        // This ViewModel is scoped to `scopedOwner`.
        // It will survive configuration changes but will be cleared when
        // the composable is removed from the UI tree.
        val viewModel = viewModel { TestViewModel("scoped_data") }
        // Use the ViewModel
    }
}

Pour les implémentations plus complexes, telles qu'un HorizontalPager ou les cas nécessitant plusieurs champs d'application indépendants, utilisez rememberViewModelStoreProvider(). Cela vous permet de générer des instances ViewModelStoreOwner distinctes pour différentes clés (comme les index de page). Ainsi, chaque page conserve son propre état ViewModel indépendant.

@Composable
fun RememberViewModelStoreProviderSample() {
    val storeProvider = rememberViewModelStoreProvider()
    val pages = listOf("Page 1", "Page 2", "Page 3")

    HorizontalPager(pageCount = pages.size) { page ->
        // Create a ViewModelStoreOwner for the specific page using the provider.
        val pageOwner = rememberViewModelStoreOwner(provider = storeProvider, key = page)

        CompositionLocalProvider(LocalViewModelStoreOwner provides pageOwner) {
            val pageViewModel = viewModel { TestViewModel(pages[page]) }
            // Use pageViewModel
        }
    }
}

ViewModels limités au graphique de navigation

Les graphiques de navigation sont également propriétaires de magasin ViewModel. Si vous utilisez Navigation Compose, vous pouvez obtenir une instance d'un ViewModel limité à un graphique de navigation à l'aide de la fonction getBackStackEntry().

viewModel() récupère l'instance du ViewModelStoreOwner fourni par le LocalViewModelStoreOwner CompositionLocal. Dans une application Compose typique utilisant Jetpack Navigation, ce propriétaire est l'entrée de la pile Retour de navigation actuelle. Cela signifie que le ViewModel reste en mémoire tant que cette destination est présente dans la pile de retour.

import androidx.lifecycle.viewmodel.compose.viewModel

@Composable
fun MyAppNavHost() {
    // ...
    composable("myScreen") { backStackEntry ->
        // Retrieve the NavBackStackEntry of "parentNavigationRoute"
        val parentEntry = remember(backStackEntry) {
            navController.getBackStackEntry("parentNavigationRoute")
        }
        // Get the ViewModel scoped to the `parentNavigationRoute` Nav graph
        val parentViewModel: SharedViewModel = viewModel(parentEntry)
        // ...
    }
}

Si vous utilisez Hilt en plus de Jetpack Navigation, vous pouvez utiliser l' hiltNavGraphViewModels(graphId) API comme suit.

import androidx.hilt.navigation.compose.hiltViewModel

@Composable
fun MyAppNavHost() {
    // ...
    composable("myScreen") { backStackEntry ->
        val parentEntry = remember(backStackEntry) {
            navController.getBackStackEntry("parentNavigationRoute")
        }

        // ViewModel API available in hilt.hilt-navigation-compose
        // The ViewModel is scoped to the `parentNavigationRoute` Navigation graph
        // and is provided using the Hilt-generated ViewModel factory
        val parentViewModel: SharedViewModel = hiltViewModel(parentEntry)
        // ...
    }
}

Ressources supplémentaires

Pour en savoir plus sur les ViewModels et le champ d'application, consultez les ressources supplémentaires suivantes :

Documentation

Afficher le contenu