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

4580c218737149bf44d012a110612010?s=128

Sebastiano Poggi

April 19, 2018
Tweet

Transcript

  1. Sebastiano Poggi Eugenio Marletti Portable Material Flutter physical models

  2. TODAY

  3. TODAY

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

    2018
  5. Droidcon Italy 2017 APRIL 2017 a year ago

  6. Droidcon Italy 2017 APRIL 2017 a year ago

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

  8. ⚠ we are boring ⚠ Flutter

  9. ⚠ we are boring ⚠ Flutter

  10. Flutter It’s going mainstream Fuchsia is looming ahead We both

    became Flutter GDEs GDE *just a label!
  11. None
  12. How?

  13. IPA APPLICATION CODE FLUTTER FRAMEWORK FLUTTER ENGINE How?

  14. How? APK APPLICATION CODE FLUTTER FRAMEWORK FLUTTER ENGINE

  15. …but most importantly look native

  16. …but most importantly look native feel native

  17. a humble card

  18. a humble card on Android

  19. None
  20. ripples! ripples!

  21. ripples!

  22. ripples! Flutter Android

  23. Android Flutter

  24. Android Forget about KitKat No ripples No rounded corners No

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

    elevation/shadows (efficiently) (efficiently)
  26. Fake it ‘till you make it RULE #1 OF COMPUTER

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

    GRAPHICS look convincing
  28. minSdkVersion 21

  29. Layer 1 Views

  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?
  31. …but we’re digressing

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

    " " " " " " " "
  33. RenderNode RenderNode RenderNode

  34. RenderNode RenderNode RenderNode DisplayList ShapeDrawable

  35. Layer 2 RenderThread

  36. No drawing done so far

  37. No drawing done so far Deferred drawing Views compile DisplayLists

    Display lists are sent down to the RenderThread
  38. DisplayList RenderThread

  39. Layer 3 Native

  40. Canvas false friend

  41. Canvas false friend HWUI

  42. ≠ Skia Canvas HWUI

  43. Canvas GPU

  44. DONE! …with the card background

  45. DONE! …with the card background

  46. What about the rest?

  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
  48. Android Flutter Flutter RESET

  49. Flutter Flutter Android

  50. 64 bit 32 bit WIP API 16+ x86 WIP On

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

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

    main thread
  53. Platform thread UI thread GPU thread I/O thread ~ main

    thread ~ RenderThread ~ AsyncTask Threading
  54. None
  55. Layer 1 Material

  56. None
  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(), ), ), );
  58. In Flutter you can use Composition Mixins Inheritance

  59. Composition A build() function

  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(), ), );
  61. Mixins Abstract class with no ctor Partial class Interface with

    state n
  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: () {}, ), ), ), ), );
  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 {}
  64. ↳ Container ↳ SizedBox ↳ Card ↳ _CardContentsPlaceholder↳

  65. ↳ Container ↳ SizedBox ↳ Card ↳ _CardContentsPlaceholder↳

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

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

    ↳ _CardContentsPlaceholder↳
  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, ), ), ); }
  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, ), ), ); }
  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, ), ), ); }
  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, ), ), ); }
  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, ), ), ); }
  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, ), ), ); }
  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<Material> with TickerProviderStateMixin { @override Widget build(context) { // ... } }
  75. ↳ Container ↳ DecoratedBox ↳ Align ↳ SizedBox ↳ Card

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

    ↳ Semantics ↳ Container ↳ Padding ↳ Material ↳ AnimatedDefaultTextStyle ↳ _CardContentsPlaceholde // flutter/packages/flutter/lib/src/material/material.dart class _MaterialState extends State<Material> with TickerProviderStateMixin { @override Widget build(context) { // ... Widget contents = AnimatedDefaultTextStyle( style: widget.textStyle ?? Theme .of(context) .textTheme .body1, duration: widget.animationDuration, child: widget.child, ); // ... } }
  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<Material> with TickerProviderStateMixin { @override Widget build(context) { // ... Widget contents = AnimatedDefaultTextStyle( style: widget.textStyle ?? Theme .of(context) .textTheme .body1, duration: widget.animationDuration, child: widget.child, ); // ... } }
  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<Material> 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<LayoutChangedNotification>( onNotification: (notification) { _inkFeatureRenderer.currentContext .findRenderObject() ._didChangeLayout(); return true; }, child: _InkFeatures( key: _inkFeatureRenderer, color: backgroundColor, child: contents, vsync: this, ), ); // ... }
  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<Material> 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<LayoutChangedNotification>( onNotification: (notification) { _inkFeatureRenderer.currentContext .findRenderObject() ._didChangeLayout(); return true; }, child: _InkFeatures( key: _inkFeatureRenderer, color: backgroundColor, child: contents, vsync: this, ), ); // ... }
  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<Material> 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<LayoutChangedNotification>( onNotification: (notification) { _inkFeatureRenderer.currentContext .findRenderObject() ._didChangeLayout(); return true; }, child: _InkFeatures( key: _inkFeatureRenderer, color: backgroundColor, child: contents, vsync: this, ), ); // ... }
  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 }
  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 }
  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 }
  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(); }
  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(); }
  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(); }
  87. t { ↳ Container ↳ DecoratedBox ↳ Align ↳ SizedBox

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

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

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

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

    ↳ Card ↳ Semantics ↳ Container ↳ Padding ↳ Material ↳ _MaterialInterior ↳ PhysicalShape ↳ _ShapeBorderPaint ↳ CustomPaint ↳ NotificationListener ↳ _InkFeatures ↳ AnimatedDefaultTextStyle↳ ↳ DefaultTextStyle
  92. Layer 2 Flutter Framework Widget

  93. t { ↳ Container ↳ DecoratedBox ↳ Align ↳ SizedBox

    ↳ Card ↳ Semantics ↳ Container ↳ Padding ↳ Material ↳ _MaterialInterior ↳ PhysicalShape ↳ _ShapeBorderPaint ↳ CustomPaint ↳ NotificationListener ↳ _InkFeatures ↳ AnimatedDefaultTextStyle↳ ↳ DefaultTextStyle
  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(); // ... }
  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 { // ... }
  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), ), ), ), ); }
  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), ), ), ), ), ); }
  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), ), ), ), ), ); }
  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 { // ... }
  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 { // ... } // ... }
  101. Layer 3 Flutter Framework Rendering

  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 { // ... }
  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; } }
  104. ↳ Container ↳ DecoratedBox ↳ Align ↳ SizedBox ↳ Card

    ↳ Semantics ↳ Container ↳ Padding ↳ Material ↳ _MaterialInterior ↳ PhysicalShape ↳ _ShapeBorderPaint ↳ CustomPaint ↳ NotificationListener ↳ _InkFeatures ↳ AnimatedDefaultTextStyle↳ ↳ DefaultTextStyle
  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↳
  106. ↳ ↳ RenderDecoratedBox ↳ RenderPositionedBox ↳ RenderConstrainedBox ↳ ↳ RenderSemanticsAnnotations

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

    ↳ RenderPhysicalShape ↳ RenderCustomPaint ↳ _RenderInkFeatures↳
  108. // flutter/packages/flutter/lib/src/rendering/proxy_box.dart class RenderPhysicalShape extends _RenderPhysicalModelBase<Path> { // ... @override

    void paint(PaintingContext context, Offset offset) { // ... if (elevation != 0.0) { canvas.drawShadow( offsetPath, shadowColor, elevation, color.alpha != 0xFF, ); } // ... } // ... }
  109. Layer 4 Flutter Framework Dart:ui

  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'; }
  111. Layer 5 Flutter engine

  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 ); } // ... }
  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); } // ... }
  114. Layer 6 Skia

  115. & QA woof

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

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