$30 off During Our Annual Pro Sale. View Details »

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
  2. A little bit of But first ✨ context ✨ @ddinorahtovar

  3. @ddinorahtovar The tweet in question:

  4. @ddinorahtovar The tweet in question:

  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
  6. @ddinorahtovar A design System First: Can save you many difficult

    hours
  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 **/
  8. We are gonna focus on We are not gonna cover

    all ✨ extends Material Design ✨ @ddinorahtovar
  9. @ddinorahtovar But, if you wanna see more about Custom designs

    @rharter, Android at Dropbox 
 Available at #ChicagoRoboto
  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
  11. @ddinorahtovar Create a Design System Token Atom Molecule Template

  12. @ddinorahtovar Atoms and Design Tokens The language of

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

    object DesignColors { val primary = Color(0XFF6E43B7) }
  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 **/
  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 **/
  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 **/
  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 **/
  18. Tokens are decisions about specifics and reachability Design @ddinorahtovar val

    primary_default_background: Color
  19. Konfío Documentation - created by Abril Garibay @abrlpumpr Generic Specifics

    Reachability
  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 **/
  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 **/
  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 **/
  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 **/
  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 } } }
  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(...) } }
  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 ) }
  27. We should strive for uses, that are on the DS

    Particular @ddinorahtovar @Composable fun FormForEmails() { Input( label = "My very specific case" ) }
  28. Let’s review Templates @ddinorahtovar @Composable fun Carousel( pagerState: PagerState, tabItems:

    List<CarouselComponentItem>, primaryOnClick: () -> Unit, secondaryOnClick: () -> Unit ) { ConstraintLayout( modifier = Modifier .fillMaxSize() .background(someColorOfDesignSystem) ) { HorizontalPager( // something here ) { page -> // some other thing } HorizontalPagerIndicator( // some more core ) } }
  29. @ddinorahtovar Abstractions Second: Can help you understand cases and uses

  30. Compose is Kotlin 100% @ddinorahtovar @Composable fun Hello() { Text("Hello

    World") }
  31. @ddinorahtovar @Composable fun SomeText() { var text by remember {

    mutableStateOf("") } TextField( value = text, onValueChange = { text = it } ) } Field Input
  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 ) }
  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 ) }
  34. @ddinorahtovar Interoperability Third: Not everything has to be compose

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

    .DisposeOnViewTreeLifecycleDestroyed) setContent { Text("Some Text") } } <androidx.constraintlayout.widget.ConstraintLayout> <androidx.compose.ui.platform.ComposeView 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" /> </androidx.constraintlayout.widget.ConstraintLayout>
  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 } } } }
  37. @ddinorahtovar Side Effects Four: Over-abusing of recomposition

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

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

    Second Interaction Re-composition @ddinorahtovar
  40. @ddinorahtovar @Composable fun SomeText() { var text by remember {

    mutableStateOf("") } TextField( value = text, onValueChange = { text = it } ) } Field Input
  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
  42. @ddinorahtovar But, if you wanna see more about Recomposition

  43. 6 weeks of Compose or 
 How a good Design

    System is vital in Compose Dinorah Tovar Google Developer Expert Android @ddinorahtovar @ddinorahtovar