Commit e1b721f0 authored by Hans Muller's avatar Hans Muller

Merge pull request #451 from HansMuller/shadows

Update Material shadow rendering

Shadows now render as three seprate MaskFilter.blur components per the most recent Material spec.

The shadows Map was replaced by a similar Map called elevationToShadow with entries that match the 10 elevations specifed by http://www.google.com/design/spec/what-is-material/elevation-shadows.html.

The "level" property (many classes) is now called "elevation", to match the Material spec.

BoxShadow now includes a spreadRadius parameter - as in CSS box-shadow. Renamed the BoxShadow blur property to blurRadius to further align BoxShadow with CSS box-shadow.
parents fd6bc856 d9153a13
...@@ -13,7 +13,7 @@ void main() { ...@@ -13,7 +13,7 @@ void main() {
gradient: new RadialGradient( gradient: new RadialGradient(
center: Point.origin, radius: 500.0, center: Point.origin, radius: 500.0,
colors: <Color>[Colors.yellow[500], Colors.blue[500]]), colors: <Color>[Colors.yellow[500], Colors.blue[500]]),
boxShadow: shadows[3]) boxShadow: elevationToShadow[8])
); );
var paddedBox = new RenderPadding( var paddedBox = new RenderPadding(
padding: const EdgeDims.all(50.0), padding: const EdgeDims.all(50.0),
......
...@@ -146,7 +146,7 @@ class StockHomeState extends State<StockHome> { ...@@ -146,7 +146,7 @@ class StockHomeState extends State<StockHome> {
Widget buildToolBar() { Widget buildToolBar() {
return new ToolBar( return new ToolBar(
level: 0, elevation: 0,
left: new IconButton( left: new IconButton(
icon: "navigation/menu", icon: "navigation/menu",
onPressed: _showDrawer onPressed: _showDrawer
......
...@@ -100,7 +100,7 @@ Widget statusBox(Widget child) { ...@@ -100,7 +100,7 @@ Widget statusBox(Widget child) {
decoration: const BoxDecoration( decoration: const BoxDecoration(
boxShadow: const <BoxShadow>[ boxShadow: const <BoxShadow>[
const BoxShadow( const BoxShadow(
color: mediumGray, offset: const Offset(6.0, 6.0), blur: 5.0) color: mediumGray, offset: const Offset(6.0, 6.0), blurRadius: 5.0)
], ],
backgroundColor: darkGray backgroundColor: darkGray
), ),
......
...@@ -23,7 +23,7 @@ class Card extends StatelessComponent { ...@@ -23,7 +23,7 @@ class Card extends StatelessComponent {
child: new Material( child: new Material(
color: color, color: color,
type: MaterialType.card, type: MaterialType.card,
level: 2, elevation: 8,
child: child child: child
) )
); );
......
...@@ -102,7 +102,7 @@ class Dialog extends StatelessComponent { ...@@ -102,7 +102,7 @@ class Dialog extends StatelessComponent {
child: new ConstrainedBox( child: new ConstrainedBox(
constraints: new BoxConstraints(minWidth: 280.0), constraints: new BoxConstraints(minWidth: 280.0),
child: new Material( child: new Material(
level: 4, elevation: 24,
color: _getColor(context), color: _getColor(context),
type: MaterialType.card, type: MaterialType.card,
child: new IntrinsicWidth( child: new IntrinsicWidth(
......
...@@ -36,7 +36,7 @@ class _Drawer extends StatelessComponent { ...@@ -36,7 +36,7 @@ class _Drawer extends StatelessComponent {
child: new ConstrainedBox( child: new ConstrainedBox(
constraints: const BoxConstraints.expand(width: _kWidth), constraints: const BoxConstraints.expand(width: _kWidth),
child: new Material( child: new Material(
level: route.level, elevation: route.elevation,
child: route.child child: route.child
) )
) )
...@@ -51,10 +51,10 @@ enum _DrawerState { ...@@ -51,10 +51,10 @@ enum _DrawerState {
} }
class _DrawerRoute extends OverlayRoute { class _DrawerRoute extends OverlayRoute {
_DrawerRoute({ this.child, this.level }); _DrawerRoute({ this.child, this.elevation });
final Widget child; final Widget child;
final int level; final int elevation;
List<WidgetBuilder> get builders => <WidgetBuilder>[ _build ]; List<WidgetBuilder> get builders => <WidgetBuilder>[ _build ];
...@@ -212,6 +212,6 @@ class _DrawerControllerState extends State<_DrawerController> { ...@@ -212,6 +212,6 @@ class _DrawerControllerState extends State<_DrawerController> {
} }
} }
void showDrawer({ BuildContext context, Widget child, int level: 3 }) { void showDrawer({ BuildContext context, Widget child, int elevation: 16 }) {
Navigator.of(context).push(new _DrawerRoute(child: child, level: level)); Navigator.of(context).push(new _DrawerRoute(child: child, elevation: elevation));
} }
...@@ -85,7 +85,7 @@ class _DropdownMenu extends StatusTransitionComponent { ...@@ -85,7 +85,7 @@ class _DropdownMenu extends StatusTransitionComponent {
final BoxPainter menuPainter = new BoxPainter(new BoxDecoration( final BoxPainter menuPainter = new BoxPainter(new BoxDecoration(
backgroundColor: Theme.of(context).canvasColor, backgroundColor: Theme.of(context).canvasColor,
borderRadius: 2.0, borderRadius: 2.0,
boxShadow: shadows[route.level] boxShadow: elevationToShadow[route.elevation]
)); ));
final RenderBox renderBox = Navigator.of(context).context.findRenderObject(); final RenderBox renderBox = Navigator.of(context).context.findRenderObject();
...@@ -129,13 +129,13 @@ class _MenuRoute extends TransitionRoute { ...@@ -129,13 +129,13 @@ class _MenuRoute extends TransitionRoute {
this.items, this.items,
this.selectedIndex, this.selectedIndex,
this.rect, this.rect,
this.level: 4 this.elevation: 8
}); });
final Completer completer; final Completer completer;
final Rect rect; final Rect rect;
final List<DropdownMenuItem> items; final List<DropdownMenuItem> items;
final int level; final int elevation;
final int selectedIndex; final int selectedIndex;
bool get opaque => false; bool get opaque => false;
...@@ -183,13 +183,13 @@ class DropdownButton<T> extends StatelessComponent { ...@@ -183,13 +183,13 @@ class DropdownButton<T> extends StatelessComponent {
this.items, this.items,
this.value, this.value,
this.onChanged, this.onChanged,
this.level: 4 this.elevation: 8
}) : super(key: key); }) : super(key: key);
final List<DropdownMenuItem<T>> items; final List<DropdownMenuItem<T>> items;
final T value; final T value;
final ValueChanged<T> onChanged; final ValueChanged<T> onChanged;
final int level; final int elevation;
void _showDropdown(BuildContext context, int selectedIndex, GlobalKey indexedStackKey) { void _showDropdown(BuildContext context, int selectedIndex, GlobalKey indexedStackKey) {
final RenderBox renderBox = indexedStackKey.currentContext.findRenderObject(); final RenderBox renderBox = indexedStackKey.currentContext.findRenderObject();
...@@ -200,7 +200,7 @@ class DropdownButton<T> extends StatelessComponent { ...@@ -200,7 +200,7 @@ class DropdownButton<T> extends StatelessComponent {
items: items, items: items,
selectedIndex: selectedIndex, selectedIndex: selectedIndex,
rect: _kMenuHorizontalPadding.inflateRect(rect), rect: _kMenuHorizontalPadding.inflateRect(rect),
level: level elevation: elevation
)); ));
completer.future.then((T newValue) { completer.future.then((T newValue) {
if (onChanged != null) if (onChanged != null)
......
...@@ -22,7 +22,7 @@ class FlatButton extends MaterialButton { ...@@ -22,7 +22,7 @@ class FlatButton extends MaterialButton {
class _FlatButtonState extends MaterialButtonState<FlatButton> { class _FlatButtonState extends MaterialButtonState<FlatButton> {
int get level => 0; int get elevation => 0;
Color getColor(BuildContext context, { bool highlight }) { Color getColor(BuildContext context, { bool highlight }) {
if (!config.enabled || !highlight) if (!config.enabled || !highlight)
......
...@@ -50,7 +50,7 @@ class _FloatingActionButtonState extends State<FloatingActionButton> { ...@@ -50,7 +50,7 @@ class _FloatingActionButtonState extends State<FloatingActionButton> {
return new Material( return new Material(
color: materialColor, color: materialColor,
type: MaterialType.circle, type: MaterialType.circle,
level: _highlight ? 3 : 2, elevation: _highlight ? 12 : 6,
child: new ClipOval( child: new ClipOval(
child: new Container( child: new Container(
width: _kSize, width: _kSize,
......
...@@ -23,16 +23,16 @@ class Material extends StatelessComponent { ...@@ -23,16 +23,16 @@ class Material extends StatelessComponent {
Key key, Key key,
this.child, this.child,
this.type: MaterialType.canvas, this.type: MaterialType.canvas,
this.level: 0, this.elevation: 0,
this.color, this.color,
this.textStyle this.textStyle
}) : super(key: key) { }) : super(key: key) {
assert(level != null); assert(elevation != null);
} }
final Widget child; final Widget child;
final MaterialType type; final MaterialType type;
final int level; final int elevation;
final Color color; final Color color;
final TextStyle textStyle; final TextStyle textStyle;
...@@ -72,7 +72,7 @@ class Material extends StatelessComponent { ...@@ -72,7 +72,7 @@ class Material extends StatelessComponent {
decoration: new BoxDecoration( decoration: new BoxDecoration(
backgroundColor: _getBackgroundColor(context), backgroundColor: _getBackgroundColor(context),
borderRadius: _kEdges[type], borderRadius: _kEdges[type],
boxShadow: level == 0 ? null : shadows[level], boxShadow: elevation == 0 ? null : elevationToShadow[elevation],
shape: type == MaterialType.circle ? Shape.circle : Shape.rectangle shape: type == MaterialType.circle ? Shape.circle : Shape.rectangle
), ),
child: contents child: contents
......
...@@ -56,7 +56,7 @@ abstract class MaterialButton extends StatefulComponent { ...@@ -56,7 +56,7 @@ abstract class MaterialButton extends StatefulComponent {
abstract class MaterialButtonState<T extends MaterialButton> extends State<T> { abstract class MaterialButtonState<T extends MaterialButton> extends State<T> {
bool highlight = false; bool highlight = false;
int get level; int get elevation;
Color getColor(BuildContext context, { bool highlight }); Color getColor(BuildContext context, { bool highlight });
ThemeBrightness getColorBrightness(BuildContext context); ThemeBrightness getColorBrightness(BuildContext context);
...@@ -102,7 +102,7 @@ abstract class MaterialButtonState<T extends MaterialButton> extends State<T> { ...@@ -102,7 +102,7 @@ abstract class MaterialButtonState<T extends MaterialButton> extends State<T> {
margin: new EdgeDims.all(8.0), margin: new EdgeDims.all(8.0),
child: new Material( child: new Material(
type: MaterialType.button, type: MaterialType.button,
level: level, elevation: elevation,
textStyle: Theme.of(context).text.button.copyWith(color: getTextColor(context)), textStyle: Theme.of(context).text.button.copyWith(color: getTextColor(context)),
child: new InkWell( child: new InkWell(
onTap: config.enabled ? config.onPressed : null, onTap: config.enabled ? config.onPressed : null,
......
...@@ -34,7 +34,7 @@ class _PopupMenu extends StatelessComponent { ...@@ -34,7 +34,7 @@ class _PopupMenu extends StatelessComponent {
final BoxPainter painter = new BoxPainter(new BoxDecoration( final BoxPainter painter = new BoxPainter(new BoxDecoration(
backgroundColor: Theme.of(context).canvasColor, backgroundColor: Theme.of(context).canvasColor,
borderRadius: 2.0, borderRadius: 2.0,
boxShadow: shadows[route.level] boxShadow: elevationToShadow[route.elevation]
)); ));
double unit = 1.0 / (route.items.length + 1.5); // 1.0 for the width and 0.5 for the last item's fade. double unit = 1.0 / (route.items.length + 1.5); // 1.0 for the width and 0.5 for the last item's fade.
...@@ -93,11 +93,11 @@ class _PopupMenu extends StatelessComponent { ...@@ -93,11 +93,11 @@ class _PopupMenu extends StatelessComponent {
} }
class _MenuRoute extends ModalRoute { class _MenuRoute extends ModalRoute {
_MenuRoute({ Completer completer, this.position, this.items, this.level }) : super(completer: completer); _MenuRoute({ Completer completer, this.position, this.items, this.elevation }) : super(completer: completer);
final ModalPosition position; final ModalPosition position;
final List<PopupMenuItem> items; final List<PopupMenuItem> items;
final int level; final int elevation;
Performance createPerformance() { Performance createPerformance() {
Performance result = super.createPerformance(); Performance result = super.createPerformance();
...@@ -113,13 +113,13 @@ class _MenuRoute extends ModalRoute { ...@@ -113,13 +113,13 @@ class _MenuRoute extends ModalRoute {
Widget buildPage(BuildContext context) => new _PopupMenu(route: this); Widget buildPage(BuildContext context) => new _PopupMenu(route: this);
} }
Future showMenu({ BuildContext context, ModalPosition position, List<PopupMenuItem> items, int level: 4 }) { Future showMenu({ BuildContext context, ModalPosition position, List<PopupMenuItem> items, int elevation: 8 }) {
Completer completer = new Completer(); Completer completer = new Completer();
Navigator.of(context).pushEphemeral(new _MenuRoute( Navigator.of(context).pushEphemeral(new _MenuRoute(
completer: completer, completer: completer,
position: position, position: position,
items: items, items: items,
level: level elevation: elevation
)); ));
return completer.future; return completer.future;
} }
...@@ -22,7 +22,7 @@ class RaisedButton extends MaterialButton { ...@@ -22,7 +22,7 @@ class RaisedButton extends MaterialButton {
class _RaisedButtonState extends MaterialButtonState<RaisedButton> { class _RaisedButtonState extends MaterialButtonState<RaisedButton> {
int get level => config.enabled ? (highlight ? 2 : 1) : 0; int get elevation => config.enabled ? (highlight ? 8 : 2) : 0;
Color getColor(BuildContext context, { bool highlight }) { Color getColor(BuildContext context, { bool highlight }) {
if (config.enabled) { if (config.enabled) {
......
...@@ -6,55 +6,72 @@ import 'dart:ui' show Color, Offset; ...@@ -6,55 +6,72 @@ import 'dart:ui' show Color, Offset;
import 'package:flutter/painting.dart'; import 'package:flutter/painting.dart';
const Map<int, List<BoxShadow>> shadows = const <int, List<BoxShadow>>{ // Based on http://www.google.com/design/spec/what-is-material/elevation-shadows.html
// Currently, only the elevation values that are bound to one or more components are
// defined here.
const Color _kKeyUmbraOpacity = const Color(0x33000000); // alpha = 0.2
const Color _kKeyPenumbraOpacity = const Color(0x24000000); // alpha = 0.14
const Color _kAmbientShadowOpacity = const Color(0x1F000000); // alpha = 0.12
const Map<int, List<BoxShadow>> elevationToShadow = const <int, List<BoxShadow>>{
1: const <BoxShadow>[ 1: const <BoxShadow>[
const BoxShadow( const BoxShadow(offset: const Offset(0.0, 2.0), blurRadius: 1.0, spreadRadius: -1.0, color: _kKeyUmbraOpacity),
color: const Color(0x1F000000), const BoxShadow(offset: const Offset(0.0, 1.0), blurRadius: 1.0, spreadRadius: 0.0, color: _kKeyPenumbraOpacity),
offset: const Offset(0.0, 1.0), const BoxShadow(offset: const Offset(0.0, 1.0), blurRadius: 3.0, spreadRadius: 0.0, color: _kAmbientShadowOpacity),
blur: 3.0), ],
const BoxShadow(
color: const Color(0x3D000000),
offset: const Offset(0.0, 1.0),
blur: 2.0),
],
2: const <BoxShadow>[ 2: const <BoxShadow>[
const BoxShadow( const BoxShadow(offset: const Offset(0.0, 3.0), blurRadius: 1.0, spreadRadius: -2.0, color: _kKeyUmbraOpacity),
color: const Color(0x29000000), const BoxShadow(offset: const Offset(0.0, 2.0), blurRadius: 2.0, spreadRadius: 0.0, color: _kKeyPenumbraOpacity),
offset: const Offset(0.0, 3.0), const BoxShadow(offset: const Offset(0.0, 1.0), blurRadius: 5.0, spreadRadius: 0.0, color: _kAmbientShadowOpacity),
blur: 6.0),
const BoxShadow(
color: const Color(0x3B000000),
offset: const Offset(0.0, 3.0),
blur: 6.0),
], ],
3: const <BoxShadow>[ 3: const <BoxShadow>[
const BoxShadow( const BoxShadow(offset: const Offset(0.0, 3.0), blurRadius: 3.0, spreadRadius: -2.0, color: _kKeyUmbraOpacity),
color: const Color(0x30000000), const BoxShadow(offset: const Offset(0.0, 3.0), blurRadius: 4.0, spreadRadius: 0.0, color: _kKeyPenumbraOpacity),
offset: const Offset(0.0, 10.0), const BoxShadow(offset: const Offset(0.0, 1.0), blurRadius: 8.0, spreadRadius: 0.0, color: _kAmbientShadowOpacity),
blur: 20.0),
const BoxShadow(
color: const Color(0x3B000000),
offset: const Offset(0.0, 6.0),
blur: 6.0),
], ],
4: const <BoxShadow>[ 4: const <BoxShadow>[
const BoxShadow( const BoxShadow(offset: const Offset(0.0, 2.0), blurRadius: 4.0, spreadRadius: -1.0, color: _kKeyUmbraOpacity),
color: const Color(0x40000000), const BoxShadow(offset: const Offset(0.0, 4.0), blurRadius: 5.0, spreadRadius: 0.0, color: _kKeyPenumbraOpacity),
offset: const Offset(0.0, 14.0), const BoxShadow(offset: const Offset(0.0, 1.0), blurRadius: 10.0, spreadRadius: 0.0, color: _kAmbientShadowOpacity),
blur: 28.0), ],
const BoxShadow(
color: const Color(0x38000000), 6: const <BoxShadow>[
offset: const Offset(0.0, 10.0), const BoxShadow(offset: const Offset(0.0, 3.0), blurRadius: 5.0, spreadRadius: -1.0, color: _kKeyUmbraOpacity),
blur: 10.0), const BoxShadow(offset: const Offset(0.0, 6.0), blurRadius: 10.0, spreadRadius: 0.0, color: _kKeyPenumbraOpacity),
], const BoxShadow(offset: const Offset(0.0, 1.0), blurRadius: 18.0, spreadRadius: 0.0, color: _kAmbientShadowOpacity),
5: const <BoxShadow>[ ],
const BoxShadow(
color: const Color(0x4E000000), 8: const <BoxShadow>[
offset: const Offset(0.0, 19.0), const BoxShadow(offset: const Offset(0.0, 5.0), blurRadius: 5.0, spreadRadius: -3.0, color: _kKeyUmbraOpacity),
blur: 28.0), const BoxShadow(offset: const Offset(0.0, 8.0), blurRadius: 10.0, spreadRadius: 1.0, color: _kKeyPenumbraOpacity),
const BoxShadow( const BoxShadow(offset: const Offset(0.0, 3.0), blurRadius: 14.0, spreadRadius: 2.0, color: _kAmbientShadowOpacity),
color: const Color(0x38000000), ],
offset: const Offset(0.0, 15.0),
blur: 12.0), 9: const <BoxShadow>[
const BoxShadow(offset: const Offset(0.0, 5.0), blurRadius: 6.0, spreadRadius: -3.0, color: _kKeyUmbraOpacity),
const BoxShadow(offset: const Offset(0.0, 9.0), blurRadius: 12.0, spreadRadius: 1.0, color: _kKeyPenumbraOpacity),
const BoxShadow(offset: const Offset(0.0, 3.0), blurRadius: 16.0, spreadRadius: 2.0, color: _kAmbientShadowOpacity),
],
12: const <BoxShadow>[
const BoxShadow(offset: const Offset(0.0, 7.0), blurRadius: 8.0, spreadRadius: -4.0, color: _kKeyUmbraOpacity),
const BoxShadow(offset: const Offset(0.0, 12.0), blurRadius: 17.0, spreadRadius: 2.0, color: _kKeyPenumbraOpacity),
const BoxShadow(offset: const Offset(0.0, 5.0), blurRadius: 22.0, spreadRadius: 4.0, color: _kAmbientShadowOpacity),
],
16: const <BoxShadow>[
const BoxShadow(offset: const Offset(0.0, 8.0), blurRadius: 10.0, spreadRadius: -5.0, color: _kKeyUmbraOpacity),
const BoxShadow(offset: const Offset(0.0, 16.0), blurRadius: 24.0, spreadRadius: 2.0, color: _kKeyPenumbraOpacity),
const BoxShadow(offset: const Offset(0.0, 6.0), blurRadius: 30.0, spreadRadius: 5.0, color: _kAmbientShadowOpacity),
],
24: const <BoxShadow>[
const BoxShadow(offset: const Offset(0.0, 11.0), blurRadius: 15.0, spreadRadius: -7.0, color: _kKeyUmbraOpacity),
const BoxShadow(offset: const Offset(0.0, 24.0), blurRadius: 38.0, spreadRadius: 3.0, color: _kKeyPenumbraOpacity),
const BoxShadow(offset: const Offset(0.0, 9.0), blurRadius: 46.0, spreadRadius: 8.0, color: _kAmbientShadowOpacity),
], ],
}; };
...@@ -77,7 +77,7 @@ class _SnackBar extends StatelessComponent { ...@@ -77,7 +77,7 @@ class _SnackBar extends StatelessComponent {
minHeight: kSnackBarHeight, minHeight: kSnackBarHeight,
maxHeight: kSnackBarHeight, maxHeight: kSnackBarHeight,
child: new Material( child: new Material(
level: 2, elevation: 6,
color: _kSnackBackground, color: _kSnackBackground,
child: new Container( child: new Container(
margin: const EdgeDims.symmetric(horizontal: _kSideMargins), margin: const EdgeDims.symmetric(horizontal: _kSideMargins),
......
...@@ -134,8 +134,8 @@ class _RenderSwitch extends RenderToggleable { ...@@ -134,8 +134,8 @@ class _RenderSwitch extends RenderToggleable {
// Draw the raised thumb with a shadow // Draw the raised thumb with a shadow
paint.color = thumbColor; paint.color = thumbColor;
ShadowDrawLooperBuilder builder = new ShadowDrawLooperBuilder(); ShadowDrawLooperBuilder builder = new ShadowDrawLooperBuilder();
for (BoxShadow boxShadow in shadows[1]) for (BoxShadow boxShadow in elevationToShadow[1])
builder.addShadow(boxShadow.offset, boxShadow.color, boxShadow.blur); builder.addShadow(boxShadow.offset, boxShadow.color, boxShadow.blurRadius);
paint.drawLooper = builder.build(); paint.drawLooper = builder.build();
// The thumb contracts slightly during the animation // The thumb contracts slightly during the animation
......
...@@ -18,7 +18,7 @@ class ToolBar extends StatelessComponent { ...@@ -18,7 +18,7 @@ class ToolBar extends StatelessComponent {
this.center, this.center,
this.right, this.right,
this.bottom, this.bottom,
this.level: 2, this.elevation: 4,
this.backgroundColor, this.backgroundColor,
this.textTheme, this.textTheme,
this.padding: EdgeDims.zero this.padding: EdgeDims.zero
...@@ -28,7 +28,7 @@ class ToolBar extends StatelessComponent { ...@@ -28,7 +28,7 @@ class ToolBar extends StatelessComponent {
final Widget center; final Widget center;
final List<Widget> right; final List<Widget> right;
final Widget bottom; final Widget bottom;
final int level; final int elevation;
final Color backgroundColor; final Color backgroundColor;
final TextTheme textTheme; final TextTheme textTheme;
final EdgeDims padding; final EdgeDims padding;
...@@ -40,7 +40,7 @@ class ToolBar extends StatelessComponent { ...@@ -40,7 +40,7 @@ class ToolBar extends StatelessComponent {
center: center, center: center,
right: right, right: right,
bottom: bottom, bottom: bottom,
level: level, elevation: elevation,
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
textTheme: textTheme, textTheme: textTheme,
padding: newPadding padding: newPadding
...@@ -95,7 +95,7 @@ class ToolBar extends StatelessComponent { ...@@ -95,7 +95,7 @@ class ToolBar extends StatelessComponent {
padding: new EdgeDims.symmetric(horizontal: 8.0), padding: new EdgeDims.symmetric(horizontal: 8.0),
decoration: new BoxDecoration( decoration: new BoxDecoration(
backgroundColor: color, backgroundColor: color,
boxShadow: level == 0 ? null : shadows[2] boxShadow: elevationToShadow[elevation]
), ),
child: new DefaultTextStyle( child: new DefaultTextStyle(
style: sideStyle, style: sideStyle,
......
...@@ -8,7 +8,6 @@ import 'dart:ui' as ui; ...@@ -8,7 +8,6 @@ import 'dart:ui' as ui;
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'basic_types.dart'; import 'basic_types.dart';
import 'shadows.dart';
/// An immutable set of offsets in each of the four cardinal directions. /// An immutable set of offsets in each of the four cardinal directions.
/// ///
...@@ -253,11 +252,13 @@ class Border { ...@@ -253,11 +252,13 @@ class Border {
/// ///
/// Note: BoxShadow can cast non-rectangular shadows if the box is /// Note: BoxShadow can cast non-rectangular shadows if the box is
/// non-rectangular (e.g., has a border radius or a circular shape). /// non-rectangular (e.g., has a border radius or a circular shape).
/// This class is similar to CSS box-shadow.
class BoxShadow { class BoxShadow {
const BoxShadow({ const BoxShadow({
this.color, this.color,
this.offset, this.offset,
this.blur this.blurRadius,
this.spreadRadius: 0.0
}); });
/// The color of the shadow /// The color of the shadow
...@@ -267,14 +268,20 @@ class BoxShadow { ...@@ -267,14 +268,20 @@ class BoxShadow {
final Offset offset; final Offset offset;
/// The standard deviation of the Gaussian to convolve with the box's shape /// The standard deviation of the Gaussian to convolve with the box's shape
final double blur; final double blurRadius;
/// Returns a new box shadow with its offset and blur scaled by the given factor final double spreadRadius;
// See SkBlurMask::ConvertRadiusToSigma()
double get _blurSigma => blurRadius * 0.57735 + 0.5;
/// Returns a new box shadow with its offset, blurRadius, and spreadRadius scaled by the given factor
BoxShadow scale(double factor) { BoxShadow scale(double factor) {
return new BoxShadow( return new BoxShadow(
color: color, color: color,
offset: offset * factor, offset: offset * factor,
blur: blur * factor blurRadius: blurRadius * factor,
spreadRadius: spreadRadius * factor
); );
} }
...@@ -282,7 +289,7 @@ class BoxShadow { ...@@ -282,7 +289,7 @@ class BoxShadow {
/// ///
/// If either box shadow is null, this function linearly interpolates from a /// If either box shadow is null, this function linearly interpolates from a
/// a box shadow that matches the other box shadow in color but has a zero /// a box shadow that matches the other box shadow in color but has a zero
/// offset and a zero blur. /// offset and a zero blurRadius.
static BoxShadow lerp(BoxShadow a, BoxShadow b, double t) { static BoxShadow lerp(BoxShadow a, BoxShadow b, double t) {
if (a == null && b == null) if (a == null && b == null)
return null; return null;
...@@ -293,7 +300,8 @@ class BoxShadow { ...@@ -293,7 +300,8 @@ class BoxShadow {
return new BoxShadow( return new BoxShadow(
color: Color.lerp(a.color, b.color, t), color: Color.lerp(a.color, b.color, t),
offset: Offset.lerp(a.offset, b.offset, t), offset: Offset.lerp(a.offset, b.offset, t),
blur: ui.lerpDouble(a.blur, b.blur, t) blurRadius: ui.lerpDouble(a.blurRadius, b.blurRadius, t),
spreadRadius: ui.lerpDouble(a.spreadRadius, b.spreadRadius, t)
); );
} }
...@@ -326,18 +334,20 @@ class BoxShadow { ...@@ -326,18 +334,20 @@ class BoxShadow {
final BoxShadow typedOther = other; final BoxShadow typedOther = other;
return color == typedOther.color && return color == typedOther.color &&
offset == typedOther.offset && offset == typedOther.offset &&
blur == typedOther.blur; blurRadius == typedOther.blurRadius &&
spreadRadius == typedOther.spreadRadius;
} }
int get hashCode { int get hashCode {
int value = 373; int value = 373;
value = 37 * value + color.hashCode; value = 37 * value + color.hashCode;
value = 37 * value + offset.hashCode; value = 37 * value + offset.hashCode;
value = 37 * value + blur.hashCode; value = 37 * value + blurRadius.hashCode;
value = 37 * value + spreadRadius.hashCode;
return value; return value;
} }
String toString() => 'BoxShadow($color, $offset, $blur)'; String toString() => 'BoxShadow($color, $offset, $blurRadius, $spreadRadius)';
} }
/// A 2D gradient /// A 2D gradient
...@@ -894,13 +904,6 @@ class BoxPainter { ...@@ -894,13 +904,6 @@ class BoxPainter {
if (_decoration.backgroundColor != null) if (_decoration.backgroundColor != null)
paint.color = _decoration.backgroundColor; paint.color = _decoration.backgroundColor;
if (_decoration.boxShadow != null) {
var builder = new ShadowDrawLooperBuilder();
for (BoxShadow boxShadow in _decoration.boxShadow)
builder.addShadow(boxShadow.offset, boxShadow.color, boxShadow.blur);
paint.drawLooper = builder.build();
}
if (_decoration.gradient != null) if (_decoration.gradient != null)
paint.shader = _decoration.gradient.createShader(); paint.shader = _decoration.gradient.createShader();
...@@ -937,29 +940,42 @@ class BoxPainter { ...@@ -937,29 +940,42 @@ class BoxPainter {
return _decoration.borderRadius > shortestSide ? shortestSide : _decoration.borderRadius; return _decoration.borderRadius > shortestSide ? shortestSide : _decoration.borderRadius;
} }
void _paintBackgroundColor(ui.Canvas canvas, Rect rect) { void _paintBox(ui.Canvas canvas, Rect rect, Paint paint) {
if (_decoration.backgroundColor != null || switch (_decoration.shape) {
_decoration.boxShadow != null || case Shape.circle:
_decoration.gradient != null) { assert(_decoration.borderRadius == null);
switch (_decoration.shape) { Point center = rect.center;
case Shape.circle: double radius = rect.shortestSide / 2.0;
assert(_decoration.borderRadius == null); canvas.drawCircle(center, radius, paint);
Point center = rect.center; break;
double radius = rect.shortestSide / 2.0; case Shape.rectangle:
canvas.drawCircle(center, radius, _backgroundPaint); if (_decoration.borderRadius == null) {
break; canvas.drawRect(rect, paint);
case Shape.rectangle: } else {
if (_decoration.borderRadius == null) { double radius = _getEffectiveBorderRadius(rect);
canvas.drawRect(rect, _backgroundPaint); canvas.drawRRect(new ui.RRect.fromRectXY(rect, radius, radius), paint);
} else { }
double radius = _getEffectiveBorderRadius(rect); break;
canvas.drawRRect(new ui.RRect.fromRectXY(rect, radius, radius), _backgroundPaint);
}
break;
}
} }
} }
void _paintShadows(ui.Canvas canvas, Rect rect) {
if (_decoration.boxShadow == null)
return;
for (BoxShadow boxShadow in _decoration.boxShadow) {
final Paint paint = new Paint()
..color = boxShadow.color
..maskFilter = new ui.MaskFilter.blur(ui.BlurStyle.normal, boxShadow._blurSigma);
final Rect bounds = rect.shift(boxShadow.offset).inflate(boxShadow.spreadRadius);
_paintBox(canvas, bounds, paint);
}
}
void _paintBackgroundColor(ui.Canvas canvas, Rect rect) {
if (_decoration.backgroundColor != null || _decoration.gradient != null)
_paintBox(canvas, rect, _backgroundPaint);
}
void _paintBackgroundImage(ui.Canvas canvas, Rect rect) { void _paintBackgroundImage(ui.Canvas canvas, Rect rect) {
final BackgroundImage backgroundImage = _decoration.backgroundImage; final BackgroundImage backgroundImage = _decoration.backgroundImage;
if (backgroundImage == null) if (backgroundImage == null)
...@@ -1071,6 +1087,7 @@ class BoxPainter { ...@@ -1071,6 +1087,7 @@ class BoxPainter {
/// Paint the box decoration into the given location on the given canvas /// Paint the box decoration into the given location on the given canvas
void paint(ui.Canvas canvas, Rect rect) { void paint(ui.Canvas canvas, Rect rect) {
_paintShadows(canvas, rect);
_paintBackgroundColor(canvas, rect); _paintBackgroundColor(canvas, rect);
_paintBackgroundImage(canvas, rect); _paintBackgroundImage(canvas, rect);
_paintBorder(canvas, rect); _paintBorder(canvas, rect);
......
...@@ -12,7 +12,7 @@ void main() { ...@@ -12,7 +12,7 @@ void main() {
gradient: new RadialGradient( gradient: new RadialGradient(
center: Point.origin, radius: 500.0, center: Point.origin, radius: 500.0,
colors: <Color>[Colors.yellow[500], Colors.blue[500]]), colors: <Color>[Colors.yellow[500], Colors.blue[500]]),
boxShadow: shadows[3]) boxShadow: elevationToShadow[3])
); );
layout(root); layout(root);
expect(root.size.width, equals(800.0)); expect(root.size.width, equals(800.0));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment