Jetpack Compose

Hi, my name is Romain Guy and I work on the Android Toolkit team and today I would like to tell you about Jetpack Compose So if you haven’t heard about it, Jetpack Compose is our next generation UI Toolkit, written entirely in Kotlin to build high quality applications for Android easily and quickly But the best way for you to understand what Jetpack Compose is and why it’s different from the existing UI Toolkit is to look at a little bit of code So we’ll start with this It’s a function, it has a single annotation called @Composable and that’s pretty much all you need to create a new widget, what we call composables in the Compose world You don’t have to extend a class, you don’t have to overwrite constructors or methods You create a function or method and that’s it It can be private, public, whatever you want, it’s just a function So then to actually generate the UI from that, you’re going to take some input So in my example, I want to produce a label for a product So I take my product as a parameter of the function, once again, very simple, just a function And from then on, I can do what we call “emit the UI.” So I’m going to invoke another composable function, this one is one of the default composable functions that are part of Jetpack Compose, it’s called Text It just creates a label onscreen And the text contains a string that’s built from my product One thing that’s interesting to notice is that composable functions can only be invoked from composable functions So in that sense, they’re like suspend functions if you’re familiar with coroutines @Composable changes the type of the function, and you can only call them in the right context One way to think about it is that your composables are just functions that take data and transform it into your UI, so your UI is a function of the data And that’s what we want with this function paradigm, not only because it’s simpler to write the code, it’s simpler to refactor, it’s also very easy to reason about So we really want the data to flow down from your business logic down to the functions What’s powerful about choosing Kotlin to write your UI this way is that you have access to all the features of the Kotlin language So let’s say, for instance, that we want to only display our label when the quantity of the product is greater than 0 We don’t have to tell Compose how to do this We don’t have to do things like, okay, let’s find this label, if it’s visible, let’s make it invisible If it’s not visible, let’s make it visible and so on Instead, we just tell Compose what we want and you can see the code on screen, super simple I just say, “Okay, if the quantity is greater than 0, there’s a label Otherwise, there’s nothing.” And Compose will take care of everything else And whenever the value of the product changes, Compose will re-invoke my composable, we call that “recomposition,” and will reevaluate that logic and will take care of updating the Tree as needed So you don’t have to worry about removing and hiding the items Compose does everything for you And of course this works in complex situations Let’s say here I have a for loop to create a column of labels If the quantity updates for the product, the number of emitted items will change but I don’t have to do anything Compose takes care of all of it for me Alright, one thing– one other thing I want to talk about is state So most of the time you want to operate only on input parameters and hopefully, immutable parameters to your function but sometimes you want a bit of state So here, I have a list of products and I want the user to be able to filter their list based on something they type on the query So to do this, I start by creating a state This is just a string and I can use this convenient function called state, and I can use it as a delegate So it creates a state object that Compose will remember We say that Compose memorizes it Then I can create a TextField I can give the filter string as the initial value to display inside the TextField And the TextField can also call a lambda whenever the user enters something inside it So in that case, I supply a lambda to onValueChange and inside the lambda I will update the state So whenever the user types something, my lambda is invoked, I update the filter, and Compose will trigger a recomposition which will update the displayed value inside the TextField And then using that filter, I can go through the list of products and just display the ones that match the query So once again, when the user types something into the TextField, a recomposition will happen and my loop will automatically re-execute And that’s all you have to do Again, no callbacks, no clean up, no setup, no listeners, nothing You just describe what you want the UI to be, not how you want it to update Alright, so here’s how Tech Compose works So there are two parts to Compose, first on the development host where you write your code It starts with the Kotlin Compiler We use a lot of features of Kotlin, for instance, trailing lambdas, you may have noticed them operating the examples And even though we use an annotation, we don’t use an annotation processor Compose uses a Compiler Plugin So it works at the typing front system, at the type system level and also the code generation level to do its magic And then finally we have Android Studio that you all know and love

