|
2 | 2 | // Use of this source code is governed by a BSD-style license that can be
|
3 | 3 | // found in the LICENSE file.
|
4 | 4 |
|
5 |
| -import 'package:flutter/foundation.dart'; |
| 5 | +import 'package:flutter/rendering.dart'; |
6 | 6 |
|
7 | 7 | import 'basic.dart';
|
8 | 8 | import 'framework.dart';
|
@@ -168,7 +168,7 @@ class Visibility extends StatelessWidget {
|
168 | 168 | /// [child] subtree is not trivial then it is significantly cheaper to not
|
169 | 169 | /// even keep the state (see [maintainState]).
|
170 | 170 | ///
|
171 |
| - /// If this property is true, [Opacity] is used instead of [Offstage]. |
| 171 | + /// If this property is false, [Offstage] is used. |
172 | 172 | ///
|
173 | 173 | /// If this property is false, then [maintainSemantics] and
|
174 | 174 | /// [maintainInteractivity] must also be false.
|
@@ -222,9 +222,9 @@ class Visibility extends StatelessWidget {
|
222 | 222 | child: child,
|
223 | 223 | );
|
224 | 224 | }
|
225 |
| - return Opacity( |
226 |
| - opacity: visible ? 1.0 : 0.0, |
227 |
| - alwaysIncludeSemantics: maintainSemantics, |
| 225 | + return _Visibility( |
| 226 | + visible: visible, |
| 227 | + maintainSemantics: maintainSemantics, |
228 | 228 | child: result,
|
229 | 229 | );
|
230 | 230 | }
|
@@ -410,8 +410,7 @@ class SliverVisibility extends StatelessWidget {
|
410 | 410 | /// [sliver] subtree is not trivial then it is significantly cheaper to not
|
411 | 411 | /// even keep the state (see [maintainState]).
|
412 | 412 | ///
|
413 |
| - /// If this property is true, [SliverOpacity] is used instead of |
414 |
| - /// [SliverOffstage]. |
| 413 | + /// If this property is false, [SliverOffstage] is used. |
415 | 414 | ///
|
416 | 415 | /// If this property is false, then [maintainSemantics] and
|
417 | 416 | /// [maintainInteractivity] must also be false.
|
@@ -460,9 +459,9 @@ class SliverVisibility extends StatelessWidget {
|
460 | 459 | ignoringSemantics: !visible && !maintainSemantics,
|
461 | 460 | );
|
462 | 461 | }
|
463 |
| - return SliverOpacity( |
464 |
| - opacity: visible ? 1.0 : 0.0, |
465 |
| - alwaysIncludeSemantics: maintainSemantics, |
| 462 | + return _SliverVisibility( |
| 463 | + visible: visible, |
| 464 | + maintainSemantics: maintainSemantics, |
466 | 465 | sliver: result,
|
467 | 466 | );
|
468 | 467 | }
|
@@ -495,3 +494,132 @@ class SliverVisibility extends StatelessWidget {
|
495 | 494 | properties.add(FlagProperty('maintainInteractivity', value: maintainInteractivity, ifFalse: 'maintainInteractivity'));
|
496 | 495 | }
|
497 | 496 | }
|
| 497 | + |
| 498 | +// A widget that conditionally hides its child, but without the forced compositing of `Opacity`. |
| 499 | +// |
| 500 | +// A fully opaque `Opacity` widget is required to leave its opacity layer in the layer tree. This |
| 501 | +// forces all parent render objects to also composite, which can break a simple scene into many |
| 502 | +// different layers. This can be significantly more expensive, so the issue is avoided by a |
| 503 | +// specialized render object that does not ever force compositing. |
| 504 | +class _Visibility extends SingleChildRenderObjectWidget { |
| 505 | + const _Visibility({ required this.visible, required this.maintainSemantics, super.child }); |
| 506 | + |
| 507 | + final bool visible; |
| 508 | + final bool maintainSemantics; |
| 509 | + |
| 510 | + @override |
| 511 | + RenderObject createRenderObject(BuildContext context) { |
| 512 | + return _RenderVisibility(visible, maintainSemantics); |
| 513 | + } |
| 514 | + |
| 515 | + @override |
| 516 | + void updateRenderObject(BuildContext context, covariant RenderObject renderObject) { |
| 517 | + (renderObject as _RenderVisibility) |
| 518 | + ..visible = visible |
| 519 | + ..maintainSemantics = maintainSemantics; |
| 520 | + } |
| 521 | +} |
| 522 | + |
| 523 | +class _RenderVisibility extends RenderProxyBox { |
| 524 | + _RenderVisibility(this._visible, this._maintainSemantics); |
| 525 | + |
| 526 | + bool get visible => _visible; |
| 527 | + bool _visible; |
| 528 | + set visible(bool value) { |
| 529 | + if (value == visible) { |
| 530 | + return; |
| 531 | + } |
| 532 | + _visible = value; |
| 533 | + markNeedsPaint(); |
| 534 | + } |
| 535 | + |
| 536 | + bool get maintainSemantics => _maintainSemantics; |
| 537 | + bool _maintainSemantics; |
| 538 | + set maintainSemantics(bool value) { |
| 539 | + if (value == maintainSemantics) { |
| 540 | + return; |
| 541 | + } |
| 542 | + _maintainSemantics = value; |
| 543 | + markNeedsSemanticsUpdate(); |
| 544 | + } |
| 545 | + |
| 546 | + @override |
| 547 | + void visitChildrenForSemantics(RenderObjectVisitor visitor) { |
| 548 | + if (maintainSemantics || visible) { |
| 549 | + super.visitChildrenForSemantics(visitor); |
| 550 | + } |
| 551 | + } |
| 552 | + |
| 553 | + @override |
| 554 | + void paint(PaintingContext context, Offset offset) { |
| 555 | + if (!visible) { |
| 556 | + return; |
| 557 | + } |
| 558 | + super.paint(context, offset); |
| 559 | + } |
| 560 | +} |
| 561 | + |
| 562 | +// A widget that conditionally hides its child, but without the forced compositing of `SliverOpacity`. |
| 563 | +// |
| 564 | +// A fully opaque `SliverOpacity` widget is required to leave its opacity layer in the layer tree. |
| 565 | +// This forces all parent render objects to also composite, which can break a simple scene into many |
| 566 | +// different layers. This can be significantly more expensive, so the issue is avoided by a |
| 567 | +// specialized render object that does not ever force compositing. |
| 568 | +class _SliverVisibility extends SingleChildRenderObjectWidget { |
| 569 | + const _SliverVisibility({ required this.visible, required this.maintainSemantics, Widget? sliver }) |
| 570 | + : super(child: sliver); |
| 571 | + |
| 572 | + final bool visible; |
| 573 | + final bool maintainSemantics; |
| 574 | + |
| 575 | + @override |
| 576 | + RenderObject createRenderObject(BuildContext context) { |
| 577 | + return _RenderSliverVisibility(visible, maintainSemantics); |
| 578 | + } |
| 579 | + |
| 580 | + @override |
| 581 | + void updateRenderObject(BuildContext context, covariant RenderObject renderObject) { |
| 582 | + (renderObject as _RenderSliverVisibility) |
| 583 | + ..visible = visible |
| 584 | + ..maintainSemantics = maintainSemantics; |
| 585 | + } |
| 586 | +} |
| 587 | + |
| 588 | +class _RenderSliverVisibility extends RenderProxySliver { |
| 589 | + _RenderSliverVisibility(this._visible, this._maintainSemantics); |
| 590 | + |
| 591 | + bool get visible => _visible; |
| 592 | + bool _visible; |
| 593 | + set visible(bool value) { |
| 594 | + if (value == visible) { |
| 595 | + return; |
| 596 | + } |
| 597 | + _visible = value; |
| 598 | + markNeedsPaint(); |
| 599 | + } |
| 600 | + |
| 601 | + bool get maintainSemantics => _maintainSemantics; |
| 602 | + bool _maintainSemantics; |
| 603 | + set maintainSemantics(bool value) { |
| 604 | + if (value == maintainSemantics) { |
| 605 | + return; |
| 606 | + } |
| 607 | + _maintainSemantics = value; |
| 608 | + markNeedsSemanticsUpdate(); |
| 609 | + } |
| 610 | + |
| 611 | + @override |
| 612 | + void visitChildrenForSemantics(RenderObjectVisitor visitor) { |
| 613 | + if (maintainSemantics || visible) { |
| 614 | + super.visitChildrenForSemantics(visitor); |
| 615 | + } |
| 616 | + } |
| 617 | + |
| 618 | + @override |
| 619 | + void paint(PaintingContext context, Offset offset) { |
| 620 | + if (!visible) { |
| 621 | + return; |
| 622 | + } |
| 623 | + super.paint(context, offset); |
| 624 | + } |
| 625 | +} |
0 commit comments