-1

I wanna do some network request and show the list results in jetpack compose. I pass the request params to list widget. But the list widget cannot recompose after the map changed, even added a key.

Below is a little demo: The int value should change after tapped the button.

Please help me to fix this.

@Preview
@Composable
fun test() {
    var map by remember { mutableStateOf(mapOf<String, Any>()) }
    Column {
        Button(
            onClick = {
                map = HashMap(map)
            }
        ) {
            Text(
                text = "Tap"
            )
        }

        key(map) { testWidget1(map = map) }
    }

}

@Composable
fun testWidget1(map: Map<String, Any>) {
    var testInt by remember { mutableStateOf(0) }
    LaunchedEffect(map) {
        testInt += 1
        request()
    }
    Text(
        text = "$testInt"
    )


}

fun request() {

}

2 Answers 2

0

Objective:
The goal was to update an integer value upon a button tap and ensure the UI (specifically testWidget1) responds accordingly.

Issue Identified:
On each button click, the map was being replaced with a new HashMap instance instead of updating the existing one. As a result, testWidget1 was not being recomposed, since the recomposition depends on observing changes to the map object itself.

Solution Implemented:
To address this, I modified the logic to update the existing map rather than reassigning it. I introduced a counter that increments with each button click. This counter is used to generate and insert a new key-value pair into the existing map.

By updating the map in place—while retaining existing entries—testWidget1 now detects the changes and recomposes with the new values.

Each click adds new entries like key1, key2, key3, and so on, which ensures the map is consistently modified and triggers the expected UI updates.

@Preview
@Composable
fun test() {
    var map by remember { mutableStateOf(mapOf<String, Any>()) }
    var count by remember { mutableStateOf(0) }
    Column {
        Button(
            onClick = {
                count += 1
                map = map + ("key$count" to "value")
            }
        ) {
            Text(
                text = "Tap"
            )
        }
         testWidget1(map = map)
    }

}

@Composable
fun testWidget1(map: Map<String, Any>) {
    var testInt by remember { mutableStateOf(0) }
    LaunchedEffect(map) {
        testInt += 1
        request()
    }
    Text(
        text = "$testInt"
    )


}

fun request() {

}
New contributor
sn_2601 is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
5
  • While this code may answer the question, it would be better to include some context, explaining how it works and why it solves the issue at hand.
    – tyg
    Commented Apr 22 at 23:14
  • Yeah, it works. And to avoid having too many keys in the map, I just update the time key with the current timestamp: map["time"] = java.util.Date().
    – magic_9527
    Commented Apr 23 at 1:29
  • Thanks for the suggestion! I've added context explaining that the issue was due to reassigning a new HashMap, which blocked recomposition. Updating the existing map and using a counter to add unique keys resolved it by ensuring state changes are properly detected. Let me know if anything else needs clarification! @tyg
    – sn_2601
    Commented 2 days ago
  • That’s a smart approach - updating a single key like "time" with the current timestamp keeps the map clean while still triggering recomposition. Thanks for sharing! If the solution works for you, a quick upvote would be appreciated! 😊 @magic_9527
    – sn_2601
    Commented 2 days ago
  • By comment of @tyg, the key is change the content of the map. If I do like this ` onClick = { val tmpMap = map.toMutableMap() tmpMap["time"] = java.util.Date() map = tmpMap.toMap() }`, it also works.
    – magic_9527
    Commented 2 days ago
0

There are two issues:

  1. testWidget1 isn't recomposed because the map does not change. Although you create a new object with HashMap(map), Compose compares objects for equality, not referential identity. And that means that the actual content of the map needs to change for Compose to detect it as something different. That is not the case in your example.

  2. By using key you explicitly tell Compose to throw away the old state of testWidget1 and create a new one whenever map changes. Therefore, even if map would change, you would still reset testInt everytime, starting from 0 (and setting it to 1 in the LaunchedEffect). You usually don't need key in a regular composable. Just remove it and call testWidget1(map = map) directly.

3
  • So what can I do to force testWidget1 to recompose when on tap, no matter map content change or not? I thought a new map reference can make this work but the reality is not.
    – magic_9527
    Commented Apr 22 at 9:30
  • You can't - and you don't need to, because nothing changed. That's not how Compose works. If you feel you need it, refactor your code so you don't anymore. If you have difficulties with that you can ask a new question with more details on what you actually want to achieve.
    – tyg
    Commented Apr 22 at 9:50
  • Thanks for explaining the theory. Have finished this by adding a new value to map.
    – magic_9527
    Commented 2 days ago

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.