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 full-size slide

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

    View full-size slide

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

    View full-size slide

  4. ⚠ we are boring ⚠
    Flutter

    View full-size slide

  5. ⚠ we are boring ⚠
    Flutter

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  8. IPA
    APPLICATION CODE
    FLUTTER
    FRAMEWORK
    FLUTTER
    ENGINE
    How?

    View full-size slide

  9. How?
    APK
    APPLICATION CODE
    FLUTTER
    FRAMEWORK
    FLUTTER
    ENGINE

    View full-size slide

  10. …but most
    importantly
    look native
    feel native

    View full-size slide

  11. a humble card
    on Android

    View full-size slide

  12. ripples!
    ripples!

    View full-size slide

  13. ripples!
    Flutter Android

    View full-size slide

  14. Android
    Flutter

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  18. minSdkVersion 21

    View full-size slide

  19. Layer 1
    Views

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  22. 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 full-size slide

  23. …but we’re digressing

    View full-size slide

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

    View full-size slide

  25. RenderNode
    RenderNode
    RenderNode

    View full-size slide

  26. RenderNode
    RenderNode
    RenderNode
    DisplayList

    View full-size slide

  27. RenderNode
    RenderNode
    RenderNode
    DisplayList
    ShapeDrawable

    View full-size slide

  28. Layer 2
    RenderThread

    View full-size slide

  29. No drawing
    done so far

    View full-size slide

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

    View full-size slide

  31. RenderThread

    View full-size slide

  32. DisplayList
    RenderThread

    View full-size slide

  33. Layer 3
    Native

    View full-size slide

  34. Canvas
    false friend

    View full-size slide

  35. Canvas
    false friend HWUI

    View full-size slide

  36. ≠ Skia
    Canvas
    HWUI

    View full-size slide

  37. DONE!
    …with the card background

    View full-size slide

  38. DONE!
    …with the card background

    View full-size slide

  39. What about the rest?

    View full-size slide

  40. What about the rest?

    View full-size slide

  41. 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 full-size slide

  42. Android
    Flutter
    Flutter

    View full-size slide

  43. Android
    Flutter
    Flutter
    RESET

    View full-size slide

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

    View full-size slide

  45. On all supported platforms

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  49. Layer 1
    Flutter Framework
    Material

    View full-size slide

  50. 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 full-size slide

  51. 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 full-size slide

  52. In Flutter you can use
    Composition
    Mixins
    Inheritance

    View full-size slide

  53. Composition
    A build() function

    View full-size slide

  54. 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 full-size slide

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

    View full-size slide

  56. 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 full-size slide

  57. 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 full-size slide

  58. ↳ Container
    ↳ SizedBox
    ↳ Card
    ↳ _CardContentsPlaceholder↳

    View full-size slide

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

    View full-size slide

  60. ↳ 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 full-size slide

  61. ↳ 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 full-size slide

  62. ↳ 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 full-size slide

  63. ↳ 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 full-size slide

  64. ↳ 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 full-size slide

  65. ↳ 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 full-size slide

  66. ↳ 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 full-size slide

  67. ↳ 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 full-size slide

  68. ↳ 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 full-size slide

  69. ↳ 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 full-size slide

  70. ↳ 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 full-size slide

  71. ↳ 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 full-size slide

  72. ↳ 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 full-size slide

  73. ↳ 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  77. Layer 2
    Flutter Framework
    Widget

    View full-size slide

  78. ↳ 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 full-size slide

  79. ↳ 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 full-size slide

  80. ↳ 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 full-size slide

  81. ↳ 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 full-size slide

  82. ↳ 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 full-size slide

  83. ↳ 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 full-size slide

  84. Layer 3
    Flutter Framework
    Rendering

    View full-size slide

  85. ↳ 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 full-size slide

  86. ↳ 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 full-size slide

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

    View full-size slide

  88. ↳ 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 full-size slide

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

    View full-size slide

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

    View full-size slide

  91. // 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 full-size slide

  92. Layer 4
    Flutter Framework
    Dart:ui

    View full-size slide

  93. // 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 full-size slide

  94. Layer 5
    Flutter engine

    View full-size slide

  95. // 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 full-size slide

  96. // 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 full-size slide

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

    View full-size slide