当 ViewModel 从不同的可组合项更新时可组合项不重组

Composable not recomposing when ViewModel updated from a different Composable

我正在使用 Jetpack Compose 开发 Android 应用程序。该应用程序显示项目列表。 它还有一个包含搜索栏的顶部栏。

我们有 3 个可组合项:项目列表、搜索栏组件和应用栏 (应用栏包含搜索栏)。

相同的 ViewModel class 被注入列表可组合项和搜索栏可组合项(使用 Hilt)。 ViewModel 具有项目列表:

var items by mutableStateOf<List<Item>?>(null)

以及搜索栏调用的搜索功能,用于过滤这些项目。

问题如下:列表可组合项在搜索项目时未重组。

注意:

所以似乎只有当搜索栏在列表中时列表才会重新组合。

当搜索栏在应用栏中时,如何让它工作?

谢谢,

编辑: 通过在初始化 ViewModel 时生成随机值,Hilt 似乎在 Searchbar 可组合项和项目可组合项中注入了两个不同的实例。

是否可以将 ViewModel 作为单例注入?还是应该将我的数据保存在存储库中,然后将这些数据提供给 ViewModel?

这是不同 classes 的代码:

Searchbar.kt

@Composable
fun SearchBar(itemViewModel: ItemViewModel = hiltViewModel()){
var searchText by remember { mutableStateOf("") }

    BasicTextField(
        singleLine = true,
        cursorBrush = SolidColor(Color.White),
        value = searchText,
        onValueChange = {
            searchText = it
            itemViewModel.searchByLabel(it)
        },
        modifier = Modifier
            .fillMaxWidth()
            .height(28.dp),
        textStyle = LocalTextStyle.current.copy(
            color = Color.White,
            fontSize = MaterialTheme.typography.body2.fontSize
        ),
        decorationBox = { innerTextField ->
            Row(
                verticalAlignment = Alignment.CenterVertically,
                horizontalArrangement = Arrangement.spacedBy(4.dp)
            ) {
                Icon(
                    Icons.Filled.Search,
                    contentDescription = "Search"
                )
                Column(Modifier.weight(1f)) {
                    Box(
                        modifier = Modifier.fillMaxWidth()
                    ) {
                        if (searchText.isEmpty()) {
                            Text(
                                "Search",
                                style = LocalTextStyle.current.copy(
                                    color = Color.White.copy(alpha = 0.7f),
                                    fontSize = MaterialTheme.typography.body2.fontSize
                                )
                            )
                        }
                        innerTextField()
                    }
                }
                if (searchText.isNotEmpty()) {
                    IconButton(onClick = { searchText = "" }) {
                        Icon(Icons.Filled.Cancel, contentDescription = "Delete")
                    }
                }
            }
        }
    )
}

Items.kt

@Composable
fun Items(
    navController: NavController,
    itemViewModel: ItemViewModel = hiltViewModel(),
){
    val items = itemViewModel.items
    items?.let { items ->
        LazyColumn(
            modifier = Modifier.fillMaxWidth(),
            verticalArrangement = Arrangement.spacedBy(24.dp)
        ) {
    
            items(items) { item ->
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .clickable {
                            navController.navigate("items/${item.id}")
                        },
                    Arrangement.spacedBy(8.dp)
                ) {
                    Text(item.label)
                }
            }
        }
    } ?: run {
        Text(
            text = "No protocols",
            modifier = Modifier.fillMaxWidth()
        )
    }
}

ItemViewModel.kt

@HiltViewModel
class ItemViewModel @Inject constructor(private val repository: ItemRepository) : ViewModel() {
     var items by mutableStateOf<List<Item>?>(null)

     init{ loadItems() }

     private fun loadItems(){
         viewModelScope.launch {
             items = repository.getItems()
         }
     }

     fun searchByLabel(value: String){
         viewModelScope.launch {
             items = repository.search(value)
         }
     }
}

ItemRepository.kt

class ItemRepository(){
    suspend fun getItems(): List<Item>? {
        return listOf(Item(1, "Label 1"), Item(2, "Label 2"), Item(3, "Label 3"))
    }

    suspend fun search(value: String): List<Item>? {
        return getItems()?.filter { it.label.lowercase().contains(value.lowercase())}
    }
}

Item.kt

data class Item(var id: Int, var label: String){}

我最终将数据存储在存储库中,该存储库作为单例注入。这样,即使创建了多个 ViewModel 实例,它们仍然访问相同的数据。