and we have some Compose-specific tools inside of Android Studio Now on your device, we have the Compose Runtime At its core, Compose doesn’t know anything about Android or UIs It just works on Trees so we could actually emit other things than UIs with Compose And then on top we have the Compose UI Core That’s your input management, measurement, drawing, layout Then we have the foundation that’s your standard layouts, like rows and columns and default interactions And finally, we have Material Design components So we have an implementation of the Material Design system so if you choose Material Design in your application, everything that you need from Material Design will be available out of the box with Compose So let’s take a look back at Compose so far We announced it a year ago at I/O ’19 We also then moved all the development into AOSP Now all the development is in the open, you can follow along and you can even contribute if you want But it was only the source code If you wanted to play with Compose, you had to build Compose yourself because we are not quite ready for prime time yet A few months later, at Android Dev Summit we announced the Developer Preview 1 of Jetpack Compose You could download the latest version of Android Studio and you could easily create new projects and start leaving us feedback And since then many of you have done so and we’ve made major changes to Compose and we think that it’s better than it’s ever been So please keep that feedback coming So today, we are launching Developer Preview 2 and that’s available right now on developer.android.com So please give it a try once again And if you haven’t done so, you will be able to look at tutorials to learn Compose Now what’s interesting is what happened between Developer Preview 1 and Developer Preview 2 We started doing biweekly releases, so I believe we’ve done 12 or 13 releases since then that some of you have decided to use and we want to thank you for your patience because we’ve made major API changes and you had to do a lot of refactorings in your test applications But that gives us the ability to quickly iterate on the feedback you are giving us and once again, Compose is that much better because of it So today, I would like to take a look at some of the things we’ve done over the past few months and also address some of the questions that many of you have asked us when we first announced Compose If you watched the Android 11 Launch Keynote this morning, you’ve already seen a demo where we presented some of the tools that we have for Jetpack Compose If you haven’t, you should watch that video But if not, here’s a quick summary in one screenshot So in this screenshot, you can see the embedded emulator on the left, it helps you run your app side by side with your code On the right, you can see a preview for Jetpack Compose composable functions So as you make tweaks to your code, you can see the updates in real time to your widgets without having to rerun the app every single time The preview can also be interactive so you can even test the logic of your widget without running the app Finally, those previews are available in the inline documentation, you can see that in the pop-up So when you do code completion, for instance, you can see what the widgets, the composables look like And we have many more ideas of what we want to do for the tools So stay tuned, the teams are hard at work so there is a lot more coming in the future Alright, so first I want to talk about Modifiers So when we first launched Developer Preview 1 [inaudible] modifiers were present, but they were not used that much And also they were a bit confusing because what you could do with modifiers you could also do with regular composables For instance, padding used to be a composable But we figured that using padding for instance as a composable was creating too much nesting So now we’ve moved a lot of features to Modifiers Modifiers decorate a single element And those decorations can be layout parameters, metadata or additional behaviors But the best way to understand modifiers is to look at an example So here, I have an image You can see at the top that I’ve created a state It’s a simple circular shape and you can see I’m using a slightly different variant of the state function that we already saw It uses destructuring assignment to get not only the value but also a lambda that we can use to modify the value, and you will see why in a little bit So first, I use an image composable to display my image resource And then I have a single modifier called size, it sets the width and the height and you can see on the right what the image looks like with its fixed dimensions Then we can add a second modifier, padding Now the image is in set within the bounds of the image composable Then we can add a drawShadow modifier So the drawShadow modifier is interesting because it is made itself of multiple modifiers and you can see it clips the image So here I’m using my circle shape from my state at the top to not only draw a circular shadow but also to clip the content of the image to the circle Then I can use drawBorder to draw a circular border so I still use the same shape I use one of the colors from my MaterialTheme But also I can stack multiple modifiers of the same type so I can add a second border or even a third border And already you can see that adds something that’s much more interesting We started with this very simple rectangular image Now we have a shadow, we have circular clipping, and we have multiple borders

