https://kotlinlang.org logo
#compose
Title
# compose
m

Mark Murphy

08/19/2019, 5:13 PM
Should the automatic recomposition feature of
@Model
be working in the current Compose code? I created this based on the
@Model
KDoc:
Copy code
@Model
    data class AgreementViewState(var terms: Boolean = true, var privacy: Boolean = false) {
        val canProceed = terms && privacy
    }

    @Composable
    private fun Agreement() {
        Padding(8.dp) {
            val viewState = +model { AgreementViewState() }

            Column(mainAxisAlignment = MainAxisAlignment.Start, crossAxisAlignment = CrossAxisAlignment.Start) {
                Row(mainAxisAlignment = MainAxisAlignment.Start) {
                    Checkbox(
                        checked = viewState.terms,
                        onCheckedChange = { viewState.terms = it }
                    )
                    Text(text = "I agree to the terms and conditions")
                }
                Row(mainAxisAlignment = MainAxisAlignment.Start) {
                    Checkbox(
                        checked = viewState.privacy,
                        onCheckedChange = { viewState.privacy = it }
                    )
                    Text(text = "I agree to the privacy policy")
                }
                Text(text = if (viewState.canProceed) "You may proceed" else "Please indicate your agreement")
            }
        }
    }
The initial state works (the top
Checkbox
is checked). And via logging I can confirm that my
onCheckedChange
lambdas are being called. However, the UI is not being recomposed as I update the
AgreementViewState
, so the checkboxes and text remain fixed. By contrast, this works with automatic recomposition as I update the `checkboxState`:
Copy code
@Composable
    private fun SimpleCheckbox() {
        Padding(8.dp) {
            val checkboxState = +state { true }

            Checkbox(
                checked = checkboxState.value,
                onCheckedChange = { checkboxState.value = it }
            )
        }
    }
I'm trying to determine if there's something that I'm doing wrong or if this is just a current limitation. Thanks!
m

matvei

08/19/2019, 5:26 PM
Hey! Is this @model class defined as nested or it's top level?
There's an issue right now that only top level classes annotated as @model will work, as far as I remember
m

Mark Murphy

08/19/2019, 5:27 PM
ah, it's nested -- let me try moving it
Well, the model is now behaving differently... but it is crashing right away, apparently in generated code. But, that's probably still progress -- thanks for the help!
m

matvei

08/19/2019, 5:40 PM
No problem, I can imagine how annoying this little problems might be. Wild guess on the crashes: if there's "not in a frame" error -- it means that you're reading or writing @model fields from non-ui thread, which we for now don't fully support.
m

Mark Murphy

08/19/2019, 5:43 PM
No, I was getting an NPE-equivalent about an
r
value not being set. Then, I mistakenly tried cleaning the project, in case I had lingering code generated from the previous (nested) location. That completely broke the project, so now I need to try to convince the Compose custom Studio build to work again. 😣
s

Sean McQuillan [G]

08/19/2019, 7:27 PM
Looking at the code, it appears
canProceed
should be a getter (or extension property).
Though I think this can/should be working as coded (with
canProceed
being a derived prop of the other observables). Do you see a similar issue if you switch it to a getter instead of a property initializer?
If so - definitely file a bug to note your expectation that properties work this way!
m

Mark Murphy

08/19/2019, 7:45 PM
I can confirm that both a function and an extension property work. I filed https://issuetracker.google.com/issues/139653744 to document the derived property not working. Let me know if you think more stuff is needed in that issue. Many thanks!
l

Luca Nicoletti

08/19/2019, 7:59 PM
Also declaring it as
var
works fine for me
👍 1
And plus, having it as a
val
and assigning it the constructor will lead to never be able to update the UI, right?
m

Mark Murphy

08/19/2019, 8:02 PM
Ummm... good point. I'm used to thinking of immutable view states, where
canProceed
would be a derived property.
@Model
is all about the mutability, though.
l

Luca Nicoletti

08/19/2019, 8:02 PM
Not saying a crash is expected, but how can it be updated?
Text(text = if (viewState.canProceed) "You may proceed" else "Please indicate your agreement")
will never print
You may proceed
if you have
canProceed
as a val initialized by default as false
☝️ 1
The best solution, imho, is using a
get()
, as I would do even not using Compose 🙂
6 Views