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

Portable Material: Flutter physical models

Portable Material: Flutter physical models

One of the most interesting new technologies to reach the mainstream eye in 2017 is undoubtedly Flutter, a novel cross-platform framework from Google that targets Android, iOS and the oh-so-elusive Fuchsia OS.

Whether you’ve heard of Flutter or not, you will be fascinated by the intriguing design that lurks under the Surface. We’ll dive in to how exactly your Flutter widgets get drawn on screen, from your code all the way down to Skia. Did you know that the UI toolkit in Flutter is based on physical models, and that Material is just one of those? Or that in Flutter the Material and Cupertino (iOS) toolkits share a common heritage in their roots?

---

Presented with Eugenio Marletti at Droidcon Turn 2018.

---

— PDF copy and Keynote source available here: https://www.dropbox.com/sh/fwbmgjvjvfcaptv/AADmDzcmZ90h_jAEB5AHL6wZa?dl=0

Sebastiano Poggi

April 19, 2018
Tweet

More Decks by Sebastiano Poggi

Other Decks in Programming

Transcript

  1. Sebastiano
    Poggi
    Eugenio
    Marletti
    Portable
    Material
    Flutter
    physical models

    View Slide

  2. TODAY

    View Slide

  3. TODAY

    View Slide

  4. TODAY I/O 2018
    APRIL 2018 MAY 2018
    DartConf 2018
    JANUARY 2018

    View Slide

  5. Droidcon Italy 2017
    APRIL 2017
    a year ago

    View Slide

  6. Droidcon Italy 2017
    APRIL 2017
    a year ago

    View Slide

  7. Droidcon Italy 2017
    APRIL 2017
    a year-ish ago

    View Slide

  8. ⚠ we are boring ⚠
    Flutter

    View Slide

  9. ⚠ we are boring ⚠
    Flutter

    View Slide

  10. Flutter
    It’s going mainstream
    Fuchsia is looming ahead
    We both became Flutter GDEs
    GDE
    *just a label!

    View Slide

  11. View Slide

  12. How?

    View Slide

  13. IPA
    APPLICATION CODE
    FLUTTER
    FRAMEWORK
    FLUTTER
    ENGINE
    How?

    View Slide

  14. How?
    APK
    APPLICATION CODE
    FLUTTER
    FRAMEWORK
    FLUTTER
    ENGINE

    View Slide

  15. …but most
    importantly
    look native

    View Slide

  16. …but most
    importantly
    look native
    feel native

    View Slide

  17. a humble card

    View Slide

  18. a humble card
    on Android

    View Slide

  19. View Slide

  20. ripples!
    ripples!

    View Slide

  21. ripples!

    View Slide

  22. ripples!
    Flutter Android

    View Slide

  23. Android
    Flutter

    View Slide

  24. Android
    Forget about KitKat
    No ripples
    No rounded corners
    No elevation/shadows
    (efficiently)
    (efficiently)

    View Slide

  25. Android
    Forget about KitKat
    No ripples
    No rounded corners
    No elevation/shadows
    (efficiently)
    (efficiently)

    View Slide

  26. Fake it ‘till you make it
    RULE #1 OF
    COMPUTER GRAPHICS

    View Slide

  27. Fake it ‘till you make it
    RULE #1 OF
    COMPUTER GRAPHICS
    look convincing

    View Slide

  28. minSdkVersion 21

    View Slide

  29. Layer 1
    Views

    View Slide

  30. Views
    1. Background
    It’s a Drawable
    To be precise: ShapeDrawable
    GradientDrawable
    no, it doesn’t have a gradient
    all about the stroke
    naming! right?

    View Slide

  31. …but we’re digressing

    View Slide

  32. Let’s digress more!
    "
    "
    "
    "
    "
    "
    "
    "
    "
    "
    "
    "
    "
    " "

    View Slide

  33. RenderNode
    RenderNode
    RenderNode

    View Slide

  34. RenderNode
    RenderNode
    RenderNode
    DisplayList
    ShapeDrawable

    View Slide

  35. Layer 2
    RenderThread

    View Slide

  36. No drawing
    done so far

    View Slide

  37. No drawing done so far
    Deferred drawing
    Views compile DisplayLists
    Display lists are sent down to the RenderThread

    View Slide

  38. DisplayList
    RenderThread

    View Slide

  39. Layer 3
    Native

    View Slide

  40. Canvas
    false friend

    View Slide

  41. Canvas
    false friend HWUI

    View Slide

  42. ≠ Skia
    Canvas
    HWUI

    View Slide

  43. Canvas
    GPU

    View Slide

  44. DONE!
    …with the card background

    View Slide

  45. DONE!
    …with the card background

    View Slide

  46. What about the rest?

    View Slide

  47. What about the rest?
    Elevation shadow for example
    Lives in the view’s main RenderNode
    RenderNode has outline and elevation
    Outline can be customised
    ShadowTesselator
    …and so on

    View Slide

  48. Android
    Flutter
    Flutter
    RESET

    View Slide

  49. Flutter
    Flutter
    Android

    View Slide

  50. 64 bit
    32 bit WIP
    API 16+
    x86 WIP
    On all supported platforms

    View Slide

  51. On all supported platforms
    Ripples (InkWell)
    Non-rectangular clipping
    Elevation shadows

    View Slide

  52. Threading
    Platform thread
    UI thread
    GPU thread
    I/O thread
    ~ main thread

    View Slide

  53. Platform thread
    UI thread
    GPU thread
    I/O thread
    ~ main thread
    ~ RenderThread
    ~ AsyncTask
    Threading

    View Slide

  54. View Slide

  55. Layer 1
    Material

    View Slide

  56. View Slide

  57. import 'package:flutter/material.dart';
    void main() => runApp(
    new Container(
    alignment: Alignment.center,
    color: const Color.fromARGB(255, 40, 185, 152),
    child: new SizedBox(
    width: 240.0,
    height: 120.0,
    child: new Card(),
    ),
    ),
    );

    View Slide

  58. In Flutter you can use
    Composition
    Mixins
    Inheritance

    View Slide

  59. Composition
    A build() function

    View Slide

  60. Composition
    A build() function
    import 'package:flutter/material.dart';
    @override
    Widget build(context) => new Container(
    alignment: Alignment.center,
    color: const Color.fromARGB(255, 40, 185, 152),
    child: new SizedBox(
    width: 240.0,
    height: 120.0,
    child: new Card(),
    ),
    );

    View Slide

  61. Mixins
    Abstract class with no ctor
    Partial class
    Interface with state
    n

    View Slide

  62. import 'package:flutter/material.dart';
    void main() => runApp(
    Container(
    alignment: Alignment.center,
    color: Colors.grey[50],
    child: SizedBox(
    width: 240.0,
    height: 120.0,
    child: Card(
    child: InkWell(
    onTap: () {},
    ),
    ),
    ),
    ),
    );

    View Slide

  63. import 'package:flutter/material.dart';
    void main() => runApp(
    Container(
    alignment: Alignment.center,
    color: Colors.grey[50],
    child: SizedBox(
    width: 240.0,
    height: 120.0,
    child: Card(
    child: _CardContentsPlaceholder(),
    ),
    ),
    ),
    );
    class _CardContentsPlaceholder extends SizedBox {}

    View Slide

  64. ↳ Container
    ↳ SizedBox
    ↳ Card
    ↳ _CardContentsPlaceholder↳

    View Slide

  65. ↳ Container
    ↳ SizedBox
    ↳ Card
    ↳ _CardContentsPlaceholder↳

    View Slide

  66. ↳ Container
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ _CardContentsPlaceholder↳

    View Slide

  67. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ _CardContentsPlaceholder↳

    View Slide

  68. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ _CardContentsPlacehol
    // packages/flutter/lib/src/material/card.dart
    class Card extends StatelessWidget {
    Card({
    Key key,
    this.color,
    this.elevation: 2.0,
    this.child,
    }) : super(key: key);
    final Widget child;
    final Color color;
    final double elevation;
    @override
    Widget build(context) => Semantics(
    container: true,
    child: Container(
    margin: EdgeInsets.all(4.0),
    child: Material(
    color: color,
    type: MaterialType.card,
    elevation: elevation,
    child: child,
    ),
    ),
    );
    }

    View Slide

  69. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ _CardContentsPlaceho
    // packages/flutter/lib/src/material/card.dart
    class Card extends StatelessWidget {
    Card({
    Key key,
    this.color,
    this.elevation: 2.0,
    this.child,
    }) : super(key: key);
    final Widget child;
    final Color color;
    final double elevation;
    @override
    Widget build(context) => Semantics(
    container: true,
    child: Container(
    margin: EdgeInsets.all(4.0),
    child: Material(
    color: color,
    type: MaterialType.card,
    elevation: elevation,
    child: child,
    ),
    ),
    );
    }

    View Slide

  70. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ _CardContentsPlace
    // packages/flutter/lib/src/material/card.dart
    class Card extends StatelessWidget {
    Card({
    Key key,
    this.color,
    this.elevation: 2.0,
    this.child,
    }) : super(key: key);
    final Widget child;
    final Color color;
    final double elevation;
    @override
    Widget build(context) => Semantics(
    container: true,
    child: Container(
    margin: EdgeInsets.all(4.0),
    child: Material(
    color: color,
    type: MaterialType.card,
    elevation: elevation,
    child: child,
    ),
    ),
    );
    }

    View Slide

  71. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Material
    ↳ _CardContentsPlac
    // packages/flutter/lib/src/material/card.dart
    class Card extends StatelessWidget {
    Card({
    Key key,
    this.color,
    this.elevation: 2.0,
    this.child,
    }) : super(key: key);
    final Widget child;
    final Color color;
    final double elevation;
    @override
    Widget build(context) => Semantics(
    container: true,
    child: Container(
    margin: EdgeInsets.all(4.0),
    child: Material(
    color: color,
    type: MaterialType.card,
    elevation: elevation,
    child: child,
    ),
    ),
    );
    }

    View Slide

  72. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Material
    ↳ _CardContentsPlac
    // packages/flutter/lib/src/material/card.dart
    class Card extends StatelessWidget {
    Card({
    Key key,
    this.color,
    this.elevation: 2.0,
    this.child,
    }) : super(key: key);
    final Widget child;
    final Color color;
    final double elevation;
    @override
    Widget build(context) => Semantics(
    container: true,
    child: Container(
    margin: EdgeInsets.all(4.0),
    child: Material(
    color: color,
    type: MaterialType.card,
    elevation: elevation,
    child: child,
    ),
    ),
    );
    }

    View Slide

  73. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _CardContentsPl
    // packages/flutter/lib/src/material/card.dart
    class Card extends StatelessWidget {
    Card({
    Key key,
    this.color,
    this.elevation: 2.0,
    this.child,
    }) : super(key: key);
    final Widget child;
    final Color color;
    final double elevation;
    @override
    Widget build(context) => Semantics(
    container: true,
    child: Container(
    margin: EdgeInsets.all(4.0),
    child: Material(
    color: color,
    type: MaterialType.card,
    elevation: elevation,
    child: child,
    ),
    ),
    );
    }

    View Slide

  74. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _CardContentsPlace
    // flutter/packages/flutter/lib/src/material/material.dart
    class Material extends StatefulWidget {
    Material({
    Key key,
    this.type: MaterialType.canvas,
    this.elevation: 0.0,
    this.color,
    this.shadowColor: Color(0xFF000000),
    this.textStyle,
    this.borderRadius,
    this.shape,
    this.animationDuration: kThemeChangeDuration,
    this.child,
    }) : assert(type != null),
    assert(elevation != null),
    assert(shadowColor != null),
    assert(!(shape != null && borderRadius != null)),
    assert(animationDuration != null),
    assert(!(identical(type, MaterialType.circle) &&
    (borderRadius != null || shape != null))),
    super(key: key);
    // ...
    @override
    _MaterialState createState() => _MaterialState();
    class _MaterialState extends State with TickerProviderStateMixin {
    @override
    Widget build(context) {
    // ...
    }
    }

    View Slide

  75. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _CardContentsPlace
    // flutter/packages/flutter/lib/src/material/material.dart
    class _MaterialState extends State
    with TickerProviderStateMixin {
    @override
    Widget build(context) {
    // ...
    }
    }

    View Slide

  76. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ AnimatedDefaultTextStyle
    ↳ _CardContentsPlaceholde
    // flutter/packages/flutter/lib/src/material/material.dart
    class _MaterialState extends State
    with TickerProviderStateMixin {
    @override
    Widget build(context) {
    // ...
    Widget contents = AnimatedDefaultTextStyle(
    style: widget.textStyle ?? Theme
    .of(context)
    .textTheme
    .body1,
    duration: widget.animationDuration,
    child: widget.child,
    );
    // ...
    }
    }

    View Slide

  77. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ AnimatedDefaultTextStyle
    ↳ DefaultTextStyle
    ↳ _CardContentsPlacehold
    // flutter/packages/flutter/lib/src/material/material.dart
    class _MaterialState extends State
    with TickerProviderStateMixin {
    @override
    Widget build(context) {
    // ...
    Widget contents = AnimatedDefaultTextStyle(
    style: widget.textStyle ?? Theme
    .of(context)
    .textTheme
    .body1,
    duration: widget.animationDuration,
    child: widget.child,
    );
    // ...
    }
    }

    View Slide

  78. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ NotificationListener
    ↳ AnimatedDefaultTextStyle
    ↳ DefaultTextStyle
    ↳ _CardContentsPlaceh
    // flutter/packages/flutter/lib/src/material/material.dart
    class _MaterialState extends State
    with TickerProviderStateMixin {
    @override
    Widget build(context) {
    // ...
    Widget contents = AnimatedDefaultTextStyle(
    style: widget.textStyle ?? Theme.of(context).textTheme.body1,
    duration: widget.animationDuration,
    child: widget.child,
    );
    final Color backgroundColor = _getBackgroundColor(context);
    contents = NotificationListener(
    onNotification: (notification) {
    _inkFeatureRenderer.currentContext
    .findRenderObject()
    ._didChangeLayout();
    return true;
    },
    child: _InkFeatures(
    key: _inkFeatureRenderer,
    color: backgroundColor,
    child: contents,
    vsync: this,
    ),
    );
    // ...
    }

    View Slide

  79. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ NotificationListener
    ↳ _InkFeatures
    ↳ AnimatedDefaultTextStyl
    ↳ DefaultTextStyle
    ↳ _CardContentsPlac
    // flutter/packages/flutter/lib/src/material/material.dart
    class _MaterialState extends State
    with TickerProviderStateMixin {
    @override
    Widget build(context) {
    // ...
    Widget contents = AnimatedDefaultTextStyle(
    style: widget.textStyle ?? Theme.of(context).textTheme.body1,
    duration: widget.animationDuration,
    child: widget.child,
    );
    final Color backgroundColor = _getBackgroundColor(context);
    contents = NotificationListener(
    onNotification: (notification) {
    _inkFeatureRenderer.currentContext
    .findRenderObject()
    ._didChangeLayout();
    return true;
    },
    child: _InkFeatures(
    key: _inkFeatureRenderer,
    color: backgroundColor,
    child: contents,
    vsync: this,
    ),
    );
    // ...
    }

    View Slide

  80. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ NotificationListener
    ↳ _InkFeatures
    ↳ AnimatedDefaultTextSt
    ↳ DefaultTextStyle
    ↳ _CardContentsPl
    // flutter/packages/flutter/lib/src/material/material.dart
    class _MaterialState extends State
    with TickerProviderStateMixin {
    @override
    Widget build(context) {
    // ...
    Widget contents = AnimatedDefaultTextStyle(
    style: widget.textStyle ?? Theme.of(context).textTheme.body1,
    duration: widget.animationDuration,
    child: widget.child,
    );
    final Color backgroundColor = _getBackgroundColor(context);
    contents = NotificationListener(
    onNotification: (notification) {
    _inkFeatureRenderer.currentContext
    .findRenderObject()
    ._didChangeLayout();
    return true;
    },
    child: _InkFeatures(
    key: _inkFeatureRenderer,
    color: backgroundColor,
    child: contents,
    vsync: this,
    ),
    );
    // ...
    }

    View Slide

  81. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ NotificationListener
    ↳ _InkFeatures
    ↳ AnimatedDefaultTextSt
    ↳ DefaultTextStyle
    ↳ _CardContentsPl
    // flutter/packages/flutter/lib/src/material/material.dart
    /// The interior of non-transparent material.
    ///
    /// Animates [elevation], [shadowColor], and [shape].
    class _MaterialInterior extends ImplicitlyAnimatedWidget {
    // ...
    extends StatefulWidget
    }

    View Slide

  82. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ NotificationListener
    ↳ _InkFeatures
    ↳ AnimatedDefaultTextSt
    ↳ DefaultTextStyle
    ↳ _CardContentsPl
    // flutter/packages/flutter/lib/src/material/material.dart
    /// The interior of non-transparent material.
    ///
    /// Animates [elevation], [shadowColor], and [shape].
    class _MaterialInterior extends ImplicitlyAnimatedWidget {
    // ... extends StatefulWidget
    }

    View Slide

  83. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ NotificationListener
    ↳ _InkFeatures
    ↳ AnimatedDefaultTextSt
    ↳ DefaultTextStyle
    ↳ _CardContentsPl
    // flutter/packages/flutter/lib/src/material/material.dart
    /// The interior of non-transparent material.
    ///
    /// Animates [elevation], [shadowColor], and [shape].
    class _MaterialInterior extends ImplicitlyAnimatedWidget {
    // ...
    @override
    _MaterialInteriorState createState() =>
    _MaterialInteriorState();
    extends StatefulWidget
    }

    View Slide

  84. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ NotificationListener
    ↳ _InkFeatures
    ↳ AnimatedDefaultTextSt
    ↳ DefaultTextStyle
    ↳ _CardContentsPl
    // flutter/packages/flutter/lib/src/material/material.dart
    /// The interior of non-transparent material.
    ///
    /// Animates [elevation], [shadowColor], and [shape].
    class _MaterialInterior extends ImplicitlyAnimatedWidget {
    // ...
    @override
    _MaterialInteriorState createState() =>
    _MaterialInteriorState();
    }

    View Slide

  85. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ PhysicalShape
    ↳ NotificationListener
    ↳ _InkFeatures
    ↳ AnimatedDefaultText
    ↳ DefaultTextStyle
    ↳ _CardContents
    // flutter/packages/flutter/lib/src/material/material.dart
    /// The interior of non-transparent material.
    ///
    /// Animates [elevation], [shadowColor], and [shape].
    class _MaterialInterior extends ImplicitlyAnimatedWidget {
    // ...
    @override
    _MaterialInteriorState createState() =>
    _MaterialInteriorState();
    }

    View Slide

  86. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ PhysicalShape
    ↳ _ShapeBorderPaint
    ↳ NotificationListener
    ↳ _InkFeatures
    ↳ AnimatedDefaultTe
    ↳ DefaultTextSty
    ↳ _CardConten
    // flutter/packages/flutter/lib/src/material/material.dart
    /// The interior of non-transparent material.
    ///
    /// Animates [elevation], [shadowColor], and [shape].
    class _MaterialInterior extends ImplicitlyAnimatedWidget {
    // ...
    @override
    _MaterialInteriorState createState() =>
    _MaterialInteriorState();
    }

    View Slide

  87. t
    {
    ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ PhysicalShape
    ↳ _ShapeBorderPaint
    ↳ NotificationListener
    ↳ _InkFeatures
    ↳ AnimatedDefaultTextStyle
    ↳ DefaultTextStyle
    ↳ _CardContentsPlaceholder↳

    View Slide

  88. t
    {
    ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ PhysicalShape
    ↳ _ShapeBorderPaint
    ↳ CustomPaint
    ↳ NotificationListener
    ↳ _InkFeatures
    ↳ AnimatedDefaultTextStyle
    ↳ DefaultTextStyle
    ↳ _CardContentsPlaceholder↳

    View Slide

  89. t
    {
    ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ PhysicalShape
    ↳ _ShapeBorderPaint
    ↳ CustomPaint
    ↳ NotificationListener
    ↳ _InkFeatures
    ↳ AnimatedDefaultTextStyle↳
    ↳ DefaultTextStyle
    ↳ _CardContentsPlaceholder↳

    View Slide

  90. t
    {
    ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ PhysicalShape
    ↳ _ShapeBorderPaint
    ↳ CustomPaint
    ↳ NotificationListener
    ↳ _InkFeatures
    ↳ AnimatedDefaultTextStyle↳
    ↳ DefaultTextStyle

    View Slide

  91. t
    {
    ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ PhysicalShape
    ↳ _ShapeBorderPaint
    ↳ CustomPaint
    ↳ NotificationListener
    ↳ _InkFeatures
    ↳ AnimatedDefaultTextStyle↳
    ↳ DefaultTextStyle

    View Slide

  92. Layer 2
    Flutter Framework
    Widget

    View Slide

  93. t
    {
    ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ PhysicalShape
    ↳ _ShapeBorderPaint
    ↳ CustomPaint
    ↳ NotificationListener
    ↳ _InkFeatures
    ↳ AnimatedDefaultTextStyle↳
    ↳ DefaultTextStyle

    View Slide

  94. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ PhysicalShape
    ↳ _ShapeBorderPaint
    ↳ CustomPaint
    ↳ NotificationLis
    ↳ _InkFeatures
    ↳ AnimatedD
    ↳ Defaul
    // flutter/packages/flutter/lib/src/widgets/framework.dart
    @immutable
    abstract class Widget extends DiagnosticableTree {
    // ...
    @protected
    Element createElement();
    // ...
    }

    View Slide

  95. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ PhysicalShape
    ↳ _ShapeBorderPaint
    ↳ CustomPaint
    ↳ NotificationLis
    ↳ _InkFeatures
    ↳ AnimatedD
    ↳ Defaul
    // flutter/packages/flutter/lib/src/widgets/framework.dart
    abstract class Element extends DiagnosticableTree
    implements BuildContext {
    // ...
    }

    View Slide

  96. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ PhysicalShape
    ↳ _ShapeBorderPaint
    ↳ CustomPaint
    ↳ NotificationLis
    ↳ _InkFeatures
    ↳ AnimatedD
    ↳ Defaul
    void main() => runApp(MyApp());
    class MyApp extends StatelessWidget {
    static const _buttonText = Text("Let there be Snackbar...");
    static const _snackbarText = Text("...and there was Snackbar");
    static const _snackbar = SnackBar(content: _snackbarText);
    @override
    Widget build(context) => MaterialApp(
    home: Scaffold(
    body: Center(
    child: RaisedButton(
    child: _buttonText,
    onPressed: () => Scaffold.of(context)
    .showSnackBar(_snackbar),
    ),
    ),
    ),
    );
    }

    View Slide

  97. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ PhysicalShape
    ↳ _ShapeBorderPaint
    ↳ CustomPaint
    ↳ NotificationLis
    ↳ _InkFeatures
    ↳ AnimatedD
    ↳ Defaul
    void main() => runApp(MyApp());
    class MyApp extends StatelessWidget {
    static const _buttonText = Text("Let there be Snackbar...");
    static const _snackbarText = Text("...and there was Snackbar");
    static const _snackbar = SnackBar(content: _snackbarText);
    @override
    Widget build(context) => MaterialApp(
    home: Scaffold(
    body: Center(
    child: Builder(
    builder: (context) => RaisedButton(
    child: _buttonText,
    onPressed: () =>
    Scaffold.of(context).showSnackBar(_snackbar),
    ),
    ),
    ),
    ),
    );
    }

    View Slide

  98. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ PhysicalShape
    ↳ _ShapeBorderPaint
    ↳ CustomPaint
    ↳ NotificationLis
    ↳ _InkFeatures
    ↳ AnimatedD
    ↳ Defaul
    void main() => runApp(MyApp());
    class MyApp extends StatelessWidget {
    static const _buttonText = Text("Let there be Snackbar...");
    static const _snackbarText = Text("...and there was Snackbar");
    static const _snackbar = SnackBar(content: _snackbarText);
    @override
    Widget build(context) => MaterialApp(
    home: Scaffold(
    body: Center(
    child: Builder(
    builder: (context) => RaisedButton(
    child: _buttonText,
    onPressed: () =>
    Scaffold.of(context).showSnackBar(_snackbar),
    ),
    ),
    ),
    ),
    );
    }

    View Slide

  99. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ PhysicalShape
    ↳ _ShapeBorderPaint
    ↳ CustomPaint
    ↳ NotificationLis
    ↳ _InkFeatures
    ↳ AnimatedD
    ↳ Defaul
    // flutter/packages/flutter/lib/src/widgets/framework.dart
    abstract class Element extends DiagnosticableTree
    implements BuildContext {
    // ...
    }

    View Slide

  100. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ PhysicalShape
    ↳ _ShapeBorderPaint
    ↳ CustomPaint
    ↳ NotificationLis
    ↳ _InkFeatures
    ↳ AnimatedD
    ↳ Defaul
    // flutter/packages/flutter/lib/src/widgets/framework.dart
    abstract class Element extends DiagnosticableTree
    implements BuildContext {
    // ...
    RenderObject get renderObject {
    // ...
    }
    // ...
    }

    View Slide

  101. Layer 3
    Flutter Framework
    Rendering

    View Slide

  102. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ PhysicalShape
    ↳ _ShapeBorderPaint
    ↳ CustomPaint
    ↳ NotificationLis
    ↳ _InkFeatures
    ↳ AnimatedD
    ↳ Defaul
    // flutter/packages/flutter/lib/src/rendering/object.dart
    abstract class RenderObject extends AbstractNode
    with DiagnosticableTreeMixin
    implements HitTestTarget {
    // ...
    }

    View Slide

  103. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ PhysicalShape
    ↳ _ShapeBorderPaint
    ↳ CustomPaint
    ↳ NotificationLis
    ↳ _InkFeatures
    ↳ AnimatedD
    ↳ Defaul
    // flutter/packages/flutter/lib/src/widgets/basic.dart
    /// Creates a physical model with an arbitrary shape clip.
    class PhysicalShape extends SingleChildRenderObjectWidget {
    // ...
    @override
    RenderPhysicalShape createRenderObject(BuildContext context) =>
    RenderPhysicalShape(
    clipper: clipper,
    elevation: elevation,
    color: color,
    shadowColor: shadowColor,
    );
    @override
    void updateRenderObject(
    BuildContext context, RenderPhysicalShape renderObject) {
    renderObject
    ..clipper = clipper
    ..elevation = elevation
    ..color = color
    ..shadowColor = shadowColor;
    }
    }

    View Slide

  104. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ PhysicalShape
    ↳ _ShapeBorderPaint
    ↳ CustomPaint
    ↳ NotificationListener
    ↳ _InkFeatures
    ↳ AnimatedDefaultTextStyle↳
    ↳ DefaultTextStyle

    View Slide

  105. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ PhysicalShape
    ↳ _ShapeBorderPaint
    ↳ CustomPaint
    ↳ NotificationListener
    ↳ _InkFeatures
    ↳ AnimatedDefaultTextStyle↳
    ↳ DefaultTextStyle

    ↳ RenderDecoratedBox
    ↳ RenderPositionedBox
    ↳ RenderConstrainedBox

    ↳ RenderSemanticsAnnotations

    ↳ RenderPadding


    ↳ RenderPhysicalShape
    ↳ RenderCustomPaint


    ↳ _RenderInkFeatures↳

    View Slide


  106. ↳ RenderDecoratedBox
    ↳ RenderPositionedBox
    ↳ RenderConstrainedBox

    ↳ RenderSemanticsAnnotations

    ↳ RenderPadding


    ↳ RenderPhysicalShape
    ↳ RenderCustomPaint


    ↳ _RenderInkFeatures↳

    View Slide

  107. ↳ RenderDecoratedBox
    ↳ RenderPositionedBox
    ↳ RenderConstrainedBox
    ↳ RenderSemanticsAnnotations
    ↳ RenderPadding
    ↳ RenderPhysicalShape
    ↳ RenderCustomPaint
    ↳ _RenderInkFeatures↳

    View Slide

  108. // flutter/packages/flutter/lib/src/rendering/proxy_box.dart
    class RenderPhysicalShape
    extends _RenderPhysicalModelBase {
    // ...
    @override
    void paint(PaintingContext context, Offset offset) {
    // ...
    if (elevation != 0.0) {
    canvas.drawShadow(
    offsetPath,
    shadowColor,
    elevation,
    color.alpha != 0xFF,
    );
    }
    // ...
    }
    // ...
    }

    View Slide

  109. Layer 4
    Flutter Framework
    Dart:ui

    View Slide

  110. // flutter/bin/cache/pkg/sky_engine/lib/ui/painting.dart
    class Canvas extends NativeFieldWrapperClass2 {
    // ...
    /// Draws a shadow for a [Path] representing the given
    /// material elevation.
    void drawShadow(Path path, Color color, double elevation,
    bool transparentOccluder) {
    assert(path != null);
    assert(color != null);
    assert(transparentOccluder != null);
    _drawShadow(path, color.value, elevation, transparentOccluder);
    }
    void _drawShadow(Path path,
    int color,
    double elevation,
    bool transparentOccluder) native 'Canvas_drawShadow';
    }

    View Slide

  111. Layer 5
    Flutter engine

    View Slide

  112. // engine/lib/ui/painting/canvas.cc
    namespace blink {
    // ...
    void Canvas::drawShadow(const CanvasPath* path,
    SkColor color,
    double elevation,
    bool transparentOccluder) {
    SkScalar dpr =
    UIDartState::Current()->window()->
    viewport_metrics().device_pixel_ratio;
    flow::PhysicalShapeLayer::DrawShadow(
    canvas_, path->path(), color, elevation,
    transparentOccluder, dpr
    );
    }
    // ...
    }

    View Slide

  113. // engine/flow/layers/physical_shape_layer.cc
    #include "third_party/skia/include/utils/SkShadowUtils.h"
    namespace flow {
    //...
    void PhysicalShapeLayer::DrawShadow(
    SkCanvas* canvas, const SkPath& path, SkColor color,
    float elevation, bool transparentOccluder, SkScalar dpr
    ) {
    // ...
    SkShadowFlags flags = // ...
    const SkRect& bounds = path.getBounds();
    SkScalar shadow_x = (bounds.left() + bounds.right()) / 2;
    SkScalar shadow_y = bounds.top() - 600.0f;
    SkColor inAmbient = SkColorSetA(color, kAmbientAlpha * SkColorGetA(color));
    SkColor inSpot = SkColorSetA(color, kSpotAlpha * SkColorGetA(color));
    SkColor ambientColor, spotColor;
    SkShadowUtils::ComputeTonalColors(inAmbient, inSpot,
    &ambientColor, &spotColor);
    SkShadowUtils::DrawShadow(canvas, path, SkPoint3::Make(0, 0, dpr * elevation),
    SkPoint3::Make(shadow_x, shadow_y, dpr * kLightHeight),
    dpr * kLightRadius, ambientColor, spotColor, flags);
    }
    // ...
    }

    View Slide

  114. Layer 6
    Skia

    View Slide

  115. &
    QA
    woof

    View Slide

  116. Sebastiano
    Poggi
    Eugenio
    Marletti
    Novoda Clue
    bit.ly/portable-material
    @seebrock3r
    @workingkills

    View Slide

  117. Links
    Widgets, Elements, and RenderObjects:

    “The Mahogany Staircase - Flutter's Layered Design” (by Ian Hickson) — youtu.be/dkyY9WCGMi0
    Best Flutter Introduction: “A New Hope” (by us!) — bit.ly/a-new-hope-flutter
    Flutter Threads: “Flutter Performance Profiling” — flutter.io/ui-performance
    Flutter Framework Sources — github.com/flutter/flutter
    Flutter Engine Sources — github.com/flutter/engine
    Skia Sources — github.com/google/skia

    View Slide

  118. Acknowledgements
    “Arvo” font — by Anton Koovit, licensed under SIL 1.1
    “Kreon” font — by Julia Petretta, licensed under SIL 1.1
    “League Mono” font — by The League of Moveable Type, licensed under SIL 1.1
    “Matteugenio” drawings — by Sio (@scottecs)
    Golden dividers collection — Designed by Raftel/Freepik

    View Slide