I can also make the composable interactive so here I use a ripple, a Material Design ripple So every time the user taps on the image, I will automatically get the nice ripple effect, the animation will be handled for me and it’s just a one line modifier I also use a clickable modifier to add interaction to my image So in this case, I call my set shape lambda that was given to me by the state function to toggle back and forth between the original circle shape and this new cut-corner shape So now, just using modifiers, I started from an image, a static image, and now I have this visually complex interactive piece of UI And that was super easy to do Alright, one of the things that a lot of you have asked us about when we first announced Compose was what about Recycle View or you know, for folks like me who started a long time ago, what about List View? You called out rightly so that all of our examples were built without using one of the fundamental elements of Android applications that we use everyday on our phones and that’s this kind of Recycle List You can see on the screen I have my demo where in my app, my shopping cart is made of multiple items and I can scroll through them and have this recycling list of items We start with a composable function You need to take a list as an input In my case, I could take a simple Kotlin list or a mutable list but I’m using LiveData We also provide support for RXJava and Flow So when you use LiveData or RXJava or Flow, you need to observe the data stream as a Compose state When we pull the extension function called observeAsState, you can see it in action here I can call observeAsState, I give back a state object, here called products and I can pass the state to my AdapterList, that’s the name of the recycle view in compose And then using a trailing lambda, every time that the AdapterList needs a new item or to replace an item, I can just describe the UI I want to emit So in my case, I will create a shopping cart item based on the product that I received as an input And inside I want a 3D model viewer for the nice 3D animations And that’s it, you don’t need to write an adapter, you don’t need to do anything more than that As soon as the LiveData object changes, if the number of items in your list increases or decreases, or the value of any of the items changes we will automatically recompose everything, re-invoke your trailing lambda for the AdapterList, and the UI will update It’s not more complicated than this ConstraintLayout is one of our most powerful and most popular layouts and that was one of the main questions we got, again, when we announced the Developer Preview 1 and Compose at IO, you wanted to know what about ConstraintLayout, how will this work in Compose? And that’s actually a very good question It stems from the fact that in Compose, because we use functions, you can’t grab a reference to view, so how do you describe constraints between different elements? So I’m going to show you how to use ConstraintLayout with Compose Now we’re going to use this small example in the bottom right That row at the bottom has a couple of buttons, it’s decrease and increase buttons Then we have some text, we have a little color swatch and we have another label So let’s take a look at how it works So first in @Composable, we create a ConstraintLayout and that ConstraintLayout needs a ConstraintSet Those used to be defined an XML or in code but now they are entirely in code But because it’s an object, you can of course make that a constant, you can pass it around, you can do whatever it is you want Here, we’re going to create it inline So to create a new constraint, use this tag function You give it a tag, the tag can be anything you want, it could be a string In that case, just a regular Kotlin object And in that tag, we can declare the constraints themselves So in this example, for the first button, the decrease button on the left, I want to constrain the left edge to the left edge of the parent and I also want to center it vertically And then all that’s left to do is emit the button, itself, and to be able to map it to the constraint we just created, we use a tag modifier So we just assign a tag to the composable and ConstraintLayout will do the rest Now because tag returns a constraint as an object, we can use that as a reference in other constraints So for the next button that goes to the right of our first button, we create a new tag And for the left constraint, we just use the right edge of the decreaseConstraint that we just created and the rest is exactly the same We create our button, we assign a tag and ConstraintLayout will match the constraint to the composable What’s really powerful about doing all of this from code is that you can add a little bit of logic So if we fast forward a little bit, I’ve added the labels and I have this little color swatch, that little red dot that you see at the bottom That red swatch is not visible in every item of my list It only appears on some items So then how do I align the last label to the right of that swatch if that swatch is not always there? So this is how you do it I create a new Constraints and for the left edge, I set constrainTo and here I can enter a complex expression So here I say, “Okay, if I have a swatch, I want it to be constrained to the constraint of that swatch.” Otherwise, I want to use the constraint of the label that came before

So this is much more convenient than doing the equivalent using both XML and code Another thing a lot of you have asked about is Animations So I want to show you how animations work with Jetpack Compose On the screen right now, you can see some of animations I have built into my demo Whenever the user selects one of the items in the shopping cart, for instance, to select multiple of them to be able to delete them quickly, I animate the radius of the different corners, I animate an overlay on top of the item and I animate a little check mark So let me show you how this works with Compose So first we’re going to create state inside our item to track the selected state of the item So once again, I use the destructuring assignment So I have selected as my state value and I have onSelected my lamba to be able to change and update that value Then at the bottom, I could use a Toggleable composable function This will just– it’s just a helper to be able to do this selection easily So I give it the selected state as the value, and whenever the value changes, I tell it invoke my onSelected lambda Then, for the ripple effect, that’s built in so I don’t have anything to do, just Modifier.ripple Here’s where things get interesting To animate the radius of the different corners, all I have to do is use this animate function And I pass it a value, and as you can see, that value is itself based on my selected state so I say, “If the item is selected, I want a radius of 48 dp for the top left corner Otherwise, I only want 8 dp So whenever the selected state changes, recomposition will happen, a different value will be passed to the animate function, and the animate function will take care of everything else It will kick off the animation, picking up where you left off, it’s fully interruptible and it’s based on physics So then after calling animate, I have another state value that I can then pass to my rounded corner shape I can just assign those values directly to the different corners, and that’s it, you don’t have to have listeners and callback, there’s no set up and there’s no clean up to do It’s that simple Of course, like I said, by default, we use physics-based animations We can use twin animations if you prefer and there’s a lot of things you can control that I won’t show you today This is another example, same concept but this time to animate the alpha So instead of passing the value to the radius of the rounded corner shape, I just pass the alpha that I’m animating with the animate function I pass as it as a color of the surface Alright, next up is Interop You can see here in the demo that I have this list of items and in every item, there is a surface view to be able to render this complex 3D scene They are also fully reactive So whenever I click the swatch in the top item of the list, you can see that the color of the 3D object updates in real time And this is something that we really care about We got really inspired by Kotlin Kotlin is great because if you have your existing Java-based application, you can start adding Kotlin at your own pace You don’t have to rewrite the entire application And we wanted to do exactly the same for Compose When you want to adapt Compose, you can just start adding Compose bit by bit, a new screen, a new part of a UI, just however you want to do it, it’s going to work So let me show you how to put views inside of Compose So we start by creating a new composable function This is my 3D viewer, it takes my product as an input Then I have a bit of state, not all viewers, just the 3D viewer, itself You don’t really have to worry about what it is exactly What’s interesting is at the bottom I call this composable AndroidView function And I give it as a parameter, an ID for an XML layout That XML layout contains a surface view When it’s done inflating, AndroidView will invoke my trailing lambda, it will give me a reference to the view that was inflated, and I can then cast it as a surface view, and from there, I can create my model viewer and tell it render into that surface view Of course, to render the 3D animations, I need to be able to refresh the content of the surface view in every frame So to do this, I can use onActive and onDispose So onActive is going to be invoked the first time your composable is added to the UI Tree if you want This is a great place where you can do some set ups or in my case, I’m going to set up a call back for the choreographers so I can get it invoked on every frame every time the screen refreshes Of course when the composable disappears from the tree, I need to be able to stop that callback so I can use onDispose to do my clean up So when you’re part of a recycling list like AdapterList, this is really powerful to make sure that you’re doing the right thing Now to react to data changes, so when I change the color of the product, I can use onCommit So onCommit takes as a parameter what I want to track So I want to track my product, itself And in onCommit, I’m just going to look at the 3D object in the scene and change its color based on the new value from the product And that’s it It’s really simple If you want to use a map view, a camera view, a surface you like I showed you or any type of view, you can incorporate them in your Compose UI

