Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Introduction to Jetpack Compose (By Muhammad Hu...

Introduction to Jetpack Compose (By Muhammad Hussain Khan) - Compose Camp 2023

Talk by Muhammad Hussain Khan (https://www.linkedin.com/in/muhammad-hussain-khan-18a861106/) at Compose Camp 2023 by GDG Lahore.

GDG Lahore

January 19, 2023
Tweet

More Decks by GDG Lahore

Other Decks in Programming

Transcript

  1. This work is licensed under the Apache 2.0 License Muhammad

    Hussain Khan Digitt+ Android Developer Camp leaders
  2. This work is licensed under the Apache 2.0 License •

    Thinking in Compose • Composable functions • Compose toolkit • Tooling Agenda
  3. This work is licensed under the Apache 2.0 License <LinearLayout

    android:orientation=“horizontal” > <ImageView android:id=”@+id/answer_image” ... /> <TextView android:id=”@+id/answer_text” ... /> <RadioButton android:id=”@+id/answer_radio_button” ... /> </LinearLayout> <!-- survey_answer.xml -->
  4. This work is licensed under the Apache 2.0 License class

    SurveyQuestionActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val image = findViewById(R.id.answer_image) val text = findViewById(R.id.answer_text) val radioButton = findViewById(R.id.answer_radio_button) // ... } } // SurveyQuestionActivity.kt
  5. This work is licensed under the Apache 2.0 License class

    SurveyQuestionActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { // ... image.setImage(...) text.setText(...) // ... } } // SurveyQuestionActivity.kt
  6. This work is licensed under the Apache 2.0 License @Composable

    fun SurveyAnswer(answer: Answer) { Row { Image(answer.image) Text(answer.text) RadioButton(false, onClick = { /* … */ }) } } // SurveyAnswer.kt
  7. This work is licensed under the Apache 2.0 License @Composable

    fun SurveyAnswer(answer: Answer) { Row { Image(answer.image) Text(answer.text) RadioButton(false, onClick = { /* … */ }) } } // SurveyAnswer.kt
  8. This work is licensed under the Apache 2.0 License @Composable

    fun SurveyAnswer(answer: Answer) { Row { Image(answer.image) Text(answer.text) RadioButton(false, onClick = { /* … */ }) } } // SurveyAnswer.kt
  9. This work is licensed under the Apache 2.0 License @Composable

    fun SurveyAnswer(answer: Answer) { Row { / * ... */ var selected: Boolean = // ... RadioButton(selected, onClick = { /* … */ }) } } // SurveyAnswer.kt
  10. This work is licensed under the Apache 2.0 License @Composable

    fun SurveyAnswer(answer: Answer) { Row { / * ... */ var selected: Boolean = // ... RadioButton(selected, onClick = { selected = !selected }) } } // SurveyAnswer.kt
  11. This work is licensed under the Apache 2.0 License @Composable

    fun SurveyAnswer(answer: Answer) { Row { / * ... */ var selected: Boolean = // ... RadioButton(selected, onClick = { selected = !selected }) } } // SurveyAnswer.kt
  12. This work is licensed under the Apache 2.0 License //

    SurveyAnswer.kt @Composable fun SurveyAnswer(answer: Answer) { Row { Image(answer.image) Text(answer.text) RadioButton(false, onClick = { /* … */ }) } }
  13. This work is licensed under the Apache 2.0 License //

    SurveyAnswer.kt @Composable fun SurveyAnswer(answer: Answer) { Row { Image(answer.image) Text(answer.text) RadioButton(false, onClick = { /* … */ }) } }
  14. This work is licensed under the Apache 2.0 License //

    SurveyAnswer.kt @Composable fun SurveyAnswer(answer: Answer) { /* … */ } @Composable fun SingleChoiceQuestion(answers: List<Answer>) { Column { answers.forEach { answer -> SurveyAnswer(answer = answer) } } }
  15. This work is licensed under the Apache 2.0 License //

    SingleChoiceQuestion.kt @Composable fun SingleChoiceQuestion(answers: List<Answer>) { Column { answers.forEach { answer -> SurveyAnswer(answer = answer) } } }
  16. This work is licensed under the Apache 2.0 License //

    SingleChoiceQuestion.kt @Composable fun SingleChoiceQuestion(answers: List<Answer>) { Column { answers.forEach { answer -> SurveyAnswer(answer = answer) } } }
  17. This work is licensed under the Apache 2.0 License //

    SingleChoiceQuestion.kt @Composable fun SingleChoiceQuestion(answers: List<Answer>) { Column { answers.forEach { answer -> // Can’t do this!! val answer = SurveyAnswer(answer = answer) } } }
  18. This work is licensed under the Apache 2.0 License //

    SingleChoiceQuestion.kt @Composable fun SingleChoiceQuestion(answers: List<Answer>) { Column { answers.forEach { answer -> SurveyAnswer(answer = answer) } } }
  19. This work is licensed under the Apache 2.0 License //

    SingleChoiceQuestion.kt @Composable fun SingleChoiceQuestion(answers: List<Answer>) { Column { if (answers.isEmpty()) { Text(“There are no answers to choose from!”) } else { answers.forEach { answer -> SurveyAnswer(answer = answer) } } } }
  20. This work is licensed under the Apache 2.0 License //

    SingleChoiceQuestion.kt @Composable fun SingleChoiceQuestion(answers: List<Answer>) { // Don’t do this! There shouldn’t be side-effects SurveyApp.didShowSingleChoiceQuestion = true Column { if (answers.isEmpty()) { /* ... */ } else { /* ... */ } } }
  21. This work is licensed under the Apache 2.0 License //

    SingleChoiceQuestion.kt @Composable fun SingleChoiceQuestion(answers: List<Answer>) { // Fast and no side-effects Column { if (answers.isEmpty()) { /* ... */ } else { /* ... */ } } }
  22. This work is licensed under the Apache 2.0 License //

    SingleChoiceQuestion.kt @Composable fun SingleChoiceQuestion(answers: List<Answer>) { Column { answers.forEach { answer -> SurveyAnswer(answer = answer) } } }
  23. This work is licensed under the Apache 2.0 License //

    SingleChoiceQuestion.kt @Composable fun SingleChoiceQuestion(answers: List<Answer>) { answers.forEach { answer -> SurveyAnswer( answer = answer, isSelected = false, ) } }
  24. This work is licensed under the Apache 2.0 License //

    SingleChoiceQuestion.kt @Composable fun SingleChoiceQuestion(answers: List<Answer>) { answers.forEach { answer -> SurveyAnswer( answer = answer, isSelected = false, ) } }
  25. This work is licensed under the Apache 2.0 License //

    SingleChoiceQuestion.kt @Composable fun SingleChoiceQuestion(answers: List<Answer>) { answers.forEach { answer -> SurveyAnswer( answer = answer, isSelected = false, ) } }
  26. This work is licensed under the Apache 2.0 License //

    SingleChoiceQuestion.kt @Composable fun SingleChoiceQuestion(answers: List<Answer>) { var selectedAnswer: Answer? = null answers.forEach { answer -> SurveyAnswer( answer = answer, isSelected = false, ) } }
  27. This work is licensed under the Apache 2.0 License //

    SingleChoiceQuestion.kt @Composable fun SingleChoiceQuestion(answers: List<Answer>) { var selectedAnswer: Answer? = null answers.forEach { answer -> SurveyAnswer( answer = answer, isSelected = false, ) } }
  28. This work is licensed under the Apache 2.0 License //

    SingleChoiceQuestion.kt @Composable fun SingleChoiceQuestion(answers: List<Answer>) { var selectedAnswer: MutableState<Answer?> = mutableStateOf(null) answers.forEach { answer -> SurveyAnswer( answer = answer, isSelected = false, ) } }
  29. This work is licensed under the Apache 2.0 License //

    SingleChoiceQuestion.kt @Composable fun SingleChoiceQuestion(answers: List<Answer>) { var selectedAnswer: MutableState<Answer?> = mutableStateOf(null) answers.forEach { answer -> SurveyAnswer( answer = answer, isSelected = false, ) } }
  30. This work is licensed under the Apache 2.0 License //

    SingleChoiceQuestion.kt @Composable fun SingleChoiceQuestion(answers: List<Answer>) { var selectedAnswer: MutableState<Answer?> = mutableStateOf(null) answers.forEach { answer -> SurveyAnswer( answer = answer, isSelected = (selectedAnswer.state == answer), ) } }
  31. This work is licensed under the Apache 2.0 License //

    SingleChoiceQuestion.kt @Composable fun SingleChoiceQuestion(answers: List<Answer>) { var selectedAnswer: MutableState<Answer?> = remember { mutableStateOf(null) } answers.forEach { answer -> SurveyAnswer( answer = answer, isSelected = (selectedAnswer.state == answer), ) } }
  32. This work is licensed under the Apache 2.0 License //

    SingleChoiceQuestion.kt @Composable fun SingleChoiceQuestion(answers: List<Answer>) { var selectedAnswer: MutableState<Answer?> = rememberSaveable { mutableStateOf(null) } answers.forEach { answer -> SurveyAnswer( answer = answer, isSelected = (selectedAnswer.state == answer), ) } }
  33. This work is licensed under the Apache 2.0 License //

    SingleChoiceQuestion.kt @Composable fun SingleChoiceQuestion(answers: List<Answer>) { var selectedAnswer: Answer? by rememberSaveable { mutableStateOf(null) } answers.forEach { answer -> SurveyAnswer( answer = answer, isSelected = (selectedAnswer.state == answer), ) } }
  34. This work is licensed under the Apache 2.0 License //

    SingleChoiceQuestion.kt @Composable fun SingleChoiceQuestion(answers: List<Answer>) { var selectedAnswer: Answer? by rememberSaveable { mutableStateOf(null) } answers.forEach { answer -> SurveyAnswer( answer = answer, isSelected = (selectedAnswer == answer), ) } }
  35. This work is licensed under the Apache 2.0 License //

    SingleChoiceQuestion.kt @Composable fun SingleChoiceQuestion(answers: List<Answer>) { var selectedAnswer: Answer? by rememberSaveable { mutableStateOf(null) } answers.forEach { answer -> SurveyAnswer( answer = answer, isSelected = (selectedAnswer == answer), onAnswerSelected = { answer -> selectedAnswer = answer } ) } }
  36. This work is licensed under the Apache 2.0 License Event

    handler UI element event state Events change State
  37. This work is licensed under the Apache 2.0 License Event

    handler UI element onAnswerSelected answer Events change State
  38. This work is licensed under the Apache 2.0 License //

    SingleChoiceQuestion.kt @Composable fun SingleChoiceQuestion(answers: List<Answer>) { val selectedAnswer: Answer? by rememberSaveable { mutableStateOf<Answer>(null) } answers.forEach { answer -> SurveyAnswer( answer = answer, isSelected = (selectedAnswer == answer), onAnswerSelected = { answer -> selectedAnswer = answer } ) } }
  39. This work is licensed under the Apache 2.0 License //

    ButtonRow.kt @Composable fun ButtonRow() { MyFancyNavigation { StartScreen() MiddleScreen() EndScreen() } }
  40. This work is licensed under the Apache 2.0 License @Composable

    fun ListComposable(myList: List<String>) { Row(horizontalArrangement = Arrangement.SpaceBetween) { Column { for (item in myList) { Text("Item: $item") } } Text("Count: ${myList.size}") } } // ListComposable.kt
  41. This work is licensed under the Apache 2.0 License @Composable

    fun ListWithBug(myList: List<String>) { var items = 0 Row(horizontalArrangement = Arrangement.SpaceBetween) { Column { for (item in myList) { Text("Item: $item”) items++ // Avoid! Side-effect of the column recomposing. } } Text("Count: $items") } } // ListComposable.kt
  42. This work is licensed under the Apache 2.0 License @Composable

    fun GreetingScreen(name: String) { Column { Header() Greeting(name = name) Footer() } } // GreenScreen.kt
  43. This work is licensed under the Apache 2.0 License Summary

    Create composables using the @Composable annotation It’s quick & easy to create composables Composables accept parameters Use MutableState and remember Composables should be side-effect free
  44. This work is licensed under the Apache 2.0 License Composables

    can: Execute in any order Run in parallel Be skipped Run frequently 1 2 3 4
  45. This work is licensed under the Apache 2.0 License MaterialTheme(

    colorScheme = MyAppsColorScheme, typography = MyAppsTypography, shapes = MyAppsShapes ) { // Content goes here }
  46. This work is licensed under the Apache 2.0 License Scaffold(

    topBar = { SmallTopAppBar(/* ... */) }, floatingActionButtonPosition = FabPosition.End, floatingActionButton = { FloatingActionButton(/* ... */) }, content = { /* ... */ } )
  47. This work is licensed under the Apache 2.0 License Surface

    { Text("Hello Compose") } Hello Compose
  48. This work is licensed under the Apache 2.0 License Surface(

    color = MaterialTheme.colorScheme.primary, ) { Text("Hello Compose") } Hello Compose
  49. This work is licensed under the Apache 2.0 License Surface(

    color = MaterialTheme.colorScheme.primary, shape = RoundedCornerShape(8.dp), ) { Text("Hello Compose") } Hello Compose
  50. This work is licensed under the Apache 2.0 License Surface(

    color = MaterialTheme.colorScheme.surface, shape = RoundedCornerShape(8.dp), border = BorderStroke(2.dp, MaterialTheme.colorScheme.outline ) ) { Text("Hello Compose") } Hello Compose
  51. This work is licensed under the Apache 2.0 License Surface(

    color = MaterialTheme.colorScheme.surface, shape = RoundedCornerShape(8.dp), border = BorderStroke(2.dp, MaterialTheme.colorScheme.surfaceVariant ), shadowElevation = 8.dp, tonalElevation = 8.dp, ) { Text("Hello Compose") } Hello Compose
  52. This work is licensed under the Apache 2.0 License Row

    { Component1() Component2() Component3() } 1 2 3 Row
  53. This work is licensed under the Apache 2.0 License 1

    2 3 Column Column { Component1() Component2() Component3() }
  54. This work is licensed under the Apache 2.0 License 2

    1 3 Box Box { Component1() Component2() Component3() }
  55. This work is licensed under the Apache 2.0 License @Composable

    fun SurveyAnswer(answer: Answer) { Row { Image(answer.image) Text(answer.text) RadioButton(/* … */) } }
  56. This work is licensed under the Apache 2.0 License @Composable

    fun SurveyAnswer(answer: Answer) { Row( verticalAlignment = Alignment.CenterVertically ) { /* ... */ } }
  57. This work is licensed under the Apache 2.0 License @Composable

    fun SurveyAnswer(answer: Answer) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween ) { /* ... */ } }
  58. This work is licensed under the Apache 2.0 License Text(

    "Hello Compose!", Modifier.background(Color.Magenta) ) Hello Compose
  59. This work is licensed under the Apache 2.0 License Text(

    "Hello Compose!", Modifier.background(Color.Magenta) .size(200.dp, 30.dp) ) Hello Compose
  60. This work is licensed under the Apache 2.0 License Text(

    "Hello Compose!", Modifier.background(Color.Magenta) .size(200.dp, 30.dp) .padding(5.dp) ) Hello Compose
  61. This work is licensed under the Apache 2.0 License Text(

    "Hello Compose!", Modifier.background(Color.Magenta) .size(200.dp, 30.dp) .padding(5.dp) .alpha(0.5f) ) Hello Compose
  62. This work is licensed under the Apache 2.0 License Text(

    "Hello Compose!", Modifier.background(Color.Magenta) .size(200.dp, 30.dp) .padding(5.dp) .alpha(0.5f) .clickable { // Called when Text clicked } ) Hello Compose
  63. This work is licensed under the Apache 2.0 License Box(Modifier.size(150.dp))

    { Text( "Hello Compose!", Modifier.align( Alignment.BottomEnd ) ) } Hello Compose
  64. This work is licensed under the Apache 2.0 License @Composable

    fun SurveyAnswer(answer: Answer) { Row(...) { Image(answer.image) Text(answer.text) RadioButton(/* … */) } } Desired Current
  65. This work is licensed under the Apache 2.0 License @Composable

    fun SurveyAnswer(answer: Answer) { Row( Modifier.fillMaxWidth(), /* ... */ ) { Image(answer.image) Text(answer.text) RadioButton(/* ... */) } } Desired Current
  66. This work is licensed under the Apache 2.0 License @Composable

    fun SurveyAnswer(answer: Answer) { Row( Modifier.fillMaxWidth() .padding(16.dp), /* ... */ ) { Image(answer.image) Text(answer.text) RadioButton(/* ... */) } } Desired Current
  67. This work is licensed under the Apache 2.0 License @Composable

    fun SurveyAnswer(answer: Answer) { Surface( border = BorderStroke( 1.dp, MaterialTheme.colorScheme.outline ), shape = MaterialTheme.shapes.small ) { Row(/* ... */) { } } } Desired Current
  68. This work is licensed under the Apache 2.0 License @Composable

    fun SurveyAnswer(answer: Answer) { Surface( border = BorderStroke( 1.dp, MaterialTheme.colorScheme.outline ), shape = MaterialTheme.shapes.small ) { Row(Modifier.fillMaxWidth().padding(16.dp)) { Image(answer.image) Text(answer.text) RadioButton(/* … */) } } }