Declarative UI Patterns

6a11050c8147e4f5fbf2637907c27964?s=47 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를 어떻게 처리하며 만들어가는 게 올바른지 소개합니다

6a11050c8147e4f5fbf2637907c27964?s=128

VCNC

October 20, 2019
Tweet

Transcript

  1. Declarative UI Patterns 김남현 (VCNC)

  2. Declarative UI Patterns 김남현, VCNC nate@vcnc.co.kr

  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
  4. What is declarative UI pattern?

  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
  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
  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
  8. Declarative UI Pattern Describes • What elements you need in

    UI • How they should look like
  9. Declarative UI Pattern Describes • What elements you need in

    UI • How they should look like Nowadays - Separation of behavior from UI
  10. var b = ViewB(...) b.setColor(red)) b.clearChildren() var c3 = ViewC(...)

    b.add(c3) Imperative Style Describes how to achieve what you want
  11. return ViewB( color: red, child: ViewC(...), ); Declarative Style Describes

    what you want
  12. return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child:

    Column(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text('Text1',), Text('Text2', style: Theme.of(context).textTheme.display1,), ], ), ), );
  13. Why a declarative UI?

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

    ViewB b ViewC c3
  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
  16. return ViewB( color: red, child: ViewC(...), ); Declarative Style ViewA

    a ViewB b ViewC c1 ViewC c2 ViewA a ViewB b ViewC c3
  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
  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
  19. Avocado toast https://www.self.com/recipe/hummus-avocado-toast

  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
  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.
  22. “ I’d like some avocado toast on charred sourdough with

    almond butter, sea salt, and red pepper flakes.” Declarative avocado toast
  23. Declarative UI much less code much more predictable

  24. Your App Basic features Custom features!

  25. Your App Basic features Custom features!

  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
  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
  28. Flutter as a declarative framework

  29. User interface in Flutter f( state ) UI

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

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

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

    on the screen state The application state Your build methods
  33. User interface in Flutter Everything is a widget!

  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
  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...
  36. Padding( padding: EdgeInsets.all(10.0), child: Text( 'Text2', style: TextStyle( fontStyle: FontStyle.italic,

    color: Color(0xff248f3f) ), ), ) Example
  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 )
  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
  39. Basic widgets • Text • AssetImage • Row, Column •

    Stack • Container • And so on
  40. Flutter system overview

  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
  42. RenderObject • Layout • Painting • Hit-testing • Expensive to

    instantiate
  43. RenderObject • Layout • Painting • Hit-testing • Expensive to

    instantiate Keep RenderObjects in memory as long as possible
  44. Padding( padding: EdgeInsets.all(10.0), child: Text( 'Text2', style: TextStyle( fontStyle: FontStyle.italic,

    color: Color(0xff248f3f) ), ), ) Example
  45. App Padding (10, 10, 10, 10) Text text2 Widget Tree

  46. App Padding (10, 10, 10, 10) Text text2 AppRender PaddingRender

    (10, 10, 10, 10) TextRender text2 Widget Tree Render tree
  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
  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
  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
  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)
  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
  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
  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
  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
  55. FlatButton( child: Text("Click!"), color: Color(0xff9d4038), onPressed: () => print("ok!"), )

    Example
  56. App FlatButton Text Click! AppRender PaddingRender (10, 10, 10, 10)

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

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

    Render tree Element Element Element Element Tree
  59. Advanced • Keys • Global keys • Sublinear layout •

    Sublinear widget building
  60. State management in Flutter

  61. The trouble with android.widgets Views own state and make their

    own changes Most view state changes have optional listeners
  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
  63. The trouble with android.widget.Spinners onSelectedItemChanged tells us when the user

    changed the value
  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
  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
  66. The trouble with UIKit Views own state and make their

    own changes Most views send a message by target-action pattern
  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
  68. The trouble with UISwitch The selector (action) is triggered when

    the user changed the value
  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
  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
  71. Data flow What is the source of truth? Who owns

    it? Who updates it?
  72. Multiple sources of truth Widget App State

  73. Multiple sources of truth Widget App State

  74. Multiple sources of truth Widget State App State

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

    own behavior!
  76. User interface in Flutter f( state ) UI The layout

    on the screen state The application state Your build methods
  77. Data flow in Flutter Single source of truth

  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
  79. Single source of truth

  80. Screen Request Builder Vehicle Request Builder Request Monitor

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

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

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

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

    Event
  85. Demo

  86. Screen Request Builder Request Monitor

  87. Screen Request Builder Request Monitor

  88. Screen Request Builder Request Monitor Request

  89. Screen Request Builder Request Monitor Request

  90. Demo

  91. Declarative programming on other platforms

  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
  93. render() { return ( <div> <h3>TODO</h3> <TodoList items={this.state.items} /> <form

    onSubmit={this.handleSubmit}> <label htmlFor="new-todo">What needs to be done?</label> <input id="new-todo" onChange={this.handleChange} value={this.state.text}/> <button>Add #{this.state.items.length + 1}</button> </form> </div> ); }
  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.
  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
  96. Batching • React batches updates inside event handlers • Excutes

    all event handlers first • Then triggers a single re-render batching all of updates together
  97. Jetpack Compose • Google introduced Jetpack compose at Google I/O

    2019 • Declarative style with annotation • A super experimental feature
  98. Google’s API Regrets • Bundled • Complicated • Too many

    lines of code • Multiple data flow
  99. Goals • Unbundle from platform releases • Fewer technology stack

    flowcharts • Write less lines of code • Clarify state ownership and event handling
  100. Jetpack Compose • UI as a function ◦ Takes data

    as input ◦ Emits UI hierarchy when invoked
  101. Jetpack Compose • UI as a function ◦ Takes data

    as input ◦ Emits UI hierarchy when invoked Declarative UI pattern!
  102. @Composable fun NewsFeed(stories: List<StoryData>) { for (story in stories) {

    StoryWidget(story) } } @Composable fun StoryWidget(story: StoryData) { Padding(8.dp) { Column { Title(story.title) Image(story.image) Text(story.content) } } }
  103. SwiftUI • Apple introduced SwiftUI at WWDC 2019 • Declarative

    syntax • iOS13+
  104. Problems • Multiple source of truths • Limitation of Storyboards,

    Interface builder
  105. View • Protocol ◦ Composition over inheritance • Value type

    • SwiftUI prefers smaller and single-purposed Views
  106. SwiftUI • Declares View with Modifier Text(name) .font(.caption) .foregroundColor(.blue) •

    SwiftUI optimizes the process down behind the scenes.
  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") } } }
  108. Wrap up

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

  110. Why a declarative UI? • Less lines of code •

    Predictability • Efficient workflow • Flexibility
  111. Flutter as a declarative framework • UI = f( state

    ) • Widget • Widget tree • Element tree • Render tree
  112. State management in Flutter • Dataflow • Single source of

    truth • ChangeNotifierProvider
  113. Declarative programming on other platforms • React • Jetpack Compose

    • SwiftUI
  114. None