just in this way Last but not least, Testing This was also one of the first questions we got So once again, in Compose, because you just invoked functions, you can’t take a reference to the widgets and we don’t have the concept of ID, so you can’t do a ‘find you by ID.’ So in our example, how do we test, let’s say, those two buttons to increase and decrease the quantity of the product in our shopping cart? So let’s take a look To do this, we use a concept called semantics Semantics are a way to add metadata to the tree of composables This is what we use, for instance, to drive accessibility but this is also at the core of testing So for instance, here in my example in my shopping cart item, I use the TestTag composable This creates a semantic node that contains a tag It’s a way for me to identify that part of the UI and you can see it just contains my button that I want to test So once we have that semantic node, we can move over to the Unit test, just a regular @Test function I create some test data, so I have a product, I have the quantity = 2 I also need to create my logic so this is a lambda that will simply decrease the quantity by 1 every time the user clicks the button Then I can set up my Test UI, I just invoke my shopping cart item and then give it the data and the lambda I just created Finally, here’s the interesting part I can use findByTag to find the part of the tree that we just tagged inside our composable And from then on I can do a lot of things I can query whether or not it’s displayed, I can check its state, I can check a value, or I can perform actions So here I perform two clicks, and finally, at the end, I make sure that the quantity is now 0 instead of 2 after two clicks The best part is Compose is designed with testing in mind so all those features come built in You don’t need something like Espresso, for instance This runOnIdleCompose that you see that I use at the bottom is part of the default APIs So that’s it for today, that’s everything we’ve been up to– well, of course, there’s a lot more– over the past few months But now I want to look ahead a little bit So we are releasing Developer Preview 2 today, and later this summer, we are going to release our Alpha release of Compose For us, Alpha is when you start adopting Compose inside your application Some features might be missing, we have to catch up with 15 years of development in the existing APIs, after all But there should be enough for most applications out there Some of our APIs may still be experimental and there might be a few changes ongoing after Alpha But we really want to start playing with it and give us all the feedback that you may have because next year, we want to release Jetpack Compose 1.0 So if you haven’t done so already, please visit developer.android.com/jetpack/compose There you can find hwo to download and install the Developer Preview 2 with the latest version of Android Studio You will find tutorials, codelabs and so on You can also read the samples that we put on GitHub And more importantly, you can come chat with the team on Slack so you should join the Compose channel on the Kotlin link Slack You will be able to talk with various members of the engineering team This is a place where we gather a lot of your feedback This has been immensely helpful to improve Compose in the past few months and it’s so much better than it was when we started You’ll also be able to chat with the rest of the community and everybody’s incredibly helpful there If you have future requests or if you find bugs, as I’m sure you will, please use the Issue Tracker that’s listed here This is the same Issue Tracker we use so we look at it all the time Finally, if you want to see what’s coming, you can look at all the changes that we make daily on AOSP at android-review.googlesource.com You can just watch everything happening, and if you want, you can even contribute So that’s pretty much it for today and I would like to thank you, all of you, for all the feedback that you’ve been giving us But before I let you go, I just wanted to say that I’ve been working on Android for close to 15 years and usually I do not use the word “excited” lightly when I give a talk, but frankly, I don’t think I’ve ever been as excited about the future of Android UI as I’ve pretty much ever been– well, maybe on my first day on Android– so I’m really looking forward to what you’re going to do with Compose so please keep the feedback coming Thanks