Уводзіны
Пры распрацоўцы прыкладання color-utils з выкарыстаннем Compose для Web я сутыкнуўся з праблемай, што remember
не хацеў забываць стан. Гэта быў вельмі раздражняльны вопыт, бо я адчуваў сябе па-дурному: Composable функцыя перакампанавана з новым значэннем, але remember
па-ранейшаму захоўвала старое значэнне.
Як звычайна, ніякай магіі тут няма, і адказ даволі просты, таму гэты артыкул не будзе вельмі доўгі.
Compose - гэта ўсё аб стане. У прыкладанні ў мяне было два ўзроўні стану:
- “domain” - бягучае значэнне
Color
. Гэты аб’ект адлюстроўвае сапраўдны колер. - “ui” - стан некаторых элементаў кіравання (тэкставых палёў), з дапамогай якіх карыстальнік можа змяняць колер. Трэба адзначыць, што бягучы стан карыстальніцкага інтэрфейсу неабавязкова можна пераўтварыць у правільны аб’ект
Color
. Напрыклад, тэкставае поле можа быць пустым - гэта азначае, што карыстальнік знаходзіцца ў працэсе змены значэння колеру, і мы не можам стварыць аб’ектColor
з несапраўднымі дадзенымі.
Значэнне дамена захоўвалася як зменлівы стан, які можа быць зменены пры змене колеру:
var foregroundColor by remember { mutableStateOf(Color.White) }
ColorPicker(foregroundColor) {
foregroundColor = it
}
Значэнне карыстацкага інтэрфейсу было атрымана з дамена і захавала бягучы стан адпаведнага тэкставага поля. Новае значэнне распаўсюджвалася на ўзровень “дамена” толькі тады, калі яно было сапраўдным:
var red: Int? by remember { mutableStateOf(color.red) }
Input(InputType.Number) {
value(value ?: Double.NaN)
style {
onInput {
val validated = validateRgbValue(it.value)
red = validated
if (validated != null) {
onChanged(color.copy().apply { this.red = validated })
}
}
}
}
Адзін аб’ект Color
адпавядае некалькім тэкставым палям, якія змяняюць альфа-узровень, чырвоны, зялёны, сіні і hex значэнні. Кожны раз, калі нейкае значэнне змяняецца на сапраўднае, ствараецца і распаўсюджваецца новы аб’ект Color
. Змена значэння Color
запускае рэкампазіцыю ўнутраных Composable функцый з новымі значэннямі, якія змяняюць значэнні тэкставых палёў на новыя.
Праблема заключалася ў тым, што пры такім змене, напрыклад, чырвонага значэння не выклікалася змена ў hex тэксту з новым значэннем, нават калі выклікалася рэкампазіцыя. Я нават паглядзеў на фактычныя значэнні:
println("${color.hex}, $hex")
Пасля таго, як новы колер быў абраны пасля рэкампазіцыі, ён надрукаваў:
ffaaffff, ffffffff
Такім чынам, функцыя была выклікана з новым значэннем Color
, але remember
па-ранейшаму забяспечвала пачатковае значэнне.
Прычына гэтага ў тым, што remember
запамінае значэнне пры рэкампазіцыі - гэта як асноўная асаблівасць гэтага метаду.
Каб перапісаць запомненае значэнне, нам трэба яўна сказаць remember
абнавіць значэнне, указаўшы ключ:
public inline fun <T> remember(key1: kotlin.Any?, calculation: @androidx.compose.runtime.DisallowComposableCalls () -> T): T
Пры змене ключа remember
прыме новае значэнне і запомніць яго.
Такім чынам, калі я дадаў новае значэнне колеру ў якасці ключа, як:
var red: Int? by remember(color) { mutableStateOf(color.red) }
Усё пачало працаваць як трэба.
Шчаслівага кадавання!