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