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

Portable Material: Flutter physical models 1.1

Portable Material: Flutter physical models 1.1

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 Android Makers 2018, Paris.

---

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

Sebastiano Poggi

April 24, 2018
Tweet

More Decks by Sebastiano Poggi

Other Decks in Technology

Transcript

  1. Sebastiano
    Poggi
    Eugenio
    Marletti
    Portable
    Material
    Flutter
    physical models

    View Slide

  2. TODAY

    View Slide

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

    View Slide

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

    View Slide

  5. ⚠ we are boring ⚠
    Flutter

    View Slide

  6. ⚠ we are boring ⚠
    Flutter

    View Slide

  7. Flutter

    View Slide

  8. Flutter
    It’s going mainstream
    Fuchsia is looming ahead
    We both became Flutter GDEs

    View Slide

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

    View Slide

  10. View Slide

  11. How?

    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
    feel native

    View Slide

  16. a humble card
    on Android

    View Slide

  17. View Slide

  18. ripples!
    ripples!

    View Slide

  19. ripples!

    View Slide

  20. ripples!
    Flutter Android

    View Slide

  21. Android
    Flutter

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  25. minSdkVersion 21

    View Slide

  26. Layer 1
    Views

    View Slide

  27. Views
    Background
    It’s a Drawable
    To be precise: ShapeDrawable

    View Slide

  28. Views
    Background
    It’s a Drawable
    To be precise: ShapeDrawable
    GradientDrawable

    View Slide

  29. Views
    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

  30. …but we’re digressing

    View Slide

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

    View Slide

  32. RenderNode

    View Slide

  33. RenderNode
    RenderNode
    RenderNode

    View Slide

  34. RenderNode
    RenderNode
    RenderNode
    DisplayList

    View Slide

  35. RenderNode
    RenderNode
    RenderNode
    DisplayList
    ShapeDrawable

    View Slide

  36. Layer 2
    RenderThread

    View Slide

  37. No drawing
    done so far

    View Slide

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

    View Slide

  39. RenderThread

    View Slide

  40. DisplayList
    RenderThread

    View Slide

  41. Layer 3
    Native

    View Slide

  42. Canvas
    false friend

    View Slide

  43. Canvas
    false friend HWUI

    View Slide

  44. ≠ Skia
    Canvas
    HWUI

    View Slide

  45. Canvas
    GPU

    View Slide

  46. Canvas
    GPU

    View Slide

  47. DONE!
    …with the card background

    View Slide

  48. DONE!
    …with the card background

    View Slide

  49. What about the rest?

    View Slide

  50. What about the rest?

    View Slide

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

    View Slide

  52. Android
    Flutter
    Flutter

    View Slide

  53. Android
    Flutter
    Flutter
    RESET

    View Slide

  54. Flutter

    View Slide

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

    View Slide

  56. On all supported platforms

    View Slide

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

    View Slide

  58. Threading

    View Slide

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

    View Slide

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

    View Slide

  61. Layer 1
    Flutter Framework
    Material

    View Slide

  62. View Slide

  63. 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

  64. 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())));
    Fits on one line!!1!

    View Slide

  65. In Flutter you can use
    Composition
    Mixins
    Inheritance

    View Slide

  66. Composition
    A build() function

    View Slide

  67. 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

  68. Mixins
    Abstract class with no ctor
    Partial class
    Interface with state

    View Slide

  69. 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:
    ),
    ),
    ),
    );
    class _CardContentsPlaceholder extends SizedBox {}
    InkWell(
    onTap: () {},
    ),

    View Slide

  70. 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()
    ),
    ),
    ),
    );
    _CardContentsPlaceholder()
    class _CardContentsPlaceholder extends SizedBox {}
    ,

    View Slide

  71. ↳ Container
    ↳ SizedBox
    ↳ Card
    ↳ _CardContentsPlaceholder↳

    View Slide

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

    View Slide

  73. ↳ 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

  74. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _CardContentsPla
    // 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

  75. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _CardContentsPla
    // 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

  76. ↳ 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

  77. ↳ 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

  78. ↳ 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

  79. ↳ 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

  80. ↳ 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

  81. ↳ 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

  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 {
    // ... 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();
    extends StatefulWidget
    }

    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. ↳ Container
    ↳ DecoratedBox
    ↳ Align
    ↳ SizedBox
    ↳ Card
    ↳ Semantics
    ↳ Container
    ↳ Padding
    ↳ Material
    ↳ _MaterialInterior
    ↳ PhysicalShape
    ↳ _ShapeBorderPaint
    ↳ CustomPaint
    ↳ NotificationListener
    ↳ _InkFeatures
    ↳ AnimatedDefaultTextStyle↳
    ↳ DefaultTextStyle
    ↳ _CardContentsPlaceholder↳

    View Slide

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

    View Slide

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

    View Slide

  90. Layer 2
    Flutter Framework
    Widget

    View Slide

  91. ↳ 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

  92. ↳ 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

  93. ↳ 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

  94. ↳ 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

  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
    // flutter/packages/flutter/lib/src/widgets/framework.dart
    abstract class Element extends DiagnosticableTree
    implements BuildContext {
    // ...
    }
    RenderObject get renderObject {
    // ...
    }
    // ...

    View Slide

  97. Layer 3
    Flutter Framework
    Rendering

    View Slide

  98. ↳ 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

  99. ↳ 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

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

    View Slide

  101. ↳ 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

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

    View Slide

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

    View Slide

  104. // 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

  105. Layer 4
    Flutter Framework
    Dart:ui

    View Slide

  106. // 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

  107. Layer 5
    Flutter engine

    View Slide

  108. // 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

  109. // 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

  110. &
    QA
    woof

    View Slide

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

    View Slide