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

6 weeks of Compose or 
How a good Design System is vital in Compose

Dinorah Tovar
September 01, 2022

6 weeks of Compose or 
How a good Design System is vital in Compose

In this talk, we will cover the pitfalls and wins our Mobile Team encountered in rewriting our mobile application in Compose in just 6 weeks We will focus, on the pros cons, pitfalls, and problems we encounter, also the learnings we gather and the process that helps us to make this happen! that probably will help everyone that may want to move from XML to Compose in a smooth way We will tackle how a good design system can save you a lot of time if we focus on writing good atoms and good components!

Dinorah Tovar

September 01, 2022
Tweet

More Decks by Dinorah Tovar

Other Decks in Technology

Transcript

  1. 6 weeks of Compose or

    How a good Design
    System is vital in Compose
    Dinorah Tovar

    Google Developer Expert Android
    @ddinorahtovar
    @ddinorahtovar

    View full-size slide

  2. A little bit of
    But first
    ✨ context ✨
    @ddinorahtovar

    View full-size slide

  3. @ddinorahtovar
    The tweet in question:

    View full-size slide

  4. @ddinorahtovar
    The tweet in question:

    View full-size slide

  5. The learnings that can help you
    This talk is about
    To migrate your app to compose
    @ddinorahtovar
    To migrate your design system to compose

    View full-size slide

  6. @ddinorahtovar
    A design System
    First:
    Can save you many difficult hours

    View full-size slide

  7. For Compose
    an approach
    We nee to
    decide
    @ddinorahtovar
    /**


    * 1. Material Design


    * 2. Extend Material Design


    * 3. Make yourself comfortable and


    * Create your own things


    **/

    View full-size slide

  8. We are gonna
    focus on
    We are not gonna cover all
    ✨ extends Material Design ✨
    @ddinorahtovar

    View full-size slide

  9. @ddinorahtovar
    But, if you wanna see more about Custom designs
    @rharter, Android at Dropbox

    Available at #ChicagoRoboto

    View full-size slide

  10. @ddinorahtovar
    Design system are discipline
    Rules
    Pattern
    Guides
    Style
    Guides
    Design Principles
    Documentation
    Language
    Design Tokens
    Bu
    tt
    ons
    Bo
    tt
    om Navigation
    Dialog
    Carousels
    ViewPagers
    Text
    TextField
    Typography
    Colors
    Icons
    Shapes
    Elevations

    View full-size slide

  11. @ddinorahtovar
    Create a Design System
    Token Atom Molecule Template

    View full-size slide

  12. @ddinorahtovar
    Atoms and Design Tokens
    The language of

    View full-size slide

  13. Let’s review
    As an example of
    design tokens
    Colors
    @ddinorahtovar
    object DesignColors {


    val primary = Color(0XFF6E43B7)


    }

    View full-size slide

  14. interface DesignColorPalette {


    val primary_default_background: Color,


    val materialColors: Colors


    }


    fun lightColorPalette() = object : DesignColorPalette {


    override val primary_default_background =


    DesignColors.primary


    override val materialColors = lightColors(


    primary = DesignColors.primary,


    background = DesignColors.neutral_white,


    surface = DesignColors.neutral_white


    )


    }
    /**


    * Color Palette


    **/

    View full-size slide

  15. interface DesignColorPalette {


    val primary_default_background: Color,


    val materialColors: Colors


    }


    fun lightColorPalette() = object : DesignColorPalette {


    override val primary_default_background =


    DesignColors.primary


    override val materialColors = lightColors(


    primary = DesignColors.primary,


    background = DesignColors.neutral_white,


    surface = DesignColors.neutral_white


    )


    }
    /**


    * Color Palette


    **/

    View full-size slide

  16. interface DesignColorPalette {


    val primary_default_background: Color,


    val materialColors: Colors


    }


    fun lightColorPalette() = object : DesignColorPalette {


    override val primary_default_background =


    DesignColors.primary


    override val materialColors = lightColors(


    primary = DesignColors.primary,


    background = DesignColors.neutral_white,


    surface = DesignColors.neutral_white


    )


    }
    /**


    * Color Palette


    **/

    View full-size slide

  17. interface DesignColorPalette {


    val primary_default_background: Color,


    val materialColors: Colors


    }


    fun lightColorPalette() = object : DesignColorPalette {


    override val primary_default_background =


    DesignColors.primary


    override val materialColors = lightColors(


    primary = DesignColors.primary,


    background = DesignColors.neutral_white,


    surface = DesignColors.neutral_white


    )


    }
    /**


    * Design Token


    **/

    View full-size slide

  18. Tokens are
    decisions about specifics
    and reachability
    Design
    @ddinorahtovar
    val primary_default_background: Color

    View full-size slide

  19. Konfío Documentation - created by Abril Garibay @abrlpumpr

    Generic
    Specifics
    Reachability

    View full-size slide

  20. @Composable


    fun DesignTheme(


    colors: DesignColorPalette = lightColorPalette(),


    children: @Composable() () -> Unit


    ) {


    CompositionLocalProvider(


    LocalDlsColors provides colors,


    ) {


    MaterialTheme(


    colors = colors.materialColors


    ) {


    children()


    }


    }


    }


    object DesignTheme {


    val colors: DesignColorPalette


    @Composable


    @ReadOnlyComposable


    get() = LocalDlsColors.current


    }


    internal val LocalDlsColors =


    staticCompositionLocalOf { lightColorPalette() }


    /**


    * Integrating the

    * Palette to


    * the DesignTheme


    **/

    View full-size slide

  21. @Composable


    fun DesignTheme(


    colors: DesignColorPalette = lightColorPalette(),


    children: @Composable() () -> Unit


    ) {


    CompositionLocalProvider(


    LocalDlsColors provides colors,


    ) {


    MaterialTheme(


    colors = colors.materialColors


    ) {


    children()


    }


    }


    }


    object DesignTheme {


    val colors: DesignColorPalette


    @Composable


    @ReadOnlyComposable


    get() = LocalDlsColors.current


    }


    internal val LocalDlsColors =


    staticCompositionLocalOf { lightColorPalette() }


    /**


    * Integrating the

    * Palette to


    * the DesignTheme


    **/

    View full-size slide

  22. @Composable


    fun DesignTheme(


    colors: DesignColorPalette = lightColorPalette(),


    children: @Composable() () -> Unit


    ) {


    CompositionLocalProvider(


    LocalDlsColors provides colors,


    ) {


    MaterialTheme(


    colors = colors.materialColors


    ) {


    children()


    }


    }


    }


    object DesignTheme {


    val colors: DesignColorPalette


    @Composable


    @ReadOnlyComposable


    get() = LocalDlsColors.current


    }


    internal val LocalDlsColors =


    staticCompositionLocalOf { lightColorPalette() }


    /**


    * Integrating the

    * Palette to


    * the DesignTheme


    **/

    View full-size slide

  23. @Composable


    fun DesignTheme(


    colors: DesignColorPalette = lightColorPalette(),


    children: @Composable() () -> Unit


    ) {


    CompositionLocalProvider(


    LocalDlsColors provides colors,


    ) {


    MaterialTheme(


    colors = colors.materialColors


    ) {


    children()


    }


    }


    }


    object DesignTheme {


    val colors: DesignColorPalette


    @Composable


    @ReadOnlyComposable


    get() = LocalDlsColors.current


    }


    internal val LocalDlsColors =


    staticCompositionLocalOf { lightColorPalette() }


    /**


    * Integrating the

    * Palette to


    * the DesignTheme


    **/

    View full-size slide

  24. With this - I can convert
    in a Atom
    A token to
    an element
    @ddinorahtovar
    setContent {


    DesignTheme {


    ConstraintLayout(


    modifier = Modifier


    .fillMaxSize()


    .background(


    DesignTheme.colors.primary_default_background


    )


    ) {


    // Content


    }


    }


    }

    View full-size slide

  25. Let’s review
    With design tokens
    Molecules
    @ddinorahtovar
    @Composable


    fun Input(


    modifier: Modifier = Modifier,


    inputField: InputField? = null,


    label: String = EMPTY_VALUE,


    text: String = EMPTY_VALUE,


    onValueChange: (String) -> Unit,


    transformation: Transformation =


    Transformation.RegularTransformation


    ....


    ) {


    ....


    Column(modifier) {


    OutlinedTextField(...)


    }


    }

    View full-size slide

  26. But they become
    An prone to errors
    Complex
    @ddinorahtovar
    OutlinedTextField(


    enabled = isEditable,


    value = text,


    onValueChange = { // Handle State },


    label = { // Another element },


    isError = onErrorState,


    maxLines = 1,


    singleLine = true,


    textStyle = DesignTheme.typography.an_re_co_body1,


    colors = TextFieldDefaults.outlinedTextFieldColors(


    focusedBorderColor =
    DesignTheme.colors.primary_default_background_default,


    focusedLabelColor =
    DesignTheme.colors.primary_default_background_default,


    unfocusedBorderColor =
    DesignTheme.colors.neutral_light_strong_border_disabled,


    unfocusedLabelColor =
    DesignTheme.colors.neutral_light_strong_border_disabled,


    errorBorderColor =
    DesignTheme.colors.state_danger_background_default,


    errorLabelColor =
    DesignTheme.colors.state_danger_background_default,


    disabledLabelColor =
    DesignTheme.colors.neutral_light_strong_border_disabled,


    disabledBorderColor =
    DesignTheme.colors.neutral_light_strong_border_disabled


    )


    }

    View full-size slide

  27. We should strive for
    uses, that are on the DS
    Particular
    @ddinorahtovar
    @Composable


    fun FormForEmails() {


    Input(


    label = "My very specific case"


    )


    }

    View full-size slide

  28. Let’s review
    Templates
    @ddinorahtovar
    @Composable


    fun Carousel(


    pagerState: PagerState,


    tabItems: List,


    primaryOnClick: () -> Unit,


    secondaryOnClick: () -> Unit


    ) {


    ConstraintLayout(


    modifier = Modifier


    .fillMaxSize()


    .background(someColorOfDesignSystem)


    ) {


    HorizontalPager(


    // something here


    ) { page ->


    // some other thing


    }


    HorizontalPagerIndicator(


    // some more core


    )


    }


    }

    View full-size slide

  29. @ddinorahtovar
    Abstractions
    Second:
    Can help you understand cases and uses

    View full-size slide

  30. Compose is
    Kotlin
    100%
    @ddinorahtovar
    @Composable


    fun Hello() {


    Text("Hello World")


    }

    View full-size slide

  31. @ddinorahtovar
    @Composable


    fun SomeText() {


    var text by


    remember { mutableStateOf("") }


    TextField(


    value = text,


    onValueChange = { text = it }


    )


    }
    Field
    Input

    View full-size slide

  32. If compose is Kotlin
    Abstractions
    Then we
    can create
    @ddinorahtovar
    enum class GenericInputField(


    @StringRes override val hint: Int,


    override val inputFieldValidator: FieldValidator,


    override val transformation: Transformation


    = Transformation.RegularTransformation,


    ) : InputField {


    EMAIL(


    hint = R.string.email,


    inputFieldValidator =


    InputFieldValidator.Builder(FieldType.OPEN).apply {


    maxLength = EMAIL_MAX_SIZE


    validationRegex = REGEX_MAIL


    }.create(),


    transformation = Transformation.EmailTransformation,


    isMandatory = true


    )


    }

    View full-size slide

  33. We can create
    Of complex process
    Abstractions
    @ddinorahtovar
    @Composable


    fun ShowForm (


    primaryFields: mutableStateListOf(generic)


    ) {


    HandleFieldsInsertion(


    primaryFields,


    // Set of GenericInputField


    someFunctionThatHandleState()


    // Function that handle state


    )


    }

    View full-size slide

  34. @ddinorahtovar
    Interoperability
    Third:
    Not everything has to be compose

    View full-size slide

  35. Compose has support for
    Views
    @ddinorahtovar
    binding.someView.apply {


    setViewCompositionStrategy(


    ViewCompositionStrategy


    .DisposeOnViewTreeLifecycleDestroyed)


    setContent {


    Text("Some Text")


    }


    }





    android:id="@+id/someView"


    android:layout_width="match_parent"


    android:layout_height="wrap_content"


    app:layout_constraintEnd_toEndOf="parent"


    app:layout_constraintStart_toStartOf="parent"


    app:layout_constraintTop_toTopOf="parent" />





    View full-size slide

  36. Compose has support for
    Fragments
    @ddinorahtovar
    class SomeFragment : Fragment() {


    override fun onCreateView(


    inflater: LayoutInflater,


    container: ViewGroup?,


    savedInstanceState: Bundle?


    ) = ComposeView(requireContext()).apply {


    setViewCompositionStrategy(


    ViewCompositionStrategy


    .DisposeOnViewTreeLifecycleDestroyed


    )


    setContent {


    DesignTheme {


    // Composable View


    }


    }


    }


    }

    View full-size slide

  37. @ddinorahtovar
    Side Effects
    Four:
    Over-abusing of recomposition

    View full-size slide

  38. @Composable fun view(data: Int)
    data: Int Composition
    emit
    Compositions shoots! 🥂
    @ddinorahtovar

    View full-size slide

  39. Compositions shoots! 🥂
    First Interaction Composition
    Insert “A”
    Insert “B”
    Second Interaction
    Re-composition
    @ddinorahtovar

    View full-size slide

  40. @ddinorahtovar
    @Composable


    fun SomeText() {


    var text by


    remember { mutableStateOf("") }


    TextField(


    value = text,


    onValueChange = { text = it }


    )


    }
    Field
    Input

    View full-size slide

  41. @ddinorahtovar
    @Composable


    fun SomeFunction (


    someId: String,


    viewModel: SomeViewModel


    ) {


    var someState by


    remember { mutableStateOf(1) }


    val someResult by


    viewModel.someResult.observeAsState()


    LaunchedEffect(key1 = someId){


    viewModel.getSomethingBasedOnId(someId)


    }


    // Some View that affects the variable someState


    }
    Field
    Input

    View full-size slide

  42. @ddinorahtovar
    But, if you wanna see more about Recomposition

    View full-size slide

  43. 6 weeks of Compose or

    How a good Design
    System is vital in Compose
    Dinorah Tovar

    Google Developer Expert Android
    @ddinorahtovar
    @ddinorahtovar

    View full-size slide