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

Declarative UI Patterns

VCNC
October 20, 2019

Declarative UI Patterns

Flutter, Jetpack compose, SwiftUI, React 를 비롯한 다양한 웹 프레임워크 등등. Declarative UI programming은 이제 모든 front-end 개발자가 알아야 하는 시대가 되었습니다. 본 세션에서는 Declarative UI patterns 이 무엇인지 살펴보고 이러한 트렌드가 어떤 의미가 있고, 왜 그것이 UI를 다루는 것에 적합한지 이해해보려고 합니다. 그리고 각 플랫폼에서는 이를 어떻게 구현하는지 알아보고, 모든 front-end 개발을 아우를 수 있는 Flutter를 통해 Declarative framework에서 State를 어떻게 처리하며 만들어가는 게 올바른지 소개합니다

VCNC

October 20, 2019
Tweet

More Decks by VCNC

Other Decks in Programming

Transcript

  1. Declarative UI Patterns
    김남현 (VCNC)

    View Slide

  2. Declarative UI Patterns
    김남현, VCNC
    [email protected]

    View Slide

  3. Contents
    1. What is declarative UI pattern?
    2. Why a declarative UI?
    3. Flutter as a declarative framework
    4. State management in Flutter
    5. Declarative programming on other platforms
    6. Wrap up

    View Slide

  4. What is declarative UI pattern?

    View Slide

  5. Declarative Programming
    ● Describes what a computation should perform
    ○ Not how to accomplish it
    ● Expresses the logic of a computation without describing its control flow
    Like SQL, HTML, XAML

    View Slide

  6. Imperative Addition
    class Number {
    num n;
    Number(num n) { this.n = n; }
    add(num n) { this.n += n; }
    }
    var my = Number(5);
    My.add(2);
    print(my)
    var sum = (a) => (b) => a + b;
    print(sum (5) (2));
    Declarative Addition

    View Slide

  7. Imperative Addition
    class Number {
    num n;
    Number(num n) { this.n = n; }
    add(num n) { this.n += n; }
    }
    var my = Number(5);
    my.add(2);
    print(my)
    var sum = (a) => (b) => a + b;
    print(sum (5) (2));
    Declarative Addition

    View Slide

  8. Declarative UI Pattern
    Describes
    ● What elements you need in UI
    ● How they should look like

    View Slide

  9. Declarative UI Pattern
    Describes
    ● What elements you need in UI
    ● How they should look like
    Nowadays - Separation of behavior from UI

    View Slide

  10. var b = ViewB(...)
    b.setColor(red))
    b.clearChildren()
    var c3 = ViewC(...)
    b.add(c3)
    Imperative Style
    Describes how to achieve what you want

    View Slide

  11. return ViewB(
    color: red,
    child: ViewC(...),
    );
    Declarative Style
    Describes what you want

    View Slide

  12. return Scaffold(
    appBar: AppBar(
    title: Text(widget.title),
    ),
    body: Center(
    child: Column(mainAxisAlignment: MainAxisAlignment.center,
    children: [
    Text('Text1',),
    Text('Text2', style: Theme.of(context).textTheme.display1,),
    ],
    ),
    ),
    );

    View Slide

  13. Why a declarative UI?

    View Slide

  14. ViewA a
    ViewB b
    ViewC c1
    ViewC c2
    ViewA a
    ViewB b
    ViewC c3

    View Slide

  15. b.setColor(red))
    b.clearChildren()
    var c3 = ViewC(...)
    b.add(c3)
    Imperative Style
    ViewA a
    ViewB b
    ViewC c1
    ViewC c2
    ViewA a
    ViewB b
    ViewC c3

    View Slide

  16. return ViewB(
    color: red,
    child: ViewC(...),
    );
    Declarative Style
    ViewA a
    ViewB b
    ViewC c1
    ViewC c2
    ViewA a
    ViewB b
    ViewC c3

    View Slide

  17. Declarative UI framework
    ● Lightens the burden on developers from having to program how to transition
    between various UI states
    ● Lets the developer describe the current state
    ● Lets the developer leaves the transitioning to the framework

    View Slide

  18. ● Takes care of the details for what exactly it takes to render elements
    ● Allows us to focus on the functionality of your app
    Declarative UI framework

    View Slide

  19. Avocado toast
    https://www.self.com/recipe/hummus-avocado-toast

    View Slide

  20. 1. Prepare ingredients: avocado, bread, almond butter, sea salt, red pepper flakes.
    2. Gather equipment: toaster, plate, butter knife.
    3. Lightly toast a slice of bread.
    4. Place toast on plate.
    5. Spread thin layer of almond butter over toast
    6. Cut avocado in half on its longer axis.
    7. Remove avocado core.
    Imperative avocado toast

    View Slide

  21. 4. Place toast on plate.
    5. Spread thin layer of almond butter over toast
    6. Cut avocado in half on its longer axis.
    7. Remove avocado core.
    8. Slice single avocado half into half-centimeter slices.
    9. Place avocado slices with 50% overlap over toast.
    10. Apply three dashes of sea salt.
    11. Apply five dashes of red pepper flakes.
    12. Clean toaster and butter knife.
    13. Eat avocado toast.

    View Slide

  22. “ I’d like some avocado toast on charred sourdough with almond butter, sea
    salt, and red pepper flakes.”
    Declarative avocado toast

    View Slide

  23. Declarative UI
    much less code
    much more predictable

    View Slide

  24. Your App
    Basic
    features
    Custom features!

    View Slide

  25. Your App
    Basic
    features
    Custom features!

    View Slide

  26. ● Breaking down the work process
    ○ Data : what drives most business applications
    ○ User Interface : what makes it meaningful
    ○ Behaviors : Being applied to enhance the experience
    ● Separation of these 3 components ensures maximum flexibility
    Flexibility

    View Slide

  27. ● Development was placed on hold until the design was completed
    ● Separation enables a more parallel workflow
    ● Development is possible having agreed upon what data is needed
    ○ Even if the interface hasn’t been designed yet
    Designer - Developer workflow

    View Slide

  28. Flutter as a declarative framework

    View Slide

  29. User interface in Flutter
    f( state )
    UI

    View Slide

  30. User interface in Flutter
    f( state )
    UI
    The layout on
    the screen

    View Slide

  31. User interface in Flutter
    f( state )
    UI
    The layout on
    the screen
    state
    The application state

    View Slide

  32. User interface in Flutter
    f( state )
    UI
    The layout on
    the screen
    state
    The application state
    Your
    build
    methods

    View Slide

  33. User interface in Flutter
    Everything is a widget!

    View Slide

  34. Flutter Widgets
    ● Take inspiration from React
    ● Are the basic building blocks of App’s user interface
    ● Describe what their view should look like given their current configuration and
    state
    ● Immutable declaration of part of the user interface

    View Slide

  35. Widget can define
    ● A structural element (like a button or menu)
    ● A stylistic element (like a font or color scheme)
    ● An aspect of layout (like padding)
    ● And so on...

    View Slide

  36. Padding(
    padding: EdgeInsets.all(10.0),
    child: Text(
    'Text2',
    style: TextStyle(
    fontStyle: FontStyle.italic,
    color: Color(0xff248f3f)
    ),
    ),
    )
    Example

    View Slide

  37. Composition over inheritance
    ● Widgets are themselves often composed of many small widgets that combine
    to produce powerful effects.
    ● Class hierarchy is shallow and broad to maximize the possible number of
    combinations.
    ● e.g. Container is made up of several widgets responsible for layout, painting,
    positioning, and sizing ( view sourcecode )

    View Slide

  38. Widget build(BuildContext context) {
    ...
    if (alignment != null)
    current = Align(alignment: alignment, child: current);
    if (effectivePadding != null)
    current = Padding(padding: effectivePadding, child: current);
    if (decoration != null)
    current = DecoratedBox(decoration: decoration, child: current);
    if (constraints != null)
    current = ConstrainedBox(constraints: constraints, child: current);
    if (margin != null)
    current = Padding(padding: margin, child: current);
    if (transform != null)
    current = Transform(transform: transform, child: current);
    return current;
    }
    Container

    View Slide

  39. Basic widgets
    ● Text
    ● AssetImage
    ● Row, Column
    ● Stack
    ● Container
    ● And so on

    View Slide

  40. Flutter system overview

    View Slide

  41. Rendering in Flutter
    ● Flutter render tree
    ○ Low-level layout and painting system
    ○ Retained tree of objects that inherit from RenderObject
    ○ All widgets are handled by the appropriate RenderObject

    View Slide

  42. RenderObject
    ● Layout
    ● Painting
    ● Hit-testing
    ● Expensive to instantiate

    View Slide

  43. RenderObject
    ● Layout
    ● Painting
    ● Hit-testing
    ● Expensive to instantiate
    Keep RenderObjects in memory as long as possible

    View Slide

  44. Padding(
    padding: EdgeInsets.all(10.0),
    child: Text(
    'Text2',
    style: TextStyle(
    fontStyle: FontStyle.italic,
    color: Color(0xff248f3f)
    ),
    ),
    )
    Example

    View Slide

  45. App
    Padding
    (10, 10, 10, 10)
    Text
    text2
    Widget Tree

    View Slide

  46. App
    Padding
    (10, 10, 10, 10)
    Text
    text2
    AppRender
    PaddingRender
    (10, 10, 10, 10)
    TextRender
    text2
    Widget Tree Render tree

    View Slide

  47. App
    Padding
    (10, 10, 10, 10)
    Text
    text2
    AppRender
    PaddingRender
    (10, 10, 10, 10)
    TextRender
    text2
    Widget Tree Render tree
    Element
    Element
    Element
    Element Tree

    View Slide

  48. Element
    ● For comparing the widget and the render object
    ● Represents the use of a widget to configure a specific location in the tree
    ● Keeps a reference to the related Widget, RenderObject

    View Slide

  49. Element
    ● For comparing the widget and the render object
    ● Represents the use of a widget to configure a specific location in the tree
    ● Keeps a reference to the related Widget and RenderObject
    Flutter uses Element tree to compare the new widget tree with the already
    existing RenderObject tree

    View Slide

  50. A basic rule
    ● Checks if the old and the new widgets are the same type
    ○ Just updates the RederObject’s configuration
    ○ Continues travelling down the tree
    ● Removes the widget
    ○ Element and RederObject form the tree (including subtrees)

    View Slide

  51. App
    Padding
    (10, 10, 10, 10)
    Text
    text2
    AppRender
    PaddingRender
    (10, 10, 10, 10)
    TextRender
    text2
    Widget Tree Render tree
    Element
    Element
    Element
    Element Tree

    View Slide

  52. App
    Padding
    (5, 5, 5, 5)
    Text
    text2
    AppRender
    PaddingRender
    (10, 10, 10, 10)
    TextRender
    text2
    Widget Tree Render tree
    Element
    Element
    Element
    Element Tree

    View Slide

  53. App
    Padding
    (5, 5, 5, 5)
    Text
    text2
    AppRender
    PaddingRender
    (5, 5, 5, 5)
    TextRender
    text2
    Widget Tree Render tree
    Element
    Element
    Element
    Element Tree

    View Slide

  54. App
    Padding
    (5, 5, 5, 5)
    Text
    text2
    AppRender
    PaddingRender
    (5, 5, 5, 5)
    TextRender
    text2
    Widget Tree Render tree
    Element
    Element
    Element
    Element Tree

    View Slide

  55. FlatButton(
    child: Text("Click!"),
    color: Color(0xff9d4038),
    onPressed: () => print("ok!"),
    )
    Example

    View Slide

  56. App
    FlatButton
    Text
    Click!
    AppRender
    PaddingRender
    (10, 10, 10, 10)
    TextRender
    text2
    Widget Tree Render tree
    Element
    Element
    Element
    Element Tree

    View Slide

  57. App
    FlatButton
    Text
    Click!
    AppRender
    Widget Tree Render tree
    Element
    Element Tree

    View Slide

  58. App
    FlatButton
    Text
    Click!
    AppRender
    FlatButtonRender
    TextRender
    Click!
    Widget Tree Render tree
    Element
    Element
    Element
    Element Tree

    View Slide

  59. Advanced
    ● Keys
    ● Global keys
    ● Sublinear layout
    ● Sublinear widget building

    View Slide

  60. State management in Flutter

    View Slide

  61. The trouble with android.widgets
    Views own state and make their own changes
    Most view state changes have optional listeners

    View Slide

  62. spinner.onItemSelectedListener = object:
    AdapterView.OnItemSelectedListener {
    override fun onNothingSelected(parent: AdapterView?) {
    value = null
    }
    override fun onItemSelected(parent: AdapterView?, view: View?,
    position: Int, id: Long) {
    value = parent?.getItemAtPosition(position)
    }
    }
    The trouble with android.widget.Spinners

    View Slide

  63. The trouble with android.widget.Spinners
    onSelectedItemChanged tells us when the user changed the value

    View Slide

  64. The trouble with android.widget.Spinners
    onSelectedItemChanged tells us when the user changed the value
    .. but it happens after the value has been changed

    View Slide

  65. The trouble with android.widget.Spinners
    onSelectedItemChanged tells us when the user changed the value
    .. but it happens after the value has been changed
    Now, we have to update the state afterwards

    View Slide

  66. The trouble with UIKit
    Views own state and make their own changes
    Most views send a message by target-action pattern

    View Slide

  67. let control = UISwitch()
    control.addTarget(
    self,
    action: #selector(onSwitchValueChanged(sender:)),
    for: .valueChanged
    )
    @objc func onSwitchValueChanged(sender: UISwitch) {
    isOn = sender.isOn
    }
    The trouble with UISwitch

    View Slide

  68. The trouble with UISwitch
    The selector (action) is triggered when the user changed the value

    View Slide

  69. The trouble with UISwitch
    The selector (action) is triggered when the user changed the value
    .. but it happens after the value has been changed

    View Slide

  70. The trouble with UISwitch
    The selector (action) is triggered when the user changed the value
    .. but it happens after the value has been changed
    Now, we have to update the state afterwards

    View Slide

  71. Data flow
    What is the source of truth?
    Who owns it?
    Who updates it?

    View Slide

  72. Multiple sources of truth
    Widget
    App State

    View Slide

  73. Multiple sources of truth
    Widget
    App State

    View Slide

  74. Multiple sources of truth
    Widget
    State
    App State

    View Slide

  75. The trouble on android / iOS
    UI widgets have their own behavior!

    View Slide

  76. User interface in Flutter
    f( state )
    UI
    The layout on
    the screen
    state
    The application state
    Your
    build
    methods

    View Slide

  77. Data flow in Flutter
    Single source of truth

    View Slide

  78. Single source of truth
    Every piece of state has one owner
    Only the owner can make changes to state
    Owner listens to events and makes changes in response

    View Slide

  79. Single source of truth

    View Slide

  80. Screen
    Request Builder
    Vehicle Request
    Builder
    Request Monitor

    View Slide

  81. Top-down Data Flow
    Screen
    Request Builder
    Vehicle Request
    Builder
    Data
    Request

    View Slide

  82. Top-down Data Flow
    Screen
    Request Builder
    Vehicle Request
    Builder
    Data
    Request

    View Slide

  83. Bottom-up Data Flow
    Screen
    Request Builder
    Vehicle Request
    Builder
    Event
    Event

    View Slide

  84. Bottom-up Data Flow
    Screen
    Request Builder
    Vehicle Request
    Builder
    Event
    Event

    View Slide

  85. Demo

    View Slide

  86. Screen
    Request Builder
    Request Monitor

    View Slide

  87. Screen
    Request Builder
    Request Monitor

    View Slide

  88. Screen
    Request Builder
    Request Monitor
    Request

    View Slide

  89. Screen
    Request Builder
    Request Monitor
    Request

    View Slide

  90. Demo

    View Slide

  91. Declarative programming on other platforms

    View Slide

  92. React
    ● A JavaScript library for building user interface
    ● Declarative and component-based library
    ● Flutter’s architecture is inspired by React
    ○ Stateful Component & Stateless Component

    View Slide

  93. render() {
    return (

    TODO


    What needs to be done?

    Add #{this.state.items.length + 1}


    );
    }

    View Slide

  94. Reconciliation
    ● The state of the art algorithms have a O(n^3) complexity
    ● React implements a heuristic O(n) algorithm
    ○ Assumptions
    ■ Two elements of different types will produce different trees
    ■ The developer can hint at which child elements may be stable across
    different renders with a key prop.

    View Slide

  95. The Diffing Algorithm
    ● Elements of different types
    ○ Tears down the old tree and builds the new tree from scratch
    ● DOM elements of the same type
    ○ Looks at the attributes of both
    ○ Only updates the changed attributes
    ● Component elements of the same type
    ○ Updates the props of the underlying component to match the new element

    View Slide

  96. Batching
    ● React batches updates inside event handlers
    ● Excutes all event handlers first
    ● Then triggers a single re-render batching all of updates together

    View Slide

  97. Jetpack Compose
    ● Google introduced Jetpack compose at Google I/O 2019
    ● Declarative style with annotation
    ● A super experimental feature

    View Slide

  98. Google’s API Regrets
    ● Bundled
    ● Complicated
    ● Too many lines of code
    ● Multiple data flow

    View Slide

  99. Goals
    ● Unbundle from platform releases
    ● Fewer technology stack flowcharts
    ● Write less lines of code
    ● Clarify state ownership and event handling

    View Slide

  100. Jetpack Compose
    ● UI as a function
    ○ Takes data as input
    ○ Emits UI hierarchy when invoked

    View Slide

  101. Jetpack Compose
    ● UI as a function
    ○ Takes data as input
    ○ Emits UI hierarchy when invoked
    Declarative UI pattern!

    View Slide

  102. @Composable
    fun NewsFeed(stories: List) {
    for (story in stories) {
    StoryWidget(story)
    }
    }
    @Composable
    fun StoryWidget(story: StoryData) {
    Padding(8.dp) {
    Column {
    Title(story.title)
    Image(story.image)
    Text(story.content)
    }
    }
    }

    View Slide

  103. SwiftUI
    ● Apple introduced SwiftUI at WWDC 2019
    ● Declarative syntax
    ● iOS13+

    View Slide

  104. Problems
    ● Multiple source of truths
    ● Limitation of Storyboards, Interface builder

    View Slide

  105. View
    ● Protocol
    ○ Composition over inheritance
    ● Value type
    ● SwiftUI prefers smaller and single-purposed Views

    View Slide

  106. SwiftUI
    ● Declares View with Modifier
    Text(name)
    .font(.caption)
    .foregroundColor(.blue)
    ● SwiftUI optimizes the process down behind the scenes.

    View Slide

  107. VStack {
    Text("Hi")
    .font(.system(size: 15))
    .foregroundColor(.red)
    Text(name)
    .font(.caption)
    .foregroundColor(.blue)
    Button(action: { self.isPlaying.toggle() }) {
    HStack {
    Image(systemName: "pause.circel")
    Text("play")
    }
    }
    }

    View Slide

  108. Wrap up

    View Slide

  109. return ViewB(
    color: red,
    child: ViewC(...),
    );
    Declarative UI pattern

    View Slide

  110. Why a declarative UI?
    ● Less lines of code
    ● Predictability
    ● Efficient workflow
    ● Flexibility

    View Slide

  111. Flutter as a declarative framework
    ● UI = f( state )
    ● Widget
    ● Widget tree
    ● Element tree
    ● Render tree

    View Slide

  112. State management in Flutter
    ● Dataflow
    ● Single source of truth
    ● ChangeNotifierProvider

    View Slide

  113. Declarative programming on other platforms
    ● React
    ● Jetpack Compose
    ● SwiftUI

    View Slide

  114. View Slide