You can use your favorite libraries in Compose. This section describes how to incorporate a few of the most useful libraries.
ViewModel
If you use the Architecture Components
ViewModel library, you can access a
ViewModel
from any composable by
calling the
viewModel()
function.
class ExampleViewModel() : ViewModel() { /*...*/ }
@Composable
fun MyExample() {
val viewModel: ExampleViewModel = viewModel()
// use viewModel here
}
viewModel()
returns an existing ViewModel
or creates a new one in the given
scope. The ViewModel
is retained as long as the scope is alive. For example,
if the composable is used in an activity, viewModel()
returns the same
instance until the activity is finished or the process is killed.
@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
val viewModel: ExampleViewModel = viewModel()
}
@Composable
fun MyExample2() {
val viewModel: ExampleViewModel = viewModel() // Same instance as in MyExample
}
If your ViewModel has dependencies, viewModel()
takes an optional
ViewModelProvider.Factory
as a parameter.
For more information about ViewModel
in Compose and how instances are used
with the Compose Navigation library, or activities and fragments,
see the Interoperability docs.
Streams of data
Compose comes with extensions for Android's most popular stream-based solutions. Each of these extensions is provided by a different artifact:
LiveData.observeAsState()
included in theandroidx.compose.runtime:runtime-livedata:$composeVersion
artifact.Flow.collectAsState()
doesn't require extra dependencies.Observable.subscribeAsState()
included in theandroidx.compose.runtime:runtime-rxjava2:$composeVersion
orandroidx.compose.runtime:runtime-rxjava3:$composeVersion
artifact.
These artifacts register as a listener and represent the values as a
State
. Whenever a new value
is emitted, Compose recomposes those parts of the UI where that state.value
is
used. For example, in this code, ShowData
recomposes every time
exampleLiveData
emits a new value.
@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)
}
}
Asynchronous operations in Compose
Jetpack Compose lets you execute asynchronous operations using coroutines from within your composables.
See the LaunchedEffect
, produceState
, and rememberCoroutineScope
APIs in
the Lifecycle and Side Effects guide for more
information.
Navigation
We recommend using the Compose navigation library to add navigation elements to your Compose projects. These elements let you add UI to navigate between composables while taking advantage of the Navigation component’s infrastructure and features.
Learn more about this integration in the Navigating with Compose documentation.
Hilt
Hilt is the recommended solution for dependency injection in Android apps, and works seamlessly with Compose.
The viewModel()
function mentioned in the ViewModel section automatically uses
the ViewModel that Hilt constructs with the @HiltViewModel
annotation. We've
provided documentation with information about Hilt's ViewModel
integration.
@HiltViewModel
class ExampleViewModel @Inject constructor(
private val savedStateHandle: SavedStateHandle,
private val repository: ExampleRepository
) : ViewModel() { /* ... */ }
@Composable
fun ExampleScreen(
exampleViewModel: ExampleViewModel = viewModel()
) { /* ... */ }
Hilt and Navigation
Hilt also integrates with the Compose navigation library. Add the following additional dependencies to your Gradle file:
app/build.gradle
...
dependencies {
...
implementation 'androidx.hilt:hilt-navigation-compose:1.0.0-alpha01'
}
If your @HiltViewModel
annotated ViewModel
is scoped to the navigation
graph,
use the hiltNavGraphViewModel
composable function that works with fragments
or activities that are annotated with @AndroidEntryPoint
.
For example, if ExampleScreen
is a destination in a navigation graph,
call hiltNavGraphViewModel()
to get an instance of ExampleViewModel
scoped
to the destination as shown in the code snippet below:
@Composable
fun MyApp() {
NavHost(navController, startDestination = startRoute) {
composable("example") { backStackEntry ->
// Creates a ViewModel from the current BackStackEntry
val exampleViewModel: ExampleViewModel =
viewModel(
HiltViewModelFactory(LocalContext.current, backStackEntry)
)
ExampleScreen(exampleViewModel)
}
/* ... */
}
}
If you need to retrieve the instance of a ViewModel
scoped to
navigation routes instead, pass the
destination root as a parameter:
@Composable
fun MyApp() {
NavHost(navController, startDestination = startRoute) {
navigation(startDestination = innerStartRoute, route = "Parent") {
// ...
composable("exampleWithRoute") { backStackEntry ->
val parentViewModel = hiltNavGraphViewModel<ParentViewModel>("Parent")
ExampleWithRouteScreen(parentViewModel)
}
}
}
}
Paging
The Paging
library
makes it easier for you to load data gradually and it's supported in Compose.
The Paging release
page contains
information about the extra paging-compose
dependency that needs to be added
to the project and its version.
Here's an example of the Paging library's Compose APIs:
@Composable
@OptIn(ExperimentalLazyDsl::class)
fun MyExample(flow: Flow<PagingData<String>>) {
val lazyPagingItems = flow.collectAsLazyPagingItems()
LazyColumn {
items(lazyPagingItems) {
Text("Item is $it")
}
}
}
Check out the Lists documentation for more information about using Paging in Compose.
Image loading
The Accompanist libraries bring easy-to-use composables to fetch and display images from external sources, such as network. It has integrations with Coil and Glide.
Here's an example of CoilImage
from the
com.google.accompanist:accompanist-coil
artifact:
@Composable
fun MyExample() {
CoilImage(
data = "https://picsum.photos/300/300",
loading = {
Box(Modifier.fillMaxSize()) {
CircularProgressIndicator(Modifier.align(Alignment.Center))
}
},
error = {
Image(painterResource(R.drawable.ic_error), contentDescription = "Error")
}
)
}