Комбинация состояний в Jetpack Compose

К примеру мне нужно показать диалог считывания, я могу создать два состояния

var showDialog by remember { mutableStateOf(true) }
var progress by remember { mutableStateOf(0.5f) }

но мне не нравится создавать много состояний, тогда

open class ReadState {
  val progress:Float = 0.0f
  var isReading = false
}

val readState: MutableSharedFlow<ReadState> = MutableSharedFlow()
...
val readState by viewModel.readState.collectAsState(ReadState.Initial)

но теперь мне нужно создавать новый объект ReadState каждый раз когда я обновляю progress, даже учитывая что MutableSharedFlow может посылать тот же объект collectAsState() не делает рекомпозицию.

Какие еще есть варианты чтобы состояние было комплексное но и без создания многих объектов?


Ответы (1 шт):

Автор решения: Wlad

для компоуза хорошим подходом является работать со стейтами.
т.е. вы делаете какой-то data class StateForMyFragment где в полях описывается абсолютно все данные, которые вам нужные.
важное условие: все поля должны быть неизменными val

следующим шагом будет использование StateFlow а не SharedFlow
основную разницу между этими видами горячих потоков почитайте отдельно. но ключевое сейчас - создаем StateFlow<StateForMyFragment>

все изменения в этом флоу выполняйте через state.update{it.copy($изменяем_наименованые_параметры)}
можно и _state.value = _state.value.copy(), но через update - покрасивее и более оптимизировано под капотом.
а через .copy() - потому что у нас поля val

если в StateFlow будут var'ы, то их изменение никак не сообщат подписчикам "тут новое значение".
есть подозрение, что mutableStateOf работает по такому же принципу и поэтому у вас

даже учитывая что MutableSharedFlow может посылать тот же объект collectAsState() не делает рекомпозицию.

но это не точно, в этот вопрос я не погружался

и в корневой compose-функции вызываем val mState by state.collectAsStateWithLifecycle()
(скорее всего еще надо будет импортировать

implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.3")

из корневой compose-функции должно быть много выходов на мелкие compose-функции. чем больше их, чем они мельче - тем меньше будет ненужных рекомпозиций.
и уже в них передавайте данные из mState

таким подходом вы будете держать абсолютно всю информацию об экране в объекте одного класса.
получать (прослушивать / коллектить) будете только в одном месте - в корне. что в любом случае будет вызывать рекомпозицию всего экрана.
но если ваша compose-функция будет состоять из мелких (которые зависят от разных параметров mState) то рекомпозицию будут происходить только в тех местах, где что-то поменялось (отдельные блоки).

данный подход являются частью MVI-паттерна. можете и про него почитать.

→ Ссылка