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

4580c218737149bf44d012a110612010?s=128

Sebastiano Poggi

April 24, 2018
Tweet

Transcript

  1. Sebastiano Poggi Eugenio Marletti Portable Material Flutter physical models

  2. TODAY

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

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

  5. ⚠ we are boring ⚠ Flutter

  6. ⚠ we are boring ⚠ Flutter

  7. Flutter

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

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

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

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

  16. a humble card on Android

  17. None
  18. ripples! ripples!

  19. ripples!

  20. ripples! Flutter Android

  21. Android Flutter

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

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

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

    GRAPHICS look convincing
  25. minSdkVersion 21

  26. Layer 1 Views

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

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

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

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

    " " " " " " " "
  32. RenderNode

  33. RenderNode RenderNode RenderNode

  34. RenderNode RenderNode RenderNode DisplayList

  35. RenderNode RenderNode RenderNode DisplayList ShapeDrawable

  36. Layer 2 RenderThread

  37. No drawing done so far

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

    Display lists are sent down to the RenderThread
  39. RenderThread

  40. DisplayList RenderThread

  41. Layer 3 Native

  42. Canvas false friend

  43. Canvas false friend HWUI

  44. ≠ Skia Canvas HWUI

  45. Canvas GPU

  46. Canvas GPU

  47. DONE! …with the card background

  48. DONE! …with the card background

  49. What about the rest?

  50. What about the rest?

  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
  52. Android Flutter Flutter

  53. Android Flutter Flutter RESET

  54. Flutter

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

    all supported platforms
  56. On all supported platforms

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

  58. Threading

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

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

    thread ~ RenderThread ~ AsyncTask Threading
  61. Layer 1 Flutter Framework Material

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

  66. Composition A build() function

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

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

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

    ↳ _CardContentsPlaceholder↳
  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, ), ), ); }
  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, ), ), ); }
  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<Material> with TickerProviderStateMixin { @override Widget build(context) { // ... } }
  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<Material> with TickerProviderStateMixin { @override Widget build(context) { // ... } } }
  77. ↳ 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) { // ... } }
  78. ↳ 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, ); // ... } }
  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<Material> with TickerProviderStateMixin { @override Widget build(context) { // ... Widget contents = AnimatedDefaultTextStyle( style: widget.textStyle ?? Theme .of(context) .textTheme .body1, duration: widget.animationDuration, child: widget.child, ); // ... } }
  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<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 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, ), ); // ... }
  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 { // ... 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(); extends StatefulWidget }
  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. ↳ Container ↳ DecoratedBox ↳ Align ↳ SizedBox ↳ Card

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

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

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

  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(); // ... }
  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 { // ... }
  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), ), ), ), ); }
  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), ), ), ), ), ); }
  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 // flutter/packages/flutter/lib/src/widgets/framework.dart abstract class Element extends DiagnosticableTree implements BuildContext { // ... } RenderObject get renderObject { // ... } // ...
  97. Layer 3 Flutter Framework Rendering

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

    ↳ Semantics ↳ Container ↳ Padding ↳ Material ↳ _MaterialInterior ↳ PhysicalShape ↳ _ShapeBorderPaint ↳ CustomPaint ↳ NotificationListener ↳ _InkFeatures ↳ AnimatedDefaultTextStyle↳ ↳ DefaultTextStyle
  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↳
  102. ↳ _RenderInkFeatures↳ ↳ RenderCustomPaint ↳ RenderPhysicalShape ↳ RenderPadding ↳ RenderSemanticsAnnotations

    ↳ RenderConstrainedBox ↳ RenderPositionedBox ↳ RenderDecoratedBox
  103. ↳ RenderDecoratedBox ↳ RenderPositionedBox ↳ RenderConstrainedBox ↳ RenderSemanticsAnnotations ↳ RenderPadding

    ↳ RenderPhysicalShape ↳ RenderCustomPaint ↳ _RenderInkFeatures↳
  104. // 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, ); } // ... } // ... }
  105. Layer 4 Flutter Framework Dart:ui

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

  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 ); } // ... }
  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); } // ... }
  110. & QA woof

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