Unit 2 notes

Kotlin fundamentals

Write conditionals in Kotlin

when statement

when (parameter) {
    condition1 -> body1
    condition2 -> body2
    condition3 -> body3
    ...
}

# Example:
when (x) {
    2, 3, 5, 7 -> println("x is a prime number between 1 and 10.")
    in 1..10 -> println("x is a number between 1 and 10, but not a prime number.")
    is Int -> println("x is an integer number, but not between 1 and 10.")
    else -> println("x isn't an integer number.")
}

if/else and when as expressions

val name = if (condition) {
    body1
} else {
    body2
}

val name = if (condition) expression1 else expression2

val expr = when (parameter) {
    condition1 -> body1
    condition2 -> body2
    condition3 -> body3
    ...
}

Interacting with UI and state

Understanding state in compose (video)

State (definition)

State - any value in an app that can change over time

  • By default, Composables do not remember automatically their state.
  • By default, during recomposition, any value or variable defined in a composable will be reset to its initial value. Then Compose recomposes the UI.

Remember

  • Composable functions can store an object across compositions by using remember. A value computed by remember is stored during initial composition and is remembered during recomposition.

MutableStateOf function

  • Each time the app data is updated, you need a way to observe the change and trigger recomposition for composables that need to reflect the updated state. Solution: mutableStateOf function. It create an observable state holder that allows Compose to observe the change and trigger recomposition.

State hoisting

  • If the state is used in multiple Composable functions, but is only defined in one Composable, he needs to be extracted from the initial composable function and be in a place where both Composables can receive it. This process is called state hoisting - Moving the state ouside a composable.
  • State hoisting can also be used to make the initial composable function stateless, so that it can be reused in other places of the app. A stateless composable is a composable ​​that doesn’t store its own state. It displays whatever state it’s given as input arguments.

Intro to state in compose

5 - The Composition

Composition

  • Composition - description of the UI built by Compose when it executes composables.

    • Compose apps call composable functions to transform data into UI.
  • Recomposition - If a state change happens, Compose re-executes the affected composable functions with the new state, which creates an updated UI.

    • Compose schedules a recomposition for you.
  • When Compose runs your composables for the first time during initial composition, it keeps track of the composables that you call to describe your UI in a Composition.

  • The Composition can only be produced by an initial composition and updated by recomposition.

    • The only way to modify the Composition is through recomposition.
  • To do this, Compose needs to know what state to track so that it can schedule the recomposition when it receives an update.

(Mutable) State and mutableStateOf

  • You use the State and MutableState types in Compose to make state in your app observable, or tracked, by Compose.

  • The State type is immutable, so you can only read the value in it

  • The MutableState type is mutable. You can use the mutableStateOf() function to create an observable MutableState. It receives an initial value as a parameter that is wrapped in a State object, which then makes its value observable.

  • The value returned by the mutableStateOf() function:

    • Holds state.
    • Is mutable, so the value can be changed.
    • Is observable, so Compose observes any changes to the value and triggers a recomposition to update the UI.
  • NOTE: Using only mutableStateOf without remember updates a value, but the variable is reset to its initial value. A recomposition is scheduled because the value changed, then the variable is reset to its initial value and in the end, a recomposition is done. In practice, the variable value does not change.

6 - Use remember function to save state

  • Composable methods can be called many times because of recomposition.

  • The composable resets its state during recomposition if it’s not saved.

  • Composable functions can store an object across recompositions with the remember.

  • A value computed by the remember function is stored in the Composition during initial composition and the stored value is returned during recomposition.

  • Usually remember and mutableStateOf functions are used together in composable functions to have the state and its updates be reflected properly in the UI.

10 - State hoisting

  • You should hoist the state when you need to:

    • Share the state with multiple composable functions.
    • Create a stateless composable that can be reused in your app.
  • When you extract state from a composable function, the resulting composable function is called stateless.

    • A stateless composable is a composable that doesn’t have a state, meaning it doesn’t hold, define, or modify a new state. On the other hand, a stateful composable is a composable that owns a piece of state that can change over time.
  • State hoisting is a pattern of moving state to its caller to make a component stateless.

  • When applied to composables, this often means introducing two parameters to the composable:

    • A value: T parameter, which is the current value to display.
    • An onValueChange: (T) -> Unit - callback lambda, which is triggered when the value changes so that the state can be updated elsewhere, such as when a user enters some text in the text box.

Write automated tests

Types of automated tests:

  • Local tests - directly test a small piece of code to ensure that it functions properly. With local tests, you can test functions, classes, and properties. Local tests are executed on your workstation, which means they run in a development environment without the need for a device or emulator.
  • Instrumentation tests
    • Is a UI test.
    • Instrumentation tests let you test parts of an app that depend on the Android API, and its platform APIs and services.
    • Unlike local tests, UI tests launch an app or part of an app, simulate user interactions, and check whether the app reacted appropriately.
    • UI tests are run on a physical device or emulator.
    • When you run an instrumentation test on Android, the test code is actually built into its own Android Application Package (APK) like a regular Android app.
      • An APK is a compressed file that contains all the code and necessary files to run the app on a device or emulator. The test APK is installed on the device or emulator along with the regular app APK. The test APK then runs its tests against the app APK.

Others - General notes

  • The best practice is to provide a default Modifier parameter to all composable functions, which increases reusability. You should add it as the first optional parameter after all required parameters.
  • The @StringRes annotation is a type-safe way to use string resources. It indicates that the integer to be passed is a string resource from the values/strings.xml file.
  • Add .verticalScroll(rememberScrollState()) to the modifier to enable the column to scroll vertically. The rememberScrollState() creates and automatically remembers the scroll state.

rememberSaveable

  • While remember helps you retain state across recompositions, the state is not retained across configuration changes. For this, you must use rememberSaveable. rememberSaveable automatically saves any value that can be saved in a Bundle. For other values, you can pass in a custom saver object.
  • The rememberSaveable API behaves similarly to remember because it retains state across recompositions, and also across activity or process recreation using the saved instance state mechanism. For example, this happens, when the screen is rotated. (process -> processo da aplicação)