1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
// 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 'dart:ui' as ui show Color;
import 'package:flutter/animation.dart';
import 'package:flutter/foundation.dart';
import 'package:vector_math/vector_math_64.dart';
import 'layer.dart';
import 'object.dart';
import 'proxy_box.dart';
import 'sliver.dart';
/// A base class for sliver render objects that resemble their children.
///
/// A proxy sliver has a single child and simply mimics all the properties of
/// that child by calling through to the child for each function in the render
/// sliver protocol. For example, a proxy sliver determines its geometry by
/// asking its sliver child to layout with the same constraints and then
/// matching the geometry.
///
/// A proxy sliver isn't useful on its own because you might as well just
/// replace the proxy sliver with its child. However, RenderProxySliver is a
/// useful base class for render objects that wish to mimic most, but not all,
/// of the properties of their sliver child.
///
/// See also:
///
/// * [RenderProxyBox], a base class for render boxes that resemble their
/// children.
abstract class RenderProxySliver extends RenderSliver with RenderObjectWithChildMixin<RenderSliver> {
/// Creates a proxy render sliver.
///
/// Proxy render slivers aren't created directly because they simply proxy
/// the render sliver protocol to their sliver [child]. Instead, use one of
/// the subclasses.
RenderProxySliver([RenderSliver? child]) {
this.child = child;
}
@override
void setupParentData(RenderObject child) {
if (child.parentData is! SliverPhysicalParentData)
child.parentData = SliverPhysicalParentData();
}
@override
void performLayout() {
assert(child != null);
child!.layout(constraints, parentUsesSize: true);
geometry = child!.geometry;
}
@override
void paint(PaintingContext context, Offset offset) {
if (child != null)
context.paintChild(child!, offset);
}
@override
bool hitTestChildren(SliverHitTestResult result, {required double mainAxisPosition, required double crossAxisPosition}) {
return child != null
&& child!.geometry!.hitTestExtent > 0
&& child!.hitTest(
result,
mainAxisPosition: mainAxisPosition,
crossAxisPosition: crossAxisPosition,
);
}
@override
double childMainAxisPosition(RenderSliver child) {
assert(child != null);
assert(child == this.child);
return 0.0;
}
@override
void applyPaintTransform(RenderObject child, Matrix4 transform) {
assert(child != null);
final SliverPhysicalParentData childParentData = child.parentData! as SliverPhysicalParentData;
childParentData.applyPaintTransform(transform);
}
}
/// Makes its sliver child partially transparent.
///
/// This class paints its sliver child into an intermediate buffer and then
/// blends the sliver child back into the scene, partially transparent.
///
/// For values of opacity other than 0.0 and 1.0, this class is relatively
/// expensive, because it requires painting the sliver child into an intermediate
/// buffer. For the value 0.0, the sliver child is simply not painted at all.
/// For the value 1.0, the sliver child is painted immediately without an
/// intermediate buffer.
class RenderSliverOpacity extends RenderProxySliver {
/// Creates a partially transparent render object.
///
/// The [opacity] argument must be between 0.0 and 1.0, inclusive.
RenderSliverOpacity({
double opacity = 1.0,
bool alwaysIncludeSemantics = false,
RenderSliver? sliver,
}) : assert(opacity != null && opacity >= 0.0 && opacity <= 1.0),
assert(alwaysIncludeSemantics != null),
_opacity = opacity,
_alwaysIncludeSemantics = alwaysIncludeSemantics,
_alpha = ui.Color.getAlphaFromOpacity(opacity) {
child = sliver;
}
@override
bool get alwaysNeedsCompositing => child != null && (_alpha > 0);
int _alpha;
/// The fraction to scale the child's alpha value.
///
/// An opacity of 1.0 is fully opaque. An opacity of 0.0 is fully transparent
/// (i.e. invisible).
///
/// The opacity must not be null.
///
/// Values 1.0 and 0.0 are painted with a fast path. Other values
/// require painting the child into an intermediate buffer, which is
/// expensive.
double get opacity => _opacity;
double _opacity;
set opacity(double value) {
assert(value != null);
assert(value >= 0.0 && value <= 1.0);
if (_opacity == value)
return;
final bool didNeedCompositing = alwaysNeedsCompositing;
final bool wasVisible = _alpha != 0;
_opacity = value;
_alpha = ui.Color.getAlphaFromOpacity(_opacity);
if (didNeedCompositing != alwaysNeedsCompositing)
markNeedsCompositingBitsUpdate();
markNeedsPaint();
if (wasVisible != (_alpha != 0) && !alwaysIncludeSemantics)
markNeedsSemanticsUpdate();
}
/// Whether child semantics are included regardless of the opacity.
///
/// If false, semantics are excluded when [opacity] is 0.0.
///
/// Defaults to false.
bool get alwaysIncludeSemantics => _alwaysIncludeSemantics;
bool _alwaysIncludeSemantics;
set alwaysIncludeSemantics(bool value) {
if (value == _alwaysIncludeSemantics)
return;
_alwaysIncludeSemantics = value;
markNeedsSemanticsUpdate();
}
@override
void paint(PaintingContext context, Offset offset) {
if (child != null && child!.geometry!.visible) {
if (_alpha == 0) {
// No need to keep the layer. We'll create a new one if necessary.
layer = null;
return;
}
assert(needsCompositing);
layer = context.pushOpacity(
offset,
_alpha,
super.paint,
oldLayer: layer as OpacityLayer?,
);
}
}
@override
void visitChildrenForSemantics(RenderObjectVisitor visitor) {
if (child != null && (_alpha != 0 || alwaysIncludeSemantics))
visitor(child!);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DoubleProperty('opacity', opacity));
properties.add(FlagProperty('alwaysIncludeSemantics', value: alwaysIncludeSemantics, ifTrue: 'alwaysIncludeSemantics'));
}
}
/// A render object that is invisible during hit testing.
///
/// When [ignoring] is true, this render object (and its subtree) is invisible
/// to hit testing. It still consumes space during layout and paints its sliver
/// child as usual. It just cannot be the target of located events, because its
/// render object returns false from [hitTest].
///
/// When [ignoringSemantics] is true, the subtree will be invisible to the
/// semantics layer (and thus e.g. accessibility tools). If [ignoringSemantics]
/// is null, it uses the value of [ignoring].
class RenderSliverIgnorePointer extends RenderProxySliver {
/// Creates a render object that is invisible to hit testing.
///
/// The [ignoring] argument must not be null. If [ignoringSemantics] is null,
/// this render object will be ignored for semantics if [ignoring] is true.
RenderSliverIgnorePointer({
RenderSliver? sliver,
bool ignoring = true,
bool? ignoringSemantics,
}) : assert(ignoring != null),
_ignoring = ignoring,
_ignoringSemantics = ignoringSemantics {
child = sliver;
}
/// Whether this render object is ignored during hit testing.
///
/// Regardless of whether this render object is ignored during hit testing, it
/// will still consume space during layout and be visible during painting.
bool get ignoring => _ignoring;
bool _ignoring;
set ignoring(bool value) {
assert(value != null);
if (value == _ignoring)
return;
_ignoring = value;
if (_ignoringSemantics == null || !_ignoringSemantics!)
markNeedsSemanticsUpdate();
}
/// Whether the semantics of this render object is ignored when compiling the
/// semantics tree.
///
/// If null, defaults to value of [ignoring].
///
/// See [SemanticsNode] for additional information about the semantics tree.
bool? get ignoringSemantics => _ignoringSemantics;
bool? _ignoringSemantics;
set ignoringSemantics(bool? value) {
if (value == _ignoringSemantics)
return;
final bool oldEffectiveValue = _effectiveIgnoringSemantics;
_ignoringSemantics = value;
if (oldEffectiveValue != _effectiveIgnoringSemantics)
markNeedsSemanticsUpdate();
}
bool get _effectiveIgnoringSemantics => ignoringSemantics ?? ignoring;
@override
bool hitTest(SliverHitTestResult result, {required double mainAxisPosition, required double crossAxisPosition}) {
return !ignoring
&& super.hitTest(
result,
mainAxisPosition: mainAxisPosition,
crossAxisPosition: crossAxisPosition,
);
}
@override
void visitChildrenForSemantics(RenderObjectVisitor visitor) {
if (child != null && !_effectiveIgnoringSemantics)
visitor(child!);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<bool>('ignoring', ignoring));
properties.add(DiagnosticsProperty<bool>('ignoringSemantics', _effectiveIgnoringSemantics, description: ignoringSemantics == null ? 'implicitly $_effectiveIgnoringSemantics' : null));
}
}
/// Lays the sliver child out as if it was in the tree, but without painting
/// anything, without making the sliver child available for hit testing, and
/// without taking any room in the parent.
class RenderSliverOffstage extends RenderProxySliver {
/// Creates an offstage render object.
RenderSliverOffstage({
bool offstage = true,
RenderSliver? sliver,
}) : assert(offstage != null),
_offstage = offstage {
child = sliver;
}
/// Whether the sliver child is hidden from the rest of the tree.
///
/// If true, the sliver child is laid out as if it was in the tree, but
/// without painting anything, without making the sliver child available for
/// hit testing, and without taking any room in the parent.
///
/// If false, the sliver child is included in the tree as normal.
bool get offstage => _offstage;
bool _offstage;
set offstage(bool value) {
assert(value != null);
if (value == _offstage)
return;
_offstage = value;
markNeedsLayoutForSizedByParentChange();
}
@override
void performLayout() {
assert(child != null);
child!.layout(constraints, parentUsesSize: true);
if (!offstage)
geometry = child!.geometry;
else
geometry = SliverGeometry.zero;
}
@override
bool hitTest(SliverHitTestResult result, {required double mainAxisPosition, required double crossAxisPosition}) {
return !offstage && super.hitTest(
result,
mainAxisPosition: mainAxisPosition,
crossAxisPosition: crossAxisPosition,
);
}
@override
bool hitTestChildren(SliverHitTestResult result, {required double mainAxisPosition, required double crossAxisPosition}) {
return !offstage
&& child != null
&& child!.geometry!.hitTestExtent > 0
&& child!.hitTest(
result,
mainAxisPosition: mainAxisPosition,
crossAxisPosition: crossAxisPosition,
);
}
@override
void paint(PaintingContext context, Offset offset) {
if (offstage)
return;
context.paintChild(child!, offset);
}
@override
void visitChildrenForSemantics(RenderObjectVisitor visitor) {
if (offstage)
return;
super.visitChildrenForSemantics(visitor);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<bool>('offstage', offstage));
}
@override
List<DiagnosticsNode> debugDescribeChildren() {
if (child == null)
return <DiagnosticsNode>[];
return <DiagnosticsNode>[
child!.toDiagnosticsNode(
name: 'child',
style: offstage ? DiagnosticsTreeStyle.offstage : DiagnosticsTreeStyle.sparse,
),
];
}
}
/// Makes its sliver child partially transparent, driven from an [Animation].
///
/// This is a variant of [RenderSliverOpacity] that uses an [Animation<double>]
/// rather than a [double] to control the opacity.
class RenderSliverAnimatedOpacity extends RenderProxySliver with RenderAnimatedOpacityMixin<RenderSliver> {
/// Creates a partially transparent render object.
///
/// The [opacity] argument must not be null.
RenderSliverAnimatedOpacity({
required Animation<double> opacity,
bool alwaysIncludeSemantics = false,
RenderSliver? sliver,
}) : assert(opacity != null),
assert(alwaysIncludeSemantics != null) {
this.opacity = opacity;
this.alwaysIncludeSemantics = alwaysIncludeSemantics;
child = sliver;
}
}