Slide 1

Slide 1 text

Sebastiano Poggi Eugenio Marletti Portable Material Flutter physical models

Slide 2

Slide 2 text

TODAY

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

Droidcon Italy 2017 APRIL 2017 a year-ish ago

Slide 5

Slide 5 text

⚠ we are boring ⚠ Flutter

Slide 6

Slide 6 text

⚠ we are boring ⚠ Flutter

Slide 7

Slide 7 text

Flutter

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

How?

Slide 12

Slide 12 text

How?

Slide 13

Slide 13 text

IPA APPLICATION CODE FLUTTER FRAMEWORK FLUTTER ENGINE How?

Slide 14

Slide 14 text

How? APK APPLICATION CODE FLUTTER FRAMEWORK FLUTTER ENGINE

Slide 15

Slide 15 text

…but most importantly look native feel native

Slide 16

Slide 16 text

a humble card on Android

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

ripples! ripples!

Slide 19

Slide 19 text

ripples!

Slide 20

Slide 20 text

ripples! Flutter Android

Slide 21

Slide 21 text

Android Flutter

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

minSdkVersion 21

Slide 26

Slide 26 text

Layer 1 Views

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

…but we’re digressing

Slide 31

Slide 31 text

Let’s digress more! " " " " " " " " " " " " " " "

Slide 32

Slide 32 text

RenderNode

Slide 33

Slide 33 text

RenderNode RenderNode RenderNode

Slide 34

Slide 34 text

RenderNode RenderNode RenderNode DisplayList

Slide 35

Slide 35 text

RenderNode RenderNode RenderNode DisplayList ShapeDrawable

Slide 36

Slide 36 text

Layer 2 RenderThread

Slide 37

Slide 37 text

No drawing done so far

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

RenderThread

Slide 40

Slide 40 text

DisplayList RenderThread

Slide 41

Slide 41 text

Layer 3 Native

Slide 42

Slide 42 text

Canvas false friend

Slide 43

Slide 43 text

Canvas false friend HWUI

Slide 44

Slide 44 text

≠ Skia Canvas HWUI

Slide 45

Slide 45 text

Canvas GPU

Slide 46

Slide 46 text

Canvas GPU

Slide 47

Slide 47 text

DONE! …with the card background

Slide 48

Slide 48 text

DONE! …with the card background

Slide 49

Slide 49 text

What about the rest?

Slide 50

Slide 50 text

What about the rest?

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

Android Flutter Flutter

Slide 53

Slide 53 text

Android Flutter Flutter RESET

Slide 54

Slide 54 text

Flutter

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

On all supported platforms

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

Threading

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

Layer 1 Flutter Framework Material

Slide 62

Slide 62 text

No content

Slide 63

Slide 63 text

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(), ), ), );

Slide 64

Slide 64 text

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!

Slide 65

Slide 65 text

In Flutter you can use Composition Mixins Inheritance

Slide 66

Slide 66 text

Composition A build() function

Slide 67

Slide 67 text

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(), ), );

Slide 68

Slide 68 text

Mixins Abstract class with no ctor Partial class Interface with state

Slide 69

Slide 69 text

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: () {}, ),

Slide 70

Slide 70 text

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 {} ,

Slide 71

Slide 71 text

↳ Container ↳ SizedBox ↳ Card ↳ _CardContentsPlaceholder↳

Slide 72

Slide 72 text

↳ Container ↳ DecoratedBox ↳ Align ↳ SizedBox ↳ Card ↳ _CardContentsPlaceholder↳

Slide 73

Slide 73 text

↳ 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, ), ), ); }

Slide 74

Slide 74 text

↳ 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, ), ), ); }

Slide 75

Slide 75 text

↳ 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) { // ... } }

Slide 76

Slide 76 text

↳ 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) { // ... } } }

Slide 77

Slide 77 text

↳ 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) { // ... } }

Slide 78

Slide 78 text

↳ 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, ); // ... } }

Slide 79

Slide 79 text

↳ 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, ); // ... } }

Slide 80

Slide 80 text

↳ 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, ), ); // ... }

Slide 81

Slide 81 text

↳ 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, ), ); // ... }

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

↳ 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(); }

Slide 86

Slide 86 text

↳ 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(); }

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

Layer 2 Flutter Framework Widget

Slide 91

Slide 91 text

↳ 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(); // ... }

Slide 92

Slide 92 text

↳ 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 { // ... }

Slide 93

Slide 93 text

↳ 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), ), ), ), ); }

Slide 94

Slide 94 text

↳ 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), ), ), ), ), ); }

Slide 95

Slide 95 text

↳ 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 { // ... }

Slide 96

Slide 96 text

↳ 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 { // ... } // ...

Slide 97

Slide 97 text

Layer 3 Flutter Framework Rendering

Slide 98

Slide 98 text

↳ 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 { // ... }

Slide 99

Slide 99 text

↳ 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; } }

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

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

Slide 102

Slide 102 text

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

Slide 103

Slide 103 text

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

Slide 104

Slide 104 text

// 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, ); } // ... } // ... }

Slide 105

Slide 105 text

Layer 4 Flutter Framework Dart:ui

Slide 106

Slide 106 text

// 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'; }

Slide 107

Slide 107 text

Layer 5 Flutter engine

Slide 108

Slide 108 text

// 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 ); } // ... }

Slide 109

Slide 109 text

// 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); } // ... }

Slide 110

Slide 110 text

& QA woof

Slide 111

Slide 111 text

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