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. 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
  2. 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
  3. 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
  4. 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
  5. 9.

    Declarative UI Pattern Describes • What elements you need in

    UI • How they should look like Nowadays - Separation of behavior from UI
  6. 10.

    var b = ViewB(...) b.setColor(red)) b.clearChildren() var c3 = ViewC(...)

    b.add(c3) Imperative Style Describes how to achieve what you want
  7. 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,), ], ), ), );
  8. 15.
  9. 16.

    return ViewB( color: red, child: ViewC(...), ); Declarative Style ViewA

    a ViewB b ViewC c1 ViewC c2 ViewA a ViewB b ViewC c3
  10. 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
  11. 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
  12. 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
  13. 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.
  14. 22.

    “ I’d like some avocado toast on charred sourdough with

    almond butter, sea salt, and red pepper flakes.” Declarative avocado toast
  15. 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
  16. 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
  17. 31.

    User interface in Flutter f( state ) UI The layout

    on the screen state The application state
  18. 32.

    User interface in Flutter f( state ) UI The layout

    on the screen state The application state Your build methods
  19. 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
  20. 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...
  21. 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 )
  22. 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
  23. 39.
  24. 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
  25. 43.

    RenderObject • Layout • Painting • Hit-testing • Expensive to

    instantiate Keep RenderObjects in memory as long as possible
  26. 46.

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

    (10, 10, 10, 10) TextRender text2 Widget Tree Render tree
  27. 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
  28. 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
  29. 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
  30. 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)
  31. 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
  32. 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
  33. 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
  34. 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
  35. 56.

    App FlatButton Text Click! AppRender PaddingRender (10, 10, 10, 10)

    TextRender text2 Widget Tree Render tree Element Element Element Element Tree
  36. 61.

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

    own changes Most view state changes have optional listeners
  37. 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
  38. 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
  39. 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
  40. 66.

    The trouble with UIKit Views own state and make their

    own changes Most views send a message by target-action pattern
  41. 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
  42. 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
  43. 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
  44. 76.

    User interface in Flutter f( state ) UI The layout

    on the screen state The application state Your build methods
  45. 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
  46. 85.
  47. 90.
  48. 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
  49. 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> ); }
  50. 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.
  51. 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
  52. 96.

    Batching • React batches updates inside event handlers • Excutes

    all event handlers first • Then triggers a single re-render batching all of updates together
  53. 97.

    Jetpack Compose • Google introduced Jetpack compose at Google I/O

    2019 • Declarative style with annotation • A super experimental feature
  54. 98.
  55. 99.

    Goals • Unbundle from platform releases • Fewer technology stack

    flowcharts • Write less lines of code • Clarify state ownership and event handling
  56. 100.

    Jetpack Compose • UI as a function ◦ Takes data

    as input ◦ Emits UI hierarchy when invoked
  57. 101.

    Jetpack Compose • UI as a function ◦ Takes data

    as input ◦ Emits UI hierarchy when invoked Declarative UI pattern!
  58. 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) } } }
  59. 105.

    View • Protocol ◦ Composition over inheritance • Value type

    • SwiftUI prefers smaller and single-purposed Views
  60. 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") } } }
  61. 108.
  62. 110.

    Why a declarative UI? • Less lines of code •

    Predictability • Efficient workflow • Flexibility
  63. 111.

    Flutter as a declarative framework • UI = f( state

    ) • Widget • Widget tree • Element tree • Render tree
  64. 114.