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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

Droidcon Italy 2017 APRIL 2017 a year ago

Slide 6

Slide 6 text

Droidcon Italy 2017 APRIL 2017 a year ago

Slide 7

Slide 7 text

Droidcon Italy 2017 APRIL 2017 a year-ish ago

Slide 8

Slide 8 text

⚠ we are boring ⚠ Flutter

Slide 9

Slide 9 text

⚠ we are boring ⚠ Flutter

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

No content

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

Slide 16

Slide 16 text

…but most importantly look native feel native

Slide 17

Slide 17 text

a humble card

Slide 18

Slide 18 text

a humble card on Android

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

ripples! ripples!

Slide 21

Slide 21 text

ripples!

Slide 22

Slide 22 text

ripples! Flutter Android

Slide 23

Slide 23 text

Android Flutter

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

minSdkVersion 21

Slide 29

Slide 29 text

Layer 1 Views

Slide 30

Slide 30 text

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?

Slide 31

Slide 31 text

…but we’re digressing

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

RenderNode RenderNode RenderNode

Slide 34

Slide 34 text

RenderNode RenderNode RenderNode DisplayList ShapeDrawable

Slide 35

Slide 35 text

Layer 2 RenderThread

Slide 36

Slide 36 text

No drawing done so far

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

DisplayList RenderThread

Slide 39

Slide 39 text

Layer 3 Native

Slide 40

Slide 40 text

Canvas false friend

Slide 41

Slide 41 text

Canvas false friend HWUI

Slide 42

Slide 42 text

≠ Skia Canvas HWUI

Slide 43

Slide 43 text

Canvas GPU

Slide 44

Slide 44 text

DONE! …with the card background

Slide 45

Slide 45 text

DONE! …with the card background

Slide 46

Slide 46 text

What about the rest?

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

Android Flutter Flutter RESET

Slide 49

Slide 49 text

Flutter Flutter Android

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

No content

Slide 55

Slide 55 text

Layer 1 Material

Slide 56

Slide 56 text

No content

Slide 57

Slide 57 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 58

Slide 58 text

In Flutter you can use Composition Mixins Inheritance

Slide 59

Slide 59 text

Composition A build() function

Slide 60

Slide 60 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 61

Slide 61 text

Mixins Abstract class with no ctor Partial class Interface with state n

Slide 62

Slide 62 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: InkWell( onTap: () {}, ), ), ), ), );

Slide 63

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

Slide 64

Slide 64 text

↳ Container ↳ SizedBox ↳ Card ↳ _CardContentsPlaceholder↳

Slide 65

Slide 65 text

↳ Container ↳ SizedBox ↳ Card ↳ _CardContentsPlaceholder↳

Slide 66

Slide 66 text

↳ Container ↳ Align ↳ SizedBox ↳ Card ↳ _CardContentsPlaceholder↳

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 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 72

Slide 72 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 73

Slide 73 text

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

Slide 74

Slide 74 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 75

Slide 75 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 76

Slide 76 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 77

Slide 77 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 78

Slide 78 text

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

Slide 79 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 80

Slide 80 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 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 /// The interior of non-transparent material. /// /// Animates [elevation], [shadowColor], and [shape]. class _MaterialInterior extends ImplicitlyAnimatedWidget { // ... extends StatefulWidget }

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 { // ... @override _MaterialInteriorState createState() => _MaterialInteriorState(); 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(); }

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

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

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

Layer 2 Flutter Framework Widget

Slide 93

Slide 93 text

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

Slide 94

Slide 94 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 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 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 97

Slide 97 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 98

Slide 98 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 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/framework.dart abstract class Element extends DiagnosticableTree implements BuildContext { // ... }

Slide 100

Slide 100 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 101

Slide 101 text

Layer 3 Flutter Framework Rendering

Slide 102

Slide 102 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 103

Slide 103 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 104

Slide 104 text

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

Slide 105

Slide 105 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 106

Slide 106 text

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

Slide 107

Slide 107 text

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

Slide 108

Slide 108 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 109

Slide 109 text

Layer 4 Flutter Framework Dart:ui

Slide 110

Slide 110 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 111

Slide 111 text

Layer 5 Flutter engine

Slide 112

Slide 112 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 113

Slide 113 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 114

Slide 114 text

Layer 6 Skia

Slide 115

Slide 115 text

& QA woof

Slide 116

Slide 116 text

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

Slide 117

Slide 117 text

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

Slide 118

Slide 118 text

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