Compose by example

[MUSIC PLAYING] NICK BUTCHER: Hi I’m Nick, and today I’m going to talk to you about how Jetpack Compose makes Android UI easier But I’m not just going to tell you, I’m going to show you Jetpack Compose is our new unbundled, declarative UI toolkit now in alpha It’s made for the demands of rich, beautiful modern apps But to me the best thing about it is that it simplifies and accelerates UI development It makes Android UI easier Not just easier, but it makes the easy things easy, and it makes the hard things possible But don’t just take my word for it I’d like to show you how Compose does this I’ll do so by introducing you to some brand new samples that the team have created, which we’re open sourcing today on GitHub Joining our existing JetNews sample, we’re adding four new samples to the Jet family Each sample demonstrates different use cases and APIs Jetchat and Jetsurvey are all simpler apps, a great starting point to begin learning Compose Jetcaster and Jetsnack demonstrate more complex UI with more theming and animation We’re also releasing free samples showing how Compose’s implementation of material design can be customized for your brand All of these samples are available right now on GitHub Today I’d like to take you through some specific examples from these apps to show you how we built them and how Compose simplified their development So let’s jump right in First up, theming Compose ships in their implementation of the material design system To our sample app, in particular, leverages Material’s theming system to highly customize its look and feel For example, it uses completely different color schemes in different sections of the app, with alternate versions of each color scheme and [? dot ?] theme Material theming is a systematic way to customize material designed to reflect your product’s brand The material theme comprises color, typography, and shape attributes Customizing these attributes updates the components throughout your app In Compose, this is modeled by the material theme Composable It accepts parameters describing your desired color, typography, and shape Let’s take a look at each of these parameters The Colors class models Material’s color system There are also Builder functions implementing the default or baseline color palette So you can build on top of this just specifying the colors you want to change So to implement OWL’s color scheme, we create a series of these color’s objects using the Builder functions Similarly, textiles and model by their Typography object, whose default parameters implement the baseline material theme, which uses Roboto So you can emit any that you don’t want to customize Each typographic style is modeled by a text style that you customize its font, size, and weight, et cetera Material shape theming specifies three categories of shapes for small, medium, and large sized components such as buttons, cards, or shapes, respectively You can create rounded shapes where they’re given corner radius or percentage or cut corners For both styles, you can specify individual corner treatments or they all should use the same So now that we understand the parameters, let’s set up our MaterialTheme We recommend doing this in a Composable function to centralize these changes and make it easy to use across your app Here, were modeling OWL’s YellowTheme, configuring the colors, typography, and shapes to use So to apply our desired styling to this screen, we simply wrap it in our YellowTheme Composable Themes can also be nested This detail screen, for example, uses a PinkTheme for most of its content, but includes a related section using a blue color scheme We can nest the BlueTheme around the section to change the theme colors Theming has always been a complex topic on Android By embracing Material and offering an implementation of it rather than the generic and loosely type system, it makes it simpler to understand and easier to work with For example, we gain type safety when working with themes Here we are retreating and using the color and type styles we set up and can even autocomplete them helping you to discover what is available and making it easy to access colors through the theme It’s also simple to derive from theme colors by copying them This avoids hard coding colors, which makes it impossible to support multiple themes like dark theme Because the components themselves understand the MaterialTheme, they can also offer smart defaults For example, when you set a background color here using a Surface component, then elements within it such as text and icons automatically default to the correct color Here we are using a primary colored background so the text color and icon tints default to the onPrimary color It’s simple to see how Material components use theming Here Compose’s FAB has a background color parameter, which defaults to Material color secondary No more hunting through XML files to work out what color a component uses Simply look at the parameter’s default value As we saw before, Compose models colors as a collection of color values We can support dark themes by adding a single parameter to our theme Here, I’m defaulting it to a function, which queries the device’s darkTheme setting,

