I’m currently working on a Jetpack Compose screen in my Android app, and I’ve noticed that every time I click on a button, the entire screen seems to recompose. This behavior is causing some performance issues, and I’m wondering why it’s happening and how I can optimize it.
Why does my Jetpack Compose screen recompose every time I click on a button?
Here’s a simplified version of my code:
class MainActivity : ComponentActivity() {
private lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val appContainer = (application as MyApplication).appContainer
viewModel = MainViewModel(appContainer.repository)
setContent {
MyRetrofitApplicationTheme {
val uiState =
viewModel.uiState.collectAsState()
LaunchedEffect(Unit) {
viewModel.fetchPost()
}
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
uiState.value.posts?.let { MyList(it) }
Column {
Text(text = "My Title")
MyButton(uiState.value,viewModel::updateTitle)
}
}
}
}
}
}
@Composable
fun MyList(posts: List<Post>){
LazyColumn {
itemsIndexed(posts) { _, item ->
Text(text = item.body)
}
}
}
@Composable
fun MyButton(uiState: UiState,onclick: ()->(Unit)){
MyTitle(uiState)
Button(
interactionSource = NoRippleInteractionSource(),
modifier = Modifier
.size(100.dp)
.background(Color.Blue),
onClick = onclick
) {
Text("click here")
}
}
class NoRippleInteractionSource : MutableInteractionSource {
override val interactions: Flow<Interaction> = emptyFlow()
override suspend fun emit(interaction: Interaction) {}
override fun tryEmit(interaction: Interaction) = true
}
@Composable
fun MyTitle(uiState: UiState) {
Text(text = uiState.counter.toString(), modifier = Modifier.size(100.dp))
}
class MainViewModel(private val repository: PostRepository) : ViewModel() {
private val _uiState = MutableStateFlow(UiState(null))
val uiState = _uiState.asStateFlow()
var job1 : Job? =null
fun fetchPost() {
job1 = viewModelScope.launch {
print("call api!!!!")
delay(2000)
when (val result = repository.getPosts()) {
is Resource.Error -> {
_uiState.update {
it.copy(counter = -1)
}
}
is Resource.Success -> _uiState.update {
it.copy(posts = result.data)
}
}
}
}
fun updateTitle(){
_uiState.update {
var mcounter = it.counter
mcounter ++
it.copy(counter = mcounter)
}
}
}
I tried only copying the value of the counter while updating the UI state, but my LazyList and Button get recomposed every time I click on the “update title” button. Any insights or suggestions would be greatly appreciated. Thanks in advance!
enter image description here
Navid Jl is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.