Compose y otras bibliotecas

Puedes usar tus bibliotecas favoritas en Compose. En esta sección se describe cómo incorporar algunas de las bibliotecas más útiles.

ViewModel

Si usas la biblioteca de ViewModel de componentes de arquitectura, puedes llamar a la función viewModel() para acceder a un objeto ViewModel desde cualquier elemento que admite composición.

class ExampleViewModel : ViewModel() { /*...*/ }

@Composable
fun MyExample(
    viewModel: ExampleViewModel = viewModel()
) {
    // use viewModel here
}

viewModel() muestra una ViewModel existente o crea una nueva dentro del alcance dado. Se conservará ViewModel mientras esté activo el alcance. Por ejemplo, si el elemento que admite composición se usa en una actividad, viewModel() muestra la misma instancia hasta que finaliza la actividad o se cierra el proceso.

@Composable
fun MyExample(
    // Returns the same instance as long as the activity is alive,
    // just as if you grabbed the instance from an Activity or Fragment
    viewModel: ExampleViewModel = viewModel()
) { /* ... */ }

@Composable
fun MyExample2(
    viewModel: ExampleViewModel = viewModel() // Same instance as in MyExample
) { /* ... */ }

Si tu ViewModel tiene dependencias, viewModel() toma un objeto ViewModelProvider.Factory opcional como parámetro.

Para obtener más información sobre ViewModel en Compose y cómo se usan las instancias con la biblioteca de navegación de Compose, o sobre actividades y fragmentos, consulta los documentos de interoperabilidad.

Flujos de datos

Compose incluye extensiones para las soluciones basadas en transmisión más populares de Android. Cada una de estas extensiones es proporcionada por otro artefacto:

Estos artefactos se registran como objetos de escucha y representan los valores como un elemento State. Cada vez que se emite un valor nuevo, Compose recompone esas partes de la IU en las que se usa ese state.value. Por ejemplo, en este código, se recompone ShowData cada vez que exampleLiveData emite un valor nuevo.

@Composable
fun MyExample(
    viewModel: ExampleViewModel = viewModel()
) {
    val dataExample = viewModel.exampleLiveData.observeAsState()

    // Because the state is read here,
    // MyExample recomposes whenever dataExample changes.
    dataExample.value?.let {
        ShowData(dataExample)
    }
}

Operaciones asíncronas en Compose

Jetpack Compose te permite ejecutar operaciones asíncronas mediante corrutinas desde tus elementos componibles.

Consulta las API de LaunchedEffect, produceState y rememberCoroutineScope en la documentación sobre efectos secundarios para obtener más información.

Recomendamos usar la biblioteca de navegación de Compose para agregar elementos de navegación a tus proyectos de Compose. Esos elementos te permiten agregar una IU para que puedas navegar entre las elementos componibles y aprovechar la infraestructura y las funciones del componente de Navigation.

Obtén más información sobre esta integración en la documentación Cómo navegar con Compose.

Hilt

Hilt es la solución recomendada para la inyección de dependencias en las apps para Android y funciona a la perfección con Compose.

La función viewModel() mencionada en la sección ViewModel usa automáticamente el ViewModel que crea Hilt con la anotación @HiltViewModel. Incluimos documentación sobre la integración de ViewModel de Hilt.

@HiltViewModel
class ExampleViewModel @Inject constructor(
    private val savedStateHandle: SavedStateHandle,
    private val repository: ExampleRepository
) : ViewModel() { /* ... */ }

@Composable
fun ExampleScreen(
    exampleViewModel: ExampleViewModel = viewModel()
) { /* ... */ }

Hilt y Navigation

Hilt también se integra con la biblioteca de navegación de Compose. Agrega las siguientes dependencias adicionales a tu archivo de Gradle:

app/build.gradle

...
dependencies {
  ...
  implementation 'androidx.hilt:hilt-navigation-compose:1.0.0-alpha03'
}

Si tu @HiltViewModel anotado ViewModel tiene como alcance el gráfico de navegación, usa la función que admite composición hiltViewModel que funciona con fragmentos o actividades con anotaciones @AndroidEntryPoint.

Por ejemplo, si ExampleScreen es un destino en un gráfico de navegación, llama a hiltViewModel() para obtener una instancia de ExampleViewModel con alcance al destino, como se muestra en el fragmento de código a continuación:

// import androidx.hilt.navigation.compose.hiltViewModel

@Composable
fun MyApp() {
    NavHost(navController, startDestination = startRoute) {
        composable("example") { backStackEntry ->
            // Creates a ViewModel from the current BackStackEntry
            // Available in the androidx.hilt:hilt-navigation-compose artifact
            val exampleViewModel = hiltViewModel<ExampleViewModel>()
            ExampleScreen(exampleViewModel)
        }
        /* ... */
    }
}

Si necesitas recuperar la instancia de un ViewModel con alcance a rutas de navegación, pasa la raíz de destino como un parámetro:

// import androidx.hilt.navigation.compose.hiltViewModel
// import androidx.navigation.compose.getBackStackEntry

@Composable
fun MyApp() {
    NavHost(navController, startDestination = startRoute) {
        navigation(startDestination = innerStartRoute, route = "Parent") {
            // ...
            composable("exampleWithRoute") { backStackEntry ->
                val parentEntry = remember {
                  navController.getBackStackEntry("Parent")
                }
                val parentViewModel = hiltViewModel<ParentViewModel>(
                  parentEntry
                )
                ExampleWithRouteScreen(parentViewModel)
            }
        }
    }
}

Paging

La biblioteca de Paging facilita la carga gradual de datos y es compatible con Compose. La página de versiones de Paging contiene información sobre la dependencia adicional paging-compose que se debe agregar al proyecto y a su versión.

A continuación, se muestra un ejemplo de las API de Compose de la biblioteca de Paging:

@Composable
fun MyExample(flow: Flow<PagingData<String>>) {
    val lazyPagingItems = flow.collectAsLazyPagingItems()
    LazyColumn {
        items(lazyPagingItems) {
            Text("Item is $it")
        }
    }
}

Consulta la documentación de listas a fin de obtener más información para usar Paging en Compose.

Carga de imágenes

La biblioteca de Coil de Instacart ofrece funciones que admiten composición para cargar imágenes de fuentes externas, como cargar imágenes remotas en la red.

Aquí hay un ejemplo de rememberImagePainter del artefacto io.coil-kt:coil-compose:

@Composable
fun MyExample() {
    val painter = rememberImagePainter(
        data = "https://picsum.photos/300/300",
        builder = {
            crossfade(true)
        }
    )

    Box {
        Image(
            painter = painter,
            contentDescription = stringResource(R.string.image_content_desc),
        )

        when (painter.state) {
            is ImagePainter.State.Loading -> {
                // Display a circular progress indicator whilst loading
                CircularProgressIndicator(Modifier.align(Alignment.Center))
            }
            is ImagePainter.State.Error -> {
                // If you wish to display some content if the request fails
            }
        }
    }
}