but you can override it in places where you want a particular theme Then we simply switch color sets based on this value Notice how there is nothing specific to light or dark themes here You can easily support multiple different color schemes using the same techniques So not only does Compose make theming easier, it makes some things possible For example, Compose enables dynamic themes The Jetcaster sample is a podcast app, which uses the artwork for the currently selected podcast to theme the UI It uses the palette library to extract the dominant color from the artwork, then copies the existing color palette, setting the primary color even animating this change It then uses these updated colors to theme the UI This was really hard to achieve with the XML theming system Themes being handled entirely within your application code also enables the possibility to test them For example, the Jetchat sample tests what happens when the theme changes between light and dark In our test, we set up a state flow of whether the test should use a dark theme In setup, we observe this flow, and pass it to our theme Any changes to the flow will now re-compose the UI Now, in our test, we can manipulate our UI into our desired initial state and a set against it Then we update the flow to switch to darkTheme This causes the UI to update We can then assert, again, checking that the theme change happened as expected We can even capture screenshots or compare these against golden images In the past, creating your own design system has been challenging While we recommend you use Material Design and compose shapes and implementation of it, there’s nothing special about it It’s built on public APIs, and it’s entirely possible to build your own design system to extend or replace it The Jetsnack sample defines its own design system with a custom color palette, including heavy use of gradients This comes together in a custom color system Here’s a screen from the app using this color system Notice how components are customized to use these colors like this button with a gradient background To implement this, we can model the color system like Material did with our own design system’s semantic names and represent the gradients as lists of colors Now, we can’t just pass this to the MaterialTheme Composable, as it doesn’t understand this type Instead, we can do exactly what MaterialTheme itself does under the hood to implement our own color system There are three steps to doing this First, define an ambient to hold your colors But wait, what’s an ambient? Well, usually in Compose, data flows down through the UI hierarchy as parameters to each composable function This can, however, be cumbersome for really widely used things like colors or type styles Instead, Compose offers ambient, which allow you to create named objects to look up values from like a service locator This is what MaterialTheme uses under the hood to store the colors, types, and shapes in ambient, allowing you to retrieve them later Ambient is a scope to a hierarchy, so you can provide different values at different levels of the tree like we saw before with the colored subsection So to implement our own color theme, we can create an ambient This associates our custom type, JetsnackColorPalette, with the ambient name, JetsnackColors, and it provides the default value Then in our theme, we use the provider’s Composable to set a value for this ambient In Jetsnack, we still use material, shape, and type theming, so we wrap a MaterialTheme, and omit the color parameter Here, I’m using an object with a colors property to mirror how MaterialTheme exposes values, but the key part is using the ambient’s current property Consuming code can now access our custom colors like this Now that we have the color system set up, we can customize components to use them If we look at Composer’s Button component, it accepts a single color parameter and is put together by combining three other composables– a surface, a textile provider, and a row The surface implements the color and shape, the text provide a defaults children to use in the Button textile, and the Row lays out its children Now, I’ve omitted some parameters here for clarity, but the entire component is less than 30 lines of code long This design is what enables you to easily customize components to create our own We can take the platform’s implementation and modify it Instead of a single color, we’ll accept a list for a gradient and default it to a value from our color system We’ll keep using Material surface, but skip its solid color by setting Transparent, and instead, we’ll draw a gradient And that’s it We’ve created our own Button component that we can use now instead of Materials Rather than a single monolithic, library Compose has built-in layers Each has its defined responsibility and builds upon those below it So you can add your own design system on top of material or replace it altogether The same principles apply within each layer As we saw, Material’s button is built from other components from the Material and Foundation layers Each component does a single job, and you assemble them to build up functionality So to create your own button, you can either build on top of Material and customize its behavior or replace Material entirely using lower-level building blocks

