Jetpack Compose: State

Android Developers · 121K views · 2021 · 5:45

Open on YouTube

Software architecture & designPython, Ruby & scripting languagesexplainerdated

Why Stack Overflow recommends it

Stack Overflow users link it as the go-to explanation of why Compose needs observable state (mutableStateOf, mutableStateListOf, LiveData.observeAsState) whenever a UI fails to update.

Recommended by 2 people across 9 different months, 2021 to 2022.

What it is

A short official Android Developers video in which engineer Florina Muntenescu explains how state flows through a Jetpack Compose app. It covers observable state, retaining state across recompositions and configuration changes, and structuring composables for good data flow.

What people said

Recommends Phil Dukhov · 2021-09 · Stack Overflow · on “Why updating PreferenceManager doesn't trigger recomposition in Jetpack Compose?”

Om is completely right about the reasons why your code doesn't work, and his answer will work. To understand why you need a MutableState in compose I suggest you start with documentation [LINK] , including this youtube video [this video] which explains the basic principles. But PreferenceManager [LINK] is deprecated and now you can use DataStore [LINK] instead. With compose it can be used like this: @Composable fun <T> rememberPreference( key: Preferences.Key<T>, defaultValue: T, ): MutableState<T> { val coroutineScope = rememberCoroutineScope() val context = LocalContext.current val state = remember { context.dataStore.data .map { it[key] ?: defaultValue } }.collectAsState(initial = defaultValue

Recommends Phil Dukhov · 2021-09 · Stack Overflow · on “How to update compose function with newer data?”

It's a pretty simple thing with the state in Compose: var pickedImage by remember { mutableStateOf<String?>(null) } Image( painter = pickedImage?.let { // this is from coil.compose rememberImagePainter(it) } ?: painterResource(id = R.drawable.my_image), "...", ) Button(onClick = { // pick image pickedImage = "new/image/url" }) { Text("Pick image") } I suggest you start with documentation [LINK] , including this youtube video [this video] which explains the basic principles.

Recommends Phil Dukhov · 2021-09 · Stack Overflow · on “How to do more efficient render a pdf with PDFRender and show in LazyColumn in JetPack Compose”

You should not make any heavy calculations or db/network calls directly from the composable functions. All composite functions are view constructors and can be reconfigured (e.g., recalled) whenever the values of a changeable state change, and for animations - up to every frame. In your code, getFile query is executed when a new item is added to the list of changeable states making an endless cycle. I suggest you start with the state in compose [LINK] documentation, including this youtube video [this video] which explains the basic principles. In your case, the fething code should be moved to view model [LINK] : The PDFScreenViewModel object will be created when the view appears and destroyed whe

Recommends Phil Dukhov · 2021-09 · Stack Overflow · on “Why need the author to add the keyword remember in this @Composable?”

You need to use remember to prevent recomputation during recomposition. Your example works without remember because this view will not recompose while you scroll through it. But if you use animations, add state variables or use a view model, your view can be recomposed many times(when animating up to once a frame), in which case getting data from the repository will be repeated many times, so you need to use remember to save the result of the computation between recompositions. So always use remember inside a view builder if the calculations are at least a little heavy, even if right now it looks like the view is not gonna be recomposed. You can read more about the state in compose in docume

Recommends Phil Dukhov · 2021-10 · Stack Overflow · on “How to use ViewModel with Jetpack Compose?”

Your Composable [LINK] will not pick up the changes in the ViewModel.number value unless the value is contained within a State [LINK] object. You can wrap the value in a mutable State [LINK] property with mutableStateOf() [LINK] or mutableIntStateOf() [LINK] , as follows: class ViewModel: ViewModel() { var number : Int by mutableIntStateOf(0) private set fun addNumber(){ number++ } } Or you can declare the property as a LiveData [LINK] or Flow [LINK] and then reference the value as a State [LINK] object in your Composable [LINK] with the LiveData.observeAsState() [LINK] or Flow.collectAsState [LINK] extension function. I suggest you start with the State and Jetpack Compose [LINK] documentati

Recommends Phil Dukhov · 2021-10 · Stack Overflow · on “How to call suspend function in Activity?”

You can get book list as soon as your view model is created using init , and store the result value in a mutable state holder: by using mutable state you're making sure your view will update when new value comes. Check out more about state in compose documentation [LINK] , including this youtube video [this video] which explains the basic principles. @HiltViewModel class BookViewModel @Inject constructor( private val repository: BookRepository ) : ViewModel() { var bookList by mutableStateOf<BookList?>(null) init { viewModelScope.launch { bookList = getBookList() } } private suspend fun getBookList() : BookList? { return repository.getBookList() } } Then you can display needed view depending on t

Recommends Phil Dukhov · 2021-11 · Stack Overflow · on “Jetpack Compose Checkbox Not Showing Changed State”

In order for mutableStateOf to trigger recomposition, the container value should be updated, e.g. specialtyList = newValue . There's no way how it can know you've changed one of inner objects. Generally speaking, data class is a great tool to be used immutable in functional programming. If you won't use var inside them, you'll exclude many mistakes. Using mutableStateListOf if your case seems much cleaner. In your view model: private val _specialtyList = mutableStateListOf( Specialty(name = "Emergency Medicine", isSelected = false), Specialty(name = "Forensic Medicine", isSelected = false), Specialty(name = "General Practitioner", isSelected = false) ) val specialtyList: List<Specialty> = _s

Recommends Phil Dukhov · 2021-11 · Stack Overflow · on “Jetpack Compose Canvas drawPath doesn't work”

remember { Path() } is caching lambda content value for next recompositions, but it cannot trigger recomposition when content on this object is changed. To make some state, which will trigger recomposition on changes in Compose, you need to use a mutable state of some kind - it's a new thing made for Compose. You can find more info about state in Compose in documentation [LINK] , including this youtube video [this video] which explains the basic principles. Storing points inside Path will not be clean, as it's not a basic type. Instead I'm using a mutable state list of points, like this: val points = remember { mutableStateListOf<Offset>() } Canvas(modifier = Modifier .fillMaxSize() .pointerInput

Recommends Phil Dukhov · 2021-11 · Stack Overflow · on “Use a custom class for the value of TextField does not survive across re-composition”

In order for mutableStateOf to trigger recomposition, the container value should be updated, e.g. strHolder = newValue . There's no way how it can know you've changed one of inner values. data class (with val -only properties) is a great tool to be used in functional programming for such cases: copying the data will save your from such mistakes: data class StrHolder(val s: String) var strHolder by remember { mutableStateOf(StrHolder("")) } Text("My text is: ${strHolder.s}") TextField( value = strHolder.s, onValueChange = { strHolder = strHolder.copy(s = it) }, ) You can find more info about state in Compose in documentation [LINK] , including this youtube video [this video] which explains the bas

Recommends Phil Dukhov · 2021-11 · Stack Overflow · on “Why I can't invoke lambda in the Jetpack Compose”

onTabSelected is marked as @Composable , that's why you can't invoke it inside onClick . You can only invoke @Composable inside an other @Composable , not in side effects. Remove this annotation, and you probably need to redesign the view which is using Tabs . You're doing the same mistake again: onTabSelected is a side effect. You can't add composable views to the view hierarchy inside side effects. Instead, you should use a state variable, and change the view depending on this state, e.g.: var selectedTab by remember { mutableStateOf(0) } ScrollableTabRow( selectedTabIndex = pagerState.currentPage, indicator = { tabPositions -> TabRowDefaults.Indicator( modifier.pagerTabIndicatorOffset(pag