Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Jetpack Compose: UI toolkit for Android and Desktop apps

Jetpack Compose: UI toolkit for Android and Desktop apps

Jetpack Compose, the new UI toolkit by Google, has been in dev and alpha stage for more than a year and it is now in beta. Throught several live examples, let’s see how you can bring high quality UI to your Android application and how functional programming helped achieving such as thing. We will go even further and I’ll introduce Jetpack Compose for Desktop application where components from your Android app can be reused to keep the same quality of design design accross the platforms.

Julien Salvi

October 22, 2021
Tweet

More Decks by Julien Salvi

Other Decks in Programming

Transcript

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

    View Slide

  2. Jetpack Compose
    introduction
    A declarative UI toolkit

    View Slide

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

    View Slide

  4. 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.

    View Slide

  5. 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

    View Slide

  6. Jetpack Compose
    From XML/View to Compose
    XML layout Activity / Fragment / View




    setContentView(...)
    inflate(...)
    findViewById(...)

    View Slide

  7. Jetpack Compose
    From XML/View to Compose
    XML layout Activity / Fragment / View




    setContentView(...)
    inflate(...)
    findViewById(...)
    title.text = ...
    desc.isVisible = ...
    group += View()
    a = MyAdapter()

    View Slide

  8. Jetpack Compose
    From XML/View to Compose
    XML layout Activity / Fragment / View




    setContentView(...)
    inflate(...)
    findViewById(...)
    title.text = ...
    desc.isVisible = ...
    group += View()
    a = MyAdapter()
    STATE

    View Slide

  9. Jetpack Compose
    From XML/View to Compose
    XML layout Activity / Fragment / View




    setContentView(...)
    inflate(...)
    findViewById(...)
    title.text = ...
    desc.isVisible = ...
    group += View()
    a = MyAdapter()
    STATE

    View Slide

  10. 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

    View Slide

  11. 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

    💥

    💥

    💥

    💥

    💥

    View Slide

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

    View Slide

  13. 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

    View Slide

  14. 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)
    }
    }
    }
    }

    View Slide

  15. 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()
    }
    }
    }

    View Slide

  16. 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)
    }
    }
    }
    }

    View Slide

  17. 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
    )
    }
    }

    View Slide

  18. 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
    )
    }

    View Slide

  19. The UI Toolkit
    Think in Compose!

    View Slide

  20. 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
    )
    }
    }

    View Slide

  21. 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))
    }
    }

    View Slide

  22. 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)
    )

    View Slide

  23. 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
    )

    View Slide

  24. 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)
    }
    }

    View Slide

  25. 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!

    View Slide

  26. 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!")
    }
    }
    }

    View Slide

  27. 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()
    }
    )
    }

    View Slide

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

    View Slide

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

    View Slide

  30. 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

    View Slide

  31. 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

    View Slide

  32. Compose Desktop
    (Almost the)
    Same but on desktop!

    View Slide

  33. 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

    View Slide

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

    View Slide

  35. 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

    View Slide

  36. 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.

    View Slide

  37. 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

    View Slide

  38. 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

    View Slide

  39. Resources
    The recipes
    Coming soon or ask me later 😅

    View Slide

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

    View Slide