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 <i>@Composable</i>
and that's pretty much all you need to create a new widget,
what we call <i>composables</i> 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 <i>Text</i>. 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. <i>@Composable</i> 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 <i>for</i> 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 <i>TextField</i>. I can give the filter string
as the initial value to display inside the <i>TextField</i>. And the <i>TextField</i> can also call a lambda whenever the user
enters something inside it. So in that case, I supply a lambda
to <i>onValueChange</i> 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 <i>TextField</i>. 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 <i>TextField</i>, 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 <i>developer.android.com.</i> 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 <i>size</i>, 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, <i>padding.</i> Now the image is in set within the bounds
of the image composable. Then we can add a <i>drawShadow</i> modifier. So the <i>drawShadow</i> 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 <i>drawBorder</i>
to draw a circular border so I still use the same shape. I use one of the colors
from my <i>MaterialTheme</i>. 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 <i>ripple</i>, 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 <i>observeAsState</i>, you can see it in action here. I can call <i>observeAsState</i>,
I give back a state object, here called products
and I can pass the state to my <i>AdapterList</i>, that's the name
of the recycle view in compose. And then using a trailing lambda,
every time that the <i>AdapterList</i> 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 <i>AdapterList</i>,
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 <i>@Composable</i>,
we create a ConstraintLayout and that ConstraintLayout
needs a <i>ConstraintSet</i>. 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 <i>constraint</i>,
use this <i>tag function</i>. 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 <i>parent</i> 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 <i>decreaseConstraint</i>
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 <i>Constraints</i>
and for the left edge, I set <i>constrainTo</i> 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 <i>onSelected</i> my lamba to be able to change
and update that value. Then at the bottom, I could use
a <i>Toggleable</i> 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 <i>onSelected</i> lambda. Then, for the ripple effect,
that's built in so I don't have anything to do,
just <i>Modifier.ripple</i>. Here's where things get interesting. To animate the radius
of the different corners, all I have to do is use
this <i>animate</i> 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 <i>animate</i> function, and the <i>animate</i> 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 <i>animate</i>,
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 <i>alpha</i>. So instead of passing the value
to the radius of the rounded corner shape,
I just pass the <i>alpha</i> 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<i>
AndroidView</i> 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,<i>
AndroidView</i> 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 <i>onActive</i>
and <i>onDispose</i>. So <i>onActive</i> 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 <i>onDispose</i> to do my clean up. So when you're part of a recycling list
like <i>AdapterList</i>, 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 <i>onCommit</i>. So <i>onCommit</i> takes as a parameter
what I want to track. So I want to track my product, itself. And in <i>onCommit</i>, 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 <i>semantics</i>. 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 <i>TestTag</i> 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 <i>@Test</i> function. I create some test data,
so I have a product, I have the <i>quantity = 2</i>. 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 <i>findByTag</i> 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 <i>runOnIdleCompose</i> 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<i>
developer.android.com/jetpack/compose</i>. 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 <i>android-review.googlesource.com</i>. 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.