| // 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)); |
| } |
| } |
| } |
| } |