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(ActivityResultContracts.GetContent()) { uri: Uri? -> imageUri = uri } Column { Button(onClick = { launcher.launch("image/*") }) { Text(text = "Load Image") } Image( painter = rememberAsyncImagePainter(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 APIs 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. Agrega la siguiente dependencia a tu archivo de Gradle:
Groovy
dependencies { implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.8.5' }
Kotlin
dependencies { implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.5") }
Luego, puedes usar la función viewModel()
en tu código.
class MyViewModel : ViewModel() { /*...*/ } // import androidx.lifecycle.viewmodel.compose.viewModel @Composable fun MyScreen( viewModel: MyViewModel = viewModel() ) { // use viewModel here }
viewModel()
muestra un ViewModel
existente o crea uno nuevo. De forma predeterminada, el ViewModel
que se muestra se define en el alcance de la actividad, el fragmento o el destino de navegación que lo contiene, y se conserva mientras el alcance esté activo.
Por ejemplo, si el elemento componible se usa en una actividad, viewModel()
muestra la misma instancia hasta que finaliza la actividad o se cierra el proceso.
class MyViewModel : ViewModel() { /*...*/ } // import androidx.lifecycle.viewmodel.compose.viewModel @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 ) { /* ... */ }
Lineamientos de uso
Por lo general, accedes a instancias de ViewModel
en elementos componibles al nivel de la pantalla, es decir, cerca de un elemento componible raíz que se llama desde una actividad, un fragmento o un destino de un gráfico de Navigation. Esto se debe a que, de forma predeterminada, los ViewModel
se asignan a esos objetos a nivel de la pantalla. Obtén más información sobre el ciclo de vida y el alcance de un ViewModel
aquí.
Intenta evitar pasar instancias de ViewModel
a otros elementos componibles, ya que esto puede dificultar su prueba y dañar las vistas previas. En su lugar, pasa solo los datos y las funciones que necesitan como parámetros.
Puedes usar instancias de ViewModel
para administrar el estado de los elementos componibles a nivel de la subpantalla. Sin embargo, ten en cuenta el ciclo de vida y el alcance de ViewModel
. Si el elemento componible es independiente, te recomendamos que uses Hilt para insertar el ViewModel
y evitar tener que pasar dependencias de elementos componibles superiores.
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.
// import androidx.lifecycle.viewmodel.compose.viewModel @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
El componente Navigation proporciona compatibilidad con aplicaciones de Jetpack Compose. Consulta Cómo navegar con Compose y Cómo migrar Jetpack Navigation a Navigation Compose para obtener más información.
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() { /* ... */ } // import androidx.lifecycle.viewmodel.compose.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.2.0' }
Kotlin
dependencies { implementation("androidx.hilt:hilt-navigation-compose:1.2.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() { val navController = rememberNavController() val startRoute = "example" 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() { val navController = rememberNavController() val startRoute = "example" val innerStartRoute = "exampleWithRoute" 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.itemCount, key = lazyPagingItems.itemKey { it } ) { index -> val item = lazyPagingItems[index] Text("Item is $item") } } }
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:
@Composable fun MapsExample() { val singapore = LatLng(1.35, 103.87) val cameraPositionState = rememberCameraPositionState { position = CameraPosition.fromLatLngZoom(singapore, 10f) } GoogleMap( modifier = Modifier.fillMaxSize(), cameraPositionState = cameraPositionState ) { Marker( state = remember { MarkerState(position = singapore) }, title = "Singapore", snippet = "Marker in Singapore" ) } }
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Efectos secundarios en Compose
- El estado y Jetpack Compose
- Cómo guardar el estado de la IU en Compose