Jetpack Compose

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
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.
Info
Channel: Android Developers
Views: 56,188
Rating: 4.9230771 out of 5
Keywords: jetpack compose, jetpack compose tutorial, intro to jetpack compose, how to jetpack compose, android jetpack, jetpack, android 11, android 11 updates, android 11 features, android 11 latest, android11, android 11 beta launch, intro to android 11, how to android 11, android developers, Jetpack Compose modifiers, ConstraintLayout, Jetpack compose animations, android latest, android updates, type: Conference Talk (Full production);, pr_pr: Android, purpose: Educate
Id: U5BwfqBpiWU
Channel Id: undefined
Length: 24min 20sec (1460 seconds)
Published: Wed Jun 10 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.