Render the Material widget with the physical model layer (#8471)
This commit is contained in:
parent
9a83659d78
commit
c7695b4ad4
@ -7,7 +7,6 @@ import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'constants.dart';
|
||||
import 'shadows.dart';
|
||||
import 'theme.dart';
|
||||
|
||||
/// Signature for the callback used by ink effects to obtain the rectangle for the effect.
|
||||
@ -205,6 +204,7 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color backgroundColor = _getBackgroundColor(context);
|
||||
assert(backgroundColor != null || config.type == MaterialType.transparency);
|
||||
Widget contents = config.child;
|
||||
BorderRadius radius = config.borderRadius ?? kMaterialEdges[config.type];
|
||||
if (contents != null) {
|
||||
@ -228,11 +228,28 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin {
|
||||
)
|
||||
);
|
||||
if (config.type == MaterialType.circle) {
|
||||
contents = new ClipOval(child: contents);
|
||||
} else if (kMaterialEdges[config.type] != null) {
|
||||
contents = new ClipRRect(
|
||||
borderRadius: radius,
|
||||
child: contents
|
||||
contents = new PhysicalModel(
|
||||
shape: BoxShape.circle,
|
||||
elevation: config.elevation,
|
||||
color: backgroundColor,
|
||||
child: contents,
|
||||
);
|
||||
} else if (config.type == MaterialType.transparency) {
|
||||
if (radius == null) {
|
||||
contents = new ClipRect(child: contents);
|
||||
} else {
|
||||
contents = new ClipRRect(
|
||||
borderRadius: radius,
|
||||
child: contents
|
||||
);
|
||||
}
|
||||
} else {
|
||||
contents = new PhysicalModel(
|
||||
shape: BoxShape.rectangle,
|
||||
borderRadius: radius ?? BorderRadius.zero,
|
||||
elevation: config.elevation,
|
||||
color: backgroundColor,
|
||||
child: contents,
|
||||
);
|
||||
}
|
||||
if (config.type != MaterialType.transparency) {
|
||||
@ -241,7 +258,6 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin {
|
||||
duration: kThemeChangeDuration,
|
||||
decoration: new BoxDecoration(
|
||||
borderRadius: radius,
|
||||
boxShadow: config.elevation == 0 ? null : kElevationToShadow[config.elevation],
|
||||
shape: config.type == MaterialType.circle ? BoxShape.circle : BoxShape.rectangle
|
||||
),
|
||||
child: new Container(
|
||||
|
@ -521,3 +521,45 @@ class BackdropFilterLayer extends ContainerLayer {
|
||||
builder.pop();
|
||||
}
|
||||
}
|
||||
|
||||
class PhysicalModelLayer extends ContainerLayer {
|
||||
/// Creates a layer with a rounded-rectangular clip.
|
||||
///
|
||||
/// The [clipRRect] property must be non-null before the compositing phase of
|
||||
/// the pipeline.
|
||||
PhysicalModelLayer({
|
||||
@required this.clipRRect,
|
||||
@required this.elevation,
|
||||
@required this.color,
|
||||
}) {
|
||||
assert(clipRRect != null);
|
||||
assert(elevation != null);
|
||||
assert(color != null);
|
||||
}
|
||||
|
||||
/// The rounded-rect to clip in the parent's coordinate system
|
||||
RRect clipRRect;
|
||||
|
||||
/// The z-coordinate at which to place this physical object.
|
||||
int elevation;
|
||||
|
||||
/// The background color.
|
||||
Color color;
|
||||
|
||||
@override
|
||||
void addToScene(ui.SceneBuilder builder, Offset layerOffset) {
|
||||
builder.pushPhysicalModel(
|
||||
rrect: clipRRect.shift(layerOffset),
|
||||
elevation: elevation,
|
||||
color: color,
|
||||
);
|
||||
addChildrenToScene(builder, layerOffset);
|
||||
builder.pop();
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillDescription(List<String> description) {
|
||||
super.debugFillDescription(description);
|
||||
description.add('clipRRect: $clipRRect');
|
||||
}
|
||||
}
|
||||
|
@ -429,6 +429,33 @@ class PaintingContext {
|
||||
painter(childContext, offset);
|
||||
childContext._stopRecordingIfNeeded();
|
||||
}
|
||||
|
||||
/// Clip using a physical model layer.
|
||||
///
|
||||
/// * `offset` is the offset from the origin of the canvas' coordinate system
|
||||
/// to the origin of the caller's coordinate system.
|
||||
/// * `bounds` is the region of the canvas (in the caller's coodinate system)
|
||||
/// into which `painter` will paint in.
|
||||
/// * `clipRRect` is the rounded-rectangle (in the caller's coodinate system)
|
||||
/// to use to clip the painting done by `painter`.
|
||||
/// * `elevation` is the z-coordinate at which to place this material.
|
||||
/// * `color` is the background color.
|
||||
/// * `painter` is a callback that will paint with the `clipRRect` applied. This
|
||||
/// function calls the `painter` synchronously.
|
||||
void pushPhysicalModel(Offset offset, Rect bounds, RRect clipRRect, int elevation, Color color, PaintingContextCallback painter) {
|
||||
final Rect offsetBounds = bounds.shift(offset);
|
||||
final RRect offsetClipRRect = clipRRect.shift(offset);
|
||||
_stopRecordingIfNeeded();
|
||||
final PhysicalModelLayer physicalModel = new PhysicalModelLayer(
|
||||
clipRRect: offsetClipRRect,
|
||||
elevation: elevation,
|
||||
color: color,
|
||||
);
|
||||
_appendLayer(physicalModel);
|
||||
final PaintingContext childContext = new PaintingContext._(physicalModel, offsetBounds);
|
||||
painter(childContext, offset);
|
||||
childContext._stopRecordingIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
/// An abstract set of layout constraints.
|
||||
|
@ -1181,6 +1181,108 @@ class RenderClipPath extends _RenderCustomClip<Path> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a physical model layer that clips its children to a rounded
|
||||
/// rectangle.
|
||||
class RenderPhysicalModel extends _RenderCustomClip<RRect> {
|
||||
/// Creates a rounded-rectangular clip.
|
||||
///
|
||||
/// The [borderRadius] defaults to [BorderRadius.zero], i.e. a rectangle with
|
||||
/// right-angled corners.
|
||||
RenderPhysicalModel({
|
||||
RenderBox child,
|
||||
BoxShape shape,
|
||||
BorderRadius borderRadius: BorderRadius.zero,
|
||||
int elevation,
|
||||
Color color,
|
||||
}) : _shape = shape,
|
||||
_borderRadius = borderRadius,
|
||||
_elevation = elevation,
|
||||
_color = color,
|
||||
super(child: child) {
|
||||
if (shape == BoxShape.rectangle)
|
||||
assert(_borderRadius != null);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get alwaysNeedsCompositing => true;
|
||||
|
||||
/// The shape of the layer.
|
||||
BoxShape get shape => _shape;
|
||||
BoxShape _shape;
|
||||
set shape (BoxShape value) {
|
||||
assert(value != null);
|
||||
if (_shape == value)
|
||||
return;
|
||||
_shape = value;
|
||||
_markNeedsClip();
|
||||
}
|
||||
|
||||
/// The border radius of the rounded corners.
|
||||
///
|
||||
/// Values are clamped so that horizontal and vertical radii sums do not
|
||||
/// exceed width/height.
|
||||
BorderRadius get borderRadius => _borderRadius;
|
||||
BorderRadius _borderRadius;
|
||||
set borderRadius (BorderRadius value) {
|
||||
assert(value != null);
|
||||
if (_borderRadius == value)
|
||||
return;
|
||||
_borderRadius = value;
|
||||
_markNeedsClip();
|
||||
}
|
||||
|
||||
/// The z-coordinate at which to place this material.
|
||||
int get elevation => _elevation;
|
||||
int _elevation;
|
||||
set elevation (int value) {
|
||||
assert(value != null);
|
||||
if (_elevation == value)
|
||||
return;
|
||||
_elevation = value;
|
||||
markNeedsPaint();
|
||||
}
|
||||
|
||||
/// The background color.
|
||||
Color get color => _color;
|
||||
Color _color;
|
||||
set color (Color value) {
|
||||
assert(value != null);
|
||||
if (_color == value)
|
||||
return;
|
||||
_color = value;
|
||||
markNeedsPaint();
|
||||
}
|
||||
|
||||
@override
|
||||
RRect get _defaultClip {
|
||||
if (_shape == BoxShape.rectangle) {
|
||||
return _borderRadius.toRRect(Point.origin & size);
|
||||
} else {
|
||||
Rect rect = Point.origin & size;
|
||||
return new RRect.fromRectXY(rect, rect.width / 2, rect.height / 2);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool hitTest(HitTestResult result, { Point position }) {
|
||||
if (_clipper != null) {
|
||||
_updateClip();
|
||||
assert(_clip != null);
|
||||
if (!_clip.contains(position))
|
||||
return false;
|
||||
}
|
||||
return super.hitTest(result, position: position);
|
||||
}
|
||||
|
||||
@override
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
if (child != null) {
|
||||
_updateClip();
|
||||
context.pushPhysicalModel(offset, _clip.outerRect, _clip, _elevation, _color, super.paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Where to paint a box decoration.
|
||||
enum DecorationPosition {
|
||||
/// Paint the box decoration behind the children.
|
||||
|
@ -398,6 +398,51 @@ class ClipPath extends SingleChildRenderObjectWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// A widget representing a physical layer that clips its children to a shape.
|
||||
class PhysicalModel extends SingleChildRenderObjectWidget {
|
||||
/// Creates a physical model with a rounded-rectangular clip.
|
||||
PhysicalModel({
|
||||
Key key,
|
||||
@required this.shape,
|
||||
this.borderRadius: BorderRadius.zero,
|
||||
@required this.elevation,
|
||||
@required this.color,
|
||||
Widget child,
|
||||
}) : super(key: key, child: child) {
|
||||
if (shape == BoxShape.rectangle)
|
||||
assert(borderRadius != null);
|
||||
assert(shape != null);
|
||||
assert(elevation != null);
|
||||
assert(color != null);
|
||||
}
|
||||
|
||||
/// The type of shape.
|
||||
final BoxShape shape;
|
||||
|
||||
/// The border radius of the rounded corners.
|
||||
///
|
||||
/// Values are clamped so that horizontal and vertical radii sums do not
|
||||
/// exceed width/height.
|
||||
final BorderRadius borderRadius;
|
||||
|
||||
/// The z-coordinate at which to place this physical object.
|
||||
final int elevation;
|
||||
|
||||
/// The background color.
|
||||
final Color color;
|
||||
|
||||
@override
|
||||
RenderPhysicalModel createRenderObject(BuildContext context) => new RenderPhysicalModel(shape: shape, borderRadius: borderRadius, elevation: elevation, color: color);
|
||||
|
||||
@override
|
||||
void updateRenderObject(BuildContext context, RenderPhysicalModel renderObject) {
|
||||
renderObject
|
||||
..shape = shape
|
||||
..borderRadius = borderRadius
|
||||
..elevation = elevation
|
||||
..color = color;
|
||||
}
|
||||
}
|
||||
|
||||
// POSITIONING AND SIZING NODES
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user