Slide 1

Slide 1 text

Jetpack Compose: UI toolkit for Android and Desktop apps Julien Salvi - Android GDE | Lead Android Engineer @ Aircall DevFest Nantes 2021 @JulienSalvi

Slide 2

Slide 2 text

Jetpack Compose introduction A declarative UI toolkit

Slide 3

Slide 3 text

- Me about Jetpack Compose Wow c’est trop stylé!

Slide 4

Slide 4 text

What’s Jetpack Compose? Jetpack Compose is a declarative UI framework made by Google. Stable since Aug. 2021. It is composed of a compiler, a runtime and a bunch of core UI libraries. Made with Kotlin. Nice interoperability with View UI framework.

Slide 5

Slide 5 text

Jetpack Compose Jetpack Compose 1.0.0 🥳 Jetpack Compose 1.0.0-alpha-01 2020 Compose 0.1.0-dev-01 Compose Web preview 2019 2021 2022 Jetpack Compose 1.0.0-beta-01 Compose Desktop dev-01 Compose Desktop and Web alpha

Slide 6

Slide 6 text

Jetpack Compose From XML/View to Compose XML layout Activity / Fragment / View setContentView(...) inflate(...) findViewById(...)

Slide 7

Slide 7 text

Jetpack Compose From XML/View to Compose XML layout Activity / Fragment / View setContentView(...) inflate(...) findViewById(...) title.text = ... desc.isVisible = ... group += View() a = MyAdapter()

Slide 8

Slide 8 text

Jetpack Compose From XML/View to Compose XML layout Activity / Fragment / View setContentView(...) inflate(...) findViewById(...) title.text = ... desc.isVisible = ... group += View() a = MyAdapter() STATE

Slide 9

Slide 9 text

Jetpack Compose From XML/View to Compose XML layout Activity / Fragment / View setContentView(...) inflate(...) findViewById(...) title.text = ... desc.isVisible = ... group += View() a = MyAdapter() STATE

Slide 10

Slide 10 text

Jetpack Compose From XML/View to Compose XML layout Activity / Fragment / View setContentView(...) inflate(...) findViewById(...) title.text = ... desc.isVisible = ... group += View() a = MyAdapter() STATE STATE STATE STATE STATE

Slide 11

Slide 11 text

Jetpack Compose From XML/View to Compose XML layout Activity / Fragment / View setContentView(...) inflate(...) findViewById(...) title.text = ... desc.isVisible = ... group += View() a = MyAdapter() STATE STATE STATE STATE STATE ⚠ 💥 ⚠ 💥 ⚠ 💥 ⚠ 💥 ⚠ 💥

Slide 12

Slide 12 text

Jetpack Compose From XML/View to Compose UI State UI 2 State 2 ?

Slide 13

Slide 13 text

Jetpack Compose Let’s explore the foundation of Compose: ● How Compose transforms a state to a UI component by demystifying the declarative paradigm ● How to make UI components: Composable ● What Compose has to offer as a UI Toolkit to build top notch apps on Android

Slide 14

Slide 14 text

Declarative UI ● Every Composable is a function annotated with @Composable ● You can split your UI into a set of tiny reusable components ● Produce and render components @Composable fun IngredientList(ingredients: List) { Column(modifier = modifier .fillMaxWidth() .wrapContentHeight() ) { Text(text = "Ingrédients") LazyRow { items(ingredients) { ing -> IngredientItem(ingredient = ing) } } } }

Slide 15

Slide 15 text

Declarative UI ● Basic conditions to show or hide UI components @Composable fun TwoPanel() { val threshold = density.run { 400.dp.toPx() } BoxWithConstraints { if (maxWidth.value > threshold) { TwoColumnsLayout() } else { HomeScreen() } } }

Slide 16

Slide 16 text

Declarative UI ● Basic conditions to show or hide UI components ● You can pass parameters to your Composable ● A change in these parameters will trigger the re-composition @Composable fun IngredientList(ingredients: List) { Column(modifier = modifier .fillMaxWidth() .wrapContentHeight() ) { Text(text = "Ingrédients") LazyRow { items(ingredients) { ing -> IngredientItem(ingredient = ing) } } } }

