1 | // Copyright 2014 The Flutter Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | import 'object.dart'; |
6 | import 'proxy_box.dart'; |
7 | import 'proxy_sliver.dart'; |
8 | import 'sliver.dart'; |
9 | |
10 | /// Paints a [Decoration] either before or after its child paints. |
11 | /// |
12 | /// If the child has infinite scroll extent, then the [Decoration] paints itself up to the |
13 | /// bottom cache extent. |
14 | class RenderDecoratedSliver extends RenderProxySliver { |
15 | /// Creates a decorated sliver. |
16 | /// |
17 | /// The [decoration], [position], and [configuration] arguments must not be |
18 | /// null. By default the decoration paints behind the child. |
19 | /// |
20 | /// The [ImageConfiguration] will be passed to the decoration (with the size |
21 | /// filled in) to let it resolve images. |
22 | RenderDecoratedSliver({ |
23 | required Decoration decoration, |
24 | DecorationPosition position = DecorationPosition.background, |
25 | ImageConfiguration configuration = ImageConfiguration.empty, |
26 | }) : _decoration = decoration, |
27 | _position = position, |
28 | _configuration = configuration; |
29 | |
30 | /// What decoration to paint. |
31 | /// |
32 | /// Commonly a [BoxDecoration]. |
33 | Decoration get decoration => _decoration; |
34 | Decoration _decoration; |
35 | set decoration(Decoration value) { |
36 | if (value == decoration) { |
37 | return; |
38 | } |
39 | _decoration = value; |
40 | _painter?.dispose(); |
41 | _painter = decoration.createBoxPainter(markNeedsPaint); |
42 | markNeedsPaint(); |
43 | } |
44 | |
45 | /// Whether to paint the box decoration behind or in front of the child. |
46 | DecorationPosition get position => _position; |
47 | DecorationPosition _position; |
48 | set position(DecorationPosition value) { |
49 | if (value == position) { |
50 | return; |
51 | } |
52 | _position = value; |
53 | markNeedsPaint(); |
54 | } |
55 | |
56 | /// The settings to pass to the decoration when painting, so that it can |
57 | /// resolve images appropriately. See [ImageProvider.resolve] and |
58 | /// [BoxPainter.paint]. |
59 | /// |
60 | /// The [ImageConfiguration.textDirection] field is also used by |
61 | /// direction-sensitive [Decoration]s for painting and hit-testing. |
62 | ImageConfiguration get configuration => _configuration; |
63 | ImageConfiguration _configuration; |
64 | set configuration(ImageConfiguration value) { |
65 | if (value == configuration) { |
66 | return; |
67 | } |
68 | _configuration = value; |
69 | markNeedsPaint(); |
70 | } |
71 | |
72 | BoxPainter? _painter; |
73 | |
74 | @override |
75 | void attach(covariant PipelineOwner owner) { |
76 | _painter = decoration.createBoxPainter(markNeedsPaint); |
77 | super.attach(owner); |
78 | } |
79 | |
80 | @override |
81 | void detach() { |
82 | _painter?.dispose(); |
83 | _painter = null; |
84 | super.detach(); |
85 | } |
86 | |
87 | @override |
88 | void dispose() { |
89 | _painter?.dispose(); |
90 | _painter = null; |
91 | super.dispose(); |
92 | } |
93 | |
94 | @override |
95 | void paint(PaintingContext context, Offset offset) { |
96 | if (child != null && child!.geometry!.visible) { |
97 | final SliverPhysicalParentData childParentData = child!.parentData! as SliverPhysicalParentData; |
98 | final Size childSize; |
99 | final Offset scrollOffset; |
100 | |
101 | // In the case where the child sliver has infinite scroll extent, the decoration |
102 | // should only extend down to the bottom cache extent. |
103 | final double cappedMainAxisExtent = child!.geometry!.scrollExtent.isInfinite |
104 | ? constraints.scrollOffset + child!.geometry!.cacheExtent + constraints.cacheOrigin |
105 | : child!.geometry!.scrollExtent; |
106 | switch (constraints.axis) { |
107 | case Axis.vertical: |
108 | childSize = Size(constraints.crossAxisExtent, cappedMainAxisExtent); |
109 | scrollOffset = Offset(0.0, -constraints.scrollOffset); |
110 | case Axis.horizontal: |
111 | childSize = Size(cappedMainAxisExtent, constraints.crossAxisExtent); |
112 | scrollOffset = Offset(-constraints.scrollOffset, 0.0); |
113 | } |
114 | final Offset childOffset = offset + childParentData.paintOffset; |
115 | if (position == DecorationPosition.background) { |
116 | _painter!.paint(context.canvas, childOffset + scrollOffset, configuration.copyWith(size: childSize)); |
117 | } |
118 | context.paintChild(child!, childOffset); |
119 | if (position == DecorationPosition.foreground) { |
120 | _painter!.paint(context.canvas, childOffset + scrollOffset, configuration.copyWith(size: childSize)); |
121 | } |
122 | } |
123 | } |
124 | } |
125 | |