galex
07/19/2020, 4:13 PMMark Murphy
07/19/2020, 4:19 PMCalculatorState be a data class with val properties, then use copy() to create the mutated object that you use to update calculatorState
Or, you could have the individual properties of CalculatorState be mutableStateOf() property delegates.
Those correspond to alternatives 1 and 2 for the replacement of @Model as noted in this Gerrit entry: https://android-review.googlesource.com/c/platform/frameworks/support/+/1311293/Adam Powell
07/19/2020, 4:30 PMAdam Powell
07/19/2020, 4:33 PMMutableState<CalculatorState>, great, but if you make CalculatorState mutable by way of mutableStateOf property delegation, don't use State<CalculatorState> to hold itAdam Powell
07/19/2020, 4:33 PMvar list: List<T> or val list: MutableList<T> but not var list: MutableList<T>Adam Powell
07/19/2020, 4:34 PMgalex
07/19/2020, 4:49 PMmutableStateOf() for its properties. Using copy feels less clean memory-wise, but a good option if the object is not mine (ex: 3rd library)Adam Powell
07/19/2020, 4:56 PMmutableStateOf creates a mutable box, and it does so with generics so anything you put in it is boxed as well. (We may do some additional optimizations around that last part later.) We also track historical values of MutableState objects to some extent in the form of the snapshot records. Consider the API ergonomics you're trying to create here first and please report back if you experience issues and we'll see what we can do to optimize.Adam Powell
07/19/2020, 4:59 PM@Composable
fun CalculatorScreen(
calculatorState: CalculatorState = remember { CalculatorState() }
) {
// ...Adam Powell
07/19/2020, 5:01 PMCalculatorScreen be self-sufficient in the simple case, but a caller that wants to also manipulate its CalculatorState via its public API can do so. This is also great for testability, since it means that CalculatorScreen itself is stateless and you can pass it any configuration you like to control and test its behavior.Adam Powell
07/19/2020, 5:02 PMCalculatorState object becomes a lot more testable, so you can decouple testing of your logic (in CalculatorState) from testing the UI presentation itselfgalex
07/19/2020, 5:12 PMremember ! I haven’t gotten to testing yet, but it seems a thousand years easier than with the old UI framework.galex
07/19/2020, 5:13 PMmutableStateOf but now it makes sense 🙂galex
07/20/2020, 5:45 AMCalculatorState in a junit simple test (not AndroidTest) and could not:
java.lang.IllegalStateException: Not in a frame
at androidx.compose.frames.FramesKt.currentFrame(Frames.kt:180)
at androidx.compose.FramedMutableState.setValue(MutableState.kt:296)
at il.co.galex.alexpizzapp.feature.calculator.CalculatorState.setNumberOfBalls(CalculatorState.kt:14)
at il.co.galex.alexpizzapp.feature.calculator.CalculatorTest.fourPizzasOf280(CalculatorTest.kt:22)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
So I am bound to test this code as an AndroidTest? In this case a simpler object using copy would be easier to test, right?Adam Powell
07/20/2020, 2:18 PMAdam Powell
07/20/2020, 2:19 PMgalex
07/20/2020, 5:09 PM