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

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com