Slide 17

Slide 17 text

Declarative UI ● ViewModel can handle LiveData or StateFlow that can be bound and collected a Composable ● Every time a new state will be pushed it will re-compose the UI @Composable fun RecipeDetailsScreen(recipeId: Int) { val vm = hiltViewModel() val recipe = vm.recipe.collectAsState().value if (recipe.ingredients.isNotEmpty()) { IngredientList( ingredients = recipe.ingredients ) } }

Slide 18

Slide 18 text

Declarative UI ● Composable are immutable... ● … but they can be dynamic! ● You can “remember” internal state in order to trigger local changes @Composable fun StepItem(step: Step) { var expand by remember{ mutableStateOf(false) } Text( text = step.desc, modifier = Modifier.clickable { expand = !expand }, style = MaterialTheme.typography.body2, color = AppColorsTheme.colors.text, maxLines = if (expanded) MAX_VALUE else 3, overflow = if (expanded) Clip else Ellipsis ) }

Slide 19

Slide 19 text

The UI Toolkit Think in Compose!

Slide 20

Slide 20 text

UI Toolkit ● Override MaterialTheme to define your default Typography, Shapes and Colors ● It has a great support for dark/light mode ● Replaces old XML styles for Views @Composable fun RecipeekTheme( darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit ) { val colors = if (darkTheme) { DarkColorPalette } else { LightColorPalette } ProvideAppColors(colors) { MaterialTheme( colors = debugColors(darkTheme), typography = Typography, shapes = RecipeekShapes, content = content ) } }

Slide 21

Slide 21 text

UI Toolkit ● Compose offers a large set of atomic components (Button, Text, TextField, Image…) ● As well as container (Box, Row, Column…) to organize your UI ● Multiple Modifier to construct your components Button( onClick = { onNavigateTo(recipe.id) }, shape = MaterialTheme.shapes.medium, contentPadding = PaddingValues(0.dp), modifier = Modifier .fillMaxWidth() .wrapContentHeight() .background( color = AppColorsTheme.colors.main shape = MaterialTheme.shapes.medium ) .padding(8.dp) ) Column(modifier = modifier) { Text( text = "Préparation", style = MaterialTheme.typography.h5, color = AppColorsTheme.colors.text, ) Spacer(modifier = Modifier.size(8.dp)) steps.forEach { step -> StepItem(step = step) Spacer(modifier = Modifier.size(8.dp)) } }

Slide 22

Slide 22 text

UI Toolkit ● Modifier allow you to shape your UI but also interact with your Compose (click, touch events…) ● When manipulating Modifer, keep in mind that the order matters! ● Use them wisely 🙂 Text( text = "Some text", modifier = Modifier .fillMaxWidth() .padding(32.dp) .border( width = 4.dp, color = red700, shape = CutCornerShape(32.dp) ) .graphicsLayer { shadowElevation = 8.dp.toPx() shape = CutCornerShape(32.dp) clip = true } .background(color = greenLight700) .padding(32.dp) )

Slide 23

Slide 23 text

UI Toolkit ● Use the @Preview annotation to have a sneak peek of your UI @MustBeDocumented @Retention(AnnotationRetention.SOURCE) @Target( AnnotationTarget.FUNCTION ) @Repeatable annotation class Preview( val name: String = "", val group: String = "", @IntRange(from = 1) val apiLevel: Int = -1, val widthDp: Int = -1, val heightDp: Int = -1, val locale: String = "", @FloatRange(from = 0.01) val fontScale: Float = 1f, val showSystemUi: Boolean = false, val showBackground: Boolean = false, val backgroundColor: Long = 0, @UiMode val uiMode: Int = 0, @Device val device: String = Devices.DEFAULT )

Slide 24

Slide 24 text

UI Toolkit ● Use the @Preview annotation to have a sneak peek of your UI ● Customize your Previews by showing the system UI or by enabling the dark mode @Preview( showSystemUi = true, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES ) @Composable fun StepPreview() { RecipeekTheme { val step = Step( position = 3, desc = "Lorem ipsum dolor sit amet" ) StepItem(step = step) } }

