Colton Idle
08/23/2025, 5:22 PMprivate val _isInitialized = mutableStateOf(false)
val isInitialized: State<Boolean> = _isInitialized
Colton Idle
08/23/2025, 5:22 PMTolriq
08/23/2025, 5:44 PMChrimaeon
08/23/2025, 6:17 PMephemient
08/23/2025, 8:36 PMprivate val _isInitialized = mutableStateOf(false)
val isInitialized: State<Boolean>
get() = _isInitialized
but it basically works the sameChrimaeon
08/23/2025, 8:41 PMephemient
08/23/2025, 8:55 PMWinson Chiu
08/23/2025, 9:36 PMState
at all. Or even using it. Just use regular by mutableStateOf() private set
.jw
08/23/2025, 10:20 PMget
version, no need to waste bytes storing the same reference in two backing fields.Colton Idle
08/24/2025, 1:30 PMWhy do you see a problem, @Colton Idle ?mostly because it's exposing
State
. I thought that was frowned upon?Colton Idle
08/24/2025, 1:33 PM@Composable
fun MyThing(name: State<String>)
Tgo1014
08/25/2025, 7:45 AMDefinitely use theWhat "two backing fields" mean here? The mutable and the immutable one? What would be the difference of using the one from the get? Doesn't it invoke the original one and apply the immutability every time it's used?version, no need to waste bytes storing the same reference in two backing fields.get
zsmb
08/25/2025, 8:18 AM_isInitialized
). When you read it, it will read the stored value from its own backing field.
val isInitialized: State<Boolean> = _isInitialized
This, on the other hand, creates a property with no backing field, so it's not initialized in any way when created. When it's read, its custom getter will read the value of _isInitialized
and return that.
val isInitialized: State<Boolean>
get() = _isInitialized
Tgo1014
08/25/2025, 8:26 AMisInitialized
can be different from the value in _isInitialized
? Isn't it keeping reference for the same mutableStateOf
?zsmb
08/25/2025, 8:39 AM_isInitialized
.zsmb
08/25/2025, 8:42 AMval
, the result here is the same, you're just wasting a bit of memory. If there were mutable variables, it could also lead to different results, such as
class StoreTwice {
var x = 1
val y = x
}
class StoreOnce {
var x = 1
val y: Int get() = x
}
fun main() {
val twice = StoreTwice()
twice.x = 2
println(twice.y) // 1
val once = StoreOnce()
once.x = 2
println(once.y) // 2
}
David Breneisen
08/25/2025, 10:34 AMColton Idle
08/25/2025, 3:57 PMZach Klippenstein (he/him) [MOD]
08/25/2025, 5:07 PMMutableState
in these kinds of cases. And in a library, avoiding State
too if possible to reduce the API footprint. Exceptions would be helpers like collectAsState
where you explicitly want your callers to be able to use property delegation.Colton Idle
08/26/2025, 12:21 AMdarkmoon_uk
08/27/2025, 6:44 AMStateFlow
from a MutableStateFlow
,
especially on public library code that you want to somewhat harden against abuse
❌ Don't do
val _someFlow = MutableStateFlow(someInitialT)
val someFlow: StateFlow<T> get() = _someFlow
✅ Do
val _someFlow = MutableStateFlow(someInitialT)
val someFlow: StateFlow<T> = _someFlow.asStateFlow() // <= Light wrapping prevents casting back to 'Mutable'
jw
08/27/2025, 9:16 AMjw
08/27/2025, 9:16 AMzsmb
08/27/2025, 9:17 AMasStateFlow
darkmoon_uk
08/27/2025, 9:20 AMephemient
08/27/2025, 9:20 AMget() = asStateFlow()
Tolriq
08/27/2025, 9:22 AMZach Klippenstein (he/him) [MOD]
08/27/2025, 8:16 PMasStateFlow()
returns. Language features can never secure you from stuff running in the same process. If you are worried about your consumer developers being malicious, you need a process boundary not a library.Zach Klippenstein (he/him) [MOD]
08/27/2025, 8:16 PMregardless of the hypothetical downcasting caller, if the wrapper is light-weight, then there's no need to put it in a backing field either. just useThat will mess with any compose code callingget() = asStateFlow()
someFlow.collectAsState()
since it will cancel and restart collection every time due to the new instance.