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 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
import 'constants.dart';
|
import 'constants.dart';
|
||||||
import 'shadows.dart';
|
|
||||||
import 'theme.dart';
|
import 'theme.dart';
|
||||||
|
|
||||||
/// Signature for the callback used by ink effects to obtain the rectangle for the effect.
|
/// 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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Color backgroundColor = _getBackgroundColor(context);
|
Color backgroundColor = _getBackgroundColor(context);
|
||||||
|
assert(backgroundColor != null || config.type == MaterialType.transparency);
|
||||||
Widget contents = config.child;
|
Widget contents = config.child;
|
||||||
BorderRadius radius = config.borderRadius ?? kMaterialEdges[config.type];
|
BorderRadius radius = config.borderRadius ?? kMaterialEdges[config.type];
|
||||||
if (contents != null) {
|
if (contents != null) {
|
||||||
@ -228,20 +228,36 @@ class _MaterialState extends State<Material> with TickerProviderStateMixin {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
if (config.type == MaterialType.circle) {
|
if (config.type == MaterialType.circle) {
|
||||||
contents = new ClipOval(child: contents);
|
contents = new PhysicalModel(
|
||||||
} else if (kMaterialEdges[config.type] != null) {
|
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(
|
contents = new ClipRRect(
|
||||||
borderRadius: radius,
|
borderRadius: radius,
|
||||||
child: contents
|
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) {
|
if (config.type != MaterialType.transparency) {
|
||||||
contents = new AnimatedContainer(
|
contents = new AnimatedContainer(
|
||||||
curve: Curves.fastOutSlowIn,
|
curve: Curves.fastOutSlowIn,
|
||||||
duration: kThemeChangeDuration,
|
duration: kThemeChangeDuration,
|
||||||
decoration: new BoxDecoration(
|
decoration: new BoxDecoration(
|
||||||
borderRadius: radius,
|
borderRadius: radius,
|
||||||
boxShadow: config.elevation == 0 ? null : kElevationToShadow[config.elevation],
|
|
||||||
shape: config.type == MaterialType.circle ? BoxShape.circle : BoxShape.rectangle
|
shape: config.type == MaterialType.circle ? BoxShape.circle : BoxShape.rectangle
|
||||||
),
|
),
|
||||||
child: new Container(
|
child: new Container(
|
||||||
|
@ -521,3 +521,45 @@ class BackdropFilterLayer extends ContainerLayer {
|
|||||||
builder.pop();
|
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);
|
painter(childContext, offset);
|
||||||
childContext._stopRecordingIfNeeded();
|
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.
|
/// 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.
|
/// Where to paint a box decoration.
|
||||||
enum DecorationPosition {
|
enum DecorationPosition {
|
||||||
/// Paint the box decoration behind the children.
|
/// 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
|
// POSITIONING AND SIZING NODES
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user