The next area where we found that Compose made things easier was layouts The samples we built varied in complexity, from a simple login page to richer forms or custom layouts like the staggered grid Compose offers a variety of layouts to build these Columns lay things out vertically, rows horizontally, or stacks enable overlapping elements Compose even supports my beloved ConstraintLayout for more complex scenarios, offering a really powerful DSL While we found these layouts worked great for building the sample app, it was other aspects of Compose’s layout system that really shone and made things easier Firstly, Compose offers a modifier system enabling you to configure and customize your components or even add behaviors to them Let me show you what I mean Let’s build a simple list item from the Crane sample This image and two lines of text can be built using a row to arrange things horizontally, first the image, then a column holding the two texts If we deploy this, we’d see something like this The image is too large, and the texts are shoved over to the side This is where modifiers come in to configure components All UI composables accept a modifier parameter allowing you to customize them Modifiers are defined as extensions on their capital M Modifier object, allowing you to discover and also complete them Here, we’re using the preferred size modifier to configure the image composable So far this is pretty similar to a view LayoutParams, but it gets way more powerful We can add a padding modifier to the column to move the text and image apart Now, modifiers support a fluent syntax, so we can chain them together to compose their functionality For example, we can add a gravity modifier to vertically center the column within the row Compose utilizes scopes to provide type safety Rows, they lay children out horizontally and only use gravity for the vertical positioning Row therefore, defines a gravity modifier in its scope, which only accepts vertical gravities Trying to use an invalid horizontal gravity won’t compile No more desperately trying different LayoutParams to see what works Modifiers aren’t only restricted to layout They can also affect drawing, interaction, and more Here, we are applying a clip modifier around the corners of the image By convention, a modifier is always the first optional parameter of a composable, so you can pass one without having to name the argument Here we’re adding some paddings to the entire row to move the content inward We can add the background color to the row by appending a background modifier, but this produces this perhaps surprising result. The reason for this is that the order of modifiers is significant So if you want the background color to include the padding, we can swap their order Now, this took me a little while to get used to, but now I think it’s a really powerful design This puts you in control of exactly how different behaviors interact It’s not down to some internal implementation detail of the component It’s under your control, and it’s deterministic So if we want some outer space around the whole row, we can add another padding modifier Yes, you can repeat them Rather than having to learn rules like the box model, which has the margins are on the outside of an element and paddings within them, you are in control If you want to make this element interactive, we can add a clickable modifier If we add this before the outer padding, then the entire element is tappable, and the resulting ripple will include this space Or we can do that afterword to confine it to the smaller area You should read modifiers like an ordered chain, each of which provides inputs for the next element in a chain and have no knowledge of the elements preceding them This example reads as add some space around my contents, then make it clickable, then draw a background color, then add some more space These were fairly straightforward modifiers But hopefully, you can see how we can easily combine them together to build up our desired behavior There are many modifiers available for adding all kinds of behavior, such as making element tolerable or even adding high-level behaviors like making something scrollable or even zoomable We use modifiers extensively for our sample apps to achieve their designs For example, Jetsnack’s search category has combined multiple modifiers to layout and draw this item You can even create your own modifiers such as this custom horizontal gradient modifier This element also shows another powerful aspect of Composer’s layout system, how easy it is to create custom layouts The design of this item calls for the text and image to be divided up as a percentage of the available space The image then peek in from the edge of the card with a minimum diameter Now, you might be able to have built this with the standard layouts, but Compose made it straightforward to implement this exact design The Layout composable is the building block for custom layouts It’s what rows and columns and cells use, but you can and should use it directly to achieve your own custom layout It accepts children to be laid out and any modifiers to apply for them You implement a measure block to create your custom behavior In this, you have three things to do, measure each item, call the layout method, and place each item Let’s walk through these Find your custom layout composable and pass the children and any modifiers onto the layout

