O Jetpack Compose foi projetado para funcionar com a abordagem estabelecida de IU com base em visualização. Se você estiver criando um app, a melhor opção pode ser implementar toda a IU com o Compose. No entanto, se você estiver modificando um app já existente, talvez não queira migrá-lo totalmente de uma só vez. Em vez disso, você pode combinar o Compose com a implementação de design da IU existente.
Como adotar o Compose no seu app
Há duas maneiras principais de integrar o Compose com uma IU com base em visualização:
É possível adicionar elementos do Compose à IU já existente, seja criando uma tela totalmente nova com base no Compose ou adicionando elementos do Compose a uma atividade, fragmento ou layout de visualização existentes.
É possível adicionar um elemento de IU com base em visualização às funções que podem ser compostas. Isso permite adicionar visualizações do Android a um design baseado no Compose.
A migração do app inteiro para o Compose funciona melhor se feita passo a passo, com a granularidade de que o projeto precisa. É possível migrar uma tela ou até mesmo um fragmento ou qualquer outro elemento de IU reutilizável por vez. É possível usar várias abordagens diferentes:
A abordagem bottom-up começa a migrar os elementos menores da IU na tela, como um
Button
ou umaTextView
, seguidos pelos elementosViewGroup
até que tudo seja convertido em funções que podem ser compostas.A abordagem top-down começa a migrar os fragmentos ou contêineres de visualização, como
FrameLayout
,ConstraintLayout
ouRecyclerView
, seguidos pelos elementos de IU menores na tela.
Essas abordagens pressupõem que cada tela esteja autônoma, mas também é possível migrar a IU compartilhada, como um sistema de design, para o Jetpack Compose. Consulte abaixo Como migrar a IU compartilhada para saber mais.
APIs de interoperabilidade
Ao adotar o Compose no seu app, as IUs do Compose e as baseadas em visualização podem ser combinadas. Veja uma lista de APIs, recomendações e dicas para facilitar a transição para o Compose.
Compose em visualizações do Android
É possível adicionar uma IU com base no Compose a um app já existente que usa um design com base em visualização.
Para criar uma tela nova e totalmente baseada no Compose, faça sua
atividade chamar o método setContent()
e transmitir as
funções que podem ser compostas que você quer usar.
class ExampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { // In here, we can call composables!
MaterialTheme {
Greeting(name = "compose")
}
}
}
}
@Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
Esse código é parecido com o que você encontraria em um app feito inteiramente com o Compose.
Se você quiser incorporar o conteúdo da IU do Compose em um fragmento ou um layout de visualização
já existente, use ComposeView
e chame o método
setContent()
dele. ComposeView
é um View
para Android.
É necessário anexar a ComposeView
a uma
ViewTreeLifecycleOwner
.
ViewTreeLifecycleOwner
permite que a visualização seja anexada e desanexada
repetidamente enquanto a composição é preservada. ComponentActivity
,
FragmentActivity
e AppCompatActivity
são exemplos de classes que
implementam ViewTreeLifecycleOwner
.
Você pode colocar a ComposeView
no seu layout XML como qualquer outra View
:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/hello_world"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello Android!" />
<androidx.compose.ui.platform.ComposeView
android:id="@+id/compose_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
No código-fonte do Kotlin, infle o layout usando o recurso
de layout definido no XML. Em seguida, acesse a
ComposeView
usando o ID do XML e chame setContent()
para usar o Compose.
class ExampleFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout for this fragment
return inflater.inflate(
R.layout.fragment_example, container, false
).apply {
findViewById<ComposeView>(R.id.compose_view).setContent {
// In Compose world
MaterialTheme {
Text("Hello Compose!")
}
}
}
}
}
Figura 1. Isso mostra a saída do código que adiciona elementos do Compose a uma
hierarquia de IU de visualização. A mensagem "Hello Android!" é exibida por um
widget TextView
. A mensagem "Hello Compose!" é exibida por um
elemento de texto do Compose.
Também será possível incluir uma ComposeView
diretamente em um fragmento se a tela cheia
for criada com o Compose, o que permite evitar totalmente o uso de um arquivo de layout XML.
class ExampleFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setContent {
MaterialTheme {
// In Compose world
Text("Hello Compose!")
}
}
}
}
}
Se houver vários elementos ComposeView
no mesmo layout, cada um precisará ter um
ID exclusivo para que savedInstanceState
funcione. Há mais informações
sobre isso na seção SavedInstanceState
.
class ExampleFragment : Fragment() {
override fun onCreateView(...): View = LinearLayout(...).apply {
addView(ComposeView(...).apply {
id = R.id.compose_view_x
...
})
addView(TextView(...))
addView(ComposeView(...).apply {
id = R.id.compose_view_y
...
})
}
}
}
Os IDs ComposeView
são definidos no arquivo res/values/ids.xml
:
<resources>
<item name="compose_view_x" type="id" />
<item name="compose_view_y" type="id" />
</resources>
Visualizações do Android no Compose
É possível incluir uma hierarquia de visualização do Android em uma IU do Compose. Essa abordagem será
útil principalmente se você quiser usar elementos da IU que ainda não estão disponíveis no
Compose, como
AdView
ou
MapView
.
Essa abordagem também permite reutilizar visualizações personalizadas que você pode ter criado.
Para incluir um elemento ou uma hierarquia de visualização, use a AndroidView
que pode ser composta.
AndroidView
recebe um lambda que retorna um
View
. AndroidView
também fornece um callback update
que é chamado quando a visualização é inflada. O AndroidView
faz a recomposição
sempre que um State
lido dentro do callback muda.
@Composable
fun CustomView() {
val selectedItem = remember { mutableStateOf(0) }
// Adds view to Compose
AndroidView(
modifier = Modifier.fillMaxSize(), // Occupy the max size in the Compose UI tree
factory = { context ->
// Creates custom view
CustomView(context).apply {
// Sets up listeners for View -> Compose communication
myView.setOnClickListener {
selectedItem.value = 1
}
}
},
update = { view ->
// View's been inflated or state read in this block has been updated
// Add logic here if necessary
// As selectedItem is read here, AndroidView will recompose
// whenever the state changes
// Example of Compose -> View communication
view.coordinator.selectedItem = selectedItem.value
}
)
}
@Composable
fun ContentExample() {
Column(Modifier.fillMaxSize()) {
Text("Look at this CustomView!")
CustomView()
}
}
.
Para incorporar um layout XML, use a API
AndroidViewBinding
,
que é fornecida pela biblioteca androidx.compose.ui:ui-viewbinding
. Para
isso, seu projeto precisa ativar a vinculação de visualizações.
O AndroidView
, assim como muitos outros elementos compostos integrados, aceita um parâmetro Modifier
que pode ser usado, por exemplo, para definir a posição dele no elemento
composto pai.
@Composable
fun AndroidViewBindingExample() {
AndroidViewBinding(ExampleLayoutBinding::inflate) {
exampleView.setBackgroundColor(Color.GRAY)
}
}
Como chamar o framework do Android no Compose
O Compose está vinculado rigidamente às classes do framework do Android. Por exemplo, ele
é hospedado em classes de visualização do Android, como Activity
ou Fragment
, e pode precisar usar
classes do framework do Android como Context
, recursos do sistema,
Service
ou BroadcastReceiver
.
Para saber mais sobre recursos do sistema, consulte a documentação Recursos no Compose.
Classes Composition Locals
As classes CompositionLocal
permitem passar dados implicitamente usando funções que podem ser compostas. Em geral, elas
recebem um valor em determinado nó da árvore da IU. Esse valor pode
ser usado pelos descendentes compostos sem declarar o CompositionLocal
como um parâmetro na função composta.
O CompositionLocal
é usado para propagar valores para tipos de framework do Android no
Compose, como Context
, Configuration
ou View
, em que o código do Compose
é hospedado com o LocalContext
,
LocalConfiguration
ou
LocalView
correspondente.
As classes CompositionLocal
têm o prefixo Local
para melhor
detecção do dispositivo com o preenchimento automático no ambiente de desenvolvimento integrado.
Acesse o valor atual de um CompositionLocal
usando a propriedade
current
. Por exemplo, o código abaixo cria uma visualização personalizada usando o Context
disponível nessa parte da árvore de IU do Compose chamando LocalContext.current
.
@Composable
fun rememberCustomView(): CustomView {
val context = LocalContext.current
return remember { CustomView(context).apply { /*...*/ } }
}
Para um exemplo mais completo, confira a seção Estudo de caso: BroadcastReceivers no fim deste documento.
Outras interações
Caso não haja um utilitário definido para a interação necessária, a prática recomendada é seguir as diretrizes gerais do Compose, o fluxo de dados desce, os eventos sobem, discutidas com mais detalhes em Trabalhando com o Compose. Por exemplo, essa função que pode ser composta inicia uma atividade diferente:
class ExampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// get data from savedInstanceState
setContent {
MaterialTheme {
ExampleComposable(data, onButtonClick = {
startActivity(/*...*/)
})
}
}
}
}
@Composable
fun ExampleComposable(data: DataExample, onButtonClick: () -> Unit) {
Button(onClick = onButtonClick) {
Text(data.title)
}
}
Integração com bibliotecas comuns
Para ver como o Compose é integrado a bibliotecas comuns, como
ViewModel
, Flow
,
Paging
ou
Hilt
, confira o Guia de
integração do Compose com bibliotecas comuns.
Temas
Seguindo o Material Design, o uso da
biblioteca Material Design Components for
Android (MDC)
é a maneira recomendada de aplicar temas a apps Android. Conforme abordado na
documentação de temas no Compose, o Compose implementa
esses conceitos com a
função composta
MaterialTheme
.
Ao criar novas telas no Compose, aplique um
MaterialTheme
antes de elementos compostos que emitem a IU da biblioteca
Material Design Components. Os componentes do Material Design (Button
, Text
etc.) dependem da existência de um
MaterialTheme
, e o comportamento deles fica indefinido sem isso.
Todas as amostras do Jetpack Compose usam um
tema personalizado do Compose criado sobre
MaterialTheme
.
Várias fontes da verdade
Um app provavelmente tem uma grande quantidade de temas e estilos para
visualizações. Quando você introduz o Compose em um app existente, é necessário migrar o
tema para usar
MaterialTheme
em qualquer tela do Compose. Isso significa que os temas do seu app terão duas fontes da
verdade: o tema baseado na visualização e o tema do Compose. Qualquer mudança no estilo
precisa ser feita em vários lugares.
Se você pretende migrar o app totalmente para o Compose, é preciso criar uma versão do tema existente para ele. O problema é que, quanto antes no processo de desenvolvimento você criar o tema do Compose, mais manutenção você precisará fazer durante o desenvolvimento.
Biblioteca MDC Compose Theme Adapter
Se estiver usando a biblioteca MDC no seu app Android, a biblioteca MDC Compose Theme Adapter permitirá que você reutilize facilmente nas suas funções compostas os temas de cor, tipografia e forma presentes nos temas baseados na visualização existentes:
import com.google.android.material.composethemeadapter.MdcTheme
class ExampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MdcTheme {
// Colors, typography, and shape have been read from the
// View-based theme used in this Activity
ExampleComposable(/*...*/)
}
}
}
}
Consulte a documentação da biblioteca MDC para saber mais.
Biblioteca AppCompat Compose Theme Adapter
A biblioteca AppCompat Compose Theme Adapter permite reutilizar facilmente
temas XML AppCompat para a definição de temas no
Jetpack Compose. Ela cria um
MaterialTheme
com os valores de cor e
tipografia usando o
tema do contexto.
import com.google.accompanist.appcompattheme.AppCompatTheme
class ExampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AppCompatTheme {
// Colors, typography, and shape have been read from the
// View-based theme used in this Activity
ExampleComposable(/*...*/)
}
}
}
}
Estilos padrão dos componentes
As bibliotecas MDC e AppCompat Compose Theme Adapter não leem nenhum estilo de widget padrão definido pelo tema. Isso ocorre porque o Compose não tem o conceito de funções default compostas.
Saiba mais sobre estilos de componentes e sistemas de design personalizados na documentação de temas.
Sobreposições de tema no Compose
Ao migrar telas baseadas em visualização para o Compose, preste atenção aos usos do atributo
android:theme
. É provável que você precise de um novo
MaterialTheme
nessa parte da árvore de IU do Compose.
Leia mais sobre isso no Guia de temas.
Animações WindowInsets e IME
Você pode processar WindowInsets
usando a
biblioteca accompanist-insets,
que fornece elementos que podem ser compostos e modificadores para gerenciá-los nos seus layouts, além de
compatibilidade com animações
do IME.
class ExampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
MaterialTheme {
ProvideWindowInsets {
MyScreen()
}
}
}
}
}
@Composable
fun MyScreen() {
Box {
FloatingActionButton(
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(16.dp) // normal 16dp of padding for FABs
.navigationBarsPadding(), // Move it out from under the nav bar
onClick = { }
) {
Icon( /* ... */)
}
}
}
Figura 2. Animações do IME usando a biblioteca accompanist-insets.
Consulte a documentação da biblioteca accompanists-insets para saber mais.
Como gerenciar mudanças no tamanho da tela
Ao migrar um app que usa layouts XML diferentes, dependendo do tamanho
da tela, use o elemento
BoxWithConstraints
para saber o tamanho mínimo e máximo que um composto pode ocupar.
@Composable
fun MyComposable() {
BoxWithConstraints {
if (minWidth < 480.dp) {
/* Show grid with 4 columns */
} else if (minWidth < 720.dp) {
/* Show grid with 8 columns */
} else {
/* Show grid with 12 columns */
}
}
}
Fonte da verdade da arquitetura e do estado
Os padrões da arquitetura do fluxo de dados unidirecional (UDF, na sigla em inglês) funcionam perfeitamente com o Compose. Caso o app use outros tipos de padrão de arquitetura, como o Model View Presenter (MVP), recomendamos migrar essa parte da IU para a arquitetura UDF antes ou durante a adoção do Compose.
ViewModels no Compose
Se você usar a biblioteca Architecture Components
ViewModel, poderá acessar um
ViewModel
em qualquer elemento composto
chamando a função
viewModel()
,
conforme explicado na documentação da integração do Compose com bibliotecas
comuns.
Ao adotar o Compose, tenha cuidado ao usar o mesmo tipo ViewModel
em
diferentes elementos compostos, considerando que os elementos ViewModel
seguem os escopos do ciclo de vida da visualização. O
escopo será a atividade do host, o fragmento ou o gráfico de navegação se a
biblioteca Navigation for usada.
Por exemplo, se os elementos compostos forem hospedados em uma atividade, o viewModel()
sempre
retornará a mesma instância que só será limpa quando a atividade for concluída.
No exemplo a seguir, o mesmo usuário será recebido duas vezes, porque a mesma instância de
GreetingViewModel
será reutilizada em todos os elementos compostos na atividade
do host. A primeira instância ViewModel
criada é reutilizada em outros elementos compostos.
class ExampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MaterialTheme {
Column {
Greeting("user1")
Greeting("user2")
}
}
}
}
}
@Composable
fun Greeting(userId: String) {
val greetingViewModel: GreetingViewModel = viewModel(
factory = GreetingViewModelFactory(userId)
)
val messageUser by greetingViewModel.message.observeAsState("")
Text(messageUser)
}
class GreetingViewModel(private val userId: String) : ViewModel() {
private val _message = MutableLiveData("Hi $userId")
val message: LiveData<String> = _message
}
Como os gráficos de navegação também incluem o escopo de elementos ViewModel
, os elementos compostos que são um
destino em um gráfico de navegação têm uma instância diferente do ViewModel
.
Nesse caso, o escopo do ViewModel
é definido como o ciclo de vida do destino e
será apagado quando o destino for removido da backstack. No exemplo
a seguir, quando o usuário navega para a tela Profile, uma nova
instância do GreetingViewModel
é criada.
@Composable
fun MyScreen() {
NavHost(rememberNavController(), startDestination = "profile/{userId}") {
/* ... */
composable("profile/{userId}") { backStackEntry ->
Greeting(backStackEntry.arguments?.getString("userId") ?: "")
}
}
}
@Composable
fun Greeting(userId: String) {
val greetingViewModel: GreetingViewModel = viewModel(
factory = GreetingViewModelFactory(userId)
)
val messageUser by greetingViewModel.message.observeAsState("")
Text(messageUser)
}
Fonte da verdade do estado
Quando você adota o Compose em uma parte da IU, é possível que o código do Compose e do sistema de visualização precisem compartilhar dados. Quando possível, recomendamos encapsular esse estado compartilhado em outra classe que siga as práticas recomendadas de UDF usadas pelas duas plataformas, como em um ViewModel que expõe um stream dos dados compartilhados para emitir atualizações de dados.
No entanto, isso nem sempre é possível se os dados a serem compartilhados forem mutáveis ou estiverem estreitamente vinculados a um elemento da IU. Nesse caso, um sistema precisa ser a fonte da verdade. Ele também precisa compartilhar as atualizações de dados com o outro sistema. Como regra geral, a fonte da verdade precisa ser de propriedade do elemento que estiver mais próximo da raiz da hierarquia da IU.
Compose como a fonte da verdade
Use o
elemento composto SideEffect
para publicar o estado do Compose em um código que não seja dele. Nesse caso, a
fonte da verdade é mantida em um elemento composto que envia atualizações de estado.
Por exemplo, um
OnBackPressedCallback
precisa ser registrado para detectar o botão "Voltar" sendo pressionado em um
OnBackPressedDispatcher
.
Para comunicar se o callback precisa ser ativado ou não, use SideEffect
para atualizar o valor dele.
@Composable
fun BackHandler(
enabled: Boolean,
backDispatcher: OnBackPressedDispatcher,
onBack: () -> Unit
) {
// Safely update the current `onBack` lambda when a new one is provided
val currentOnBack by rememberUpdatedState(onBack)
// Remember in Composition a back callback that calls the `onBack` lambda
val backCallback = remember {
// Always intercept back events. See the SideEffect for a more complete version
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
currentOnBack()
}
}
}
// On every successful composition, update the callback with the `enabled` value
// to tell `backCallback` whether back events should be intercepted or not
SideEffect {
backCallback.isEnabled = enabled
}
// If `backDispatcher` changes, dispose and reset the effect
DisposableEffect(backDispatcher) {
// Add callback to the backDispatcher
backDispatcher.addCallback(backCallback)
// When the effect leaves the Composition, remove the callback
onDispose {
backCallback.remove()
}
}
}
Para mais informações sobre efeitos colaterais, consulte a documentação de ciclo de vida e efeitos colaterais.
Sistema de visualização como fonte da verdade
Se o sistema de visualização é proprietário do estado e o compartilha com o Compose, recomendamos que
você una o estado em objetos mutableStateOf
para torná-lo seguro para linhas de execução
no Compose. Se você usar essa abordagem, as funções compostas serão simplificadas, porque
não terão mais a fonte da verdade. Mas o sistema de visualização precisará atualizar o
estado imutável e as visualizações que usam esse estado.
No exemplo a seguir, um CustomViewGroup
contém uma TextView
e uma
ComposeView
com um elemento composto TextField
. O TextView
precisa mostrar
o conteúdo digitado pelo usuário no TextField
.
class CustomViewGroup @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0
) : LinearLayout(context, attrs, defStyle) {
// Source of truth in the View system as mutableStateOf
// to make it thread-safe for Compose
private var text by mutableStateOf("")
private val textView: TextView
init {
orientation = VERTICAL
textView = TextView(context)
val composeView = ComposeView(context).apply {
setContent {
MaterialTheme {
TextField(value = text, onValueChange = { updateState(it) })
}
}
}
addView(textView)
addView(composeView)
}
// Update both the source of truth and the TextView
private fun updateState(newValue: String) {
text = newValue
textView.text = newValue
}
}
Como migrar uma IU compartilhada
Caso você esteja migrando gradualmente para o Compose, talvez precise usar elementos de
IU compartilhados no Compose e no sistema de visualização. Por exemplo, caso seu app tenha um
componente CallToActionButton
personalizado, pode ser necessário usá-lo nas telas do Compose
e nas baseadas em visualizações.
No Compose, os elementos de IU compartilhados se tornam elementos compostos que podem ser reutilizados em
todo o app, independentemente de o elemento ser estilizado usando XML ou ser uma visualização personalizada. Por
exemplo, você pode criar um elemento CallToActionButton
composto para o componente Button
de chamada para ação personalizado.
Para usar esse elemento em telas baseadas em visualização, é necessário criar um
wrapper de visualização personalizado que se estenda de AbstractComposeView
. No
Content
substituído, coloque o elemento composto que você criou incluído no seu tema
do Compose, conforme mostrado no exemplo abaixo:
@Composable
fun CallToActionButton(
text: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Button(
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.secondary
),
onClick = onClick,
modifier = modifier,
) {
Text(text)
}
}
class CallToActionViewButton @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0
) : AbstractComposeView(context, attrs, defStyle) {
var text by mutableStateOf<String>("")
var onClick by mutableStateOf<() -> Unit>({})
@Composable
override fun Content() {
YourAppTheme {
CallToActionButton(text, onClick)
}
}
}
Observe que os parâmetros compostos se tornam variáveis mutáveis dentro da visualização
personalizada. Isso torna a visualização personalizada CallToActionViewButton
inflável e utilizável,
por exemplo, com vinculação de visualizações, assim como uma
visualização tradicional. Veja o exemplo abaixo:
class ExampleActivity : Activity() {
private lateinit var binding: ActivityExampleBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityExampleBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.callToAction.apply {
text = getString(R.string.something)
onClick = { /* Do something */ }
}
}
}
Caso o componente personalizado tenha um estado mutável, confira a seção de fonte da verdade do estado acima.
Teste
É possível testar o código combinado da visualização e do Compose usando a
API
createAndroidComposeRule()
. Para ver mais informações, consulte Como testar o layout do Compose.
Estudo de caso: BroadcastReceivers
Para ver um exemplo mais realista dos recursos que você quer migrar ou implementar
no Compose e para demonstrar o CompositionLocal
e os efeitos
colaterais, digamos que um
BroadcastReceiver
precise ser registrado usando
uma função que pode ser composta.
A solução utiliza LocalContext
para usar o contexto atual e
os efeitos colaterais de rememberUpdatedState
e DisposableEffect
.
@Composable
fun SystemBroadcastReceiver(
systemAction: String,
onSystemEvent: (intent: Intent?) -> Unit
) {
// Grab the current context in this part of the UI tree
val context = LocalContext.current
// Safely use the latest onSystemEvent lambda passed to the function
val currentOnSystemEvent by rememberUpdatedState(onSystemEvent)
// If either context or systemAction changes, unregister and register again
DisposableEffect(context, systemAction) {
val intentFilter = IntentFilter(systemAction)
val broadcast = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
onSystemEvent(intent)
}
}
context.registerReceiver(broadcast, intentFilter)
// When the effect leaves the Composition, remove the callback
onDispose {
context.unregisterReceiver(broadcast)
}
}
}
@Composable
fun HomeScreen() {
SystemBroadcastReceiver(Intent.ACTION_BATTERY_CHANGED) { batteryStatus ->
val isCharging = /* Get from batteryStatus ... */ true
/* Do something if the device is charging */
}
/* Rest of the HomeScreen */
}