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/// @docImport 'animated_cross_fade.dart';
6/// @docImport 'animated_switcher.dart';
7/// @docImport 'implicit_animations.dart';
8/// @docImport 'navigator.dart';
9/// @docImport 'transitions.dart';
10library;
11
12import 'package:flutter/rendering.dart';
13
14import 'basic.dart';
15import 'focus_scope.dart';
16import 'framework.dart';
17import 'sliver.dart';
18import 'ticker_provider.dart';
19
20/// Whether to show or hide a child.
21///
22/// By default, the [visible] property controls whether the [child] is included
23/// in the subtree or not; when it is not [visible], the [replacement] child
24/// (typically a zero-sized box) is included instead.
25///
26/// A variety of flags can be used to tweak exactly how the child is hidden.
27/// (Changing the flags dynamically is discouraged, as it can cause the [child]
28/// subtree to be rebuilt, with any state in the subtree being discarded.
29/// Typically, only the [visible] flag is changed dynamically.)
30///
31/// These widgets provide some of the facets of this one:
32///
33/// * [Opacity], which can stop its child from being painted.
34/// * [Offstage], which can stop its child from being laid out or painted.
35/// * [TickerMode], which can stop its child from being animated.
36/// * [ExcludeSemantics], which can hide the child from accessibility tools.
37/// * [IgnorePointer], which can disable touch interactions with the child.
38///
39/// Using this widget is not necessary to hide children. The simplest way to
40/// hide a child is just to not include it, or, if a child _must_ be given (e.g.
41/// because the parent is a [StatelessWidget]) then to use [SizedBox.shrink]
42/// instead of the child that would otherwise be included.
43///
44/// See also:
45///
46/// * [AnimatedSwitcher], which can fade from one child to the next as the
47/// subtree changes.
48/// * [AnimatedCrossFade], which can fade between two specific children.
49/// * [SliverVisibility], the sliver equivalent of this widget.
50class Visibility extends StatelessWidget {
51 /// Control whether the given [child] is [visible].
52 ///
53 /// The [maintainSemantics] and [maintainInteractivity] arguments can only be
54 /// set if [maintainSize] is set.
55 ///
56 /// The [maintainSize] argument can only be set if [maintainAnimation] is set.
57 ///
58 /// The [maintainAnimation] argument can only be set if [maintainState] is
59 /// set.
60 const Visibility({
61 super.key,
62 required this.child,
63 this.replacement = const SizedBox.shrink(),
64 this.visible = true,
65 this.maintainState = false,
66 this.maintainAnimation = false,
67 this.maintainSize = false,
68 this.maintainSemantics = false,
69 this.maintainInteractivity = false,
70 this.maintainFocusability = false,
71 }) : assert(
72 maintainState || !maintainAnimation,
73 'Cannot maintain animations if the state is not also maintained.',
74 ),
75 assert(
76 maintainAnimation || !maintainSize,
77 'Cannot maintain size if animations are not maintained.',
78 ),
79 assert(
80 maintainSize || !maintainSemantics,
81 'Cannot maintain semantics if size is not maintained.',
82 ),
83 assert(
84 maintainSize || !maintainInteractivity,
85 'Cannot maintain interactivity if size is not maintained.',
86 );
87
88 /// Control whether the given [child] is [visible].
89 ///
90 /// This is equivalent to the default [Visibility] constructor with all
91 /// "maintain" fields set to true. This constructor should be used in place of
92 /// an [Opacity] widget that only takes on values of `0.0` or `1.0`, as it
93 /// avoids extra compositing when fully opaque.
94 const Visibility.maintain({super.key, required this.child, this.visible = true})
95 : maintainState = true,
96 maintainAnimation = true,
97 maintainSize = true,
98 maintainSemantics = true,
99 maintainInteractivity = true,
100 maintainFocusability = true,
101 replacement = const SizedBox.shrink(); // Unused since maintainState is always true.
102
103 /// The widget to show or hide, as controlled by [visible].
104 ///
105 /// {@macro flutter.widgets.ProxyWidget.child}
106 final Widget child;
107
108 /// The widget to use when the child is not [visible], assuming that none of
109 /// the `maintain` flags (in particular, [maintainState]) are set.
110 ///
111 /// The normal behavior is to replace the widget with a zero by zero box
112 /// ([SizedBox.shrink]).
113 ///
114 /// See also:
115 ///
116 /// * [AnimatedCrossFade], which can animate between two children.
117 final Widget replacement;
118
119 /// Switches between showing the [child] or hiding it.
120 ///
121 /// The `maintain` flags should be set to the same values regardless of the
122 /// state of the [visible] property, otherwise they will not operate correctly
123 /// (specifically, the state will be lost regardless of the state of
124 /// [maintainState] whenever any of the `maintain` flags are changed, since
125 /// doing so will result in a subtree shape change).
126 ///
127 /// Unless [maintainState] is set, the [child] subtree will be disposed
128 /// (removed from the tree) while hidden.
129 final bool visible;
130
131 /// Whether to maintain the [State] objects of the [child] subtree when it is
132 /// not [visible].
133 ///
134 /// Keeping the state of the subtree is potentially expensive (because it
135 /// means all the objects are still in memory; their resources are not
136 /// released). It should only be maintained if it cannot be recreated on
137 /// demand. One example of when the state would be maintained is if the child
138 /// subtree contains a [Navigator], since that widget maintains elaborate
139 /// state that cannot be recreated on the fly.
140 ///
141 /// If this property is true, an [Offstage] widget is used to hide the child
142 /// instead of replacing it with [replacement].
143 ///
144 /// If this property is false, then [maintainAnimation] must also be false.
145 ///
146 /// Dynamically changing this value may cause the current state of the
147 /// subtree to be lost (and a new instance of the subtree, with new [State]
148 /// objects, to be immediately created if [visible] is true).
149 final bool maintainState;
150
151 /// Whether to maintain animations within the [child] subtree when it is
152 /// not [visible].
153 ///
154 /// To set this, [maintainState] must also be set.
155 ///
156 /// Keeping animations active when the widget is not visible is even more
157 /// expensive than only maintaining the state.
158 ///
159 /// One example when this might be useful is if the subtree is animating its
160 /// layout in time with an [AnimationController], and the result of that
161 /// layout is being used to influence some other logic. If this flag is false,
162 /// then any [AnimationController]s hosted inside the [child] subtree will be
163 /// muted while the [visible] flag is false.
164 ///
165 /// If this property is true, no [TickerMode] widget is used.
166 ///
167 /// If this property is false, then [maintainSize] must also be false.
168 ///
169 /// Dynamically changing this value may cause the current state of the
170 /// subtree to be lost (and a new instance of the subtree, with new [State]
171 /// objects, to be immediately created if [visible] is true).
172 final bool maintainAnimation;
173
174 /// Whether to maintain space for where the widget would have been.
175 ///
176 /// To set this, [maintainAnimation] and [maintainState] must also be set.
177 ///
178 /// Maintaining the size when the widget is not [visible] is not notably more
179 /// expensive than just keeping animations running without maintaining the
180 /// size, and may in some circumstances be slightly cheaper if the subtree is
181 /// simple and the [visible] property is frequently toggled, since it avoids
182 /// triggering a layout change when the [visible] property is toggled. If the
183 /// [child] subtree is not trivial then it is significantly cheaper to not
184 /// even keep the state (see [maintainState]).
185 ///
186 /// If this property is false, [Offstage] is used.
187 ///
188 /// If this property is false, then [maintainSemantics] and
189 /// [maintainInteractivity] must also be false.
190 ///
191 /// Dynamically changing this value may cause the current state of the
192 /// subtree to be lost (and a new instance of the subtree, with new [State]
193 /// objects, to be immediately created if [visible] is true).
194 ///
195 /// See also:
196 ///
197 /// * [AnimatedOpacity] and [FadeTransition], which apply animations to the
198 /// opacity for a more subtle effect.
199 final bool maintainSize;
200
201 /// Whether to maintain the semantics for the widget when it is hidden (e.g.
202 /// for accessibility).
203 ///
204 /// To set this, [maintainSize] must also be set.
205 ///
206 /// By default, with [maintainSemantics] set to false, the [child] is not
207 /// visible to accessibility tools when it is hidden from the user. If this
208 /// flag is set to true, then accessibility tools will report the widget as if
209 /// it was present.
210 final bool maintainSemantics;
211
212 /// Whether to allow the widget to be interactive when hidden.
213 ///
214 /// To set this, [maintainSize] must also be set.
215 ///
216 /// By default, with [maintainInteractivity] set to false, touch events cannot
217 /// reach the [child] when it is hidden from the user. If this flag is set to
218 /// true, then touch events will nonetheless be passed through.
219 final bool maintainInteractivity;
220
221 /// Whether to allow the widget to receive focus when hidden. Only in effect if [visible] is false.
222 ///
223 /// Defaults to false.
224 final bool maintainFocusability;
225
226 /// Tells the visibility state of an element in the tree based off its
227 /// ancestor [Visibility] elements.
228 ///
229 /// If there's one or more [Visibility] widgets in the ancestor tree, this
230 /// will return true if and only if all of those widgets have [visible] set
231 /// to true. If there is no [Visibility] widget in the ancestor tree of the
232 /// specified build context, this will return true.
233 ///
234 /// This will register a dependency from the specified context on any
235 /// [Visibility] elements in the ancestor tree, such that if any of their
236 /// visibilities changes, the specified context will be rebuilt.
237 static bool of(BuildContext context) {
238 bool isVisible = true;
239 BuildContext ancestorContext = context;
240 InheritedElement? ancestor = ancestorContext
241 .getElementForInheritedWidgetOfExactType<_VisibilityScope>();
242 while (isVisible && ancestor != null) {
243 final _VisibilityScope scope = context.dependOnInheritedElement(ancestor) as _VisibilityScope;
244 isVisible = scope.isVisible;
245 ancestor.visitAncestorElements((Element parent) {
246 ancestorContext = parent;
247 return false;
248 });
249 ancestor = ancestorContext.getElementForInheritedWidgetOfExactType<_VisibilityScope>();
250 }
251 return isVisible;
252 }
253
254 @override
255 Widget build(BuildContext context) {
256 Widget result = ExcludeFocus(excluding: !visible && !maintainFocusability, child: child);
257 if (maintainSize) {
258 result = _Visibility(
259 visible: visible,
260 maintainSemantics: maintainSemantics,
261 child: IgnorePointer(ignoring: !visible && !maintainInteractivity, child: result),
262 );
263 } else {
264 assert(!maintainInteractivity);
265 assert(!maintainSemantics);
266 assert(!maintainSize);
267 if (maintainState) {
268 if (!maintainAnimation) {
269 result = TickerMode(enabled: visible, child: result);
270 }
271 result = Offstage(offstage: !visible, child: result);
272 } else {
273 assert(!maintainAnimation);
274 assert(!maintainState);
275 result = visible ? child : replacement;
276 }
277 }
278 return _VisibilityScope(isVisible: visible, child: result);
279 }
280
281 @override
282 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
283 super.debugFillProperties(properties);
284 properties.add(FlagProperty('visible', value: visible, ifFalse: 'hidden', ifTrue: 'visible'));
285 properties.add(FlagProperty('maintainState', value: maintainState, ifFalse: 'maintainState'));
286 properties.add(
287 FlagProperty('maintainAnimation', value: maintainAnimation, ifFalse: 'maintainAnimation'),
288 );
289 properties.add(FlagProperty('maintainSize', value: maintainSize, ifFalse: 'maintainSize'));
290 properties.add(
291 FlagProperty('maintainSemantics', value: maintainSemantics, ifFalse: 'maintainSemantics'),
292 );
293 properties.add(
294 FlagProperty(
295 'maintainInteractivity',
296 value: maintainInteractivity,
297 ifFalse: 'maintainInteractivity',
298 ),
299 );
300 }
301}
302
303/// Inherited widget that allows descendants to find their visibility status.
304class _VisibilityScope extends InheritedWidget {
305 const _VisibilityScope({required this.isVisible, required super.child});
306
307 final bool isVisible;
308
309 @override
310 bool updateShouldNotify(_VisibilityScope old) {
311 return isVisible != old.isVisible;
312 }
313}
314
315/// Whether to show or hide a sliver child.
316///
317/// By default, the [visible] property controls whether the [sliver] is included
318/// in the subtree or not; when it is not [visible], the [replacementSliver] is
319/// included instead.
320///
321/// A variety of flags can be used to tweak exactly how the sliver is hidden.
322/// (Changing the flags dynamically is discouraged, as it can cause the [sliver]
323/// subtree to be rebuilt, with any state in the subtree being discarded.
324/// Typically, only the [visible] flag is changed dynamically.)
325///
326/// These widgets provide some of the facets of this one:
327///
328/// * [SliverOpacity], which can stop its sliver child from being painted.
329/// * [SliverOffstage], which can stop its sliver child from being laid out or
330/// painted.
331/// * [TickerMode], which can stop its child from being animated.
332/// * [ExcludeSemantics], which can hide the child from accessibility tools.
333/// * [SliverIgnorePointer], which can disable touch interactions with the
334/// sliver child.
335///
336/// Using this widget is not necessary to hide children. The simplest way to
337/// hide a child is just to not include it. If a child _must_ be given (e.g.
338/// because the parent is a [StatelessWidget]), then including a childless
339/// [SliverToBoxAdapter] instead of the child that would otherwise be included
340/// is typically more efficient than using [SliverVisibility].
341///
342/// See also:
343///
344/// * [Visibility], the equivalent widget for boxes.
345class SliverVisibility extends StatelessWidget {
346 /// Control whether the given [sliver] is [visible].
347 ///
348 /// The [maintainSemantics] and [maintainInteractivity] arguments can only be
349 /// set if [maintainSize] is set.
350 ///
351 /// The [maintainSize] argument can only be set if [maintainAnimation] is set.
352 ///
353 /// The [maintainAnimation] argument can only be set if [maintainState] is
354 /// set.
355 const SliverVisibility({
356 super.key,
357 required this.sliver,
358 this.replacementSliver = const SliverToBoxAdapter(),
359 this.visible = true,
360 this.maintainState = false,
361 this.maintainAnimation = false,
362 this.maintainSize = false,
363 this.maintainSemantics = false,
364 this.maintainInteractivity = false,
365 }) : assert(
366 maintainState || !maintainAnimation,
367 'Cannot maintain animations if the state is not also maintained.',
368 ),
369 assert(
370 maintainAnimation || !maintainSize,
371 'Cannot maintain size if animations are not maintained.',
372 ),
373 assert(
374 maintainSize || !maintainSemantics,
375 'Cannot maintain semantics if size is not maintained.',
376 ),
377 assert(
378 maintainSize || !maintainInteractivity,
379 'Cannot maintain interactivity if size is not maintained.',
380 );
381
382 /// Control whether the given [sliver] is [visible].
383 ///
384 /// This is equivalent to the default [SliverVisibility] constructor with all
385 /// "maintain" fields set to true. This constructor should be used in place of
386 /// a [SliverOpacity] widget that only takes on values of `0.0` or `1.0`, as it
387 /// avoids extra compositing when fully opaque.
388 const SliverVisibility.maintain({
389 super.key,
390 required this.sliver,
391 this.replacementSliver = const SliverToBoxAdapter(),
392 this.visible = true,
393 }) : maintainState = true,
394 maintainAnimation = true,
395 maintainSize = true,
396 maintainSemantics = true,
397 maintainInteractivity = true;
398
399 /// The sliver to show or hide, as controlled by [visible].
400 final Widget sliver;
401
402 /// The widget to use when the sliver child is not [visible], assuming that
403 /// none of the `maintain` flags (in particular, [maintainState]) are set.
404 ///
405 /// The normal behavior is to replace the widget with a childless
406 /// [SliverToBoxAdapter], which by default has a geometry of
407 /// [SliverGeometry.zero].
408 final Widget replacementSliver;
409
410 /// Switches between showing the [sliver] or hiding it.
411 ///
412 /// The `maintain` flags should be set to the same values regardless of the
413 /// state of the [visible] property, otherwise they will not operate correctly
414 /// (specifically, the state will be lost regardless of the state of
415 /// [maintainState] whenever any of the `maintain` flags are changed, since
416 /// doing so will result in a subtree shape change).
417 ///
418 /// Unless [maintainState] is set, the [sliver] subtree will be disposed
419 /// (removed from the tree) while hidden.
420 final bool visible;
421
422 /// Whether to maintain the [State] objects of the [sliver] subtree when it is
423 /// not [visible].
424 ///
425 /// Keeping the state of the subtree is potentially expensive (because it
426 /// means all the objects are still in memory; their resources are not
427 /// released). It should only be maintained if it cannot be recreated on
428 /// demand. One example of when the state would be maintained is if the sliver
429 /// subtree contains a [Navigator], since that widget maintains elaborate
430 /// state that cannot be recreated on the fly.
431 ///
432 /// If this property is true, a [SliverOffstage] widget is used to hide the
433 /// sliver instead of replacing it with [replacementSliver].
434 ///
435 /// If this property is false, then [maintainAnimation] must also be false.
436 ///
437 /// Dynamically changing this value may cause the current state of the
438 /// subtree to be lost (and a new instance of the subtree, with new [State]
439 /// objects, to be immediately created if [visible] is true).
440 final bool maintainState;
441
442 /// Whether to maintain animations within the [sliver] subtree when it is
443 /// not [visible].
444 ///
445 /// To set this, [maintainState] must also be set.
446 ///
447 /// Keeping animations active when the widget is not visible is even more
448 /// expensive than only maintaining the state.
449 ///
450 /// One example when this might be useful is if the subtree is animating its
451 /// layout in time with an [AnimationController], and the result of that
452 /// layout is being used to influence some other logic. If this flag is false,
453 /// then any [AnimationController]s hosted inside the [sliver] subtree will be
454 /// muted while the [visible] flag is false.
455 ///
456 /// If this property is true, no [TickerMode] widget is used.
457 ///
458 /// If this property is false, then [maintainSize] must also be false.
459 ///
460 /// Dynamically changing this value may cause the current state of the
461 /// subtree to be lost (and a new instance of the subtree, with new [State]
462 /// objects, to be immediately created if [visible] is true).
463 final bool maintainAnimation;
464
465 /// Whether to maintain space for where the sliver would have been.
466 ///
467 /// To set this, [maintainAnimation] must also be set.
468 ///
469 /// Maintaining the size when the sliver is not [visible] is not notably more
470 /// expensive than just keeping animations running without maintaining the
471 /// size, and may in some circumstances be slightly cheaper if the subtree is
472 /// simple and the [visible] property is frequently toggled, since it avoids
473 /// triggering a layout change when the [visible] property is toggled. If the
474 /// [sliver] subtree is not trivial then it is significantly cheaper to not
475 /// even keep the state (see [maintainState]).
476 ///
477 /// If this property is false, [SliverOffstage] is used.
478 ///
479 /// If this property is false, then [maintainSemantics] and
480 /// [maintainInteractivity] must also be false.
481 ///
482 /// Dynamically changing this value may cause the current state of the
483 /// subtree to be lost (and a new instance of the subtree, with new [State]
484 /// objects, to be immediately created if [visible] is true).
485 final bool maintainSize;
486
487 /// Whether to maintain the semantics for the sliver when it is hidden (e.g.
488 /// for accessibility).
489 ///
490 /// To set this, [maintainSize] must also be set.
491 ///
492 /// By default, with [maintainSemantics] set to false, the [sliver] is not
493 /// visible to accessibility tools when it is hidden from the user. If this
494 /// flag is set to true, then accessibility tools will report the widget as if
495 /// it was present.
496 final bool maintainSemantics;
497
498 /// Whether to allow the sliver to be interactive when hidden.
499 ///
500 /// To set this, [maintainSize] must also be set.
501 ///
502 /// By default, with [maintainInteractivity] set to false, touch events cannot
503 /// reach the [sliver] when it is hidden from the user. If this flag is set to
504 /// true, then touch events will nonetheless be passed through.
505 final bool maintainInteractivity;
506
507 @override
508 Widget build(BuildContext context) {
509 if (maintainSize) {
510 Widget result = sliver;
511 result = SliverIgnorePointer(ignoring: !visible && !maintainInteractivity, sliver: result);
512 return _SliverVisibility(
513 visible: visible,
514 maintainSemantics: maintainSemantics,
515 sliver: result,
516 );
517 }
518 assert(!maintainInteractivity);
519 assert(!maintainSemantics);
520 assert(!maintainSize);
521 if (maintainState) {
522 Widget result = sliver;
523 if (!maintainAnimation) {
524 result = TickerMode(enabled: visible, child: sliver);
525 }
526 return SliverOffstage(sliver: result, offstage: !visible);
527 }
528 assert(!maintainAnimation);
529 assert(!maintainState);
530 return visible ? sliver : replacementSliver;
531 }
532
533 @override
534 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
535 super.debugFillProperties(properties);
536 properties.add(FlagProperty('visible', value: visible, ifFalse: 'hidden', ifTrue: 'visible'));
537 properties.add(FlagProperty('maintainState', value: maintainState, ifFalse: 'maintainState'));
538 properties.add(
539 FlagProperty('maintainAnimation', value: maintainAnimation, ifFalse: 'maintainAnimation'),
540 );
541 properties.add(FlagProperty('maintainSize', value: maintainSize, ifFalse: 'maintainSize'));
542 properties.add(
543 FlagProperty('maintainSemantics', value: maintainSemantics, ifFalse: 'maintainSemantics'),
544 );
545 properties.add(
546 FlagProperty(
547 'maintainInteractivity',
548 value: maintainInteractivity,
549 ifFalse: 'maintainInteractivity',
550 ),
551 );
552 }
553}
554
555// A widget that conditionally hides its child, but without the forced compositing of `Opacity`.
556//
557// A fully opaque `Opacity` widget is required to leave its opacity layer in the layer tree. This
558// forces all parent render objects to also composite, which can break a simple scene into many
559// different layers. This can be significantly more expensive, so the issue is avoided by a
560// specialized render object that does not ever force compositing.
561class _Visibility extends SingleChildRenderObjectWidget {
562 const _Visibility({required this.visible, required this.maintainSemantics, super.child});
563
564 final bool visible;
565 final bool maintainSemantics;
566
567 @override
568 _RenderVisibility createRenderObject(BuildContext context) {
569 return _RenderVisibility(visible, maintainSemantics);
570 }
571
572 @override
573 void updateRenderObject(BuildContext context, _RenderVisibility renderObject) {
574 renderObject
575 ..visible = visible
576 ..maintainSemantics = maintainSemantics;
577 }
578}
579
580class _RenderVisibility extends RenderProxyBox {
581 _RenderVisibility(this._visible, this._maintainSemantics);
582
583 bool get visible => _visible;
584 bool _visible;
585 set visible(bool value) {
586 if (value == visible) {
587 return;
588 }
589 _visible = value;
590 markNeedsPaint();
591 }
592
593 bool get maintainSemantics => _maintainSemantics;
594 bool _maintainSemantics;
595 set maintainSemantics(bool value) {
596 if (value == maintainSemantics) {
597 return;
598 }
599 _maintainSemantics = value;
600 markNeedsSemanticsUpdate();
601 }
602
603 @override
604 void visitChildrenForSemantics(RenderObjectVisitor visitor) {
605 if (maintainSemantics || visible) {
606 super.visitChildrenForSemantics(visitor);
607 }
608 }
609
610 @override
611 void paint(PaintingContext context, Offset offset) {
612 if (!visible) {
613 return;
614 }
615 super.paint(context, offset);
616 }
617}
618
619// A widget that conditionally hides its child, but without the forced compositing of `SliverOpacity`.
620//
621// A fully opaque `SliverOpacity` widget is required to leave its opacity layer in the layer tree.
622// This forces all parent render objects to also composite, which can break a simple scene into many
623// different layers. This can be significantly more expensive, so the issue is avoided by a
624// specialized render object that does not ever force compositing.
625class _SliverVisibility extends SingleChildRenderObjectWidget {
626 const _SliverVisibility({required this.visible, required this.maintainSemantics, Widget? sliver})
627 : super(child: sliver);
628
629 final bool visible;
630 final bool maintainSemantics;
631
632 @override
633 RenderObject createRenderObject(BuildContext context) {
634 return _RenderSliverVisibility(visible, maintainSemantics);
635 }
636
637 @override
638 void updateRenderObject(BuildContext context, _RenderSliverVisibility renderObject) {
639 renderObject
640 ..visible = visible
641 ..maintainSemantics = maintainSemantics;
642 }
643}
644
645class _RenderSliverVisibility extends RenderProxySliver {
646 _RenderSliverVisibility(this._visible, this._maintainSemantics);
647
648 bool get visible => _visible;
649 bool _visible;
650 set visible(bool value) {
651 if (value == visible) {
652 return;
653 }
654 _visible = value;
655 markNeedsPaint();
656 }
657
658 bool get maintainSemantics => _maintainSemantics;
659 bool _maintainSemantics;
660 set maintainSemantics(bool value) {
661 if (value == maintainSemantics) {
662 return;
663 }
664 _maintainSemantics = value;
665 markNeedsSemanticsUpdate();
666 }
667
668 @override
669 void visitChildrenForSemantics(RenderObjectVisitor visitor) {
670 if (maintainSemantics || visible) {
671 super.visitChildrenForSemantics(visitor);
672 }
673 }
674
675 @override
676 void paint(PaintingContext context, Offset offset) {
677 if (!visible) {
678 return;
679 }
680 super.paint(context, offset);
681 }
682}
683