Then implement a measure block as a training lander We’re given inputs of a list of measurable items and some incoming constraints The constraints object is passed down from your parent telling you the minimum or maximum size you can be Each measurable object has one main method, measure, where you measure each item with a set of constraints So step one, measuring In a simple implementation, we’ll measure each item with the constraints that were passed into us, but you can and should alter these constraints to implement the layout you want Measuring an item returns a placeable This contains the width and height the item would like to be and offers a place method Now that we’ve measured, we need to call the layout method This is where we report how large our Composable should be You can calculate this based on the incoming constraints and the layout you’re trying to achieve For example, a column might sum the heights of each element it contains The layout method accepts a placement block where we need to place each item on the screen Here, we call place for each placeable that we’ve measured, specifying it’s x and y-coordinates Let’s use this technique to build the SearchCategory item It’s a custom composable wrapping a call to the layout composable Here, we specify the modified chain we saw before to size the item, render the background in shadow, and clip it to a rounded rectangle We directly specify that this item should contain a text and an image Our design calls for the text to occupy a percentage of the overall width In our measure block, we calculate this as a fraction of the incoming constraint’s maximum width Then we call measure on the text item, passing constraints with this exact width that we want it to be A design calls for the image to have a minimum size of 140 depth, so we measure exactly the size We can then call the layout method using the incoming constraints maximum width and height to fill the available space In the placement block, we place the text on the left and center it vertically Then we place the image to the right of the text by specifying the text width as the x-coordinate and also center it vertically The image may overflow the bounds of this item, but the clip modifier we specified will mask it Like this, we’ve achieved the exact design we wanted Compose made it as easy as implementing a function Compose’s layout system also enables new possibilities It disallows you from measuring an item twice If you try and do so, it will throw an exception This gives you performance guarantees as you can’t get into excessively costly measure cycles It means that you can measure and layout in places that might have been prohibitive reviews For example, this detail screen scales and translates the main image as you scroll the content This is entirely achieved for a custom layout We calculate a collapse fraction based upon how far you’ve scrolled, then we use the lerp function to calculate the size for this fraction We measure the image at this size and calculate the appropriate coordinates to move it from the center to the top right over the course of the gesture and place it there You can even directly animate an element size In this example, Jetsnack’s main navigation called for the selected item to be wider than the others This was achieved using a custom layout that holds a fraction between 0 and 1 for how selected each item is When an item is selected or deselected, we animate this value and use each item selection function to calculate its width, measuring it with this exact width Achieving this kind of effect with a view system was prohibitively expensive and likely to be unperformanced Composer’s design makes it not only easy to implement the exact layout that you want, but even to animate changes to it Speaking of animation, this is another area that we found Compose greatly simplified OWL, for example, presents a number of topics which animate between selected states To round the corner of a selected item, we can conditionally set a different corner radius of its shape To animate this change, we simply wrap this if with the animate composable function Whenever the selectors state changes, it will animate between these two dp values and any composables that read the radius will be updated That’s it This works great if we only want to animate a single property, but our desired animation is slightly more complex We went round the corner, but also fade in a pink overlay and a checkmark To do this, we can switch to a different API, Transitions This allows you to animate sets of properties together First, we define key for each property that we want to animate, and the type of value will be animating Here, we’ll use dp properties for the corner radius and float properties for the others Next, we define the different states we support here using an enum We only have two states selected or not, but you can define as many as you need We then create a transition definition specifying the values our properties should have for a given state So here, we’re specifying the values when the item is selected Now, in my topical item, I can switch to the transition composable function, passing in the definition and the state it should be in Whenever the selection state changes, this will recompose, kicking off an animation It returns an object holding the animating values

from which we can retrieve each property using getter syntax Compose animation uses a spring-based system by default, which provides smooth interruptions out of the box You can also specify tweens using durations and easings To help you build great animations, I want to give you a sneak peak of some tooling that we’re working on The Animation Inspector lets you preview animations, helping you to get them just right You can select different states to check the animations between them You can slow down, playback, or scrub through the timeline to dial in those values This is coming soon to an Android Studio Canary release near you So not only does Compose make our animation code much easier to write and reason about, it also enables new possibilities like testing The Rally sample uses a prominent animated chart to display your finances This is a key moment in the app, so we want to add Test for it In Test, you have access to a test animation clock giving you the ability to control time You can pause and manually advance the clock, which drives all animations To test our animation, we can start our test with a paused clock We add our animated elements to the UI, and then manually advance the clock to some point during the animation Now, we can assert about the state of UI at this point, or here, we’re actually capturing a screenshot of the element to compare against the golden image In the sample, we’re running a simple comparison on device, but you could easily put this into your Test infrastructure to run on CI Hopefully, I’ve shown how Compose makes Android UI easier, and it enables new possibilities for building amazing apps In addition to the samples you saw today, we published five code labs and more documentation to help you learn all about Compose Compose is now in alpha launching 1.0 next year Now is the best time to try it and give us feedback The major concepts are established, but API is still evolving Your feedback and feature requests can help shape it Check out the samples, code labs, and docs, and give us feedback on the issue tracker or talk to us on Slack Thanks for watching