$30 off During Our Annual Pro Sale. View Details »

Android Summit 2020: Learn Jetpack Compose By Example

vinaygaba
October 09, 2020

Android Summit 2020: Learn Jetpack Compose By Example

Over the course of the last few years Android development has gone through significant changes in how we structure our apps, the language we use for development, the tooling & libraries that help us speed up our development and the improvements in testing our apps. What had not changed in all these years is the Android UI toolkit. This changes with Jetpack Compose that aims to reimagine what Android UI development would look like using declarative programming principles. It is heavily influenced by existing web and mobile frameworks such as React, Litho, Vue & Flutter and would be a paradigm shift in Android UI development as we know it.

In this talk, we will take a deeper look at what declarative programming means and how we should think about it when building our apps. We will look into the main principles of Jetpack Compose and try to draw parallels with the “old Android way” of doing common tasks. Lastly, we will dive into various code examples and learn how to build layouts, manage state, write custom views, style our views, access resources and more, all using Jetpack Compose.

Companion code – https://github.com/vinaygaba/Learn-Jetpack-Compose-By-Example

vinaygaba

October 09, 2020
Tweet

More Decks by vinaygaba

Other Decks in Technology

Transcript

  1. Learn Jetpack Compose
    by Example
    Vinay Gaba
    @vinaygaba
    Join the conversation in #session-chat-d2-s2-t1

    View Slide

  2. Jetpack Compose
    / jet·pak kuhm·powz /
    noun
    Jetpack Compose is a declarative & modern toolkit for building native
    Android UI. It simplifies and accelerates UI development on Android.

    View Slide

  3. Why do we need
    Compose?

    View Slide

  4. Why do we need
    Compose?
    UI Toolkit is tied to the OS
    State Management is tricky
    Lots of context switching
    Simple things still require a lot of code

    View Slide

  5. Why do we need
    Compose?
    UI Toolkit is tied to the OS
    State Management is tricky
    Lots of context switching
    Simple things still require a lot of code

    View Slide

  6. Why do we need
    Compose?
    UI Toolkit is tied to the OS
    State Management is tricky
    Lots of context switching
    Simple things still require a lot of code

    View Slide

  7. Why do we need
    Compose?
    UI Toolkit is tied to the OS
    State Management is tricky
    Lots of context switching
    Simple things still require a lot of code

    View Slide

  8. Why do we need
    Compose?
    UI Toolkit is tied to the OS
    State Management is tricky
    Lots of context switching
    Simple things still require a lot of code

    View Slide

  9. Disclaimer

    View Slide

  10. Disclaimer
    Examples are based on 1.0.0-alpha03
    Compose is in alpha so API’s can still change
    Beta release expected in the next few months

    View Slide

  11. Disclaimer
    Examples are based on 1.0.0-alpha03
    Compose is in alpha so API’s can still change
    Beta release expected in the next few months

    View Slide

  12. Disclaimer
    Examples are based on 1.0.0-alpha03
    Compose is in alpha so API’s can still change
    Beta release expected in the next few months

    View Slide

  13. Disclaimer
    Examples are based on 1.0.0-alpha03
    Compose is in alpha so API’s can still change
    Beta release expected in the next few months

    View Slide

  14. Examples

    View Slide

  15. Hello World

    View Slide

  16. class HelloWorldActivity: AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    }

    }

    View Slide

  17. class HelloWorldActivity: AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContent {



    }

    }

    }

    View Slide

  18. fun ComponentActivity.setContent(

    recomposer: Recomposer = Recomposer.current(),

    content: @Composable () "-> Unit

    ): Composition {

    "// Some magic ✨

    }

    View Slide

  19. class HelloWorldActivity: AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContent {



    }

    }

    }

    View Slide

  20. class HelloWorldActivity: AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContent {

    Text(text = "Hello World")

    }

    }

    }

    View Slide

  21. class HelloWorldActivity: AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContent {

    Text(text = "Hello World")

    }

    }

    }

    View Slide

  22. class HelloWorldActivity: AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContent {

    Text(text = "Hello World")

    }

    }

    }

    View Slide

  23. @Composable

    fun CustomTextComponent() {

    Text(text = "Hello World")

    }

    View Slide

  24. @Composable

    fun CustomTextComponent(displayText: String) {

    Text(

    text = displayText,

    style = TextStyle(

    fontSize = 18.sp,

    fontFamily = FontFamily.Monospace

    )

    )

    }

    View Slide

  25. @Composable

    fun CustomTextComponent(displayText: String) {

    Text(

    text = displayText,

    style = TextStyle(

    fontSize = 18.sp,

    fontFamily = FontFamily.Monospace

    )

    )

    }

    View Slide

  26. @Composable

    fun CustomTextComponent(displayText: String) {

    Text(

    text = displayText,

    style = TextStyle(

    fontSize = 18.sp,

    fontFamily = FontFamily.Monospace

    )

    )

    }

    @Preview

    @Composable

    fun CustomTextComponentPreview() {

    CustomTextComponent("Hello World")

    }

    View Slide

  27. View Slide

  28. View Slide

  29. Display Image

    View Slide

  30. @Composable

    fun DrawableImage() {

    }

    View Slide

  31. @Composable

    fun DrawableImage() {

    val image = loadImageResource(R.drawable.landscape)

    }

    View Slide

  32. @Composable

    fun DrawableImage() {

    val image = loadImageResource(R.drawable.landscape)

    image.resource.resource"?.let {

    Image(asset = it, modifier = Modifier.preferredSize(200.dp))

    }

    }
    Modifiers are your best friends!

    View Slide

  33. @Composable

    fun DrawableImage(@DrawableRes resId: Int) {

    val image = loadImageResource(resId)

    image.resource.resource"?.let {

    Image(

    asset = it,

    modifier = Modifier.preferredSize(200.dp)

    )

    }

    }

    View Slide

  34. @Composable

    fun DrawableImage(@DrawableRes resId: Int) {

    val image = loadImageResource(resId)

    image.resource.resource"?.let {

    Image(

    asset = it,

    modifier = Modifier.preferredSize(200.dp)

    )

    }

    }

    View Slide

  35. Modifiers

    View Slide

  36. Text(

    text = "Hello",

    modifier = Modifier.padding(16.dp)

    )

    View Slide

  37. Text(

    text = "Hello",

    modifier = Modifier.padding(16.dp)

    .background(color = Color.Red)

    )

    View Slide

  38. Text(

    text = "Hello",

    modifier = Modifier.padding(16.dp)

    .background(color = Color.Red)

    )

    View Slide

  39. Text(

    text = "Hello",

    modifier = Modifier.padding(16.dp)

    .background(color = Color.Red)

    )
    Hello

    View Slide

  40. Text(

    text = "Hello",

    modifier = Modifier.background(color = Color.Red)

    .padding(16.dp)

    )

    View Slide

  41. Text(

    text = "Hello",

    modifier = Modifier.background(color = Color.Red)

    .padding(16.dp)

    )

    View Slide

  42. Text(

    text = "Hello",

    modifier = Modifier.background(color = Color.Red)

    .padding(16.dp)

    )
    Hello
    The order of a Modifier has an impact on the behavior

    View Slide

  43. Alert Dialog

    View Slide

  44. Jetpack Compose
    / jet·pak kuhm·powz /
    noun
    Jetpack Compose is a declarative & modern toolkit for building native
    Android UI. It simplifies and accelerates UI development on Android.

    View Slide

  45. How What
    vs

    View Slide

  46. How What

    View Slide

  47. How What
    // Classic Android
    val alertDialog = AlertDialog.Builder(context)
    .setTitle("Android Summit!”)
    .setMessage("Isn't this conference amazing?")
    // Somewhere else in code
    if (some_condition_is_met()) {
    alertDialog.show()
    }
    // Somewhere else in code
    if (some_other_condition_is_met()) {
    alertDialog.dismiss()
    }

    View Slide

  48. How What
    // Classic Android
    val alertDialog = AlertDialog.Builder(context)
    .setTitle("Android Summit!”)
    .setMessage("Isn't this conference amazing?")
    // Somewhere else in code
    if (some_condition_is_met()) {
    alertDialog.show()
    }
    // Somewhere else in code
    if (some_other_condition_is_met()) {
    alertDialog.dismiss()
    }
    @Composable
    fun AlertDialogComponent() {
    }

    View Slide

  49. How What
    // Classic Android
    val alertDialog = AlertDialog.Builder(context)
    .setTitle("Android Summit!”)
    .setMessage("Isn't this conference amazing?")
    // Somewhere else in code
    if (some_condition_is_met()) {
    alertDialog.show()
    }
    // Somewhere else in code
    if (some_other_condition_is_met()) {
    alertDialog.dismiss()
    }
    @Composable
    fun AlertDialogComponent() {
    if (some_condition_is_met()) {
    }
    }

    View Slide

  50. How What
    // Classic Android
    val alertDialog = AlertDialog.Builder(context)
    .setTitle("Android Summit!”)
    .setMessage("Isn't this conference amazing?")
    // Somewhere else in code
    if (some_condition_is_met()) {
    alertDialog.show()
    }
    // Somewhere else in code
    if (some_other_condition_is_met()) {
    alertDialog.dismiss()
    }
    @Composable
    fun AlertDialogComponent() {
    if (some_condition_is_met()) {
    AlertDialog(
    title = {
    Text("Android Summit!")
    },
    text = {
    Text(text = "Isn't this amazing?")
    }
    )
    }
    }

    View Slide

  51. State

    View Slide

  52. @Composable

    fun AlertDialogComponent() {

    var showPopup by remember { mutableStateOf(false) }

    }

    View Slide

  53. @Composable

    fun AlertDialogComponent() {

    var showPopup by remember { mutableStateOf(false) }

    Button(onClick = { showPopup = true }) {

    Text(text = "Click Me")

    }

    }

    View Slide

  54. @Composable

    fun AlertDialogComponent() {

    var showPopup by remember { mutableStateOf(false) }

    Button(onClick = { showPopup = true }) {

    Text(text = "Click Me")

    }

    if (showPopup) {

    AlertDialog(

    onCloseRequest = { showPopup = false },

    text = {

    Text("Congratulations! You just clicked the text successfully")

    },

    confirmButton = {

    Button(

    onClick = onPopupDismissed

    ) {

    Text(text = "Ok")

    }

    }

    )

    }

    }

    View Slide

  55. @Composable

    fun AlertDialogComponent() {

    var showPopup by remember { mutableStateOf(false) }

    Button(onClick = { showPopup = true }) {

    Text(text = "Click Me")

    }

    if (showPopup) {

    AlertDialog(

    onCloseRequest = { showPopup = false },

    text = {

    Text("Congratulations! You just clicked the text successfully")

    },

    confirmButton = {

    Button(

    onClick = onPopupDismissed

    ) {

    Text(text = "Ok")

    }

    }

    )

    }

    }

    View Slide

  56. @Composable

    fun AlertDialogComponent() {

    var showPopup by remember { mutableStateOf(false) }

    if (!showPopup) {

    Button(onClick = { showPopup = true }) {

    Text(text = "Click Me")

    }

    } else {

    AlertDialog(

    onCloseRequest = { showPopup = false },

    text = {

    Text("Congratulations! You just clicked the text successfully")

    },

    confirmButton = {

    Button(

    onClick = onPopupDismissed

    ) {

    Text(text = "Ok")

    }

    }

    )

    }

    }

    View Slide

  57. Recomposition

    View Slide

  58. Recompose
    / re·kuhm·powz /
    verb
    In an imperative UI model, to change a widget, you call a setter on the
    widget to change its internal state. In Compose, you call the composable
    function again with new data. Doing so causes the function to
    be recomposed--the widgets emitted by the function are redrawn, if
    necessary, with new data. The Compose framework can intelligently
    recompose only the components that changed.

    View Slide

  59. (user: User)
    (name: String) (age: Int) (user: User)
    [counter: Int] (user: User)
    (imageURL: String)
    [scale: Float]
    (address: String)

    View Slide

  60. (user: User)
    (name: String) (age: Int) (user: User)
    [counter: Int] (user: User)
    (imageURL: String)
    [scale: Float]
    (address: String)

    View Slide

  61. (user: User)
    (name: String) (age: Int) (user: User)
    [counter: Int] (user: User)
    (imageURL: String)
    [scale: Float]
    (address: String)

    View Slide

  62. (user: User)
    (name: String) (age: Int) (user: User)
    [counter: Int] (user: User)
    (imageURL: String)
    [scale: Float]
    (address: String)

    View Slide

  63. (user: User)
    (name: String) (age: Int) (user: User)
    [counter: Int] (user: User)
    (imageURL: String)
    [scale: Float]
    (address: String)

    View Slide

  64. (user: User)
    (name: String) (age: Int) (user: User)
    [counter: Int] (user: User)
    (imageURL: String)
    [scale: Float]
    (address: String)

    View Slide

  65. Rules of
    Recomposition

    View Slide

  66. Rules of
    Recomposition
    Some composable functions could be skipped
    Composable functions can be called frequently
    Composable functions can execute in any order
    Composable functions can run in parallel

    View Slide

  67. @Composable

    fun ParentComposable() {

    "// Don’t write logic that always depends

    "// on the execution of all the composable

    Child1Composable()

    Child2Composable()

    Child3Composable()

    }

    View Slide

  68. Rules of
    Recomposition
    Some composable functions could be skipped
    Composable functions can be called frequently
    Composable functions can execute in any order
    Composable functions can run in parallel

    View Slide

  69. Rules of
    Recomposition
    Some composable functions could be skipped
    Composable functions can be called frequently
    Composable functions can execute in any order
    Composable functions can run in parallel

    View Slide

  70. "// When this component is called from inside an animation,

    "// it will be called on every frame.

    @Composable

    fun ComponentCalledFromAnimation() {

    "// expensiveOperation takes 2 seconds to run

    val result = expensiveOperation()

    Text(result.message)

    }

    View Slide

  71. "// When this component is called from inside an animation,

    "// it will be called on every frame.

    @Composable

    fun ComponentCalledFromAnimation() {

    "// expensiveOperation takes 2 seconds to run

    val result = expensiveOperation()

    Text(result.message)

    }
    launchInComposition

    View Slide

  72. Rules of
    Recomposition
    Some composable functions could be skipped
    Composable functions can be called frequently
    Composable functions can execute in any order
    Composable functions can run in parallel

    View Slide

  73. @Composable

    fun ParentComposable() {

    "// Can be called in any order

    Child1Composable()

    Child2Composable()

    Child3Composable()

    }

    View Slide

  74. Rules of
    Recomposition
    Composable functions could be skipped
    Composable functions can be called frequently
    Composable functions can execute in any order
    Composable functions can run in parallel

    View Slide

  75. Simple Layouts

    View Slide

  76. View Slide

  77. Row

    View Slide

  78. Row
    1 2

    View Slide

  79. 1
    Column
    1
    Row
    2

    View Slide

  80. @Composable

    fun ImageWithTitleSubtitleComponent() {



    }

    View Slide

  81. @Composable

    fun ImageWithTitleSubtitleComponent() {

    Row() {

    Column() {

    }

    }

    }

    View Slide

  82. @Composable

    fun ImageWithTitleSubtitleComponent() {

    Row(modifier = Modifier.fillMaxWidth().padding(16.dp)) {

    Column(modifier = Modifier.padding(start = 16.dp)) {



    }

    }

    }

    View Slide

  83. @Composable

    fun ImageWithTitleSubtitleComponent() {

    Row(modifier = Modifier.fillMaxWidth().padding(16.dp)) {

    DrawableImage(R.drawable.landscape)

    Column(modifier = Modifier.padding(start = 16.dp)) {

    CustomTextComponent(displayText = "Title")

    CustomTextComponent(displayText = "Subtitle")

    }

    }

    }

    View Slide

  84. @Composable

    fun ImageWithTitleSubtitleComponent(

    title: String,

    subtitle: String,

    imageUrl: String

    ) {

    Row(modifier = Modifier.fillMaxWidth().padding(16.dp)) {

    NetworkImage(imageUrl)

    Column(modifier = Modifier.padding(start = 16.dp)) {

    CustomTextComponent(displayText = title)

    CustomTextComponent(displayText = subtitle)

    }

    }

    }

    View Slide

  85. @Composable

    fun ImageWithTitleSubtitleComponent(

    title: String,

    subtitle: String,

    imageUrl: String

    ) {

    Row(modifier = Modifier.fillMaxWidth().padding(16.dp)) {

    NetworkImage(imageUrl)

    Column(modifier = Modifier.padding(start = 16.dp)) {

    CustomTextComponent(displayText = title)

    CustomTextComponent(displayText = subtitle)

    }

    }

    }

    View Slide

  86. View Slide

  87. Display List

    View Slide

  88. View Slide

  89. View Slide

  90. @Composable

    fun ListComponent(superheroList: List) {



    }

    View Slide

  91. @Composable

    fun ListComponent(superheroList: List) {

    ScrollableColumn {

    for(person in superheroList) {

    SimpleRowComponent(

    person.name,

    person.age,

    person.profilePictureUrl

    )

    }

    }

    }

    View Slide

  92. www.JetpackCompose.app

    View Slide

  93. @Composable

    fun ListComponent(superheroList: List) {



    }

    View Slide

  94. @Composable

    fun ListComponent(superheroList: List) {

    LazyColumnFor(items = superheroList) { person "->



    }

    }

    View Slide

  95. @Composable

    fun ListComponent(superheroList: List) {

    LazyColumnFor(items = superheroList) { person "->

    SimpleRowComponent(

    person.name,

    person.age,

    person.profilePictureUrl

    )

    }

    }

    View Slide

  96. @Composable

    fun ListComponent(superheroList: List) {

    LazyColumnFor(items = superheroList) { person "->

    SimpleRowComponent(

    person.name,

    person.age,

    person.profilePictureUrl

    )

    }

    }

    View Slide

  97. Thank You Droid God,
    For this new day. I will rest in your promises(coroutines) of a world free of
    fragments. Guide me with compile-time checks and help me in every
    @SuppressWarnings that I add.

    View Slide

  98. Click Gesture

    View Slide

  99. @Composable

    fun SimpleRowComponent(

    titleText: String,

    subtitleText: String,

    imageUrl: String

    ) {

    Card(

    modifier = Modifier.fillMaxWidth()

    .padding(8.dp),

    shape = RoundedCornerShape(4.dp)

    ) {

    ""...

    ""...

    ""...

    }

    }

    View Slide

  100. @Composable

    fun SimpleRowComponent(

    titleText: String,

    subtitleText: String,

    imageUrl: String,

    viewModel: SuperheroViewModel

    ) {

    Card(

    modifier = Modifier.fillMaxWidth()

    .padding(8.dp)

    .cl

    shape = RoundedCornerShape(4.dp)

    ) {

    ....

    ....

    }

    }

    View Slide

  101. @Composable

    fun SimpleRowComponent(

    titleText: String,

    subtitleText: String,

    imageUrl: String,

    viewModel: SuperheroViewModel

    ) {

    Card(

    modifier = Modifier.fillMaxWidth()

    .padding(8.dp)

    .cl

    shape = RoundedCornerShape(4.dp)

    ) {

    ....

    ....

    }

    }

    clip
    clickable
    clipToBounds

    View Slide

  102. @Composable

    fun SimpleRowComponent(

    titleText: String,

    subtitleText: String,

    imageUrl: String,

    viewModel: SuperheroViewModel

    ) {

    Card(

    modifier = Modifier.fillMaxWidth()

    .padding(8.dp) +

    .clickable {

    viewModel.updateSelectedSuperhero()

    },

    shape = RoundedCornerShape(4.dp)

    ) {

    ....

    ....

    }

    }

    View Slide


  103. @Composable

    fun SimpleRowComponent(

    titleText: String,

    subtitleText: String,

    imageUrl: String,

    viewModel: SuperheroViewModel

    ) {

    Card(

    modifier = Modifier.fillMaxWidth()

    .padding(8.dp) +

    .clickable {

    viewModel.updateSelectedSuperhero()

    },

    shape = RoundedCornerShape(4.dp)

    ) {

    ....

    ....

    }

    }

    View Slide

  104. @Composable

    fun SimpleRowComponent(

    titleText: String,

    subtitleText: String,

    imageUrl: String,

    onClick: () "-> Unit

    ) {

    Card(

    modifier = Modifier.fillMaxWidth()

    .padding(8.dp)

    .clickable {

    onClick()

    },

    shape = RoundedCornerShape(4.dp)

    ) {

    ....

    ....

    }

    }

    View Slide

  105. @Composable

    fun SimpleRowComponent(

    titleText: String,

    subtitleText: String,

    imageUrl: String,

    onClick: () "-> Unit

    ) {

    Card(

    modifier = Modifier.fillMaxWidth()

    .padding(8.dp)

    .clickable {

    onClick()

    },

    shape = RoundedCornerShape(4.dp)

    ) {

    ....

    ....

    }

    }

    View Slide

  106. Pinch-to-Zoom & Drag

    View Slide

  107. @Composable

    fun ZoomableImageComponent(imageUrl: String) {



    }

    View Slide

  108. @Composable

    fun ZoomableImageComponent(imageUrl: String) {

    var scale by state { 1f }

    var panOffset by state { Offset(0f, 0f) }

    }

    View Slide

  109. @Composable

    fun ZoomableImageComponent(imageUrl: String) {

    var scale by state { 1f }

    var panOffset by state { Offset(0f, 0f) }

    Box(gravity = Alignment.Center) {

    NetworkImage(

    imageUrl = imageUrl,

    modifier = Modifier.fillMaxSize()

    )

    }

    }

    View Slide

  110. @Composable

    fun ZoomableImageComponent(imageUrl: String) {

    var scale by state { 1f }

    var panOffset by state { Offset(0f, 0f) }

    Box(

    gravity = Alignment.Center,

    modifier = Modifier.zoomable(onZoomDelta = { scale *= it })

    ) {

    NetworkImage(

    imageUrl = imageUrl,

    modifier = Modifier.fillMaxSize().drawLayer(

    scaleX = scale,

    scaleY = scale

    )

    )

    }

    }

    View Slide

  111. @Composable

    fun ZoomableImageComponent(imageUrl: String) {

    var scale by state { 1f }

    var panOffset by state { Offset(0f, 0f) }

    Box(

    gravity = Alignment.Center,

    modifier = Modifier.zoomable(onZoomDelta = { scale *= it }).rawDragGestureFilter(

    object : DragObserver {

    override fun onDrag(dragDistance: Offset): Offset {

    panOffset = panOffset.plus(dragDistance)

    return super.onDrag(dragDistance)

    }

    })

    ) {

    NetworkImage(

    imageUrl = imageUrl,

    modifier = Modifier.fillMaxSize().drawLayer(

    scaleX = scale,

    scaleY = scale,

    translationX = panOffset.x,

    translationY = panOffset.y

    )

    )

    }

    }

    View Slide

  112. @Composable

    fun ZoomableImageComponent(imageUrl: String) {

    var scale by state { 1f }

    var panOffset by state { Offset(0f, 0f) }

    Box(

    gravity = Alignment.Center,

    modifier = Modifier.zoomable(onZoomDelta = { scale *= it }).rawDragGestureFilter(

    object : DragObserver {

    override fun onDrag(dragDistance: Offset): Offset {

    panOffset = panOffset.plus(dragDistance)

    return super.onDrag(dragDistance)

    }

    })

    ) {

    NetworkImage(

    imageUrl = imageUrl,

    modifier = Modifier.fillMaxSize().drawLayer(

    scaleX = scale,

    scaleY = scale,

    translationX = panOffset.x,

    translationY = panOffset.y

    )

    )

    }

    }

    View Slide

  113. View Slide

  114. Compose in Classic Android

    View Slide




  115. android:layout_width="match_parent"

    android:layout_height="match_parent">


    android:id="@+id/text_view"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content" "/>


    android:id="@+id/compose_view"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content" "/>

    "
    activity_compose_in_classic_android.xml

    View Slide




  116. android:layout_width="match_parent"

    android:layout_height="match_parent">


    android:id="@+id/text_view"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content" "/>


    android:id="@+id/compose_view"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content" "/>

    "
    activity_compose_in_classic_android.xml

    View Slide

  117. class ComposeInClassicAndroidActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_compose_in_classic_android)

    }

    }

    View Slide

  118. class ComposeInClassicAndroidActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_compose_in_classic_android)

    val composeView = findViewById(R.id.compose_view)

    composeView.setContent {

    SimpleRowComponent()

    }

    }

    }

    View Slide

  119. class ComposeInClassicAndroidActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_compose_in_classic_android)

    val composeView = findViewById(R.id.compose_view)

    composeView.setContent {

    SimpleRowComponent()

    }

    }

    }

    View Slide

  120. Classic Android in Compose

    View Slide

  121. @Composable

    fun ClassAndroidInComposeComponent() {

    }

    View Slide

  122. @Composable

    fun ClassAndroidInComposeComponent() {

    val context = ContextAmbient.current

    val classicTextView = remember { TextView(context) }

    }

    View Slide

  123. @Composable

    fun ClassAndroidInComposeComponent() {

    val context = ContextAmbient.current

    val classicTextView = remember { TextView(context) }

    AndroidView(viewBlock = { classicTextView }) { view "->

    "// view is inflated here. Do anything if your logic requires it

    }

    }

    View Slide

  124. ViewModel

    View Slide

  125. @Composable

    fun MoviesComponent() {

    val viewModel: MoviesViewModel = viewModel()

    "// or

    val viewModel: MoviesViewModel = viewModel(ViewModelProvider.Factory)

    }

    View Slide

  126. LiveData

    View Slide

  127. @Composable

    fun MoviesComponent() {

    val viewModel: MoviesViewModel = viewModel()

    }

    View Slide

  128. @Composable

    fun MoviesComponent() {

    val viewModel: MoviesViewModel = viewModel()

    val movieList = viewModel.movieListLiveData

    }

    View Slide

  129. @Composable

    fun MoviesComponent() {

    val viewModel: MoviesViewModel = viewModel()

    val movieList = viewModel.movieListLiveData.observeAsState()

    }

    View Slide

  130. @Composable

    fun MoviesComponent() {

    val viewModel: MoviesViewModel = viewModel()

    val movieList = viewModel.movieListLiveData.observeAsState()

    }
    ‘X’AsState()

    View Slide

  131. Testing

    View Slide

  132. @Composable

    fun SimpleRowComponent(

    titleText: String,

    subtitleText: String,

    imageUrl: String

    ) {

    Card(

    modifier = Modifier.fillMaxWidth()

    .padding(8.dp),

    shape = RoundedCornerShape(4.dp)

    ) {

    ""...

    ""...

    }

    }

    View Slide

  133. @RunWith(JUnit4"::class)

    class SimpleRowComponentTest {



    }

    View Slide

  134. @RunWith(JUnit4"::class)

    class SimpleRowComponentTest {

    @get:Rule

    val composeTestRule = createComposeRule(disableTransitions = true)

    }

    View Slide

  135. @RunWith(JUnit4"::class)

    class SimpleRowComponentTest {

    @get:Rule

    val composeTestRule = createComposeRule(disableTransitions = true)

    @Before

    fun setUp() {

    composeTestRule.setContent {

    SimpleRowComponent(

    titleText = "Title",

    subtitleText = "Subtitle",

    imageUrl = "https:"//www.google.com/demo.jpg"

    )

    }

    }

    }

    View Slide

  136. @RunWith(JUnit4"::class)

    class SimpleRowComponentTest {

    @get:Rule

    val composeTestRule = createComposeRule(disableTransitions = true)

    @Before

    fun setUp() {

    composeTestRule.setContent {

    SimpleRowComponent(

    titleText = "Title",

    subtitleText = "Subtitle",

    imageUrl = "https:"//www.google.com/demo.jpg"

    )

    }

    }

    @Test

    fun check_if_card_is_displayed() {

    composeTestRule.onNodeWithText("Title")

    }

    }

    View Slide

  137. @Test

    fun check_if_card_is_displayed() {

    composeTestRule.onNodeWithText("Title")

    }

    View Slide

  138. @Test

    fun check_if_card_is_displayed() {

    composeTestRule.onNodeWithSubstring("Ti")

    }

    View Slide

  139. @Test

    fun check_if_card_is_displayed() {

    composeTestRule.onNodeWithTag("TitleTag")

    }

    View Slide

  140. @Test

    fun check_if_card_is_displayed() {

    composeTestRule.onNodeWithTag("TitleTag")

    }

    "// In the component

    @Composable

    fun SimpleRowComponent(""...) {

    Card(

    modifier = Modifier.testTag("TitleTag")

    ) {

    ""...

    }

    }

    View Slide

  141. @Test

    fun check_if_card_is_displayed() {

    composeTestRule.onNodeWithText("Title")

    }

    View Slide

  142. @Test

    fun check_if_card_is_displayed() {

    composeTestRule.onNodeWithText("Title").assertIsDisplayed()

    }

    View Slide

  143. @Test

    fun check_if_card_is_displayed() {

    composeTestRule.onNodeWithText("Title").assertIsHidden()

    }

    View Slide

  144. @Test

    fun check_if_card_is_displayed() {

    composeTestRule.onNodeWithText("Title").assertHasClickAction()

    }

    View Slide

  145. @Test

    fun check_if_card_is_displayed() {

    composeTestRule.onNodeWithText("Title").assertHeightIsAtLeast(100.dp)

    }

    View Slide

  146. @Test

    fun check_if_card_is_displayed() {

    composeTestRule.onNodeWithText("Title").performClick()

    }

    View Slide

  147. @Test

    fun check_if_card_is_displayed() {

    composeTestRule.onNodeWithText("Title").performGesture { swipeDown() }

    }

    View Slide

  148. @Test

    fun check_if_card_is_displayed() {

    composeTestRule.onNodeWithText("Title").assertIsDisplayed()

    }

    View Slide

  149. Resources

    View Slide

  150. Christmas came early"!!

    View Slide

  151. https:"//bit.ly/ComposeByExample

    View Slide

  152. https:"//www.JetpackCompose.app/

    View Slide

  153. https:"//www.JetpackCompose.app/FAQ

    View Slide

  154. https:"//www.JetpackCompose.app/Quick-Bites
    @vinaygaba

    View Slide

  155. https:"//github.com/airbnb/Showkase

    View Slide

  156. Learn Jetpack Compose
    by Example
    Vinay Gaba
    @vinaygaba
    https://bit.ly/ComposeByExample

    View Slide