Puedes usar tus bibliotecas favoritas en Compose. En esta sección se describe cómo incorporar algunas de las bibliotecas más útiles.
Activity
Para usar Compose en una actividad, debes usar ComponentActivity
, una subclase de Activity
que proporciona LifecycleOwner
y los componentes correctos para Compose. También brinda las API adicionales que separan el código de los métodos de anulación en la clase de actividad.
Activity Compose expone estas API a elementos que admiten composición, de modo que ya no es necesario anular métodos fuera de estos elementos ni recuperar una instancia Activity
explícita.
Además, estas API garantizan que solo se inicialicen una vez, permanezcan vigentes tras la recomposición y se borren, de manera correcta, si se quita de la composición el elemento que la admite.
ActivityResult
La API de rememberLauncherForActivityResult()
te permite obtener un resultado de una actividad en el elemento que admite composición:
@Composable
fun GetContentExample() {
var imageUri by remember { mutableStateOf<Uri?>(null) }
val launcher = rememberLauncherForActivityResult(GetContent()) { uri: Uri? ->
imageUri = uri
}
Column {
Button(onClick = { launcher.launch("image/*") }) {
Text(text = "Load Image")
}
Image(
painter = rememberImagePainter(imageUri),
contentDescription = "My Image"
)
}
}
En este ejemplo, se muestra un contrato GetContent()
simple. Cuando se presiona el botón, se inicia la solicitud. Se invoca la lambda final para rememberLauncherForActivityResult()
una vez que el usuario selecciona una imagen y regresa a la actividad de inicio.
De esta manera, se carga la imagen seleccionada con la función rememberImagePainter()
de Coil.
Cualquier subclase de ActivityResultContract
puede usarse como primer argumento para rememberLauncherForActivityResult()
.
Es decir, puedes usar esta técnica para solicitar contenido desde el framework y en otros patrones comunes. También puedes crear tus propios contratos personalizados y usarlos con esta técnica.
Cómo solicitar permisos de tiempo de ejecución
La misma API de Activity Result y rememberLauncherForActivityResult()
que se explicaron anteriormente se pueden usar para solicitar permisos de tiempo de ejecución con el contrato RequestPermission
para un único permiso o RequestMultiplePermissions
para varios permisos.
La biblioteca de permisos de acompañamiento también se puede usar en una capa encima de esas API para asignar el estado actual que se otorga a los permisos en un estado que puede usar la IU de Compose.
Cómo controlar el botón Atrás del sistema
Para brindar navegación personalizada hacia atrás y anular el comportamiento predeterminado del botón Atrás del sistema desde el elemento que admite composición, este elemento puede usar BackHandler
a fin de interceptar ese evento:
var backHandlingEnabled by remember { mutableStateOf(true) }
BackHandler(backHandlingEnabled) {
// Handle back press
}
El primer argumento controla si, en ese momento, el objeto BackHandler
está habilitado; puedes usar este argumento para inhabilitar el controlador de forma temporal, según el estado del componente. Se invocará la lambda final si el usuario activa un evento del botón Atrás del sistema y si, en ese momento, BackHandler
está habilitado.
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 MyViewModel : ViewModel() { /*...*/ }
@Composable
fun MyScreen(
viewModel: MyViewModel = 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 MyScreen(
// Returns the same instance as long as the activity is alive,
// just as if you grabbed the instance from an Activity or Fragment
viewModel: MyViewModel = viewModel()
) { /* ... */ }
@Composable
fun MyScreen2(
viewModel: MyViewModel = viewModel() // Same instance as in MyScreen
) { /* ... */ }
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 el modo en que 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:
- El artefacto
androidx.compose.runtime:runtime-livedata:$composeVersion
incluyeLiveData.observeAsState()
. Flow.collectAsState()
no requiere dependencias adicionales.Observable.subscribeAsState()
se incluye en el artefactoandroidx.compose.runtime:runtime-rxjava2:$composeVersion
o enandroidx.compose.runtime:runtime-rxjava3:$composeVersion
.
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 MyScreen(
viewModel: MyViewModel = viewModel()
) {
val dataExample = viewModel.exampleLiveData.observeAsState()
// Because the state is read here,
// MyScreen recomposes whenever dataExample changes.
dataExample.value?.let {
ShowData(dataExample)
}
}
Operaciones asíncronas en Compose
Jetpack Compose te permite usar corrutinas desde tus elementos componibles a fin de ejecutar operaciones asíncronas.
Consulta las API de LaunchedEffect
, produceState
y rememberCoroutineScope
en la documentación sobre efectos secundarios para obtener más información.
Navegació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 los elementos que admiten composición 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 MyViewModel @Inject constructor(
private val savedStateHandle: SavedStateHandle,
private val repository: ExampleRepository
) : ViewModel() { /* ... */ }
@Composable
fun MyScreen(
viewModel: MyViewModel = 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:
Groovy
dependencies { implementation 'androidx.hilt:hilt-navigation-compose:1.0.0' }
Kotlin
dependencies { implementation("androidx.hilt:hilt-navigation-compose:1.0.0") }
Cuando uses la navegación de Compose, usa siempre la función de componibilidad hiltViewModel
para obtener una instancia de ViewModel
con anotaciones @HiltViewModel
.
Esto 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 viewModel = hiltViewModel<MyViewModel>()
MyScreen(viewModel)
}
/* ... */
}
}
Si necesitas recuperar la instancia de un ViewModel
con alcance a rutas de navegación o el gráfico de navegación, usa la función de componibilidad hiltViewModel
y pasa el backStackEntry
correspondiente como 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(backStackEntry) {
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 MyScreen(flow: Flow<PagingData<String>>) {
val lazyPagingItems = flow.collectAsLazyPagingItems()
LazyColumn {
items(lazyPagingItems) {
Text("Item is $it")
}
}
}
Consulta la documentación de listas y cuadrículas para obtener más información sobre el uso de Paging en Compose.
Maps
Puedes usar la biblioteca de Maps Compose para ofrecer Google Maps en tu app. A continuación, se muestra un ejemplo de uso:
val singapore = LatLng(1.35, 103.87)
val cameraPositionState = rememberCameraPositionState {
position = CameraPosition.fromLatLngZoom(singapore, 10f)
}
GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = cameraPositionState
) {
Marker(
state = MarkerState(position = singapore),
title = "Singapore",
snippet = "Marker in Singapore"
)
}