Slide 25

Slide 25 text

UI Toolkit ● Use the @Preview annotation to have a sneak peek of your UI ● Customize your Previews by showing the system UI or by enabling the dark mode ● Mock your data to see it in action!

Slide 26

Slide 26 text

UI Toolkit ● Support for interoperability with the View framework ● Call Composable in View code val myComposeView = ComposeView(requireContext()).apply { // Dispose the Composition // when the view's LifecycleOwner is destroyed setViewCompositionStrategy( DisposeOnViewTreeLifecycleDestroyed ) setContent { // Compose stuff MaterialTheme { Text("Hello DevFest Nantes!") } } }

Slide 27

Slide 27 text

UI Toolkit ● Support for interoperability with the View framework ● Call Composable in View code ● Call View code in Composable @Composable fun CustomView() { // Adds view to Compose AndroidView( modifier = Modifier.fillMaxSize(), factory = { context -> // Creates custom view CustomView(context).apply { // Setup your View here } }, update = { view -> // Populate your view with new state // -> re-compose view.setStuff = newStuff() } ) }

Slide 28

Slide 28 text

What’s next? source: https://developer.android.com/jetpack/compose

Slide 29

Slide 29 text

Jetpack Compose Live coding! Let’s code a small recipe app 🍱 ⚠ Architecture in MVC (Model-View-Crado) ⚠

Slide 30

Slide 30 text

Compose Android Let’s build a small list/detail app: RECIPEEK On the main screen we’ll have a Toolbar, a search bar and a list of recipes with some information What we’re going to learn: - Modifier - LazyColumn - State - Theming https://github.com/oleur/recipeek

Slide 31

Slide 31 text

Compose Android On the recipe details screen we’ll have a look at more Modifiers and how to place Composables and add some nice Animations. What we’re going to learn: - More Modifiers - Shapes - ConstraintLayout Compose - State - Animations

Slide 32

Slide 32 text

Compose Desktop (Almost the) Same but on desktop!

Slide 33

Slide 33 text

Compose Desktop Developed and maintained by JetBrains based on Jetpack Compose for Android Uses Skia for a power rendering Desktop extensions for mouse/keyboard events, menus, window manipulation... AWT and Swing interoperability

Slide 34

Slide 34 text

Compose for Desktop Live coding! Let’s bring our recipe app to desktop 🍱 ⚠ Architecture in MVC (Model-View-Crado) ⚠

Slide 35

Slide 35 text

Compose Desktop With the desktop version of the app we’ll see how to adapt Compose Android code to make it work on desktop. What we’re going to learn: - Desktop Modifiers - Theme for Desktop - Layouts - State - Animations https://github.com/oleur/Recipeek-desktop

Slide 36

Slide 36 text

Compose Multiplatform If you want to maintain only one code base to develop your app for Android, Desktop or iOS you might want to take a look at Kotlin Multiplatform. Some Compose tools are only supported on Android (ConstraintLayout, Lifecycle, Coil…) Developing for desktop might take a bit longer than Android as the library ecosystem is not as rich as Android yet.

Slide 37

Slide 37 text

Resources Compose for Android Official Documentation https://developer.android.com/jetpack/compose Compose Codelabs https://codelabs.developers.google.com/?cat=android Code Samples https://github.com/android/compose-samples Recipeek https://github.com/oleur/Recipeek

Slide 38

Slide 38 text

Resources Compose Desktop & Web Official Documentation https://www.jetbrains.com/lp/compose/ Sources, Samples & Tutorials https://github.com/jetbrains/compose-jb Recipeek Desktop https://github.com/oleur/Recipeek-desktop

Slide 39

Slide 39 text

Resources The recipes Coming soon or ask me later 😅

Slide 40

Slide 40 text

Merci! Julien Salvi - Android GDE | Lead Android Engineer @ Aircall DevFest Nantes 2021 @JulienSalvi Have fun with Jetpack Compose!