pathway_3_interact_with_ui_and_state
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| pathway_3_interact_with_ui_and_state [2024/10/22 14:25] – [3.10 State hoisting] kalaschnikow | pathway_3_interact_with_ui_and_state [2024/10/25 09:12] (current) – [6. Project: Create an Art Space app] kalaschnikow | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | ====== Pathway 3: Interact with UI and state ====== | ||
| + | https:// | ||
| + | |||
| + | This is a part of [[Android Basics with Compose]] course. | ||
| + | |||
| + | ===== 1. Understanding state in Compose ===== | ||
| + | |||
| + | Optional video. | ||
| + | |||
| + | <code kotlin> | ||
| + | var input by remember { | ||
| + | mutableStateOf("" | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | * State can be saved: | ||
| + | * locally | ||
| + | * in the UI element | ||
| + | * outside | ||
| + | * if it is needed by other UI elements | ||
| + | * this is named "state hoisting" | ||
| + | |||
| + | |||
| + | ===== 2. Intro to tip calculator | ||
| + | ... | ||
| + | Tip = Gratuity (Trinkgeld in DE) | ||
| + | |||
| + | |||
| + | ===== 3. Intro to state in Compose ===== | ||
| + | ==== 3.1 Before you begin ==== | ||
| + | ... | ||
| + | |||
| + | ==== 3.2 Get started ==== | ||
| + | ... | ||
| + | |||
| + | ==== 3.3 Get Starter Code ==== | ||
| + | * clone repo and switch to '' | ||
| + | * add '' | ||
| + | ==== 3.4 Use state in Compose ==== | ||
| + | |||
| + | ... | ||
| + | ==== 3.5 The Composition ==== | ||
| + | * If a state change happens, Compose re-executes the affected composable functions with the new state, which creates an updated UI—this is called // | ||
| + | * You use the '' | ||
| + | * ... | ||
| + | |||
| + | ==== 3.6 Use remember function to save state ==== | ||
| + | |||
| + | ... | ||
| + | |||
| + | <code kotlin> | ||
| + | @Composable | ||
| + | fun EditNumberField(modifier: | ||
| + | var amountInput by remember { mutableStateOf("" | ||
| + | |||
| + | |||
| + | TextField( | ||
| + | value = amountInput, | ||
| + | onValueChange = { amountInput = it }, | ||
| + | modifier = modifier | ||
| + | ) | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | * Adding the delegate' | ||
| + | |||
| + | ... | ||
| + | |||
| + | ==== 3.7 State and recomposition in action ==== | ||
| + | |||
| + | ... | ||
| + | ==== 3.8 Modify the appearance ==== | ||
| + | |||
| + | <code kotlin> | ||
| + | @Composable | ||
| + | fun EditNumberField(modifier: | ||
| + | var amountInput by remember { mutableStateOf("" | ||
| + | TextField( | ||
| + | value = amountInput, | ||
| + | onValueChange = { amountInput = it }, | ||
| + | singleLine = true, | ||
| + | label = { Text(stringResource(R.string.bill_amount)) }, | ||
| + | keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), | ||
| + | modifier = modifier | ||
| + | ) | ||
| + | } | ||
| + | </ | ||
| + | ==== 3.9 Display the tip amount ==== | ||
| + | |||
| + | ==== 3.10 State hoisting ==== | ||
| + | * The state (contained in the variable '' | ||
| + | |||
| + | <code kotlin> | ||
| + | @Composable | ||
| + | fun TipTimeLayout() { | ||
| + | var amountInput by remember { mutableStateOf("" | ||
| + | val amount = amountInput.toDoubleOrNull() ?: 0.0 | ||
| + | val tip = calculateTip(amount) | ||
| + | |||
| + | | ||
| + | | ||
| + | .statusBarsPadding() | ||
| + | .padding(horizontal = 40.dp) | ||
| + | .verticalScroll(rememberScrollState()) | ||
| + | .safeDrawingPadding(), | ||
| + | | ||
| + | | ||
| + | ) { | ||
| + | Text( | ||
| + | text = stringResource(R.string.calculate_tip), | ||
| + | | ||
| + | | ||
| + | | ||
| + | ) | ||
| + | | ||
| + | value = amountInput, | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | ) | ||
| + | Text( | ||
| + | text = stringResource(R.string.tip_amount, | ||
| + | style = MaterialTheme.typography.displaySmall | ||
| + | ) | ||
| + | | ||
| + | } | ||
| + | } | ||
| + | |||
| + | @Composable | ||
| + | fun EditNumberField( | ||
| + | | ||
| + | | ||
| + | | ||
| + | ) { | ||
| + | | ||
| + | value = value, | ||
| + | | ||
| + | | ||
| + | label = { Text(stringResource(R.string.bill_amount)) }, | ||
| + | | ||
| + | | ||
| + | ) | ||
| + | } | ||
| + | </ | ||
| + | ==== 3.11 Get the solution code ==== | ||
| + | ... | ||
| + | |||
| + | ==== 3.12 Conclusion ==== | ||
| + | |||
| + | * Summary | ||
| + | * //State// in an app is any value that can change over time. | ||
| + | * The // | ||
| + | * //Initial composition// | ||
| + | * // | ||
| + | * //State hoisting// is a pattern of moving state to its caller to make a component stateless. | ||
| + | |||
| + | ===== 4. Calculate a custom tip ===== | ||
| + | |||
| + | ==== 4.1. Before you begin ==== | ||
| + | |||
| + | ... | ||
| + | |||
| + | |||
| + | ==== 4.2 Get the starter code ==== | ||
| + | |||
| + | ... | ||
| + | |||
| + | ==== 4.3 Starter app overview ==== | ||
| + | |||
| + | ... | ||
| + | |||
| + | ==== 4.4 Add a tip-percentage text field ==== | ||
| + | |||
| + | ... | ||
| + | * add '' | ||
| + | ... | ||
| + | * '' | ||
| + | ... | ||
| + | |||
| + | ==== 4.5 Set an action button ==== | ||
| + | |||
| + | ... | ||
| + | <code kotlin> | ||
| + | keyboardOptions = KeyboardOptions.Default.copy( | ||
| + | keyboardType = KeyboardType.Number, | ||
| + | imeAction = ImeAction.Next | ||
| + | ), | ||
| + | </ | ||
| + | ==== 4.5 Add a switch ==== | ||
| + | |||
| + | ... | ||
| + | |||
| + | <code kotlin> | ||
| + | Switch( | ||
| + | checked = roundUp, | ||
| + | onCheckedChange = onRoundUpChanged, | ||
| + | modifier = modifier | ||
| + | .fillMaxWidth() | ||
| + | .wrapContentWidth(Alignment.End), | ||
| + | ) | ||
| + | </ | ||
| + | ... | ||
| + | ==== 4.7 Add support for landscape orientation ==== | ||
| + | |||
| + | ... | ||
| + | |||
| + | <code kotlin> | ||
| + | @Composable | ||
| + | fun TipTimeLayout() { | ||
| + | // ... | ||
| + | Column( | ||
| + | modifier = Modifier | ||
| + | .padding(40.dp) | ||
| + | .verticalScroll(rememberScrollState()), | ||
| + | horizontalAlignment = Alignment.CenterHorizontally, | ||
| + | verticalArrangement = Arrangement.Center | ||
| + | ) { | ||
| + | //... | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | ==== 4.8 Add leading icon to text fields (optional) ==== | ||
| + | |||
| + | ... | ||
| + | |||
| + | <code kotlin> | ||
| + | TextField( | ||
| + | value = value, | ||
| + | leadingIcon = { Icon(painter = painterResource(id = leadingIcon), | ||
| + | //... | ||
| + | ) | ||
| + | </ | ||
| + | ==== 4.9 Get the solution code ==== | ||
| + | |||
| + | ... | ||
| + | |||
| + | ==== 4.10 Conclusion ==== | ||
| + | |||
| + | ... | ||
| + | |||
| + | |||
| + | ===== 5. Write automated tests ===== | ||
| + | |||
| + | ==== 5.1 Before you begin ==== | ||
| + | ... | ||
| + | ==== 5.2 Get the starter code ==== | ||
| + | ... | ||
| + | ==== 5.3 Automated tests ==== | ||
| + | |||
| + | * Type of automated tests | ||
| + | * Local tests | ||
| + | * Local tests are a type of automated test that 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, | ||
| + | * Instrumentation tests | ||
| + | * An instrumentation test is a UI test. | ||
| + | * 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. | ||
| + | ... | ||
| + | |||
| + | |||
| + | * '' | ||
| + | |||
| + | ... | ||
| + | |||
| + | ==== 5.4 Write a local test ==== | ||
| + | |||
| + | Local tests directly test methods from the app code, so the methods to be tested must be available to the testing classes and methods. So: | ||
| + | * => designate '' | ||
| + | * add '' | ||
| + | * this makes the method public, but only for testing purposes | ||
| + | |||
| + | |||
| + | The following list of concepts apply to local and instrumentation tests: | ||
| + | |||
| + | * Write automated tests in the form of methods. | ||
| + | * Annotate the method with the @Test annotation. This lets the compiler know that the method is a test method and runs the method accordingly. | ||
| + | * Ensure that the name clearly describes what the test tests for and what the expected result is. | ||
| + | * Test methods don't use logic like regular app methods. They aren't concerned with how something is implemented. They strictly check an expected output for a given input. That is to say, test methods only execute a set of instructions to assert that an app's UI or logic functions correctly. You don't need to understand what this means yet because you see what this looks like later, but remember that test code may look quite different from the app code that you're used to. | ||
| + | * Tests typically end with an assertion, which is used to ensure that a given condition is met. Assertions come in the form of a method call that has assert in its name. For example: the '' | ||
| + | |||
| + | ... | ||
| + | ==== 5.5 Write an instrumentation test ==== | ||
| + | |||
| + | * usually located in '' | ||
| + | |||
| + | ... | ||
| + | ==== 5.6 Get the solution code ==== | ||
| + | ... | ||
| + | ==== 5.7 Conclusion ==== | ||
| + | |||
| + | Summary: | ||
| + | |||
| + | * What automated tests are. | ||
| + | * Why automated tests are important. | ||
| + | * The difference between local tests and instrumentation tests | ||
| + | * Fundamental best practices for writing automated tests. | ||
| + | * Where to find and place local and instrumentation test classes in an Android project. | ||
| + | * How to create a test method. | ||
| + | * How to create local and instrumentation test classes. | ||
| + | * How to make assertions in local and instrumentation tests. | ||
| + | * How to use test rules. | ||
| + | * How to use '' | ||
| + | * How to interact with composables in an instrumentation test. | ||
| + | * How to run tests. | ||
| + | |||
| + | |||
| + | ===== 6. Project: Create an Art Space app ===== | ||
| + | |||
| + | ==== 6.1 Before you begin ==== | ||
| + | ... | ||
| + | ==== 6.2 Build static UI with composables ==== | ||
| + | ... | ||
| + | ==== 6.3 Make the app interactive ==== | ||
| + | ... | ||
| + | ==== 6.4 Challenge: Build for different screen sizes ==== | ||
| + | ... | ||
| + | ==== 6.5 Good luck! ==== | ||
| + | ... | ||
| + | |||
| + | ===== 7. What's next? ===== | ||
| + | ... | ||
