Прослушивать изменения только в конкретном объекте в StateFlow типа List?
Как прослушивать только определенный элемент в списке типов MutableStateFlow? Если мы прослушаем весь список, то все представления, которые прослушивают список, если они будут изменены, перестроятся, чего я не хочу.
class ArticleViewModel: ViewModel() {
val articles = MutableStateFlow<List<Article>>( /* articles list */)
}
@Composable
fun ArticlePreview (index: Int) {
val model: ArticleViewModel: viewModel();
// Problem: Since it is listening to entire list. If the list changes, the entire ArticlePreview widgets will rebuild but I only want to change a particular item if some property of the current item changes in the list.
val previews by model.articles.collectAsState();
val currentPreview = previews[index];
Text(text = currentPreview.body);
}
4 ответа
Compose обычно достаточно умен, чтобы понять это сам: он «знает» старый список, «знает» новый список, поэтому он «знает» разницу. Затем следует перекомпоновать только разницу.
Однако для этого требуется работа с функциями равенства. Самый простой способ сделать это – убедиться, что вашArticle
этоdata class
.
если измениться, представления восстановятся сами, чего я не хочу.
Хотя это правда, что чрезмерная перестройка пользовательского интерфейса нежелательна, вам придется привыкнуть время от времени составлять перестроенный интерфейс.
Вы можете использовать в своей функции составления следующее:
val currentPreview = remember {
derivedStateOf {
previews[index]
}
}
Text(text = currentPreview.value)
The
Поскольку вы не хотите слушать весь список, вам следует иметь другой, который будет содержать только отдельныеArticle
. И у вас должна быть функция, которая передает индекс вviewModel
и это вызовет новыйmutableStateFlow
. Таким образом, весь ваш виджет не будет обновляться.
class ArticleViewModel: ViewModel() {
val articles = MutableStateFlow<List<Article>>( /* articles list */)
private val _article = MutableStateFlow<Article>()
val article = _article.asStateFlow()
fun selectedArticle(index: String) {
_article.value = articles[index]
}
}
ЭтотselectedArticle
функция вызоветarticle
поток состояний, который вы можете собрать из своего компонуемого объекта. И ваш составной виджет не будет обновляться, если ваш список изменится.
Чтобы изменить данные потока, вы можете использовать.
Он возвращает поток, чтобы иметь необязательное значение в составном элементе, вы можете построить из него поток состояний, используяstateIn
Чтобы иметь более чистый код, я использую следующие расширения — они применяются так жеtransform
как к исходным, так и к сопоставленным значениям:
fun <T, R> StateFlow<T>.mapState(
scope: CoroutineScope,
transform: (value: T) -> R,
): StateFlow<R> =
map(transform)
.stateIn(
scope,
SharingStarted.Eagerly,
transform(value)
)
Затем вы можете легко использовать его в компоновке:
val coroutineScope = rememberCoroutineScope()
val article by remember(index) {
viewModel.articles
.mapState(coroutineScope) {
it[index]
}
}.collectAsState()
В этом случаеStateFlow
жизненный цикл будет привязан к составному. Обратите внимание, чтоmapState
следует вызвать внутриremember
чтобы предотвратить дополнительную работу по рекомпозиции.
Обычно вы можете использовать тот же метод в модели представления, но в случае, если вашmap
зависит отindex
параметр, это проще сделать на уровне представления.