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

깔끔한 선언형 UI with Jetpack Compose

깔끔한 선언형 UI with Jetpack Compose

HYPER-APP 2024 발표 자료입니다.

junsuPark

July 21, 2024
Tweet

More Decks by junsuPark

Other Decks in Programming

Transcript

  1. ߅ળࣻ, ইಕ۽ ӭՔೠ ࢶ঱ഋ UI with Jetpack Compose ࢶ঱ഋ UIܳ

    ঠޖ૑ѱ ׮ܖח ߑߨী ؀ೠ ੋࢎ੉౟ܳ ٘݀פ׮.
  2. @Composable fun Button( onClick: () -> Unit, modi fi er:

    Modi fi er = Modi fi er, enabled: Boolean = true, shape: Shape = ButtonDefaults.shape, colors: ButtonColors = ButtonDefaults.buttonColors(), elevation: ButtonElevation? = ButtonDefaults.buttonElevation(), border: BorderStroke? = null, contentPadding: PaddingValues = ButtonDefaults.ContentPadding, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, content: @Composable RowScope.() -> Unit ) { /* ... */ }
  3. @Composable fun Button( onClick: () -> Unit, modi fi enabled:

    Boolean = true, shape: Shape = ButtonDefaults.shape, colors: ButtonColors = ButtonDefaults.buttonColors(), elevation: ButtonElevation? = ButtonDefaults.buttonElevation(), border: BorderStroke? = null, contentPadding: PaddingValues = ButtonDefaults.ContentPadding, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, content: @Composable RowScope.() -> Unit ) { /* ... */ } @Composable fun OutlinedButton( onClick: () -> Unit, /* ... */ contentPadding: PaddingValues = ButtonDefaults.OutlinedButtonContentPadding, content: @Composable RowScope.() -> Unit ) = Button( /* ... */ )
  4. @Composable fun Button( onClick: () -> Unit, modi fi enabled:

    Boolean = true, shape: Shape = ButtonDefaults.shape, colors: ButtonColors = ButtonDefaults.buttonColors(), elevation: ButtonElevation? = ButtonDefaults.buttonElevation(), border: BorderStroke? = null, contentPadding: PaddingValues = ButtonDefaults.ContentPadding, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, content: @Composable RowScope.() -> Unit ) { /* ... */ } @Composable fun TextButton( onClick: () -> Unit, /* ... */ contentPadding: PaddingValues = ButtonDefaults.TextButtonContentPadding, content: @Composable RowScope.() -> Unit ) = Button( /* ... */ )
  5. @Composable fun Button( onClick: () -> Unit, modi fi enabled:

    Boolean = true, shape: Shape = ButtonDefaults.shape, colors: ButtonColors = ButtonDefaults.buttonColors(), elevation: ButtonElevation? = ButtonDefaults.buttonElevation(), border: BorderStroke? = null, contentPadding: PaddingValues = ButtonDefaults.ContentPadding, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, content: @Composable RowScope.() -> Unit ) { /* ... */ } @Composable fun ElevatedButton( onClick: () -> Unit, /* ... */ contentPadding: PaddingValues = ButtonDefaults.ContentPadding, content: @Composable RowScope.() -> Unit ) = Button( /* ... */ )
  6. @Composable fun FancyButton(text: String, onClick: () -> Unit) { /*

    ... */ } @Composable fun fancyButton(text: String, onClick: () -> Unit) { /* ... */ }
  7. UIܳ о૑ח Composable ೣࣻ ֎੉߁ ӏ஗ - ౵झணா੉झ - ݺࢎ

    ࢎਊ - زࢎ, ੹஖ࢎ ߂ ࠗࢎ ࢎਊ ૑ন - ࢲࣿ੸ ਊߨ੄ ഋਊࢎ ೣԋ ࢎਊ оמ
  8. @Composable fun defaultStyle(): Style { /* ... */ } @Composable

    fun DefaultStyle(): Style { /* ... */ }
  9. чਸ ߈ജೞח Composable ೣࣻ ֎੉߁ ӏ஗ - ஠ݮா੉झ - ੌ߈੸ੋ

    ௏ౣܽ ೣࣻ ֎੉߁ ஶ߮࣌ਸ ٮܴ - ੌ߈ ೣࣻ - ஠ݮா੉झ - ಂషܻ ೣࣻ - ৘৻੸ਵ۽ ஠ݮா੉झ + ௿ېझݺ ࢎਊ - ౵झணா੉झח UIܳ о૑ח Composable ೣࣻ৬ ഒز੄ ৈ૑о ੓ӝ ٸޙ
  10. @Composable fun rememberCoroutineScope(): CoroutineScope = remember { /* ... */

    } @Composable fun createCoroutineScope(): CoroutineScope = remember { /* ... */ }
  11. чਸ remember {} ೞח Composable ೣࣻ
 ֎੉߁ ӏ஗ - rememberܳ

    ੹஖೧ঠ ೣ - ഐ୹੗ীѱ Recomposition, ࠗࣻ ബҗ١ਵ۽ࠗఠ ਬ૑ؼ ࣻ ੓ח ч੐ਸ ݺद੸ਵ۽ ಴അ
  12. UIܳ о૑ח Composable fun FancyButton() fun fancyButton() - ౵झணா੉झ -

    ݺࢎ ࢎਊ чਸ ߈ജೞח Composable fun defaultStyle(): Style fun DefaultStyle(): Style - ஠ݮா੉झ - ಂషܻ ೣࣻ - ౵झணா੉झ чਸ remember {} ೞח Composable fun rememberCoroutineScope(): CoroutineScope fun createCoroutineScope(): CoroutineScope - remember ੹஖
  13. @Composable fun Button( onClick: () -> Unit, ) { }

    val border = LocalButtonBorder.current
  14. @Composable fun Button( onClick: () -> Unit, ) { }

    LocalButtonBorder.current border: BorderStroke = @Composable fun Button( onClick: () -> Unit, ) { val border = LocalButtonBorder.current }
  15. ݺद੸ੋ ੄ઓࢿҗ ঐद੸ੋ ੄ઓࢿ - ݺद੸ੋ ੄ઓࢿ - ೣࣻ ౵ۄ޷ఠ۽

    ੄ઓࢿਸ ࢸ੿ - ஹನք౟੄ ز੘ਸ ৘ஏೞӝ ए਑ - ࢎਊࢿ੉ જҊ పझ౟ ਊ੉ - ঐद੸ੋ ੄ઓࢿ - CompositionLocal۽ ૑੿ - ழझథ ӝמਸ ࢎਊೞӝ ਤ೧ ௏٘ ୶੸੉ ೙ো੸ - Ӗ۽ߥೠ झఋੌ݂੉ ೙ਃೡ ҃਋ ࢎਊ
  16. @Composable fun Icon( bitmap: ImageBitmap, // no modi fi er

    parameter tint: Color = Color.Black ) @Composable fun Icon( bitmap: ImageBitmap, tint: Color = Color.Black, // 1: modi fi er is not the fi rst optional parameter // 2: padding will be lost as soon as the user sets its own modi fi er modi fi er: Modi fi er = Modi fi er.padding(8.dp) ) @Composable fun CheckboxRow( checked: Boolean, onCheckedChange: (Boolean) -> Unit, // DON'T - modi fi er is intended to specify the external behavior of // the CheckboxRow itself, not its subparts. Make them slots instead rowModi fi er: Modi fi er = Modi fi er, checkboxModi fi er: Modi fi er = Modi fi er ) @Composable fun IconButton( buttonBitmap: ImageBitmap, modi fi er: Modi fi er = Modi fi er, tint: Color = Color.Black ) { Box(Modi fi er.padding(16.dp)) { Icon( buttonBitmap, // modi fi er should be applied to the outer-most layout // and be the fi rst one in the chain modi fi er = Modi fi er.aspectRatio(1f).then(modi fi er), tint = tint ) } }
  17. @Composable fun Icon( bitmap: ImageBitmap, // no modi fi er

    parameter tint: Color = Color.Black ) @Composable fun Icon( bitmap: ImageBitmap, tint: Color = Color.Black, // 1: modi fi // 2: padding will be lost as soon as the user sets its own modi fi modi fi ) @Composable fun CheckboxRow( checked: Boolean, onCheckedChange: (Boolean) -> Unit, // DON'T - modi fi // the CheckboxRow itself, not its subparts. Make them slots instead rowModi fi checkboxModi fi ) @Composable fun IconButton( buttonBitmap: ImageBitmap, modi fi tint: Color = Color.Black ) { Box(Modi fi Icon( buttonBitmap, // modi fi // and be the fi modi fi tint = tint ) } }
  18. @Composable fun Icon( bitmap: ImageBitmap, // no modi fi tint:

    Color = Color.Black ) @Composable fun Icon( bitmap: ImageBitmap, tint: Color = Color.Black, // 1: modi fi er is not the fi rst optional parameter // 2: padding will be lost as soon as the user sets its own modi fi er modi fi er: Modi fi er = Modi fi er.padding(8.dp) ) @Composable fun CheckboxRow( checked: Boolean, onCheckedChange: (Boolean) -> Unit, // DON'T - modi fi // the CheckboxRow itself, not its subparts. Make them slots instead rowModi fi checkboxModi fi ) @Composable fun IconButton( buttonBitmap: ImageBitmap, modi fi tint: Color = Color.Black ) { Box(Modi fi Icon( buttonBitmap, // modi fi // and be the fi modi fi tint = tint ) } }
  19. @Composable fun Icon( bitmap: ImageBitmap, // no modi fi tint:

    Color = Color.Black ) @Composable fun Icon( bitmap: ImageBitmap, tint: Color = Color.Black, // 1: modi fi // 2: padding will be lost as soon as the user sets its own modi fi modi fi ) @Composable fun IconButton( buttonBitmap: ImageBitmap, modi fi tint: Color = Color.Black ) { Box(Modi fi Icon( buttonBitmap, // modi fi // and be the fi modi fi tint = tint ) } } @Composable fun CheckboxRow( checked: Boolean, onCheckedChange: (Boolean) -> Unit, // DON'T - modi fi er is intended to specify the external behavior of // the CheckboxRow itself, not its subparts. Make them slots instead rowModi fi er: Modi fi er = Modi fi er, checkboxModi fi er: Modi fi er = Modi fi er )
  20. @Composable fun Icon( bitmap: ImageBitmap, // no modi fi tint:

    Color = Color.Black ) @Composable fun Icon( bitmap: ImageBitmap, tint: Color = Color.Black, // 1: modi fi // 2: padding will be lost as soon as the user sets its own modi fi modi fi ) @Composable fun CheckboxRow( checked: Boolean, onCheckedChange: (Boolean) -> Unit, // DON'T - modi fi // the CheckboxRow itself, not its subparts. Make them slots instead rowModi fi checkboxModi fi ) @Composable fun IconButton( buttonBitmap: ImageBitmap, modi fi er: Modi fi er = Modi fi er, tint: Color = Color.Black ) { Box(Modi fi er.padding(16.dp)) { Icon( buttonBitmap, // modi fi er should be applied to the outer-most layout // and be the fi rst one in the chain modi fi er = Modi fi er.aspectRatio(1f).then(modi fi er), tint = tint ) } }
  21. @Composable fun IconButton( buttonBitmap: ImageBitmap, // good: fi rst optional

    parameter, single of its kind modi fi er: Modi fi er = Modi fi er, tint: Color = Color.Black ) { // good: applied before other modi fi ers to the outer layout Box(modi fi er.padding(16.dp)) { Icon(buttonBitmap, modi fi er = Modi fi er.aspectRatio(1f), tint = tint) } } @Composable fun ColoredCanvas( // ok: canvas has no intrinsic size, asking for size modi fi ers modi fi er: Modi fi er, color: Color = Color.White, ... ) { // good: applied before other modi fi ers to the outer layout Box(modi fi er.background(color)) { ... } }
  22. @Composable fun IconButton( buttonBitmap: ImageBitmap, // good: fi rst optional

    parameter, single of its kind modi fi er: Modi fi er = Modi fi er, tint: Color = Color.Black ) { // good: applied before other modi fi ers to the outer layout Box(modi fi er.padding(16.dp)) { Icon(buttonBitmap, modi fi er = Modi fi er.aspectRatio(1f), tint = tint) } } @Composable fun ColoredCanvas( // ok: canvas has no intrinsic size, asking for size modi fi modi fi color: Color = Color.White, ... ) { // good: applied before other modi fi Box(modi fi ... } }
  23. @Composable fun IconButton( buttonBitmap: ImageBitmap, // good: fi modi fi

    tint: Color = Color.Black ) { // good: applied before other modi fi Box(modi fi Icon(buttonBitmap, modi fi } } @Composable fun ColoredCanvas( // ok: canvas has no intrinsic size, asking for size modi fi ers modi fi er: Modi fi er, color: Color = Color.White, ... ) { // good: applied before other modi fi ers to the outer layout Box(modi fi er.background(color)) { ... } }
  24. Modifier - Optional ౵ۄ޷ఠ੄ ୐ ߣ૩ ࣽࢲী ਤ஖ - ইޖ۠

    ബҗо ੸ਊغ૑ ঋ਷ Modifier ӝࠄчਸ о૗ - ೞա੄ ஹನք౟ח ױ ೞա੄ Modifier݅ਸ о૗ - ৻ࠗীࢲ ೠ ஹನք౟੄ ز੘җ ഋకܳ ߸҃ೞӝ ਤೠ ݾ੸ - ஹನք౟ ղࠗ੄ ׮ܲ ஹನք౟ܳ ࣻ੿ೞӝ ਤ೧ࢲ Slot ࢎਊ - Composable ࠶۾ীࢲ о੢ ߄Ӵীࢲ ഐ୹غח ஹನք౟ী Modifier ೡ׼
  25. ౵ۄ޷ఠ ࢶ঱ ࣽࢲ - ೙ࣻ ౵ۄ޷ఠ - Modifier - Optional

    ౵ۄ޷ఠ - @Composable ೣࣻ - content ١
  26. @Composable fun Button( onClick: () -> Unit, text: String? =

    null, icon: ImageBitmap? = null ) {} @Composable fun Button( onClick: () -> Unit, text: @Composable () -> Unit, icon: @Composable () -> Unit ) {}
  27. ठ܃ - ೠ ஹನք౟੄ ஹನք౟ ҅க ಴അਸ ਤೠ ۈ׮ ౵ۄ޷ఠ

    - ഐ୹ೞח ଃীࢲ ੗ਬ܂ѱ ழझథ оמ
  28. @Composable fun Checkbox( initialValue: Boolean, onChecked: (Boolean) -> Unit )

    { var checkedState by remember { mutableStateOf(initialValue) } // ... // Usage: (Checkbox owns the checked state, caller noti fi ed of changes) // Caller cannot easily implement a validation policy. Checkbox(false, onToggled = { callerCheckedState = it })
  29. @Composable fun Checkbox( isChecked: Boolean, onToggle: () -> Unit )

    { // ... // Usage: (caller mutates optIn and owns the source of truth) Checkbox( myState.optIn, onToggle = { myState.optIn = !myState.optIn } ) @Composable fun Checkbox( initialValue: Boolean, onChecked: (Boolean) -> Unit ) { var checkedState by remember { mutableStateOf(initialValue) } // ... // Usage: (Checkbox owns the checked state, caller noti fi // Caller cannot easily implement a validation policy. Checkbox(false, onToggled = { callerCheckedState = it })
  30. Stateless ૑ೱ - SSOT(Single Source of Truth) - ࢚ਤ ஹನք౟۽

    ࢚క ਤ੐ - mutableStateOf()۽ State<T> ࢎਊ - ೞਤ ஹನք౟ীࢲ ࢚క ҙ଴ ߂ UI সؘ੉౟ - ࢚క ୶੸ী ਊ੉