Прослушивать изменения только в конкретном объекте в 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параметр, это проще сделать на уровне представления.

Другие вопросы по тегам