blob: 68a85f7348ef2607ede1072ef2ee0321651059cf [file] [log] [blame] [edit]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'object.dart';
import 'proxy_box.dart';
import 'proxy_sliver.dart';
import 'sliver.dart';
/// Paints a [Decoration] either before or after its child paints.
///
/// If the child has infinite scroll extent, then the [Decoration] paints itself up to the
/// bottom cache extent.
class RenderDecoratedSliver extends RenderProxySliver {
/// Creates a decorated sliver.
///
/// The [decoration], [position], and [configuration] arguments must not be
/// null. By default the decoration paints behind the child.
///
/// The [ImageConfiguration] will be passed to the decoration (with the size
/// filled in) to let it resolve images.
RenderDecoratedSliver({
required Decoration decoration,
DecorationPosition position = DecorationPosition.background,
ImageConfiguration configuration = ImageConfiguration.empty,
}) : _decoration = decoration,
_position = position,
_configuration = configuration;
/// What decoration to paint.
///
/// Commonly a [BoxDecoration].
Decoration get decoration => _decoration;
Decoration _decoration;
set decoration(Decoration value) {
if (value == decoration) {
return;
}
_decoration = value;
_painter?.dispose();
_painter = decoration.createBoxPainter(markNeedsPaint);
markNeedsPaint();
}
/// Whether to paint the box decoration behind or in front of the child.
DecorationPosition get position => _position;
DecorationPosition _position;
set position(DecorationPosition value) {
if (value == position) {
return;
}
_position = value;
markNeedsPaint();
}
/// The settings to pass to the decoration when painting, so that it can
/// resolve images appropriately. See [ImageProvider.resolve] and
/// [BoxPainter.paint].
///
/// The [ImageConfiguration.textDirection] field is also used by
/// direction-sensitive [Decoration]s for painting and hit-testing.
ImageConfiguration get configuration => _configuration;
ImageConfiguration _configuration;
set configuration(ImageConfiguration value) {
if (value == configuration) {
return;
}
_configuration = value;
markNeedsPaint();
}
BoxPainter? _painter;
@override
void attach(covariant PipelineOwner owner) {
_painter = decoration.createBoxPainter(markNeedsPaint);
super.attach(owner);
}
@override
void detach() {
_painter?.dispose();
_painter = null;
super.detach();
}
@override
void dispose() {
_painter?.dispose();
_painter = null;
super.dispose();
}
@override
void paint(PaintingContext context, Offset offset) {
if (child != null && child!.geometry!.visible) {
final SliverPhysicalParentData childParentData = child!.parentData! as SliverPhysicalParentData;
final Size childSize;
final Offset scrollOffset;
// In the case where the child sliver has infinite scroll extent, the decoration
// should only extend down to the bottom cache extent.
final double cappedMainAxisExtent = child!.geometry!.scrollExtent.isInfinite
? constraints.scrollOffset + child!.geometry!.cacheExtent + constraints.cacheOrigin
: child!.geometry!.scrollExtent;
switch (constraints.axis) {
case Axis.vertical:
childSize = Size(constraints.crossAxisExtent, cappedMainAxisExtent);
scrollOffset = Offset(0.0, -constraints.scrollOffset);
case Axis.horizontal:
childSize = Size(cappedMainAxisExtent, constraints.crossAxisExtent);
scrollOffset = Offset(-constraints.scrollOffset, 0.0);
}
final Offset childOffset = offset + childParentData.paintOffset;
if (position == DecorationPosition.background) {
_painter!.paint(context.canvas, childOffset + scrollOffset, configuration.copyWith(size: childSize));
}
context.paintChild(child!, childOffset);
if (position == DecorationPosition.foreground) {
_painter!.paint(context.canvas, childOffset + scrollOffset, configuration.copyWith(size: childSize));
}
}
}
}