• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2015 The Chromium 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:async';
6
7import 'dart:ui' as ui show ImageFilter, Gradient, Image;
8
9import 'package:flutter/animation.dart';
10import 'package:flutter/foundation.dart';
11import 'package:flutter/gestures.dart';
12import 'package:flutter/painting.dart';
13import 'package:flutter/semantics.dart';
14
15import 'package:vector_math/vector_math_64.dart';
16
17import 'binding.dart';
18import 'box.dart';
19import 'layer.dart';
20import 'object.dart';
21
22export 'package:flutter/gestures.dart' show
23  PointerEvent,
24  PointerDownEvent,
25  PointerMoveEvent,
26  PointerUpEvent,
27  PointerCancelEvent;
28
29/// A base class for render objects that resemble their children.
30///
31/// A proxy box has a single child and simply mimics all the properties of that
32/// child by calling through to the child for each function in the render box
33/// protocol. For example, a proxy box determines its size by asking its child
34/// to layout with the same constraints and then matching the size.
35///
36/// A proxy box isn't useful on its own because you might as well just replace
37/// the proxy box with its child. However, RenderProxyBox is a useful base class
38/// for render objects that wish to mimic most, but not all, of the properties
39/// of their child.
40class RenderProxyBox extends RenderBox with RenderObjectWithChildMixin<RenderBox>, RenderProxyBoxMixin<RenderBox> {
41  /// Creates a proxy render box.
42  ///
43  /// Proxy render boxes are rarely created directly because they simply proxy
44  /// the render box protocol to [child]. Instead, consider using one of the
45  /// subclasses.
46  RenderProxyBox([RenderBox child]) {
47    this.child = child;
48  }
49}
50
51/// Implementation of [RenderProxyBox].
52///
53/// Use this mixin in situations where the proxying behavior
54/// of [RenderProxyBox] is desired but inheriting from [RenderProxyBox] is
55/// impractical (e.g. because you want to mix in other classes as well).
56// TODO(ianh): Remove this class once https://github.com/dart-lang/sdk/issues/31543 is fixed
57@optionalTypeArgs
58mixin RenderProxyBoxMixin<T extends RenderBox> on RenderBox, RenderObjectWithChildMixin<T> {
59  @override
60  void setupParentData(RenderObject child) {
61    // We don't actually use the offset argument in BoxParentData, so let's
62    // avoid allocating it at all.
63    if (child.parentData is! ParentData)
64      child.parentData = ParentData();
65  }
66
67  @override
68  double computeMinIntrinsicWidth(double height) {
69    if (child != null)
70      return child.getMinIntrinsicWidth(height);
71    return 0.0;
72  }
73
74  @override
75  double computeMaxIntrinsicWidth(double height) {
76    if (child != null)
77      return child.getMaxIntrinsicWidth(height);
78    return 0.0;
79  }
80
81  @override
82  double computeMinIntrinsicHeight(double width) {
83    if (child != null)
84      return child.getMinIntrinsicHeight(width);
85    return 0.0;
86  }
87
88  @override
89  double computeMaxIntrinsicHeight(double width) {
90    if (child != null)
91      return child.getMaxIntrinsicHeight(width);
92    return 0.0;
93  }
94
95  @override
96  double computeDistanceToActualBaseline(TextBaseline baseline) {
97    if (child != null)
98      return child.getDistanceToActualBaseline(baseline);
99    return super.computeDistanceToActualBaseline(baseline);
100  }
101
102  @override
103  void performLayout() {
104    if (child != null) {
105      child.layout(constraints, parentUsesSize: true);
106      size = child.size;
107    } else {
108      performResize();
109    }
110  }
111
112  @override
113  bool hitTestChildren(BoxHitTestResult result, { Offset position }) {
114    return child?.hitTest(result, position: position) ?? false;
115  }
116
117  @override
118  void applyPaintTransform(RenderObject child, Matrix4 transform) { }
119
120  @override
121  void paint(PaintingContext context, Offset offset) {
122    if (child != null)
123      context.paintChild(child, offset);
124  }
125}
126
127/// How to behave during hit tests.
128enum HitTestBehavior {
129  /// Targets that defer to their children receive events within their bounds
130  /// only if one of their children is hit by the hit test.
131  deferToChild,
132
133  /// Opaque targets can be hit by hit tests, causing them to both receive
134  /// events within their bounds and prevent targets visually behind them from
135  /// also receiving events.
136  opaque,
137
138  /// Translucent targets both receive events within their bounds and permit
139  /// targets visually behind them to also receive events.
140  translucent,
141}
142
143/// A RenderProxyBox subclass that allows you to customize the
144/// hit-testing behavior.
145abstract class RenderProxyBoxWithHitTestBehavior extends RenderProxyBox {
146  /// Initializes member variables for subclasses.
147  ///
148  /// By default, the [behavior] is [HitTestBehavior.deferToChild].
149  RenderProxyBoxWithHitTestBehavior({
150    this.behavior = HitTestBehavior.deferToChild,
151    RenderBox child,
152  }) : super(child);
153
154  /// How to behave during hit testing.
155  HitTestBehavior behavior;
156
157  @override
158  bool hitTest(BoxHitTestResult result, { Offset position }) {
159    bool hitTarget = false;
160    if (size.contains(position)) {
161      hitTarget = hitTestChildren(result, position: position) || hitTestSelf(position);
162      if (hitTarget || behavior == HitTestBehavior.translucent)
163        result.add(BoxHitTestEntry(this, position));
164    }
165    return hitTarget;
166  }
167
168  @override
169  bool hitTestSelf(Offset position) => behavior == HitTestBehavior.opaque;
170
171  @override
172  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
173    super.debugFillProperties(properties);
174    properties.add(EnumProperty<HitTestBehavior>('behavior', behavior, defaultValue: null));
175  }
176}
177
178/// Imposes additional constraints on its child.
179///
180/// A render constrained box proxies most functions in the render box protocol
181/// to its child, except that when laying out its child, it tightens the
182/// constraints provided by its parent by enforcing the [additionalConstraints]
183/// as well.
184///
185/// For example, if you wanted [child] to have a minimum height of 50.0 logical
186/// pixels, you could use `const BoxConstraints(minHeight: 50.0)` as the
187/// [additionalConstraints].
188class RenderConstrainedBox extends RenderProxyBox {
189  /// Creates a render box that constrains its child.
190  ///
191  /// The [additionalConstraints] argument must not be null and must be valid.
192  RenderConstrainedBox({
193    RenderBox child,
194    @required BoxConstraints additionalConstraints,
195  }) : assert(additionalConstraints != null),
196       assert(additionalConstraints.debugAssertIsValid()),
197       _additionalConstraints = additionalConstraints,
198       super(child);
199
200  /// Additional constraints to apply to [child] during layout
201  BoxConstraints get additionalConstraints => _additionalConstraints;
202  BoxConstraints _additionalConstraints;
203  set additionalConstraints(BoxConstraints value) {
204    assert(value != null);
205    assert(value.debugAssertIsValid());
206    if (_additionalConstraints == value)
207      return;
208    _additionalConstraints = value;
209    markNeedsLayout();
210  }
211
212  @override
213  double computeMinIntrinsicWidth(double height) {
214    if (_additionalConstraints.hasBoundedWidth && _additionalConstraints.hasTightWidth)
215      return _additionalConstraints.minWidth;
216    final double width = super.computeMinIntrinsicWidth(height);
217    assert(width.isFinite);
218    if (!_additionalConstraints.hasInfiniteWidth)
219      return _additionalConstraints.constrainWidth(width);
220    return width;
221  }
222
223  @override
224  double computeMaxIntrinsicWidth(double height) {
225    if (_additionalConstraints.hasBoundedWidth && _additionalConstraints.hasTightWidth)
226      return _additionalConstraints.minWidth;
227    final double width = super.computeMaxIntrinsicWidth(height);
228    assert(width.isFinite);
229    if (!_additionalConstraints.hasInfiniteWidth)
230      return _additionalConstraints.constrainWidth(width);
231    return width;
232  }
233
234  @override
235  double computeMinIntrinsicHeight(double width) {
236    if (_additionalConstraints.hasBoundedHeight && _additionalConstraints.hasTightHeight)
237      return _additionalConstraints.minHeight;
238    final double height = super.computeMinIntrinsicHeight(width);
239    assert(height.isFinite);
240    if (!_additionalConstraints.hasInfiniteHeight)
241      return _additionalConstraints.constrainHeight(height);
242    return height;
243  }
244
245  @override
246  double computeMaxIntrinsicHeight(double width) {
247    if (_additionalConstraints.hasBoundedHeight && _additionalConstraints.hasTightHeight)
248      return _additionalConstraints.minHeight;
249    final double height = super.computeMaxIntrinsicHeight(width);
250    assert(height.isFinite);
251    if (!_additionalConstraints.hasInfiniteHeight)
252      return _additionalConstraints.constrainHeight(height);
253    return height;
254  }
255
256  @override
257  void performLayout() {
258    if (child != null) {
259      child.layout(_additionalConstraints.enforce(constraints), parentUsesSize: true);
260      size = child.size;
261    } else {
262      size = _additionalConstraints.enforce(constraints).constrain(Size.zero);
263    }
264  }
265
266  @override
267  void debugPaintSize(PaintingContext context, Offset offset) {
268    super.debugPaintSize(context, offset);
269    assert(() {
270      Paint paint;
271      if (child == null || child.size.isEmpty) {
272        paint = Paint()
273          ..color = const Color(0x90909090);
274        context.canvas.drawRect(offset & size, paint);
275      }
276      return true;
277    }());
278  }
279
280  @override
281  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
282    super.debugFillProperties(properties);
283    properties.add(DiagnosticsProperty<BoxConstraints>('additionalConstraints', additionalConstraints));
284  }
285}
286
287/// Constrains the child's [BoxConstraints.maxWidth] and
288/// [BoxConstraints.maxHeight] if they're otherwise unconstrained.
289///
290/// This has the effect of giving the child a natural dimension in unbounded
291/// environments. For example, by providing a [maxHeight] to a widget that
292/// normally tries to be as big as possible, the widget will normally size
293/// itself to fit its parent, but when placed in a vertical list, it will take
294/// on the given height.
295///
296/// This is useful when composing widgets that normally try to match their
297/// parents' size, so that they behave reasonably in lists (which are
298/// unbounded).
299class RenderLimitedBox extends RenderProxyBox {
300  /// Creates a render box that imposes a maximum width or maximum height on its
301  /// child if the child is otherwise unconstrained.
302  ///
303  /// The [maxWidth] and [maxHeight] arguments not be null and must be
304  /// non-negative.
305  RenderLimitedBox({
306    RenderBox child,
307    double maxWidth = double.infinity,
308    double maxHeight = double.infinity,
309  }) : assert(maxWidth != null && maxWidth >= 0.0),
310       assert(maxHeight != null && maxHeight >= 0.0),
311       _maxWidth = maxWidth,
312       _maxHeight = maxHeight,
313       super(child);
314
315  /// The value to use for maxWidth if the incoming maxWidth constraint is infinite.
316  double get maxWidth => _maxWidth;
317  double _maxWidth;
318  set maxWidth(double value) {
319    assert(value != null && value >= 0.0);
320    if (_maxWidth == value)
321      return;
322    _maxWidth = value;
323    markNeedsLayout();
324  }
325
326  /// The value to use for maxHeight if the incoming maxHeight constraint is infinite.
327  double get maxHeight => _maxHeight;
328  double _maxHeight;
329  set maxHeight(double value) {
330    assert(value != null && value >= 0.0);
331    if (_maxHeight == value)
332      return;
333    _maxHeight = value;
334    markNeedsLayout();
335  }
336
337  BoxConstraints _limitConstraints(BoxConstraints constraints) {
338    return BoxConstraints(
339      minWidth: constraints.minWidth,
340      maxWidth: constraints.hasBoundedWidth ? constraints.maxWidth : constraints.constrainWidth(maxWidth),
341      minHeight: constraints.minHeight,
342      maxHeight: constraints.hasBoundedHeight ? constraints.maxHeight : constraints.constrainHeight(maxHeight),
343    );
344  }
345
346  @override
347  void performLayout() {
348    if (child != null) {
349      child.layout(_limitConstraints(constraints), parentUsesSize: true);
350      size = constraints.constrain(child.size);
351    } else {
352      size = _limitConstraints(constraints).constrain(Size.zero);
353    }
354  }
355
356  @override
357  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
358    super.debugFillProperties(properties);
359    properties.add(DoubleProperty('maxWidth', maxWidth, defaultValue: double.infinity));
360    properties.add(DoubleProperty('maxHeight', maxHeight, defaultValue: double.infinity));
361  }
362}
363
364/// Attempts to size the child to a specific aspect ratio.
365///
366/// The render object first tries the largest width permitted by the layout
367/// constraints. The height of the render object is determined by applying the
368/// given aspect ratio to the width, expressed as a ratio of width to height.
369///
370/// For example, a 16:9 width:height aspect ratio would have a value of
371/// 16.0/9.0. If the maximum width is infinite, the initial width is determined
372/// by applying the aspect ratio to the maximum height.
373///
374/// Now consider a second example, this time with an aspect ratio of 2.0 and
375/// layout constraints that require the width to be between 0.0 and 100.0 and
376/// the height to be between 0.0 and 100.0. We'll select a width of 100.0 (the
377/// biggest allowed) and a height of 50.0 (to match the aspect ratio).
378///
379/// In that same situation, if the aspect ratio is 0.5, we'll also select a
380/// width of 100.0 (still the biggest allowed) and we'll attempt to use a height
381/// of 200.0. Unfortunately, that violates the constraints because the child can
382/// be at most 100.0 pixels tall. The render object will then take that value
383/// and apply the aspect ratio again to obtain a width of 50.0. That width is
384/// permitted by the constraints and the child receives a width of 50.0 and a
385/// height of 100.0. If the width were not permitted, the render object would
386/// continue iterating through the constraints. If the render object does not
387/// find a feasible size after consulting each constraint, the render object
388/// will eventually select a size for the child that meets the layout
389/// constraints but fails to meet the aspect ratio constraints.
390class RenderAspectRatio extends RenderProxyBox {
391  /// Creates as render object with a specific aspect ratio.
392  ///
393  /// The [aspectRatio] argument must be a finite, positive value.
394  RenderAspectRatio({
395    RenderBox child,
396    @required double aspectRatio,
397  }) : assert(aspectRatio != null),
398       assert(aspectRatio > 0.0),
399       assert(aspectRatio.isFinite),
400       _aspectRatio = aspectRatio,
401       super(child);
402
403  /// The aspect ratio to attempt to use.
404  ///
405  /// The aspect ratio is expressed as a ratio of width to height. For example,
406  /// a 16:9 width:height aspect ratio would have a value of 16.0/9.0.
407  double get aspectRatio => _aspectRatio;
408  double _aspectRatio;
409  set aspectRatio(double value) {
410    assert(value != null);
411    assert(value > 0.0);
412    assert(value.isFinite);
413    if (_aspectRatio == value)
414      return;
415    _aspectRatio = value;
416    markNeedsLayout();
417  }
418
419  @override
420  double computeMinIntrinsicWidth(double height) {
421    if (height.isFinite)
422      return height * _aspectRatio;
423    if (child != null)
424      return child.getMinIntrinsicWidth(height);
425    return 0.0;
426  }
427
428  @override
429  double computeMaxIntrinsicWidth(double height) {
430    if (height.isFinite)
431      return height * _aspectRatio;
432    if (child != null)
433      return child.getMaxIntrinsicWidth(height);
434    return 0.0;
435  }
436
437  @override
438  double computeMinIntrinsicHeight(double width) {
439    if (width.isFinite)
440      return width / _aspectRatio;
441    if (child != null)
442      return child.getMinIntrinsicHeight(width);
443    return 0.0;
444  }
445
446  @override
447  double computeMaxIntrinsicHeight(double width) {
448    if (width.isFinite)
449      return width / _aspectRatio;
450    if (child != null)
451      return child.getMaxIntrinsicHeight(width);
452    return 0.0;
453  }
454
455  Size _applyAspectRatio(BoxConstraints constraints) {
456    assert(constraints.debugAssertIsValid());
457    assert(() {
458      if (!constraints.hasBoundedWidth && !constraints.hasBoundedHeight) {
459        throw FlutterError(
460          '$runtimeType has unbounded constraints.\n'
461          'This $runtimeType was given an aspect ratio of $aspectRatio but was given '
462          'both unbounded width and unbounded height constraints. Because both '
463          'constraints were unbounded, this render object doesn\'t know how much '
464          'size to consume.'
465        );
466      }
467      return true;
468    }());
469
470    if (constraints.isTight)
471      return constraints.smallest;
472
473    double width = constraints.maxWidth;
474    double height;
475
476    // We default to picking the height based on the width, but if the width
477    // would be infinite, that's not sensible so we try to infer the height
478    // from the width.
479    if (width.isFinite) {
480      height = width / _aspectRatio;
481    } else {
482      height = constraints.maxHeight;
483      width = height * _aspectRatio;
484    }
485
486    // Similar to RenderImage, we iteratively attempt to fit within the given
487    // constraints while maintaining the given aspect ratio. The order of
488    // applying the constraints is also biased towards inferring the height
489    // from the width.
490
491    if (width > constraints.maxWidth) {
492      width = constraints.maxWidth;
493      height = width / _aspectRatio;
494    }
495
496    if (height > constraints.maxHeight) {
497      height = constraints.maxHeight;
498      width = height * _aspectRatio;
499    }
500
501    if (width < constraints.minWidth) {
502      width = constraints.minWidth;
503      height = width / _aspectRatio;
504    }
505
506    if (height < constraints.minHeight) {
507      height = constraints.minHeight;
508      width = height * _aspectRatio;
509    }
510
511    return constraints.constrain(Size(width, height));
512  }
513
514  @override
515  void performLayout() {
516    size = _applyAspectRatio(constraints);
517    if (child != null)
518      child.layout(BoxConstraints.tight(size));
519  }
520
521  @override
522  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
523    super.debugFillProperties(properties);
524    properties.add(DoubleProperty('aspectRatio', aspectRatio));
525  }
526}
527
528/// Sizes its child to the child's intrinsic width.
529///
530/// Sizes its child's width to the child's maximum intrinsic width. If
531/// [stepWidth] is non-null, the child's width will be snapped to a multiple of
532/// the [stepWidth]. Similarly, if [stepHeight] is non-null, the child's height
533/// will be snapped to a multiple of the [stepHeight].
534///
535/// This class is useful, for example, when unlimited width is available and
536/// you would like a child that would otherwise attempt to expand infinitely to
537/// instead size itself to a more reasonable width.
538///
539/// This class is relatively expensive, because it adds a speculative layout
540/// pass before the final layout phase. Avoid using it where possible. In the
541/// worst case, this render object can result in a layout that is O(N²) in the
542/// depth of the tree.
543class RenderIntrinsicWidth extends RenderProxyBox {
544  /// Creates a render object that sizes itself to its child's intrinsic width.
545  ///
546  /// If [stepWidth] is non-null it must be > 0.0. Similarly If [stepHeight] is
547  /// non-null it must be > 0.0.
548  RenderIntrinsicWidth({
549    double stepWidth,
550    double stepHeight,
551    RenderBox child,
552  }) : assert(stepWidth == null || stepWidth > 0.0),
553       assert(stepHeight == null || stepHeight > 0.0),
554       _stepWidth = stepWidth,
555       _stepHeight = stepHeight,
556       super(child);
557
558  /// If non-null, force the child's width to be a multiple of this value.
559  ///
560  /// This value must be null or > 0.0.
561  double get stepWidth => _stepWidth;
562  double _stepWidth;
563  set stepWidth(double value) {
564    assert(value == null || value > 0.0);
565    if (value == _stepWidth)
566      return;
567    _stepWidth = value;
568    markNeedsLayout();
569  }
570
571  /// If non-null, force the child's height to be a multiple of this value.
572  ///
573  /// This value must be null or > 0.0.
574  double get stepHeight => _stepHeight;
575  double _stepHeight;
576  set stepHeight(double value) {
577    assert(value == null || value > 0.0);
578    if (value == _stepHeight)
579      return;
580    _stepHeight = value;
581    markNeedsLayout();
582  }
583
584  static double _applyStep(double input, double step) {
585    assert(input.isFinite);
586    if (step == null)
587      return input;
588    return (input / step).ceil() * step;
589  }
590
591  @override
592  double computeMinIntrinsicWidth(double height) {
593    return computeMaxIntrinsicWidth(height);
594  }
595
596  @override
597  double computeMaxIntrinsicWidth(double height) {
598    if (child == null)
599      return 0.0;
600    final double width = child.getMaxIntrinsicWidth(height);
601    return _applyStep(width, _stepWidth);
602  }
603
604  @override
605  double computeMinIntrinsicHeight(double width) {
606    if (child == null)
607      return 0.0;
608    if (!width.isFinite)
609      width = computeMaxIntrinsicWidth(double.infinity);
610    assert(width.isFinite);
611    final double height = child.getMinIntrinsicHeight(width);
612    return _applyStep(height, _stepHeight);
613  }
614
615  @override
616  double computeMaxIntrinsicHeight(double width) {
617    if (child == null)
618      return 0.0;
619    if (!width.isFinite)
620      width = computeMaxIntrinsicWidth(double.infinity);
621    assert(width.isFinite);
622    final double height = child.getMaxIntrinsicHeight(width);
623    return _applyStep(height, _stepHeight);
624  }
625
626  @override
627  void performLayout() {
628    if (child != null) {
629      BoxConstraints childConstraints = constraints;
630      if (!childConstraints.hasTightWidth) {
631        final double width = child.getMaxIntrinsicWidth(childConstraints.maxHeight);
632        assert(width.isFinite);
633        childConstraints = childConstraints.tighten(width: _applyStep(width, _stepWidth));
634      }
635      if (_stepHeight != null) {
636        final double height = child.getMaxIntrinsicHeight(childConstraints.maxWidth);
637        assert(height.isFinite);
638        childConstraints = childConstraints.tighten(height: _applyStep(height, _stepHeight));
639      }
640      child.layout(childConstraints, parentUsesSize: true);
641      size = child.size;
642    } else {
643      performResize();
644    }
645  }
646
647  @override
648  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
649    super.debugFillProperties(properties);
650    properties.add(DoubleProperty('stepWidth', stepWidth));
651    properties.add(DoubleProperty('stepHeight', stepHeight));
652  }
653}
654
655/// Sizes its child to the child's intrinsic height.
656///
657/// This class is useful, for example, when unlimited height is available and
658/// you would like a child that would otherwise attempt to expand infinitely to
659/// instead size itself to a more reasonable height.
660///
661/// This class is relatively expensive, because it adds a speculative layout
662/// pass before the final layout phase. Avoid using it where possible. In the
663/// worst case, this render object can result in a layout that is O(N²) in the
664/// depth of the tree.
665class RenderIntrinsicHeight extends RenderProxyBox {
666  /// Creates a render object that sizes itself to its child's intrinsic height.
667  RenderIntrinsicHeight({
668    RenderBox child,
669  }) : super(child);
670
671  @override
672  double computeMinIntrinsicWidth(double height) {
673    if (child == null)
674      return 0.0;
675    if (!height.isFinite)
676      height = child.getMaxIntrinsicHeight(double.infinity);
677    assert(height.isFinite);
678    return child.getMinIntrinsicWidth(height);
679  }
680
681  @override
682  double computeMaxIntrinsicWidth(double height) {
683    if (child == null)
684      return 0.0;
685    if (!height.isFinite)
686      height = child.getMaxIntrinsicHeight(double.infinity);
687    assert(height.isFinite);
688    return child.getMaxIntrinsicWidth(height);
689  }
690
691  @override
692  double computeMinIntrinsicHeight(double width) {
693    return computeMaxIntrinsicHeight(width);
694  }
695
696  @override
697  void performLayout() {
698    if (child != null) {
699      BoxConstraints childConstraints = constraints;
700      if (!childConstraints.hasTightHeight) {
701        final double height = child.getMaxIntrinsicHeight(childConstraints.maxWidth);
702        assert(height.isFinite);
703        childConstraints = childConstraints.tighten(height: height);
704      }
705      child.layout(childConstraints, parentUsesSize: true);
706      size = child.size;
707    } else {
708      performResize();
709    }
710  }
711
712}
713
714int _getAlphaFromOpacity(double opacity) => (opacity * 255).round();
715
716/// Makes its child partially transparent.
717///
718/// This class paints its child into an intermediate buffer and then blends the
719/// child back into the scene partially transparent.
720///
721/// For values of opacity other than 0.0 and 1.0, this class is relatively
722/// expensive because it requires painting the child into an intermediate
723/// buffer. For the value 0.0, the child is simply not painted at all. For the
724/// value 1.0, the child is painted immediately without an intermediate buffer.
725class RenderOpacity extends RenderProxyBox {
726  /// Creates a partially transparent render object.
727  ///
728  /// The [opacity] argument must be between 0.0 and 1.0, inclusive.
729  RenderOpacity({
730    double opacity = 1.0,
731    bool alwaysIncludeSemantics = false,
732    RenderBox child,
733  }) : assert(opacity != null),
734       assert(opacity >= 0.0 && opacity <= 1.0),
735       assert(alwaysIncludeSemantics != null),
736       _opacity = opacity,
737       _alwaysIncludeSemantics = alwaysIncludeSemantics,
738       _alpha = _getAlphaFromOpacity(opacity),
739       super(child);
740
741  @override
742  bool get alwaysNeedsCompositing => child != null && (_alpha != 0 && _alpha != 255);
743
744  int _alpha;
745
746  /// The fraction to scale the child's alpha value.
747  ///
748  /// An opacity of 1.0 is fully opaque. An opacity of 0.0 is fully transparent
749  /// (i.e., invisible).
750  ///
751  /// The opacity must not be null.
752  ///
753  /// Values 1.0 and 0.0 are painted with a fast path. Other values
754  /// require painting the child into an intermediate buffer, which is
755  /// expensive.
756  double get opacity => _opacity;
757  double _opacity;
758  set opacity(double value) {
759    assert(value != null);
760    assert(value >= 0.0 && value <= 1.0);
761    if (_opacity == value)
762      return;
763    final bool didNeedCompositing = alwaysNeedsCompositing;
764    final bool wasVisible = _alpha != 0;
765    _opacity = value;
766    _alpha = _getAlphaFromOpacity(_opacity);
767    if (didNeedCompositing != alwaysNeedsCompositing)
768      markNeedsCompositingBitsUpdate();
769    markNeedsPaint();
770    if (wasVisible != (_alpha != 0))
771      markNeedsSemanticsUpdate();
772  }
773
774  /// Whether child semantics are included regardless of the opacity.
775  ///
776  /// If false, semantics are excluded when [opacity] is 0.0.
777  ///
778  /// Defaults to false.
779  bool get alwaysIncludeSemantics => _alwaysIncludeSemantics;
780  bool _alwaysIncludeSemantics;
781  set alwaysIncludeSemantics(bool value) {
782    if (value == _alwaysIncludeSemantics)
783      return;
784    _alwaysIncludeSemantics = value;
785    markNeedsSemanticsUpdate();
786  }
787
788  @override
789  void paint(PaintingContext context, Offset offset) {
790    if (child != null) {
791      if (_alpha == 0) {
792        // No need to keep the layer. We'll create a new one if necessary.
793        layer = null;
794        return;
795      }
796      if (_alpha == 255) {
797        // No need to keep the layer. We'll create a new one if necessary.
798        layer = null;
799        context.paintChild(child, offset);
800        return;
801      }
802      assert(needsCompositing);
803      layer = context.pushOpacity(offset, _alpha, super.paint, oldLayer: layer);
804    }
805  }
806
807  @override
808  void visitChildrenForSemantics(RenderObjectVisitor visitor) {
809    if (child != null && (_alpha != 0 || alwaysIncludeSemantics))
810      visitor(child);
811  }
812
813  @override
814  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
815    super.debugFillProperties(properties);
816    properties.add(DoubleProperty('opacity', opacity));
817    properties.add(FlagProperty('alwaysIncludeSemantics', value: alwaysIncludeSemantics, ifTrue: 'alwaysIncludeSemantics'));
818  }
819}
820
821/// Makes its child partially transparent, driven from an [Animation].
822///
823/// This is a variant of [RenderOpacity] that uses an [Animation<double>] rather
824/// than a [double] to control the opacity.
825class RenderAnimatedOpacity extends RenderProxyBox {
826  /// Creates a partially transparent render object.
827  ///
828  /// The [opacity] argument must not be null.
829  RenderAnimatedOpacity({
830    @required Animation<double> opacity,
831    bool alwaysIncludeSemantics = false,
832    RenderBox child,
833  }) : assert(opacity != null),
834       assert(alwaysIncludeSemantics != null),
835       _alwaysIncludeSemantics = alwaysIncludeSemantics,
836       super(child) {
837    this.opacity = opacity;
838  }
839
840  int _alpha;
841
842  @override
843  bool get alwaysNeedsCompositing => child != null && _currentlyNeedsCompositing;
844  bool _currentlyNeedsCompositing;
845
846  /// The animation that drives this render object's opacity.
847  ///
848  /// An opacity of 1.0 is fully opaque. An opacity of 0.0 is fully transparent
849  /// (i.e., invisible).
850  ///
851  /// To change the opacity of a child in a static manner, not animated,
852  /// consider [RenderOpacity] instead.
853  Animation<double> get opacity => _opacity;
854  Animation<double> _opacity;
855  set opacity(Animation<double> value) {
856    assert(value != null);
857    if (_opacity == value)
858      return;
859    if (attached && _opacity != null)
860      _opacity.removeListener(_updateOpacity);
861    _opacity = value;
862    if (attached)
863      _opacity.addListener(_updateOpacity);
864    _updateOpacity();
865  }
866
867  /// Whether child semantics are included regardless of the opacity.
868  ///
869  /// If false, semantics are excluded when [opacity] is 0.0.
870  ///
871  /// Defaults to false.
872  bool get alwaysIncludeSemantics => _alwaysIncludeSemantics;
873  bool _alwaysIncludeSemantics;
874  set alwaysIncludeSemantics(bool value) {
875    if (value == _alwaysIncludeSemantics)
876      return;
877    _alwaysIncludeSemantics = value;
878    markNeedsSemanticsUpdate();
879  }
880
881  @override
882  void attach(PipelineOwner owner) {
883    super.attach(owner);
884    _opacity.addListener(_updateOpacity);
885    _updateOpacity(); // in case it changed while we weren't listening
886  }
887
888  @override
889  void detach() {
890    _opacity.removeListener(_updateOpacity);
891    super.detach();
892  }
893
894  void _updateOpacity() {
895    final int oldAlpha = _alpha;
896    _alpha = _getAlphaFromOpacity(_opacity.value.clamp(0.0, 1.0));
897    if (oldAlpha != _alpha) {
898      final bool didNeedCompositing = _currentlyNeedsCompositing;
899      _currentlyNeedsCompositing = _alpha > 0 && _alpha < 255;
900      if (child != null && didNeedCompositing != _currentlyNeedsCompositing)
901        markNeedsCompositingBitsUpdate();
902      markNeedsPaint();
903      if (oldAlpha == 0 || _alpha == 0)
904        markNeedsSemanticsUpdate();
905    }
906  }
907
908  @override
909  void paint(PaintingContext context, Offset offset) {
910    if (child != null) {
911      if (_alpha == 0) {
912        // No need to keep the layer. We'll create a new one if necessary.
913        layer = null;
914        return;
915      }
916      if (_alpha == 255) {
917        // No need to keep the layer. We'll create a new one if necessary.
918        layer = null;
919        context.paintChild(child, offset);
920        return;
921      }
922      assert(needsCompositing);
923      layer = context.pushOpacity(offset, _alpha, super.paint, oldLayer: layer);
924    }
925  }
926
927  @override
928  void visitChildrenForSemantics(RenderObjectVisitor visitor) {
929    if (child != null && (_alpha != 0 || alwaysIncludeSemantics))
930      visitor(child);
931  }
932
933  @override
934  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
935    super.debugFillProperties(properties);
936    properties.add(DiagnosticsProperty<Animation<double>>('opacity', opacity));
937    properties.add(FlagProperty('alwaysIncludeSemantics', value: alwaysIncludeSemantics, ifTrue: 'alwaysIncludeSemantics'));
938  }
939}
940
941/// Signature for a function that creates a [Shader] for a given [Rect].
942///
943/// Used by [RenderShaderMask] and the [ShaderMask] widget.
944typedef ShaderCallback = Shader Function(Rect bounds);
945
946/// Applies a mask generated by a [Shader] to its child.
947///
948/// For example, [RenderShaderMask] can be used to gradually fade out the edge
949/// of a child by using a [new ui.Gradient.linear] mask.
950class RenderShaderMask extends RenderProxyBox {
951  /// Creates a render object that applies a mask generated by a [Shader] to its child.
952  ///
953  /// The [shaderCallback] and [blendMode] arguments must not be null.
954  RenderShaderMask({
955    RenderBox child,
956    @required ShaderCallback shaderCallback,
957    BlendMode blendMode = BlendMode.modulate,
958  }) : assert(shaderCallback != null),
959       assert(blendMode != null),
960       _shaderCallback = shaderCallback,
961       _blendMode = blendMode,
962       super(child);
963
964  @override
965  ShaderMaskLayer get layer => super.layer;
966
967  /// Called to creates the [Shader] that generates the mask.
968  ///
969  /// The shader callback is called with the current size of the child so that
970  /// it can customize the shader to the size and location of the child.
971  // TODO(abarth): Use the delegate pattern here to avoid generating spurious
972  // repaints when the ShaderCallback changes identity.
973  ShaderCallback get shaderCallback => _shaderCallback;
974  ShaderCallback _shaderCallback;
975  set shaderCallback(ShaderCallback value) {
976    assert(value != null);
977    if (_shaderCallback == value)
978      return;
979    _shaderCallback = value;
980    markNeedsPaint();
981  }
982
983  /// The [BlendMode] to use when applying the shader to the child.
984  ///
985  /// The default, [BlendMode.modulate], is useful for applying an alpha blend
986  /// to the child. Other blend modes can be used to create other effects.
987  BlendMode get blendMode => _blendMode;
988  BlendMode _blendMode;
989  set blendMode(BlendMode value) {
990    assert(value != null);
991    if (_blendMode == value)
992      return;
993    _blendMode = value;
994    markNeedsPaint();
995  }
996
997  @override
998  bool get alwaysNeedsCompositing => child != null;
999
1000  @override
1001  void paint(PaintingContext context, Offset offset) {
1002    if (child != null) {
1003      assert(needsCompositing);
1004      layer ??= ShaderMaskLayer();
1005      layer
1006        ..shader = _shaderCallback(offset & size)
1007        ..maskRect = offset & size
1008        ..blendMode = _blendMode;
1009      context.pushLayer(layer, super.paint, offset);
1010    } else {
1011      layer = null;
1012    }
1013  }
1014}
1015
1016/// Applies a filter to the existing painted content and then paints [child].
1017///
1018/// This effect is relatively expensive, especially if the filter is non-local,
1019/// such as a blur.
1020class RenderBackdropFilter extends RenderProxyBox {
1021  /// Creates a backdrop filter.
1022  ///
1023  /// The [filter] argument must not be null.
1024  RenderBackdropFilter({ RenderBox child, @required ui.ImageFilter filter })
1025    : assert(filter != null),
1026      _filter = filter,
1027      super(child);
1028
1029  @override
1030  BackdropFilterLayer get layer => super.layer;
1031
1032  /// The image filter to apply to the existing painted content before painting
1033  /// the child.
1034  ///
1035  /// For example, consider using [new ui.ImageFilter.blur] to create a backdrop
1036  /// blur effect.
1037  ui.ImageFilter get filter => _filter;
1038  ui.ImageFilter _filter;
1039  set filter(ui.ImageFilter value) {
1040    assert(value != null);
1041    if (_filter == value)
1042      return;
1043    _filter = value;
1044    markNeedsPaint();
1045  }
1046
1047  @override
1048  bool get alwaysNeedsCompositing => child != null;
1049
1050  @override
1051  void paint(PaintingContext context, Offset offset) {
1052    if (child != null) {
1053      assert(needsCompositing);
1054      layer ??= BackdropFilterLayer();
1055      layer.filter = _filter;
1056      context.pushLayer(layer, super.paint, offset);
1057    } else {
1058      layer = null;
1059    }
1060  }
1061}
1062
1063/// An interface for providing custom clips.
1064///
1065/// This class is used by a number of clip widgets (e.g., [ClipRect] and
1066/// [ClipPath]).
1067///
1068/// The [getClip] method is called whenever the custom clip needs to be updated.
1069///
1070/// The [shouldReclip] method is called when a new instance of the class
1071/// is provided, to check if the new instance actually represents different
1072/// information.
1073///
1074/// The most efficient way to update the clip provided by this class is to
1075/// supply a `reclip` argument to the constructor of the [CustomClipper]. The
1076/// custom object will listen to this animation and update the clip whenever the
1077/// animation ticks, avoiding both the build and layout phases of the pipeline.
1078///
1079/// See also:
1080///
1081///  * [ClipRect], which can be customized with a [CustomClipper<Rect>].
1082///  * [ClipRRect], which can be customized with a [CustomClipper<RRect>].
1083///  * [ClipOval], which can be customized with a [CustomClipper<Rect>].
1084///  * [ClipPath], which can be customized with a [CustomClipper<Path>].
1085///  * [ShapeBorderClipper], for specifying a clip path using a [ShapeBorder].
1086abstract class CustomClipper<T> {
1087  /// Creates a custom clipper.
1088  ///
1089  /// The clipper will update its clip whenever [reclip] notifies its listeners.
1090  const CustomClipper({ Listenable reclip }) : _reclip = reclip;
1091
1092  final Listenable _reclip;
1093
1094  /// Returns a description of the clip given that the render object being
1095  /// clipped is of the given size.
1096  T getClip(Size size);
1097
1098  /// Returns an approximation of the clip returned by [getClip], as
1099  /// an axis-aligned Rect. This is used by the semantics layer to
1100  /// determine whether widgets should be excluded.
1101  ///
1102  /// By default, this returns a rectangle that is the same size as
1103  /// the RenderObject. If getClip returns a shape that is roughly the
1104  /// same size as the RenderObject (e.g. it's a rounded rectangle
1105  /// with very small arcs in the corners), then this may be adequate.
1106  Rect getApproximateClipRect(Size size) => Offset.zero & size;
1107
1108  /// Called whenever a new instance of the custom clipper delegate class is
1109  /// provided to the clip object, or any time that a new clip object is created
1110  /// with a new instance of the custom painter delegate class (which amounts to
1111  /// the same thing, because the latter is implemented in terms of the former).
1112  ///
1113  /// If the new instance represents different information than the old
1114  /// instance, then the method should return true, otherwise it should return
1115  /// false.
1116  ///
1117  /// If the method returns false, then the [getClip] call might be optimized
1118  /// away.
1119  ///
1120  /// It's possible that the [getClip] method will get called even if
1121  /// [shouldReclip] returns false or if the [shouldReclip] method is never
1122  /// called at all (e.g. if the box changes size).
1123  bool shouldReclip(covariant CustomClipper<T> oldClipper);
1124
1125  @override
1126  String toString() => '$runtimeType';
1127}
1128
1129/// A [CustomClipper] that clips to the outer path of a [ShapeBorder].
1130class ShapeBorderClipper extends CustomClipper<Path> {
1131  /// Creates a [ShapeBorder] clipper.
1132  ///
1133  /// The [shape] argument must not be null.
1134  ///
1135  /// The [textDirection] argument must be provided non-null if [shape]
1136  /// has a text direction dependency (for example if it is expressed in terms
1137  /// of "start" and "end" instead of "left" and "right"). It may be null if
1138  /// the border will not need the text direction to paint itself.
1139  const ShapeBorderClipper({
1140    @required this.shape,
1141    this.textDirection,
1142  }) : assert(shape != null);
1143
1144  /// The shape border whose outer path this clipper clips to.
1145  final ShapeBorder shape;
1146
1147  /// The text direction to use for getting the outer path for [shape].
1148  ///
1149  /// [ShapeBorder]s can depend on the text direction (e.g having a "dent"
1150  /// towards the start of the shape).
1151  final TextDirection textDirection;
1152
1153  /// Returns the outer path of [shape] as the clip.
1154  @override
1155  Path getClip(Size size) {
1156    return shape.getOuterPath(Offset.zero & size, textDirection: textDirection);
1157  }
1158
1159  @override
1160  bool shouldReclip(CustomClipper<Path> oldClipper) {
1161    if (oldClipper.runtimeType != ShapeBorderClipper)
1162      return true;
1163    final ShapeBorderClipper typedOldClipper = oldClipper;
1164    return typedOldClipper.shape != shape
1165        || typedOldClipper.textDirection != textDirection;
1166  }
1167}
1168
1169abstract class _RenderCustomClip<T> extends RenderProxyBox {
1170  _RenderCustomClip({
1171    RenderBox child,
1172    CustomClipper<T> clipper,
1173    Clip clipBehavior = Clip.antiAlias,
1174  }) : assert(clipBehavior != null),
1175       _clipper = clipper,
1176       _clipBehavior = clipBehavior,
1177       super(child);
1178
1179  /// If non-null, determines which clip to use on the child.
1180  CustomClipper<T> get clipper => _clipper;
1181  CustomClipper<T> _clipper;
1182  set clipper(CustomClipper<T> newClipper) {
1183    if (_clipper == newClipper)
1184      return;
1185    final CustomClipper<T> oldClipper = _clipper;
1186    _clipper = newClipper;
1187    assert(newClipper != null || oldClipper != null);
1188    if (newClipper == null || oldClipper == null ||
1189        newClipper.runtimeType != oldClipper.runtimeType ||
1190        newClipper.shouldReclip(oldClipper)) {
1191      _markNeedsClip();
1192    }
1193    if (attached) {
1194      oldClipper?._reclip?.removeListener(_markNeedsClip);
1195      newClipper?._reclip?.addListener(_markNeedsClip);
1196    }
1197  }
1198
1199  @override
1200  void attach(PipelineOwner owner) {
1201    super.attach(owner);
1202    _clipper?._reclip?.addListener(_markNeedsClip);
1203  }
1204
1205  @override
1206  void detach() {
1207    _clipper?._reclip?.removeListener(_markNeedsClip);
1208    super.detach();
1209  }
1210
1211  void _markNeedsClip() {
1212    _clip = null;
1213    markNeedsPaint();
1214    markNeedsSemanticsUpdate();
1215  }
1216
1217  T get _defaultClip;
1218  T _clip;
1219
1220  Clip get clipBehavior => _clipBehavior;
1221  set clipBehavior(Clip value) {
1222    if (value != _clipBehavior) {
1223      _clipBehavior = value;
1224      markNeedsPaint();
1225    }
1226  }
1227  Clip _clipBehavior;
1228
1229  @override
1230  void performLayout() {
1231    final Size oldSize = hasSize ? size : null;
1232    super.performLayout();
1233    if (oldSize != size)
1234      _clip = null;
1235  }
1236
1237  void _updateClip() {
1238    _clip ??= _clipper?.getClip(size) ?? _defaultClip;
1239  }
1240
1241  @override
1242  Rect describeApproximatePaintClip(RenderObject child) {
1243    return _clipper?.getApproximateClipRect(size) ?? Offset.zero & size;
1244  }
1245
1246  Paint _debugPaint;
1247  TextPainter _debugText;
1248  @override
1249  void debugPaintSize(PaintingContext context, Offset offset) {
1250    assert(() {
1251      _debugPaint ??= Paint()
1252        ..shader = ui.Gradient.linear(
1253          const Offset(0.0, 0.0),
1254          const Offset(10.0, 10.0),
1255          <Color>[const Color(0x00000000), const Color(0xFFFF00FF), const Color(0xFFFF00FF), const Color(0x00000000)],
1256          <double>[0.25, 0.25, 0.75, 0.75],
1257          TileMode.repeated,
1258        )
1259        ..strokeWidth = 2.0
1260        ..style = PaintingStyle.stroke;
1261      _debugText ??= TextPainter(
1262        text: const TextSpan(
1263          text: '✂',
1264          style: TextStyle(
1265            color: Color(0xFFFF00FF),
1266              fontSize: 14.0,
1267            ),
1268          ),
1269          textDirection: TextDirection.rtl, // doesn't matter, it's one character
1270        )
1271        ..layout();
1272      return true;
1273    }());
1274  }
1275}
1276
1277/// Clips its child using a rectangle.
1278///
1279/// By default, [RenderClipRect] prevents its child from painting outside its
1280/// bounds, but the size and location of the clip rect can be customized using a
1281/// custom [clipper].
1282class RenderClipRect extends _RenderCustomClip<Rect> {
1283  /// Creates a rectangular clip.
1284  ///
1285  /// If [clipper] is null, the clip will match the layout size and position of
1286  /// the child.
1287  ///
1288  /// The [clipBehavior] cannot be [Clip.none].
1289  RenderClipRect({
1290    RenderBox child,
1291    CustomClipper<Rect> clipper,
1292    Clip clipBehavior = Clip.antiAlias,
1293  }) : super(child: child, clipper: clipper, clipBehavior: clipBehavior);
1294
1295  @override
1296  Rect get _defaultClip => Offset.zero & size;
1297
1298  @override
1299  bool hitTest(BoxHitTestResult result, { Offset position }) {
1300    if (_clipper != null) {
1301      _updateClip();
1302      assert(_clip != null);
1303      if (!_clip.contains(position))
1304        return false;
1305    }
1306    return super.hitTest(result, position: position);
1307  }
1308
1309  @override
1310  void paint(PaintingContext context, Offset offset) {
1311    if (child != null) {
1312      _updateClip();
1313      layer = context.pushClipRect(needsCompositing, offset, _clip, super.paint, clipBehavior: clipBehavior, oldLayer: layer);
1314    } else {
1315      layer = null;
1316    }
1317  }
1318
1319  @override
1320  void debugPaintSize(PaintingContext context, Offset offset) {
1321    assert(() {
1322      if (child != null) {
1323        super.debugPaintSize(context, offset);
1324        context.canvas.drawRect(_clip.shift(offset), _debugPaint);
1325        _debugText.paint(context.canvas, offset + Offset(_clip.width / 8.0, -_debugText.text.style.fontSize * 1.1));
1326      }
1327      return true;
1328    }());
1329  }
1330}
1331
1332/// Clips its child using a rounded rectangle.
1333///
1334/// By default, [RenderClipRRect] uses its own bounds as the base rectangle for
1335/// the clip, but the size and location of the clip can be customized using a
1336/// custom [clipper].
1337class RenderClipRRect extends _RenderCustomClip<RRect> {
1338  /// Creates a rounded-rectangular clip.
1339  ///
1340  /// The [borderRadius] defaults to [BorderRadius.zero], i.e. a rectangle with
1341  /// right-angled corners.
1342  ///
1343  /// If [clipper] is non-null, then [borderRadius] is ignored.
1344  ///
1345  /// The [clipBehavior] cannot be [Clip.none].
1346  RenderClipRRect({
1347    RenderBox child,
1348    BorderRadius borderRadius = BorderRadius.zero,
1349    CustomClipper<RRect> clipper,
1350    Clip clipBehavior = Clip.antiAlias,
1351  }) : assert(clipBehavior != Clip.none),
1352       _borderRadius = borderRadius,
1353       super(child: child, clipper: clipper, clipBehavior: clipBehavior) {
1354    assert(_borderRadius != null || clipper != null);
1355  }
1356
1357  /// The border radius of the rounded corners.
1358  ///
1359  /// Values are clamped so that horizontal and vertical radii sums do not
1360  /// exceed width/height.
1361  ///
1362  /// This value is ignored if [clipper] is non-null.
1363  BorderRadius get borderRadius => _borderRadius;
1364  BorderRadius _borderRadius;
1365  set borderRadius(BorderRadius value) {
1366    assert(value != null);
1367    if (_borderRadius == value)
1368      return;
1369    _borderRadius = value;
1370    _markNeedsClip();
1371  }
1372
1373  @override
1374  RRect get _defaultClip => _borderRadius.toRRect(Offset.zero & size);
1375
1376  @override
1377  bool hitTest(BoxHitTestResult result, { Offset position }) {
1378    if (_clipper != null) {
1379      _updateClip();
1380      assert(_clip != null);
1381      if (!_clip.contains(position))
1382        return false;
1383    }
1384    return super.hitTest(result, position: position);
1385  }
1386
1387  @override
1388  void paint(PaintingContext context, Offset offset) {
1389    if (child != null) {
1390      _updateClip();
1391      layer = context.pushClipRRect(needsCompositing, offset, _clip.outerRect, _clip, super.paint, clipBehavior: clipBehavior, oldLayer: layer);
1392    } else {
1393      layer = null;
1394    }
1395  }
1396
1397  @override
1398  void debugPaintSize(PaintingContext context, Offset offset) {
1399    assert(() {
1400      if (child != null) {
1401        super.debugPaintSize(context, offset);
1402        context.canvas.drawRRect(_clip.shift(offset), _debugPaint);
1403        _debugText.paint(context.canvas, offset + Offset(_clip.tlRadiusX, -_debugText.text.style.fontSize * 1.1));
1404      }
1405      return true;
1406    }());
1407  }
1408}
1409
1410/// Clips its child using an oval.
1411///
1412/// By default, inscribes an axis-aligned oval into its layout dimensions and
1413/// prevents its child from painting outside that oval, but the size and
1414/// location of the clip oval can be customized using a custom [clipper].
1415class RenderClipOval extends _RenderCustomClip<Rect> {
1416  /// Creates an oval-shaped clip.
1417  ///
1418  /// If [clipper] is null, the oval will be inscribed into the layout size and
1419  /// position of the child.
1420  ///
1421  /// The [clipBehavior] cannot be [Clip.none].
1422  RenderClipOval({
1423    RenderBox child,
1424    CustomClipper<Rect> clipper,
1425    Clip clipBehavior = Clip.antiAlias,
1426  }) : assert(clipBehavior != Clip.none),
1427       super(child: child, clipper: clipper, clipBehavior: clipBehavior);
1428
1429  Rect _cachedRect;
1430  Path _cachedPath;
1431
1432  Path _getClipPath(Rect rect) {
1433    if (rect != _cachedRect) {
1434      _cachedRect = rect;
1435      _cachedPath = Path()..addOval(_cachedRect);
1436    }
1437    return _cachedPath;
1438  }
1439
1440  @override
1441  Rect get _defaultClip => Offset.zero & size;
1442
1443  @override
1444  bool hitTest(BoxHitTestResult result, { Offset position }) {
1445    _updateClip();
1446    assert(_clip != null);
1447    final Offset center = _clip.center;
1448    // convert the position to an offset from the center of the unit circle
1449    final Offset offset = Offset((position.dx - center.dx) / _clip.width,
1450                                     (position.dy - center.dy) / _clip.height);
1451    // check if the point is outside the unit circle
1452    if (offset.distanceSquared > 0.25) // x^2 + y^2 > r^2
1453      return false;
1454    return super.hitTest(result, position: position);
1455  }
1456
1457  @override
1458  void paint(PaintingContext context, Offset offset) {
1459    if (child != null) {
1460      _updateClip();
1461      layer = context.pushClipPath(needsCompositing, offset, _clip, _getClipPath(_clip), super.paint, clipBehavior: clipBehavior, oldLayer: layer);
1462    } else {
1463      layer = null;
1464    }
1465  }
1466
1467  @override
1468  void debugPaintSize(PaintingContext context, Offset offset) {
1469    assert(() {
1470      if (child != null) {
1471        super.debugPaintSize(context, offset);
1472        context.canvas.drawPath(_getClipPath(_clip).shift(offset), _debugPaint);
1473        _debugText.paint(context.canvas, offset + Offset((_clip.width - _debugText.width) / 2.0, -_debugText.text.style.fontSize * 1.1));
1474      }
1475      return true;
1476    }());
1477  }
1478}
1479
1480/// Clips its child using a path.
1481///
1482/// Takes a delegate whose primary method returns a path that should
1483/// be used to prevent the child from painting outside the path.
1484///
1485/// Clipping to a path is expensive. Certain shapes have more
1486/// optimized render objects:
1487///
1488///  * To clip to a rectangle, consider [RenderClipRect].
1489///  * To clip to an oval or circle, consider [RenderClipOval].
1490///  * To clip to a rounded rectangle, consider [RenderClipRRect].
1491class RenderClipPath extends _RenderCustomClip<Path> {
1492  /// Creates a path clip.
1493  ///
1494  /// If [clipper] is null, the clip will be a rectangle that matches the layout
1495  /// size and location of the child. However, rather than use this default,
1496  /// consider using a [RenderClipRect], which can achieve the same effect more
1497  /// efficiently.
1498  ///
1499  /// The [clipBehavior] cannot be [Clip.none].
1500  RenderClipPath({
1501    RenderBox child,
1502    CustomClipper<Path> clipper,
1503    Clip clipBehavior = Clip.antiAlias,
1504  }) : assert(clipBehavior != Clip.none),
1505       super(child: child, clipper: clipper, clipBehavior: clipBehavior);
1506
1507  @override
1508  Path get _defaultClip => Path()..addRect(Offset.zero & size);
1509
1510  @override
1511  bool hitTest(BoxHitTestResult result, { Offset position }) {
1512    if (_clipper != null) {
1513      _updateClip();
1514      assert(_clip != null);
1515      if (!_clip.contains(position))
1516        return false;
1517    }
1518    return super.hitTest(result, position: position);
1519  }
1520
1521  @override
1522  void paint(PaintingContext context, Offset offset) {
1523    if (child != null) {
1524      _updateClip();
1525      layer = context.pushClipPath(needsCompositing, offset, Offset.zero & size, _clip, super.paint, clipBehavior: clipBehavior, oldLayer: layer);
1526    } else {
1527      layer = null;
1528    }
1529  }
1530
1531  @override
1532  void debugPaintSize(PaintingContext context, Offset offset) {
1533    assert(() {
1534      if (child != null) {
1535        super.debugPaintSize(context, offset);
1536        context.canvas.drawPath(_clip.shift(offset), _debugPaint);
1537        _debugText.paint(context.canvas, offset);
1538      }
1539      return true;
1540    }());
1541  }
1542}
1543
1544/// A physical model layer casts a shadow based on its [elevation].
1545///
1546/// The concrete implementations [RenderPhysicalModel] and [RenderPhysicalShape]
1547/// determine the actual shape of the physical model.
1548abstract class _RenderPhysicalModelBase<T> extends _RenderCustomClip<T> {
1549  /// The [shape], [elevation], [color], and [shadowColor] must not be null.
1550  /// Additionally, the [elevation] must be non-negative.
1551  _RenderPhysicalModelBase({
1552    @required RenderBox child,
1553    @required double elevation,
1554    @required Color color,
1555    @required Color shadowColor,
1556    Clip clipBehavior = Clip.none,
1557    CustomClipper<T> clipper,
1558  }) : assert(elevation != null && elevation >= 0.0),
1559       assert(color != null),
1560       assert(shadowColor != null),
1561       assert(clipBehavior != null),
1562       _elevation = elevation,
1563       _color = color,
1564       _shadowColor = shadowColor,
1565       super(child: child, clipBehavior: clipBehavior, clipper: clipper);
1566
1567  /// The z-coordinate relative to the parent at which to place this material.
1568  ///
1569  /// The value is non-negative.
1570  ///
1571  /// If [debugDisableShadows] is set, this value is ignored and no shadow is
1572  /// drawn (an outline is rendered instead).
1573  double get elevation => _elevation;
1574  double _elevation;
1575  set elevation(double value) {
1576    assert(value != null && value >= 0.0);
1577    if (elevation == value)
1578      return;
1579    final bool didNeedCompositing = alwaysNeedsCompositing;
1580    _elevation = value;
1581    if (didNeedCompositing != alwaysNeedsCompositing)
1582      markNeedsCompositingBitsUpdate();
1583    markNeedsPaint();
1584  }
1585
1586  /// The shadow color.
1587  Color get shadowColor => _shadowColor;
1588  Color _shadowColor;
1589  set shadowColor(Color value) {
1590    assert(value != null);
1591    if (shadowColor == value)
1592      return;
1593    _shadowColor = value;
1594    markNeedsPaint();
1595  }
1596
1597  /// The background color.
1598  Color get color => _color;
1599  Color _color;
1600  set color(Color value) {
1601    assert(value != null);
1602    if (color == value)
1603      return;
1604    _color = value;
1605    markNeedsPaint();
1606  }
1607
1608  @override
1609  bool get alwaysNeedsCompositing => true;
1610
1611  @override
1612  void describeSemanticsConfiguration(SemanticsConfiguration config) {
1613    super.describeSemanticsConfiguration(config);
1614    config.elevation = elevation;
1615  }
1616
1617  @override
1618  void debugFillProperties(DiagnosticPropertiesBuilder description) {
1619    super.debugFillProperties(description);
1620    description.add(DoubleProperty('elevation', elevation));
1621    description.add(ColorProperty('color', color));
1622    description.add(ColorProperty('shadowColor', color));
1623  }
1624}
1625
1626/// Creates a physical model layer that clips its child to a rounded
1627/// rectangle.
1628///
1629/// A physical model layer casts a shadow based on its [elevation].
1630class RenderPhysicalModel extends _RenderPhysicalModelBase<RRect> {
1631  /// Creates a rounded-rectangular clip.
1632  ///
1633  /// The [color] is required.
1634  ///
1635  /// The [shape], [elevation], [color], and [shadowColor] must not be null.
1636  /// Additionally, the [elevation] must be non-negative.
1637  RenderPhysicalModel({
1638    RenderBox child,
1639    BoxShape shape = BoxShape.rectangle,
1640    Clip clipBehavior = Clip.none,
1641    BorderRadius borderRadius,
1642    double elevation = 0.0,
1643    @required Color color,
1644    Color shadowColor = const Color(0xFF000000),
1645  }) : assert(shape != null),
1646       assert(clipBehavior != null),
1647       assert(elevation != null && elevation >= 0.0),
1648       assert(color != null),
1649       assert(shadowColor != null),
1650       _shape = shape,
1651       _borderRadius = borderRadius,
1652       super(
1653         clipBehavior: clipBehavior,
1654         child: child,
1655         elevation: elevation,
1656         color: color,
1657         shadowColor: shadowColor
1658       );
1659
1660  @override
1661  PhysicalModelLayer get layer => super.layer;
1662
1663  /// The shape of the layer.
1664  ///
1665  /// Defaults to [BoxShape.rectangle]. The [borderRadius] affects the corners
1666  /// of the rectangle.
1667  BoxShape get shape => _shape;
1668  BoxShape _shape;
1669  set shape(BoxShape value) {
1670    assert(value != null);
1671    if (shape == value)
1672      return;
1673    _shape = value;
1674    _markNeedsClip();
1675  }
1676
1677  /// The border radius of the rounded corners.
1678  ///
1679  /// Values are clamped so that horizontal and vertical radii sums do not
1680  /// exceed width/height.
1681  ///
1682  /// This property is ignored if the [shape] is not [BoxShape.rectangle].
1683  ///
1684  /// The value null is treated like [BorderRadius.zero].
1685  BorderRadius get borderRadius => _borderRadius;
1686  BorderRadius _borderRadius;
1687  set borderRadius(BorderRadius value) {
1688    if (borderRadius == value)
1689      return;
1690    _borderRadius = value;
1691    _markNeedsClip();
1692  }
1693
1694  @override
1695  RRect get _defaultClip {
1696    assert(hasSize);
1697    assert(_shape != null);
1698    switch (_shape) {
1699      case BoxShape.rectangle:
1700        return (borderRadius ?? BorderRadius.zero).toRRect(Offset.zero & size);
1701      case BoxShape.circle:
1702        final Rect rect = Offset.zero & size;
1703        return RRect.fromRectXY(rect, rect.width / 2, rect.height / 2);
1704    }
1705    return null;
1706  }
1707
1708  @override
1709  bool hitTest(BoxHitTestResult result, { Offset position }) {
1710    if (_clipper != null) {
1711      _updateClip();
1712      assert(_clip != null);
1713      if (!_clip.contains(position))
1714        return false;
1715    }
1716    return super.hitTest(result, position: position);
1717  }
1718
1719  @override
1720  void paint(PaintingContext context, Offset offset) {
1721    if (child != null) {
1722      _updateClip();
1723      final RRect offsetRRect = _clip.shift(offset);
1724      final Rect offsetBounds = offsetRRect.outerRect;
1725      final Path offsetRRectAsPath = Path()..addRRect(offsetRRect);
1726      bool paintShadows = true;
1727      assert(() {
1728        if (debugDisableShadows) {
1729          if (elevation > 0.0) {
1730            context.canvas.drawRRect(
1731              offsetRRect,
1732              Paint()
1733                ..color = shadowColor
1734                ..style = PaintingStyle.stroke
1735                ..strokeWidth = elevation * 2.0,
1736            );
1737          }
1738          paintShadows = false;
1739        }
1740        return true;
1741      }());
1742      layer ??= PhysicalModelLayer();
1743      layer
1744        ..clipPath = offsetRRectAsPath
1745        ..clipBehavior = clipBehavior
1746        ..elevation = paintShadows ? elevation : 0.0
1747        ..color = color
1748        ..shadowColor = shadowColor;
1749      context.pushLayer(layer, super.paint, offset, childPaintBounds: offsetBounds);
1750      assert(() {
1751        layer.debugCreator = debugCreator;
1752        return true;
1753      }());
1754    } else {
1755      layer = null;
1756    }
1757  }
1758
1759  @override
1760  void debugFillProperties(DiagnosticPropertiesBuilder description) {
1761    super.debugFillProperties(description);
1762    description.add(DiagnosticsProperty<BoxShape>('shape', shape));
1763    description.add(DiagnosticsProperty<BorderRadius>('borderRadius', borderRadius));
1764  }
1765}
1766
1767/// Creates a physical shape layer that clips its child to a [Path].
1768///
1769/// A physical shape layer casts a shadow based on its [elevation].
1770///
1771/// See also:
1772///
1773///  * [RenderPhysicalModel], which is optimized for rounded rectangles and
1774///    circles.
1775class RenderPhysicalShape extends _RenderPhysicalModelBase<Path> {
1776  /// Creates an arbitrary shape clip.
1777  ///
1778  /// The [color] and [shape] parameters are required.
1779  ///
1780  /// The [clipper], [elevation], [color] and [shadowColor] must not be null.
1781  /// Additionally, the [elevation] must be non-negative.
1782  RenderPhysicalShape({
1783    RenderBox child,
1784    @required CustomClipper<Path> clipper,
1785    Clip clipBehavior = Clip.none,
1786    double elevation = 0.0,
1787    @required Color color,
1788    Color shadowColor = const Color(0xFF000000),
1789  }) : assert(clipper != null),
1790       assert(elevation != null && elevation >= 0.0),
1791       assert(color != null),
1792       assert(shadowColor != null),
1793       super(
1794         child: child,
1795         elevation: elevation,
1796         color: color,
1797         shadowColor: shadowColor,
1798         clipper: clipper,
1799         clipBehavior: clipBehavior
1800       );
1801
1802  @override
1803  PhysicalModelLayer get layer => super.layer;
1804
1805  @override
1806  Path get _defaultClip => Path()..addRect(Offset.zero & size);
1807
1808  @override
1809  bool hitTest(BoxHitTestResult result, { Offset position }) {
1810    if (_clipper != null) {
1811      _updateClip();
1812      assert(_clip != null);
1813      if (!_clip.contains(position))
1814        return false;
1815    }
1816    return super.hitTest(result, position: position);
1817  }
1818
1819  @override
1820  void paint(PaintingContext context, Offset offset) {
1821    if (child != null) {
1822      _updateClip();
1823      final Rect offsetBounds = offset & size;
1824      final Path offsetPath = _clip.shift(offset);
1825      bool paintShadows = true;
1826      assert(() {
1827        if (debugDisableShadows) {
1828          if (elevation > 0.0) {
1829            context.canvas.drawPath(
1830              offsetPath,
1831              Paint()
1832                ..color = shadowColor
1833                ..style = PaintingStyle.stroke
1834                ..strokeWidth = elevation * 2.0,
1835            );
1836          }
1837          paintShadows = false;
1838        }
1839        return true;
1840      }());
1841      layer ??= PhysicalModelLayer();
1842      layer
1843        ..clipPath = offsetPath
1844        ..clipBehavior = clipBehavior
1845        ..elevation = paintShadows ? elevation : 0.0
1846        ..color = color
1847        ..shadowColor = shadowColor;
1848      context.pushLayer(layer, super.paint, offset, childPaintBounds: offsetBounds);
1849      assert(() {
1850        layer.debugCreator = debugCreator;
1851        return true;
1852      }());
1853    } else {
1854      layer = null;
1855    }
1856  }
1857
1858  @override
1859  void debugFillProperties(DiagnosticPropertiesBuilder description) {
1860    super.debugFillProperties(description);
1861    description.add(DiagnosticsProperty<CustomClipper<Path>>('clipper', clipper));
1862  }
1863}
1864
1865/// Where to paint a box decoration.
1866enum DecorationPosition {
1867  /// Paint the box decoration behind the children.
1868  background,
1869
1870  /// Paint the box decoration in front of the children.
1871  foreground,
1872}
1873
1874/// Paints a [Decoration] either before or after its child paints.
1875class RenderDecoratedBox extends RenderProxyBox {
1876  /// Creates a decorated box.
1877  ///
1878  /// The [decoration], [position], and [configuration] arguments must not be
1879  /// null. By default the decoration paints behind the child.
1880  ///
1881  /// The [ImageConfiguration] will be passed to the decoration (with the size
1882  /// filled in) to let it resolve images.
1883  RenderDecoratedBox({
1884    @required Decoration decoration,
1885    DecorationPosition position = DecorationPosition.background,
1886    ImageConfiguration configuration = ImageConfiguration.empty,
1887    RenderBox child,
1888  }) : assert(decoration != null),
1889       assert(position != null),
1890       assert(configuration != null),
1891       _decoration = decoration,
1892       _position = position,
1893       _configuration = configuration,
1894       super(child);
1895
1896  BoxPainter _painter;
1897
1898  /// What decoration to paint.
1899  ///
1900  /// Commonly a [BoxDecoration].
1901  Decoration get decoration => _decoration;
1902  Decoration _decoration;
1903  set decoration(Decoration value) {
1904    assert(value != null);
1905    if (value == _decoration)
1906      return;
1907    _painter?.dispose();
1908    _painter = null;
1909    _decoration = value;
1910    markNeedsPaint();
1911  }
1912
1913  /// Whether to paint the box decoration behind or in front of the child.
1914  DecorationPosition get position => _position;
1915  DecorationPosition _position;
1916  set position(DecorationPosition value) {
1917    assert(value != null);
1918    if (value == _position)
1919      return;
1920    _position = value;
1921    markNeedsPaint();
1922  }
1923
1924  /// The settings to pass to the decoration when painting, so that it can
1925  /// resolve images appropriately. See [ImageProvider.resolve] and
1926  /// [BoxPainter.paint].
1927  ///
1928  /// The [ImageConfiguration.textDirection] field is also used by
1929  /// direction-sensitive [Decoration]s for painting and hit-testing.
1930  ImageConfiguration get configuration => _configuration;
1931  ImageConfiguration _configuration;
1932  set configuration(ImageConfiguration value) {
1933    assert(value != null);
1934    if (value == _configuration)
1935      return;
1936    _configuration = value;
1937    markNeedsPaint();
1938  }
1939
1940  @override
1941  void detach() {
1942    _painter?.dispose();
1943    _painter = null;
1944    super.detach();
1945    // Since we're disposing of our painter, we won't receive change
1946    // notifications. We mark ourselves as needing paint so that we will
1947    // resubscribe to change notifications. If we didn't do this, then, for
1948    // example, animated GIFs would stop animating when a DecoratedBox gets
1949    // moved around the tree due to GlobalKey reparenting.
1950    markNeedsPaint();
1951  }
1952
1953  @override
1954  bool hitTestSelf(Offset position) {
1955    return _decoration.hitTest(size, position, textDirection: configuration.textDirection);
1956  }
1957
1958  @override
1959  void paint(PaintingContext context, Offset offset) {
1960    assert(size.width != null);
1961    assert(size.height != null);
1962    _painter ??= _decoration.createBoxPainter(markNeedsPaint);
1963    final ImageConfiguration filledConfiguration = configuration.copyWith(size: size);
1964    if (position == DecorationPosition.background) {
1965      int debugSaveCount;
1966      assert(() {
1967        debugSaveCount = context.canvas.getSaveCount();
1968        return true;
1969      }());
1970      _painter.paint(context.canvas, offset, filledConfiguration);
1971      assert(() {
1972        if (debugSaveCount != context.canvas.getSaveCount()) {
1973          throw FlutterError(
1974            '${_decoration.runtimeType} painter had mismatching save and restore calls.\n'
1975            'Before painting the decoration, the canvas save count was $debugSaveCount. '
1976            'After painting it, the canvas save count was ${context.canvas.getSaveCount()}. '
1977            'Every call to save() or saveLayer() must be matched by a call to restore().\n'
1978            'The decoration was:\n'
1979            '  $decoration\n'
1980            'The painter was:\n'
1981            '  $_painter'
1982          );
1983        }
1984        return true;
1985      }());
1986      if (decoration.isComplex)
1987        context.setIsComplexHint();
1988    }
1989    super.paint(context, offset);
1990    if (position == DecorationPosition.foreground) {
1991      _painter.paint(context.canvas, offset, filledConfiguration);
1992      if (decoration.isComplex)
1993        context.setIsComplexHint();
1994    }
1995  }
1996
1997  @override
1998  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
1999    super.debugFillProperties(properties);
2000    properties.add(_decoration.toDiagnosticsNode(name: 'decoration'));
2001    properties.add(DiagnosticsProperty<ImageConfiguration>('configuration', configuration));
2002  }
2003}
2004
2005/// Applies a transformation before painting its child.
2006class RenderTransform extends RenderProxyBox {
2007  /// Creates a render object that transforms its child.
2008  ///
2009  /// The [transform] argument must not be null.
2010  RenderTransform({
2011    @required Matrix4 transform,
2012    Offset origin,
2013    AlignmentGeometry alignment,
2014    TextDirection textDirection,
2015    this.transformHitTests = true,
2016    RenderBox child,
2017  }) : assert(transform != null),
2018       super(child) {
2019    this.transform = transform;
2020    this.alignment = alignment;
2021    this.textDirection = textDirection;
2022    this.origin = origin;
2023  }
2024
2025  /// The origin of the coordinate system (relative to the upper left corner of
2026  /// this render object) in which to apply the matrix.
2027  ///
2028  /// Setting an origin is equivalent to conjugating the transform matrix by a
2029  /// translation. This property is provided just for convenience.
2030  Offset get origin => _origin;
2031  Offset _origin;
2032  set origin(Offset value) {
2033    if (_origin == value)
2034      return;
2035    _origin = value;
2036    markNeedsPaint();
2037    markNeedsSemanticsUpdate();
2038  }
2039
2040  /// The alignment of the origin, relative to the size of the box.
2041  ///
2042  /// This is equivalent to setting an origin based on the size of the box.
2043  /// If it is specified at the same time as an offset, both are applied.
2044  ///
2045  /// An [AlignmentDirectional.start] value is the same as an [Alignment]
2046  /// whose [Alignment.x] value is `-1.0` if [textDirection] is
2047  /// [TextDirection.ltr], and `1.0` if [textDirection] is [TextDirection.rtl].
2048  /// Similarly [AlignmentDirectional.end] is the same as an [Alignment]
2049  /// whose [Alignment.x] value is `1.0` if [textDirection] is
2050  /// [TextDirection.ltr], and `-1.0` if [textDirection] is [TextDirection.rtl].
2051  AlignmentGeometry get alignment => _alignment;
2052  AlignmentGeometry _alignment;
2053  set alignment(AlignmentGeometry value) {
2054    if (_alignment == value)
2055      return;
2056    _alignment = value;
2057    markNeedsPaint();
2058    markNeedsSemanticsUpdate();
2059  }
2060
2061  /// The text direction with which to resolve [alignment].
2062  ///
2063  /// This may be changed to null, but only after [alignment] has been changed
2064  /// to a value that does not depend on the direction.
2065  TextDirection get textDirection => _textDirection;
2066  TextDirection _textDirection;
2067  set textDirection(TextDirection value) {
2068    if (_textDirection == value)
2069      return;
2070    _textDirection = value;
2071    markNeedsPaint();
2072    markNeedsSemanticsUpdate();
2073  }
2074
2075  /// When set to true, hit tests are performed based on the position of the
2076  /// child as it is painted. When set to false, hit tests are performed
2077  /// ignoring the transformation.
2078  ///
2079  /// [applyPaintTransform], and therefore [localToGlobal] and [globalToLocal],
2080  /// always honor the transformation, regardless of the value of this property.
2081  bool transformHitTests;
2082
2083  // Note the lack of a getter for transform because Matrix4 is not immutable
2084  Matrix4 _transform;
2085
2086  /// The matrix to transform the child by during painting.
2087  set transform(Matrix4 value) {
2088    assert(value != null);
2089    if (_transform == value)
2090      return;
2091    _transform = Matrix4.copy(value);
2092    markNeedsPaint();
2093    markNeedsSemanticsUpdate();
2094  }
2095
2096  /// Sets the transform to the identity matrix.
2097  void setIdentity() {
2098    _transform.setIdentity();
2099    markNeedsPaint();
2100    markNeedsSemanticsUpdate();
2101  }
2102
2103  /// Concatenates a rotation about the x axis into the transform.
2104  void rotateX(double radians) {
2105    _transform.rotateX(radians);
2106    markNeedsPaint();
2107    markNeedsSemanticsUpdate();
2108  }
2109
2110  /// Concatenates a rotation about the y axis into the transform.
2111  void rotateY(double radians) {
2112    _transform.rotateY(radians);
2113    markNeedsPaint();
2114    markNeedsSemanticsUpdate();
2115  }
2116
2117  /// Concatenates a rotation about the z axis into the transform.
2118  void rotateZ(double radians) {
2119    _transform.rotateZ(radians);
2120    markNeedsPaint();
2121    markNeedsSemanticsUpdate();
2122  }
2123
2124  /// Concatenates a translation by (x, y, z) into the transform.
2125  void translate(double x, [ double y = 0.0, double z = 0.0 ]) {
2126    _transform.translate(x, y, z);
2127    markNeedsPaint();
2128    markNeedsSemanticsUpdate();
2129  }
2130
2131  /// Concatenates a scale into the transform.
2132  void scale(double x, [ double y, double z ]) {
2133    _transform.scale(x, y, z);
2134    markNeedsPaint();
2135    markNeedsSemanticsUpdate();
2136  }
2137
2138  Matrix4 get _effectiveTransform {
2139    final Alignment resolvedAlignment = alignment?.resolve(textDirection);
2140    if (_origin == null && resolvedAlignment == null)
2141      return _transform;
2142    final Matrix4 result = Matrix4.identity();
2143    if (_origin != null)
2144      result.translate(_origin.dx, _origin.dy);
2145    Offset translation;
2146    if (resolvedAlignment != null) {
2147      translation = resolvedAlignment.alongSize(size);
2148      result.translate(translation.dx, translation.dy);
2149    }
2150    result.multiply(_transform);
2151    if (resolvedAlignment != null)
2152      result.translate(-translation.dx, -translation.dy);
2153    if (_origin != null)
2154      result.translate(-_origin.dx, -_origin.dy);
2155    return result;
2156  }
2157
2158  @override
2159  bool hitTest(BoxHitTestResult result, { Offset position }) {
2160    // RenderTransform objects don't check if they are
2161    // themselves hit, because it's confusing to think about
2162    // how the untransformed size and the child's transformed
2163    // position interact.
2164    return hitTestChildren(result, position: position);
2165  }
2166
2167  @override
2168  bool hitTestChildren(BoxHitTestResult result, { Offset position }) {
2169    assert(!transformHitTests || _effectiveTransform != null);
2170    return result.addWithPaintTransform(
2171      transform: transformHitTests ? _effectiveTransform : null,
2172      position: position,
2173      hitTest: (BoxHitTestResult result, Offset position) {
2174        return super.hitTestChildren(result, position: position);
2175      },
2176    );
2177  }
2178
2179  @override
2180  void paint(PaintingContext context, Offset offset) {
2181    if (child != null) {
2182      final Matrix4 transform = _effectiveTransform;
2183      final Offset childOffset = MatrixUtils.getAsTranslation(transform);
2184      if (childOffset == null) {
2185        layer = context.pushTransform(needsCompositing, offset, transform, super.paint, oldLayer: layer);
2186      } else {
2187        super.paint(context, offset + childOffset);
2188        layer = null;
2189      }
2190    }
2191  }
2192
2193  @override
2194  void applyPaintTransform(RenderBox child, Matrix4 transform) {
2195    transform.multiply(_effectiveTransform);
2196  }
2197
2198  @override
2199  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
2200    super.debugFillProperties(properties);
2201    properties.add(TransformProperty('transform matrix', _transform));
2202    properties.add(DiagnosticsProperty<Offset>('origin', origin));
2203    properties.add(DiagnosticsProperty<Alignment>('alignment', alignment));
2204    properties.add(EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
2205    properties.add(DiagnosticsProperty<bool>('transformHitTests', transformHitTests));
2206  }
2207}
2208
2209/// Scales and positions its child within itself according to [fit].
2210class RenderFittedBox extends RenderProxyBox {
2211  /// Scales and positions its child within itself.
2212  ///
2213  /// The [fit] and [alignment] arguments must not be null.
2214  RenderFittedBox({
2215    BoxFit fit = BoxFit.contain,
2216    AlignmentGeometry alignment = Alignment.center,
2217    TextDirection textDirection,
2218    RenderBox child,
2219  }) : assert(fit != null),
2220       assert(alignment != null),
2221       _fit = fit,
2222       _alignment = alignment,
2223       _textDirection = textDirection,
2224       super(child);
2225
2226  Alignment _resolvedAlignment;
2227
2228  void _resolve() {
2229    if (_resolvedAlignment != null)
2230      return;
2231    _resolvedAlignment = alignment.resolve(textDirection);
2232  }
2233
2234  void _markNeedResolution() {
2235    _resolvedAlignment = null;
2236    markNeedsPaint();
2237  }
2238
2239  /// How to inscribe the child into the space allocated during layout.
2240  BoxFit get fit => _fit;
2241  BoxFit _fit;
2242  set fit(BoxFit value) {
2243    assert(value != null);
2244    if (_fit == value)
2245      return;
2246    _fit = value;
2247    _clearPaintData();
2248    markNeedsPaint();
2249  }
2250
2251  /// How to align the child within its parent's bounds.
2252  ///
2253  /// An alignment of (0.0, 0.0) aligns the child to the top-left corner of its
2254  /// parent's bounds. An alignment of (1.0, 0.5) aligns the child to the middle
2255  /// of the right edge of its parent's bounds.
2256  ///
2257  /// If this is set to an [AlignmentDirectional] object, then
2258  /// [textDirection] must not be null.
2259  AlignmentGeometry get alignment => _alignment;
2260  AlignmentGeometry _alignment;
2261  set alignment(AlignmentGeometry value) {
2262    assert(value != null);
2263    if (_alignment == value)
2264      return;
2265    _alignment = value;
2266    _clearPaintData();
2267    _markNeedResolution();
2268  }
2269
2270  /// The text direction with which to resolve [alignment].
2271  ///
2272  /// This may be changed to null, but only after [alignment] has been changed
2273  /// to a value that does not depend on the direction.
2274  TextDirection get textDirection => _textDirection;
2275  TextDirection _textDirection;
2276  set textDirection(TextDirection value) {
2277    if (_textDirection == value)
2278      return;
2279    _textDirection = value;
2280    _clearPaintData();
2281    _markNeedResolution();
2282  }
2283
2284  // TODO(ianh): The intrinsic dimensions of this box are wrong.
2285
2286  @override
2287  void performLayout() {
2288    if (child != null) {
2289      child.layout(const BoxConstraints(), parentUsesSize: true);
2290      size = constraints.constrainSizeAndAttemptToPreserveAspectRatio(child.size);
2291      _clearPaintData();
2292    } else {
2293      size = constraints.smallest;
2294    }
2295  }
2296
2297  bool _hasVisualOverflow;
2298  Matrix4 _transform;
2299
2300  void _clearPaintData() {
2301    _hasVisualOverflow = null;
2302    _transform = null;
2303  }
2304
2305  void _updatePaintData() {
2306    if (_transform != null)
2307      return;
2308
2309    if (child == null) {
2310      _hasVisualOverflow = false;
2311      _transform = Matrix4.identity();
2312    } else {
2313      _resolve();
2314      final Size childSize = child.size;
2315      final FittedSizes sizes = applyBoxFit(_fit, childSize, size);
2316      final double scaleX = sizes.destination.width / sizes.source.width;
2317      final double scaleY = sizes.destination.height / sizes.source.height;
2318      final Rect sourceRect = _resolvedAlignment.inscribe(sizes.source, Offset.zero & childSize);
2319      final Rect destinationRect = _resolvedAlignment.inscribe(sizes.destination, Offset.zero & size);
2320      _hasVisualOverflow = sourceRect.width < childSize.width || sourceRect.height < childSize.height;
2321      assert(scaleX.isFinite && scaleY.isFinite);
2322      _transform = Matrix4.translationValues(destinationRect.left, destinationRect.top, 0.0)
2323        ..scale(scaleX, scaleY, 1.0)
2324        ..translate(-sourceRect.left, -sourceRect.top);
2325      assert(_transform.storage.every((double value) => value.isFinite));
2326    }
2327  }
2328
2329  TransformLayer _paintChildWithTransform(PaintingContext context, Offset offset) {
2330    final Offset childOffset = MatrixUtils.getAsTranslation(_transform);
2331    if (childOffset == null)
2332      return context.pushTransform(needsCompositing, offset, _transform, super.paint,
2333          oldLayer: layer is TransformLayer ? layer : null);
2334    else
2335      super.paint(context, offset + childOffset);
2336    return null;
2337  }
2338
2339  @override
2340  void paint(PaintingContext context, Offset offset) {
2341    if (size.isEmpty || child.size.isEmpty)
2342      return;
2343    _updatePaintData();
2344    if (child != null) {
2345      if (_hasVisualOverflow)
2346        layer = context.pushClipRect(needsCompositing, offset, Offset.zero & size, _paintChildWithTransform,
2347            oldLayer: layer is ClipRectLayer ? layer : null);
2348      else
2349        layer = _paintChildWithTransform(context, offset);
2350    }
2351  }
2352
2353  @override
2354  bool hitTestChildren(BoxHitTestResult result, { Offset position }) {
2355    if (size.isEmpty || child?.size?.isEmpty == true)
2356      return false;
2357    _updatePaintData();
2358    return result.addWithPaintTransform(
2359      transform: _transform,
2360      position: position,
2361      hitTest: (BoxHitTestResult result, Offset position) {
2362        return super.hitTestChildren(result, position: position);
2363      },
2364    );
2365  }
2366
2367  @override
2368  void applyPaintTransform(RenderBox child, Matrix4 transform) {
2369    if (size.isEmpty || child.size.isEmpty) {
2370      transform.setZero();
2371    } else {
2372      _updatePaintData();
2373      transform.multiply(_transform);
2374    }
2375  }
2376
2377  @override
2378  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
2379    super.debugFillProperties(properties);
2380    properties.add(EnumProperty<BoxFit>('fit', fit));
2381    properties.add(DiagnosticsProperty<Alignment>('alignment', alignment));
2382    properties.add(EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
2383  }
2384}
2385
2386/// Applies a translation transformation before painting its child.
2387///
2388/// The translation is expressed as an [Offset] scaled to the child's size. For
2389/// example, an [Offset] with a `dx` of 0.25 will result in a horizontal
2390/// translation of one quarter the width of the child.
2391///
2392/// Hit tests will only be detected inside the bounds of the
2393/// [RenderFractionalTranslation], even if the contents are offset such that
2394/// they overflow.
2395class RenderFractionalTranslation extends RenderProxyBox {
2396  /// Creates a render object that translates its child's painting.
2397  ///
2398  /// The [translation] argument must not be null.
2399  RenderFractionalTranslation({
2400    @required Offset translation,
2401    this.transformHitTests = true,
2402    RenderBox child,
2403  }) : assert(translation != null),
2404       _translation = translation,
2405       super(child);
2406
2407  /// The translation to apply to the child, scaled to the child's size.
2408  ///
2409  /// For example, an [Offset] with a `dx` of 0.25 will result in a horizontal
2410  /// translation of one quarter the width of the child.
2411  Offset get translation => _translation;
2412  Offset _translation;
2413  set translation(Offset value) {
2414    assert(value != null);
2415    if (_translation == value)
2416      return;
2417    _translation = value;
2418    markNeedsPaint();
2419  }
2420
2421  @override
2422  bool hitTest(BoxHitTestResult result, { Offset position }) {
2423    // RenderFractionalTranslation objects don't check if they are
2424    // themselves hit, because it's confusing to think about
2425    // how the untransformed size and the child's transformed
2426    // position interact.
2427    return hitTestChildren(result, position: position);
2428  }
2429
2430  /// When set to true, hit tests are performed based on the position of the
2431  /// child as it is painted. When set to false, hit tests are performed
2432  /// ignoring the transformation.
2433  ///
2434  /// applyPaintTransform(), and therefore localToGlobal() and globalToLocal(),
2435  /// always honor the transformation, regardless of the value of this property.
2436  bool transformHitTests;
2437
2438  @override
2439  bool hitTestChildren(BoxHitTestResult result, { Offset position }) {
2440    assert(!debugNeedsLayout);
2441    return result.addWithPaintOffset(
2442      offset: transformHitTests
2443          ? Offset(translation.dx * size.width, translation.dy * size.height)
2444          : null,
2445      position: position,
2446      hitTest: (BoxHitTestResult result, Offset position) {
2447        return super.hitTestChildren(result, position: position);
2448      },
2449    );
2450  }
2451
2452  @override
2453  void paint(PaintingContext context, Offset offset) {
2454    assert(!debugNeedsLayout);
2455    if (child != null) {
2456      super.paint(context, Offset(
2457        offset.dx + translation.dx * size.width,
2458        offset.dy + translation.dy * size.height,
2459      ));
2460    }
2461  }
2462
2463  @override
2464  void applyPaintTransform(RenderBox child, Matrix4 transform) {
2465    transform.translate(
2466      translation.dx * size.width,
2467      translation.dy * size.height,
2468    );
2469  }
2470
2471  @override
2472  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
2473    super.debugFillProperties(properties);
2474    properties.add(DiagnosticsProperty<Offset>('translation', translation));
2475    properties.add(DiagnosticsProperty<bool>('transformHitTests', transformHitTests));
2476  }
2477}
2478
2479/// Signature for listening to [PointerDownEvent] events.
2480///
2481/// Used by [Listener] and [RenderPointerListener].
2482typedef PointerDownEventListener = void Function(PointerDownEvent event);
2483
2484/// Signature for listening to [PointerMoveEvent] events.
2485///
2486/// Used by [Listener] and [RenderPointerListener].
2487typedef PointerMoveEventListener = void Function(PointerMoveEvent event);
2488
2489/// Signature for listening to [PointerUpEvent] events.
2490///
2491/// Used by [Listener] and [RenderPointerListener].
2492typedef PointerUpEventListener = void Function(PointerUpEvent event);
2493
2494/// Signature for listening to [PointerCancelEvent] events.
2495///
2496/// Used by [Listener] and [RenderPointerListener].
2497typedef PointerCancelEventListener = void Function(PointerCancelEvent event);
2498
2499/// Signature for listening to [PointerSignalEvent] events.
2500///
2501/// Used by [Listener] and [RenderPointerListener].
2502typedef PointerSignalEventListener = void Function(PointerSignalEvent event);
2503
2504/// Calls callbacks in response to common pointer events.
2505///
2506/// It responds to events that can construct gestures, such as when the
2507/// pointer is pressed, moved, then released or canceled.
2508///
2509/// It does not respond to events that are exclusive to mouse, such as when the
2510/// mouse enters, exits or hovers a region without pressing any buttons. For
2511/// these events, use [RenderMouseRegion].
2512///
2513/// If it has a child, defers to the child for sizing behavior.
2514///
2515/// If it does not have a child, grows to fit the parent-provided constraints.
2516class RenderPointerListener extends RenderProxyBoxWithHitTestBehavior {
2517  /// Creates a render object that forwards pointer events to callbacks.
2518  ///
2519  /// The [behavior] argument defaults to [HitTestBehavior.deferToChild].
2520  RenderPointerListener({
2521    this.onPointerDown,
2522    this.onPointerMove,
2523    this.onPointerUp,
2524    this.onPointerCancel,
2525    this.onPointerSignal,
2526    HitTestBehavior behavior = HitTestBehavior.deferToChild,
2527    RenderBox child,
2528  }) : super(behavior: behavior, child: child);
2529
2530  /// Called when a pointer comes into contact with the screen (for touch
2531  /// pointers), or has its button pressed (for mouse pointers) at this widget's
2532  /// location.
2533  PointerDownEventListener onPointerDown;
2534
2535  /// Called when a pointer that triggered an [onPointerDown] changes position.
2536  PointerMoveEventListener onPointerMove;
2537
2538  /// Called when a pointer that triggered an [onPointerDown] is no longer in
2539  /// contact with the screen.
2540  PointerUpEventListener onPointerUp;
2541
2542  /// Called when the input from a pointer that triggered an [onPointerDown] is
2543  /// no longer directed towards this receiver.
2544  PointerCancelEventListener onPointerCancel;
2545
2546  /// Called when a pointer signal occurs over this object.
2547  PointerSignalEventListener onPointerSignal;
2548
2549  @override
2550  void performResize() {
2551    size = constraints.biggest;
2552  }
2553
2554  @override
2555  void handleEvent(PointerEvent event, HitTestEntry entry) {
2556    assert(debugHandleEvent(event, entry));
2557    if (onPointerDown != null && event is PointerDownEvent)
2558      return onPointerDown(event);
2559    if (onPointerMove != null && event is PointerMoveEvent)
2560      return onPointerMove(event);
2561    if (onPointerUp != null && event is PointerUpEvent)
2562      return onPointerUp(event);
2563    if (onPointerCancel != null && event is PointerCancelEvent)
2564      return onPointerCancel(event);
2565    if (onPointerSignal != null && event is PointerSignalEvent)
2566      return onPointerSignal(event);
2567  }
2568
2569  @override
2570  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
2571    super.debugFillProperties(properties);
2572    final List<String> listeners = <String>[];
2573    if (onPointerDown != null)
2574      listeners.add('down');
2575    if (onPointerMove != null)
2576      listeners.add('move');
2577    if (onPointerUp != null)
2578      listeners.add('up');
2579    if (onPointerCancel != null)
2580      listeners.add('cancel');
2581    if (onPointerSignal != null)
2582      listeners.add('signal');
2583    if (listeners.isEmpty)
2584      listeners.add('<none>');
2585    properties.add(IterableProperty<String>('listeners', listeners));
2586    // TODO(jacobr): add raw listeners to the diagnostics data.
2587  }
2588}
2589
2590/// Calls callbacks in response to pointer events that are exclusive to mice.
2591///
2592/// Simply put, it responds to events that are related to hovering,
2593/// i.e. when the mouse enters, exits or hovers a region without pressing.
2594///
2595/// It does not respond to common events that construct gestures, such as when
2596/// the pointer is pressed, moved, then released or canceled. For these events,
2597/// use [RenderPointerListener].
2598///
2599/// If it has a child, it defers to the child for sizing behavior.
2600///
2601/// If it does not have a child, it grows to fit the parent-provided constraints.
2602class RenderMouseRegion extends RenderProxyBox {
2603  /// Creates a render object that forwards pointer events to callbacks.
2604  RenderMouseRegion({
2605    PointerEnterEventListener onEnter,
2606    PointerHoverEventListener onHover,
2607    PointerExitEventListener onExit,
2608    RenderBox child,
2609  }) : _onEnter = onEnter,
2610       _onHover = onHover,
2611       _onExit = onExit,
2612       _annotationIsActive = false,
2613       super(child) {
2614    _hoverAnnotation = MouseTrackerAnnotation(
2615      onEnter: _handleEnter,
2616      onHover: _handleHover,
2617      onExit: _handleExit,
2618    );
2619  }
2620
2621  /// Called when a hovering pointer enters the region for this widget.
2622  ///
2623  /// If this is a mouse pointer, this will fire when the mouse pointer enters
2624  /// the region defined by this widget.
2625  PointerEnterEventListener get onEnter => _onEnter;
2626  set onEnter(PointerEnterEventListener value) {
2627    if (_onEnter != value) {
2628      _onEnter = value;
2629      _updateAnnotations();
2630    }
2631  }
2632  PointerEnterEventListener _onEnter;
2633  void _handleEnter(PointerEnterEvent event) {
2634    if (_onEnter != null)
2635      _onEnter(event);
2636  }
2637
2638  /// Called when a pointer that has not triggered an [onPointerDown] changes
2639  /// position.
2640  ///
2641  /// Typically only triggered for mouse pointers.
2642  PointerHoverEventListener get onHover => _onHover;
2643  set onHover(PointerHoverEventListener value) {
2644    if (_onHover != value) {
2645      _onHover = value;
2646      _updateAnnotations();
2647    }
2648  }
2649  PointerHoverEventListener _onHover;
2650  void _handleHover(PointerHoverEvent event) {
2651    if (_onHover != null)
2652      _onHover(event);
2653  }
2654
2655  /// Called when a hovering pointer leaves the region for this widget.
2656  ///
2657  /// If this is a mouse pointer, this will fire when the mouse pointer leaves
2658  /// the region defined by this widget.
2659  PointerExitEventListener get onExit => _onExit;
2660  set onExit(PointerExitEventListener value) {
2661    if (_onExit != value) {
2662      _onExit = value;
2663      _updateAnnotations();
2664    }
2665  }
2666  PointerExitEventListener _onExit;
2667  void _handleExit(PointerExitEvent event) {
2668    if (_onExit != null)
2669      _onExit(event);
2670  }
2671
2672  // Object used for annotation of the layer used for hover hit detection.
2673  MouseTrackerAnnotation _hoverAnnotation;
2674
2675  /// Object used for annotation of the layer used for hover hit detection.
2676  ///
2677  /// This is only public to allow for testing of Listener widgets. Do not call
2678  /// in other contexts.
2679  @visibleForTesting
2680  MouseTrackerAnnotation get hoverAnnotation => _hoverAnnotation;
2681
2682  void _updateAnnotations() {
2683    final bool annotationWasActive = _annotationIsActive;
2684    final bool annotationWillBeActive = (
2685        _onEnter != null ||
2686        _onHover != null ||
2687        _onExit != null
2688      ) &&
2689      RendererBinding.instance.mouseTracker.mouseIsConnected;
2690    if (annotationWasActive != annotationWillBeActive) {
2691      markNeedsPaint();
2692      markNeedsCompositingBitsUpdate();
2693      if (annotationWillBeActive) {
2694        RendererBinding.instance.mouseTracker.attachAnnotation(_hoverAnnotation);
2695      } else {
2696        RendererBinding.instance.mouseTracker.detachAnnotation(_hoverAnnotation);
2697      }
2698      _annotationIsActive = annotationWillBeActive;
2699    }
2700  }
2701
2702  @override
2703  void attach(PipelineOwner owner) {
2704    super.attach(owner);
2705    // Add a listener to listen for changes in mouseIsConnected.
2706    RendererBinding.instance.mouseTracker.addListener(_updateAnnotations);
2707    _updateAnnotations();
2708  }
2709
2710  /// Attaches the annotation for this render object, if any.
2711  ///
2712  /// This is called by the [MouseRegion]'s [Element] to tell this
2713  /// [RenderMouseRegion] that it has transitioned from "inactive"
2714  /// state to "active". We call it here so that
2715  /// [MouseTrackerAnnotation.onEnter] isn't called during the build step for
2716  /// the widget that provided the callback, and [State.setState] can safely be
2717  /// called within that callback.
2718  void postActivate() {
2719    if (_annotationIsActive)
2720      RendererBinding.instance.mouseTracker.attachAnnotation(_hoverAnnotation);
2721  }
2722
2723  /// Detaches the annotation for this render object, if any.
2724  ///
2725  /// This is called by the [MouseRegion]'s [Element] to tell this
2726  /// [RenderMouseRegion] that it will shortly be transitioned from "active"
2727  /// state to "inactive". We call it here so that
2728  /// [MouseTrackerAnnotation.onExit] isn't called during the build step for the
2729  /// widget that provided the callback, and [State.setState] can safely be
2730  /// called within that callback.
2731  void preDeactivate() {
2732    if (_annotationIsActive)
2733      RendererBinding.instance.mouseTracker.detachAnnotation(_hoverAnnotation);
2734  }
2735
2736  @override
2737  void detach() {
2738    RendererBinding.instance.mouseTracker.removeListener(_updateAnnotations);
2739    super.detach();
2740  }
2741
2742  bool _annotationIsActive;
2743
2744  @override
2745  bool get needsCompositing => super.needsCompositing || _annotationIsActive;
2746
2747  @override
2748  void paint(PaintingContext context, Offset offset) {
2749    if (_annotationIsActive) {
2750      // Annotated region layers are not retained because they do not create engine layers.
2751      final AnnotatedRegionLayer<MouseTrackerAnnotation> layer = AnnotatedRegionLayer<MouseTrackerAnnotation>(
2752        _hoverAnnotation,
2753        size: size,
2754        offset: offset,
2755      );
2756      context.pushLayer(layer, super.paint, offset);
2757    } else {
2758      super.paint(context, offset);
2759    }
2760  }
2761
2762  @override
2763  void performResize() {
2764    size = constraints.biggest;
2765  }
2766
2767  @override
2768  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
2769    super.debugFillProperties(properties);
2770    final List<String> listeners = <String>[];
2771    if (onEnter != null)
2772      listeners.add('enter');
2773    if (onHover != null)
2774      listeners.add('hover');
2775    if (onExit != null)
2776      listeners.add('exit');
2777    if (listeners.isEmpty)
2778      listeners.add('<none>');
2779    properties.add(IterableProperty<String>('listeners', listeners));
2780    // TODO(jacobr): add raw listeners to the diagnostics data.
2781  }
2782}
2783
2784/// Creates a separate display list for its child.
2785///
2786/// This render object creates a separate display list for its child, which
2787/// can improve performance if the subtree repaints at different times than
2788/// the surrounding parts of the tree. Specifically, when the child does not
2789/// repaint but its parent does, we can re-use the display list we recorded
2790/// previously. Similarly, when the child repaints but the surround tree does
2791/// not, we can re-record its display list without re-recording the display list
2792/// for the surround tree.
2793///
2794/// In some cases, it is necessary to place _two_ (or more) repaint boundaries
2795/// to get a useful effect. Consider, for example, an e-mail application that
2796/// shows an unread count and a list of e-mails. Whenever a new e-mail comes in,
2797/// the list would update, but so would the unread count. If only one of these
2798/// two parts of the application was behind a repaint boundary, the entire
2799/// application would repaint each time. On the other hand, if both were behind
2800/// a repaint boundary, a new e-mail would only change those two parts of the
2801/// application and the rest of the application would not repaint.
2802///
2803/// To tell if a particular RenderRepaintBoundary is useful, run your
2804/// application in checked mode, interacting with it in typical ways, and then
2805/// call [debugDumpRenderTree]. Each RenderRepaintBoundary will include the
2806/// ratio of cases where the repaint boundary was useful vs the cases where it
2807/// was not. These counts can also be inspected programmatically using
2808/// [debugAsymmetricPaintCount] and [debugSymmetricPaintCount] respectively.
2809class RenderRepaintBoundary extends RenderProxyBox {
2810  /// Creates a repaint boundary around [child].
2811  RenderRepaintBoundary({ RenderBox child }) : super(child);
2812
2813  @override
2814  bool get isRepaintBoundary => true;
2815
2816  /// Capture an image of the current state of this render object and its
2817  /// children.
2818  ///
2819  /// The returned [ui.Image] has uncompressed raw RGBA bytes in the dimensions
2820  /// of the render object, multiplied by the [pixelRatio].
2821  ///
2822  /// To use [toImage], the render object must have gone through the paint phase
2823  /// (i.e. [debugNeedsPaint] must be false).
2824  ///
2825  /// The [pixelRatio] describes the scale between the logical pixels and the
2826  /// size of the output image. It is independent of the
2827  /// [window.devicePixelRatio] for the device, so specifying 1.0 (the default)
2828  /// will give you a 1:1 mapping between logical pixels and the output pixels
2829  /// in the image.
2830  ///
2831  /// {@tool sample}
2832  ///
2833  /// The following is an example of how to go from a `GlobalKey` on a
2834  /// `RepaintBoundary` to a PNG:
2835  ///
2836  /// ```dart
2837  /// class PngHome extends StatefulWidget {
2838  ///   PngHome({Key key}) : super(key: key);
2839  ///
2840  ///   @override
2841  ///   _PngHomeState createState() => _PngHomeState();
2842  /// }
2843  ///
2844  /// class _PngHomeState extends State<PngHome> {
2845  ///   GlobalKey globalKey = GlobalKey();
2846  ///
2847  ///   Future<void> _capturePng() async {
2848  ///     RenderRepaintBoundary boundary = globalKey.currentContext.findRenderObject();
2849  ///     ui.Image image = await boundary.toImage();
2850  ///     ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png);
2851  ///     Uint8List pngBytes = byteData.buffer.asUint8List();
2852  ///     print(pngBytes);
2853  ///   }
2854  ///
2855  ///   @override
2856  ///   Widget build(BuildContext context) {
2857  ///     return RepaintBoundary(
2858  ///       key: globalKey,
2859  ///       child: Center(
2860  ///         child: FlatButton(
2861  ///           child: Text('Hello World', textDirection: TextDirection.ltr),
2862  ///           onPressed: _capturePng,
2863  ///         ),
2864  ///       ),
2865  ///     );
2866  ///   }
2867  /// }
2868  /// ```
2869  /// {@end-tool}
2870  ///
2871  /// See also:
2872  ///
2873  ///  * [OffsetLayer.toImage] for a similar API at the layer level.
2874  ///  * [dart:ui.Scene.toImage] for more information about the image returned.
2875  Future<ui.Image> toImage({ double pixelRatio = 1.0 }) {
2876    assert(!debugNeedsPaint);
2877    final OffsetLayer offsetLayer = layer;
2878    return offsetLayer.toImage(Offset.zero & size, pixelRatio: pixelRatio);
2879  }
2880
2881
2882  /// The number of times that this render object repainted at the same time as
2883  /// its parent. Repaint boundaries are only useful when the parent and child
2884  /// paint at different times. When both paint at the same time, the repaint
2885  /// boundary is redundant, and may be actually making performance worse.
2886  ///
2887  /// Only valid when asserts are enabled. In release builds, always returns
2888  /// zero.
2889  ///
2890  /// Can be reset using [debugResetMetrics]. See [debugAsymmetricPaintCount]
2891  /// for the corresponding count of times where only the parent or only the
2892  /// child painted.
2893  int get debugSymmetricPaintCount => _debugSymmetricPaintCount;
2894  int _debugSymmetricPaintCount = 0;
2895
2896  /// The number of times that either this render object repainted without the
2897  /// parent being painted, or the parent repainted without this object being
2898  /// painted. When a repaint boundary is used at a seam in the render tree
2899  /// where the parent tends to repaint at entirely different times than the
2900  /// child, it can improve performance by reducing the number of paint
2901  /// operations that have to be recorded each frame.
2902  ///
2903  /// Only valid when asserts are enabled. In release builds, always returns
2904  /// zero.
2905  ///
2906  /// Can be reset using [debugResetMetrics]. See [debugSymmetricPaintCount] for
2907  /// the corresponding count of times where both the parent and the child
2908  /// painted together.
2909  int get debugAsymmetricPaintCount => _debugAsymmetricPaintCount;
2910  int _debugAsymmetricPaintCount = 0;
2911
2912  /// Resets the [debugSymmetricPaintCount] and [debugAsymmetricPaintCount]
2913  /// counts to zero.
2914  ///
2915  /// Only valid when asserts are enabled. Does nothing in release builds.
2916  void debugResetMetrics() {
2917    assert(() {
2918      _debugSymmetricPaintCount = 0;
2919      _debugAsymmetricPaintCount = 0;
2920      return true;
2921    }());
2922  }
2923
2924  @override
2925  void debugRegisterRepaintBoundaryPaint({ bool includedParent = true, bool includedChild = false }) {
2926    assert(() {
2927      if (includedParent && includedChild)
2928        _debugSymmetricPaintCount += 1;
2929      else
2930        _debugAsymmetricPaintCount += 1;
2931      return true;
2932    }());
2933  }
2934
2935  @override
2936  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
2937    super.debugFillProperties(properties);
2938    bool inReleaseMode = true;
2939    assert(() {
2940      inReleaseMode = false;
2941      if (debugSymmetricPaintCount + debugAsymmetricPaintCount == 0) {
2942        properties.add(MessageProperty('usefulness ratio', 'no metrics collected yet (never painted)'));
2943      } else {
2944        final double fraction = debugAsymmetricPaintCount / (debugSymmetricPaintCount + debugAsymmetricPaintCount);
2945        String diagnosis;
2946        if (debugSymmetricPaintCount + debugAsymmetricPaintCount < 5) {
2947          diagnosis = 'insufficient data to draw conclusion (less than five repaints)';
2948        } else if (fraction > 0.9) {
2949          diagnosis = 'this is an outstandingly useful repaint boundary and should definitely be kept';
2950        } else if (fraction > 0.5) {
2951          diagnosis = 'this is a useful repaint boundary and should be kept';
2952        } else if (fraction > 0.30) {
2953          diagnosis = 'this repaint boundary is probably useful, but maybe it would be more useful in tandem with adding more repaint boundaries elsewhere';
2954        } else if (fraction > 0.1) {
2955          diagnosis = 'this repaint boundary does sometimes show value, though currently not that often';
2956        } else if (debugAsymmetricPaintCount == 0) {
2957          diagnosis = 'this repaint boundary is astoundingly ineffectual and should be removed';
2958        } else {
2959          diagnosis = 'this repaint boundary is not very effective and should probably be removed';
2960        }
2961        properties.add(PercentProperty('metrics', fraction, unit: 'useful', tooltip: '$debugSymmetricPaintCount bad vs $debugAsymmetricPaintCount good'));
2962        properties.add(MessageProperty('diagnosis', diagnosis));
2963      }
2964      return true;
2965    }());
2966    if (inReleaseMode)
2967      properties.add(DiagnosticsNode.message('(run in checked mode to collect repaint boundary statistics)'));
2968  }
2969}
2970
2971/// A render object that is invisible during hit testing.
2972///
2973/// When [ignoring] is true, this render object (and its subtree) is invisible
2974/// to hit testing. It still consumes space during layout and paints its child
2975/// as usual. It just cannot be the target of located events, because its render
2976/// object returns false from [hitTest].
2977///
2978/// When [ignoringSemantics] is true, the subtree will be invisible to
2979/// the semantics layer (and thus e.g. accessibility tools). If
2980/// [ignoringSemantics] is null, it uses the value of [ignoring].
2981///
2982/// See also:
2983///
2984///  * [RenderAbsorbPointer], which takes the pointer events but prevents any
2985///    nodes in the subtree from seeing them.
2986class RenderIgnorePointer extends RenderProxyBox {
2987  /// Creates a render object that is invisible to hit testing.
2988  ///
2989  /// The [ignoring] argument must not be null. If [ignoringSemantics], this
2990  /// render object will be ignored for semantics if [ignoring] is true.
2991  RenderIgnorePointer({
2992    RenderBox child,
2993    bool ignoring = true,
2994    bool ignoringSemantics,
2995  }) : _ignoring = ignoring,
2996       _ignoringSemantics = ignoringSemantics,
2997       super(child) {
2998    assert(_ignoring != null);
2999  }
3000
3001  /// Whether this render object is ignored during hit testing.
3002  ///
3003  /// Regardless of whether this render object is ignored during hit testing, it
3004  /// will still consume space during layout and be visible during painting.
3005  bool get ignoring => _ignoring;
3006  bool _ignoring;
3007  set ignoring(bool value) {
3008    assert(value != null);
3009    if (value == _ignoring)
3010      return;
3011    _ignoring = value;
3012    if (ignoringSemantics == null)
3013      markNeedsSemanticsUpdate();
3014  }
3015
3016  /// Whether the semantics of this render object is ignored when compiling the semantics tree.
3017  ///
3018  /// If null, defaults to value of [ignoring].
3019  ///
3020  /// See [SemanticsNode] for additional information about the semantics tree.
3021  bool get ignoringSemantics => _ignoringSemantics;
3022  bool _ignoringSemantics;
3023  set ignoringSemantics(bool value) {
3024    if (value == _ignoringSemantics)
3025      return;
3026    final bool oldEffectiveValue = _effectiveIgnoringSemantics;
3027    _ignoringSemantics = value;
3028    if (oldEffectiveValue != _effectiveIgnoringSemantics)
3029      markNeedsSemanticsUpdate();
3030  }
3031
3032  bool get _effectiveIgnoringSemantics => ignoringSemantics ?? ignoring;
3033
3034  @override
3035  bool hitTest(BoxHitTestResult result, { Offset position }) {
3036    return !ignoring && super.hitTest(result, position: position);
3037  }
3038
3039  // TODO(ianh): figure out a way to still include labels and flags in
3040  // descendants, just make them non-interactive, even when
3041  // _effectiveIgnoringSemantics is true
3042  @override
3043  void visitChildrenForSemantics(RenderObjectVisitor visitor) {
3044    if (child != null && !_effectiveIgnoringSemantics)
3045      visitor(child);
3046  }
3047
3048  @override
3049  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
3050    super.debugFillProperties(properties);
3051    properties.add(DiagnosticsProperty<bool>('ignoring', ignoring));
3052    properties.add(
3053      DiagnosticsProperty<bool>(
3054        'ignoringSemantics',
3055        _effectiveIgnoringSemantics,
3056        description: ignoringSemantics == null ? 'implicitly $_effectiveIgnoringSemantics' : null,
3057      )
3058    );
3059  }
3060}
3061
3062/// Lays the child out as if it was in the tree, but without painting anything,
3063/// without making the child available for hit testing, and without taking any
3064/// room in the parent.
3065class RenderOffstage extends RenderProxyBox {
3066  /// Creates an offstage render object.
3067  RenderOffstage({
3068    bool offstage = true,
3069    RenderBox child,
3070  }) : assert(offstage != null),
3071       _offstage = offstage,
3072       super(child);
3073
3074  /// Whether the child is hidden from the rest of the tree.
3075  ///
3076  /// If true, the child is laid out as if it was in the tree, but without
3077  /// painting anything, without making the child available for hit testing, and
3078  /// without taking any room in the parent.
3079  ///
3080  /// If false, the child is included in the tree as normal.
3081  bool get offstage => _offstage;
3082  bool _offstage;
3083  set offstage(bool value) {
3084    assert(value != null);
3085    if (value == _offstage)
3086      return;
3087    _offstage = value;
3088    markNeedsLayoutForSizedByParentChange();
3089  }
3090
3091  @override
3092  double computeMinIntrinsicWidth(double height) {
3093    if (offstage)
3094      return 0.0;
3095    return super.computeMinIntrinsicWidth(height);
3096  }
3097
3098  @override
3099  double computeMaxIntrinsicWidth(double height) {
3100    if (offstage)
3101      return 0.0;
3102    return super.computeMaxIntrinsicWidth(height);
3103  }
3104
3105  @override
3106  double computeMinIntrinsicHeight(double width) {
3107    if (offstage)
3108      return 0.0;
3109    return super.computeMinIntrinsicHeight(width);
3110  }
3111
3112  @override
3113  double computeMaxIntrinsicHeight(double width) {
3114    if (offstage)
3115      return 0.0;
3116    return super.computeMaxIntrinsicHeight(width);
3117  }
3118
3119  @override
3120  double computeDistanceToActualBaseline(TextBaseline baseline) {
3121    if (offstage)
3122      return null;
3123    return super.computeDistanceToActualBaseline(baseline);
3124  }
3125
3126  @override
3127  bool get sizedByParent => offstage;
3128
3129  @override
3130  void performResize() {
3131    assert(offstage);
3132    size = constraints.smallest;
3133  }
3134
3135  @override
3136  void performLayout() {
3137    if (offstage) {
3138      child?.layout(constraints);
3139    } else {
3140      super.performLayout();
3141    }
3142  }
3143
3144  @override
3145  bool hitTest(BoxHitTestResult result, { Offset position }) {
3146    return !offstage && super.hitTest(result, position: position);
3147  }
3148
3149  @override
3150  void paint(PaintingContext context, Offset offset) {
3151    if (offstage)
3152      return;
3153    super.paint(context, offset);
3154  }
3155
3156  @override
3157  void visitChildrenForSemantics(RenderObjectVisitor visitor) {
3158    if (offstage)
3159      return;
3160    super.visitChildrenForSemantics(visitor);
3161  }
3162
3163  @override
3164  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
3165    super.debugFillProperties(properties);
3166    properties.add(DiagnosticsProperty<bool>('offstage', offstage));
3167  }
3168
3169  @override
3170  List<DiagnosticsNode> debugDescribeChildren() {
3171    if (child == null)
3172      return <DiagnosticsNode>[];
3173    return <DiagnosticsNode>[
3174      child.toDiagnosticsNode(
3175        name: 'child',
3176        style: offstage ? DiagnosticsTreeStyle.offstage : DiagnosticsTreeStyle.sparse,
3177      ),
3178    ];
3179  }
3180}
3181
3182/// A render object that absorbs pointers during hit testing.
3183///
3184/// When [absorbing] is true, this render object prevents its subtree from
3185/// receiving pointer events by terminating hit testing at itself. It still
3186/// consumes space during layout and paints its child as usual. It just prevents
3187/// its children from being the target of located events, because its render
3188/// object returns true from [hitTest].
3189///
3190/// See also:
3191///
3192///  * [RenderIgnorePointer], which has the opposite effect: removing the
3193///    subtree from considering entirely for the purposes of hit testing.
3194class RenderAbsorbPointer extends RenderProxyBox {
3195  /// Creates a render object that absorbs pointers during hit testing.
3196  ///
3197  /// The [absorbing] argument must not be null.
3198  RenderAbsorbPointer({
3199    RenderBox child,
3200    bool absorbing = true,
3201    bool ignoringSemantics,
3202  }) : assert(absorbing != null),
3203       _absorbing = absorbing,
3204       _ignoringSemantics = ignoringSemantics,
3205       super(child);
3206
3207  /// Whether this render object absorbs pointers during hit testing.
3208  ///
3209  /// Regardless of whether this render object absorbs pointers during hit
3210  /// testing, it will still consume space during layout and be visible during
3211  /// painting.
3212  bool get absorbing => _absorbing;
3213  bool _absorbing;
3214  set absorbing(bool value) {
3215    if (_absorbing == value)
3216      return;
3217    _absorbing = value;
3218    if (ignoringSemantics == null)
3219      markNeedsSemanticsUpdate();
3220  }
3221
3222  /// Whether the semantics of this render object is ignored when compiling the semantics tree.
3223  ///
3224  /// If null, defaults to value of [absorbing].
3225  ///
3226  /// See [SemanticsNode] for additional information about the semantics tree.
3227  bool get ignoringSemantics => _ignoringSemantics;
3228  bool _ignoringSemantics;
3229  set ignoringSemantics(bool value) {
3230    if (value == _ignoringSemantics)
3231      return;
3232    final bool oldEffectiveValue = _effectiveIgnoringSemantics;
3233    _ignoringSemantics = value;
3234    if (oldEffectiveValue != _effectiveIgnoringSemantics)
3235      markNeedsSemanticsUpdate();
3236  }
3237
3238  bool get _effectiveIgnoringSemantics => ignoringSemantics ?? absorbing;
3239
3240  @override
3241  bool hitTest(BoxHitTestResult result, { Offset position }) {
3242    return absorbing
3243        ? size.contains(position)
3244        : super.hitTest(result, position: position);
3245  }
3246
3247  @override
3248  void visitChildrenForSemantics(RenderObjectVisitor visitor) {
3249    if (child != null && !_effectiveIgnoringSemantics)
3250      visitor(child);
3251  }
3252
3253  @override
3254  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
3255    super.debugFillProperties(properties);
3256    properties.add(DiagnosticsProperty<bool>('absorbing', absorbing));
3257    properties.add(
3258      DiagnosticsProperty<bool>(
3259        'ignoringSemantics',
3260        _effectiveIgnoringSemantics,
3261        description: ignoringSemantics == null ? 'implicitly $_effectiveIgnoringSemantics' : null,
3262      ),
3263    );
3264  }
3265}
3266
3267/// Holds opaque meta data in the render tree.
3268///
3269/// Useful for decorating the render tree with information that will be consumed
3270/// later. For example, you could store information in the render tree that will
3271/// be used when the user interacts with the render tree but has no visual
3272/// impact prior to the interaction.
3273class RenderMetaData extends RenderProxyBoxWithHitTestBehavior {
3274  /// Creates a render object that hold opaque meta data.
3275  ///
3276  /// The [behavior] argument defaults to [HitTestBehavior.deferToChild].
3277  RenderMetaData({
3278    this.metaData,
3279    HitTestBehavior behavior = HitTestBehavior.deferToChild,
3280    RenderBox child,
3281  }) : super(behavior: behavior, child: child);
3282
3283  /// Opaque meta data ignored by the render tree
3284  dynamic metaData;
3285
3286  @override
3287  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
3288    super.debugFillProperties(properties);
3289    properties.add(DiagnosticsProperty<dynamic>('metaData', metaData));
3290  }
3291}
3292
3293/// Listens for the specified gestures from the semantics server (e.g.
3294/// an accessibility tool).
3295class RenderSemanticsGestureHandler extends RenderProxyBox {
3296  /// Creates a render object that listens for specific semantic gestures.
3297  ///
3298  /// The [scrollFactor] argument must not be null.
3299  RenderSemanticsGestureHandler({
3300    RenderBox child,
3301    GestureTapCallback onTap,
3302    GestureLongPressCallback onLongPress,
3303    GestureDragUpdateCallback onHorizontalDragUpdate,
3304    GestureDragUpdateCallback onVerticalDragUpdate,
3305    this.scrollFactor = 0.8,
3306  }) : assert(scrollFactor != null),
3307       _onTap = onTap,
3308       _onLongPress = onLongPress,
3309       _onHorizontalDragUpdate = onHorizontalDragUpdate,
3310       _onVerticalDragUpdate = onVerticalDragUpdate,
3311       super(child);
3312
3313  /// If non-null, the set of actions to allow. Other actions will be omitted,
3314  /// even if their callback is provided.
3315  ///
3316  /// For example, if [onTap] is non-null but [validActions] does not contain
3317  /// [SemanticsAction.tap], then the semantic description of this node will
3318  /// not claim to support taps.
3319  ///
3320  /// This is normally used to filter the actions made available by
3321  /// [onHorizontalDragUpdate] and [onVerticalDragUpdate]. Normally, these make
3322  /// both the right and left, or up and down, actions available. For example,
3323  /// if [onHorizontalDragUpdate] is set but [validActions] only contains
3324  /// [SemanticsAction.scrollLeft], then the [SemanticsAction.scrollRight]
3325  /// action will be omitted.
3326  Set<SemanticsAction> get validActions => _validActions;
3327  Set<SemanticsAction> _validActions;
3328  set validActions(Set<SemanticsAction> value) {
3329    if (setEquals<SemanticsAction>(value, _validActions))
3330      return;
3331    _validActions = value;
3332    markNeedsSemanticsUpdate();
3333  }
3334
3335  /// Called when the user taps on the render object.
3336  GestureTapCallback get onTap => _onTap;
3337  GestureTapCallback _onTap;
3338  set onTap(GestureTapCallback value) {
3339    if (_onTap == value)
3340      return;
3341    final bool hadHandler = _onTap != null;
3342    _onTap = value;
3343    if ((value != null) != hadHandler)
3344      markNeedsSemanticsUpdate();
3345  }
3346
3347  /// Called when the user presses on the render object for a long period of time.
3348  GestureLongPressCallback get onLongPress => _onLongPress;
3349  GestureLongPressCallback _onLongPress;
3350  set onLongPress(GestureLongPressCallback value) {
3351    if (_onLongPress == value)
3352      return;
3353    final bool hadHandler = _onLongPress != null;
3354    _onLongPress = value;
3355    if ((value != null) != hadHandler)
3356      markNeedsSemanticsUpdate();
3357  }
3358
3359  /// Called when the user scrolls to the left or to the right.
3360  GestureDragUpdateCallback get onHorizontalDragUpdate => _onHorizontalDragUpdate;
3361  GestureDragUpdateCallback _onHorizontalDragUpdate;
3362  set onHorizontalDragUpdate(GestureDragUpdateCallback value) {
3363    if (_onHorizontalDragUpdate == value)
3364      return;
3365    final bool hadHandler = _onHorizontalDragUpdate != null;
3366    _onHorizontalDragUpdate = value;
3367    if ((value != null) != hadHandler)
3368      markNeedsSemanticsUpdate();
3369  }
3370
3371  /// Called when the user scrolls up or down.
3372  GestureDragUpdateCallback get onVerticalDragUpdate => _onVerticalDragUpdate;
3373  GestureDragUpdateCallback _onVerticalDragUpdate;
3374  set onVerticalDragUpdate(GestureDragUpdateCallback value) {
3375    if (_onVerticalDragUpdate == value)
3376      return;
3377    final bool hadHandler = _onVerticalDragUpdate != null;
3378    _onVerticalDragUpdate = value;
3379    if ((value != null) != hadHandler)
3380      markNeedsSemanticsUpdate();
3381  }
3382
3383  /// The fraction of the dimension of this render box to use when
3384  /// scrolling. For example, if this is 0.8 and the box is 200 pixels
3385  /// wide, then when a left-scroll action is received from the
3386  /// accessibility system, it will translate into a 160 pixel
3387  /// leftwards drag.
3388  double scrollFactor;
3389
3390  @override
3391  void describeSemanticsConfiguration(SemanticsConfiguration config) {
3392    super.describeSemanticsConfiguration(config);
3393
3394    if (onTap != null && _isValidAction(SemanticsAction.tap))
3395      config.onTap = onTap;
3396    if (onLongPress != null && _isValidAction(SemanticsAction.longPress))
3397      config.onLongPress = onLongPress;
3398    if (onHorizontalDragUpdate != null) {
3399      if (_isValidAction(SemanticsAction.scrollRight))
3400        config.onScrollRight = _performSemanticScrollRight;
3401      if (_isValidAction(SemanticsAction.scrollLeft))
3402        config.onScrollLeft = _performSemanticScrollLeft;
3403    }
3404    if (onVerticalDragUpdate != null) {
3405      if (_isValidAction(SemanticsAction.scrollUp))
3406        config.onScrollUp = _performSemanticScrollUp;
3407      if (_isValidAction(SemanticsAction.scrollDown))
3408        config.onScrollDown = _performSemanticScrollDown;
3409    }
3410  }
3411
3412  bool _isValidAction(SemanticsAction action) {
3413    return validActions == null || validActions.contains(action);
3414  }
3415
3416  void _performSemanticScrollLeft() {
3417    if (onHorizontalDragUpdate != null) {
3418      final double primaryDelta = size.width * -scrollFactor;
3419      onHorizontalDragUpdate(DragUpdateDetails(
3420        delta: Offset(primaryDelta, 0.0), primaryDelta: primaryDelta,
3421        globalPosition: localToGlobal(size.center(Offset.zero)),
3422      ));
3423    }
3424  }
3425
3426  void _performSemanticScrollRight() {
3427    if (onHorizontalDragUpdate != null) {
3428      final double primaryDelta = size.width * scrollFactor;
3429      onHorizontalDragUpdate(DragUpdateDetails(
3430        delta: Offset(primaryDelta, 0.0), primaryDelta: primaryDelta,
3431        globalPosition: localToGlobal(size.center(Offset.zero)),
3432      ));
3433    }
3434  }
3435
3436  void _performSemanticScrollUp() {
3437    if (onVerticalDragUpdate != null) {
3438      final double primaryDelta = size.height * -scrollFactor;
3439      onVerticalDragUpdate(DragUpdateDetails(
3440        delta: Offset(0.0, primaryDelta), primaryDelta: primaryDelta,
3441        globalPosition: localToGlobal(size.center(Offset.zero)),
3442      ));
3443    }
3444  }
3445
3446  void _performSemanticScrollDown() {
3447    if (onVerticalDragUpdate != null) {
3448      final double primaryDelta = size.height * scrollFactor;
3449      onVerticalDragUpdate(DragUpdateDetails(
3450        delta: Offset(0.0, primaryDelta), primaryDelta: primaryDelta,
3451        globalPosition: localToGlobal(size.center(Offset.zero)),
3452      ));
3453    }
3454  }
3455
3456  @override
3457  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
3458    super.debugFillProperties(properties);
3459    final List<String> gestures = <String>[];
3460    if (onTap != null)
3461      gestures.add('tap');
3462    if (onLongPress != null)
3463      gestures.add('long press');
3464    if (onHorizontalDragUpdate != null)
3465      gestures.add('horizontal scroll');
3466    if (onVerticalDragUpdate != null)
3467      gestures.add('vertical scroll');
3468    if (gestures.isEmpty)
3469      gestures.add('<none>');
3470    properties.add(IterableProperty<String>('gestures', gestures));
3471  }
3472}
3473
3474/// Add annotations to the [SemanticsNode] for this subtree.
3475class RenderSemanticsAnnotations extends RenderProxyBox {
3476  /// Creates a render object that attaches a semantic annotation.
3477  ///
3478  /// The [container] argument must not be null.
3479  ///
3480  /// If the [label] is not null, the [textDirection] must also not be null.
3481  RenderSemanticsAnnotations({
3482    RenderBox child,
3483    bool container = false,
3484    bool explicitChildNodes,
3485    bool excludeSemantics = false,
3486    bool enabled,
3487    bool checked,
3488    bool toggled,
3489    bool selected,
3490    bool button,
3491    bool header,
3492    bool textField,
3493    bool readOnly,
3494    bool focused,
3495    bool inMutuallyExclusiveGroup,
3496    bool obscured,
3497    bool multiline,
3498    bool scopesRoute,
3499    bool namesRoute,
3500    bool hidden,
3501    bool image,
3502    bool liveRegion,
3503    String label,
3504    String value,
3505    String increasedValue,
3506    String decreasedValue,
3507    String hint,
3508    SemanticsHintOverrides hintOverrides,
3509    TextDirection textDirection,
3510    SemanticsSortKey sortKey,
3511    VoidCallback onTap,
3512    VoidCallback onDismiss,
3513    VoidCallback onLongPress,
3514    VoidCallback onScrollLeft,
3515    VoidCallback onScrollRight,
3516    VoidCallback onScrollUp,
3517    VoidCallback onScrollDown,
3518    VoidCallback onIncrease,
3519    VoidCallback onDecrease,
3520    VoidCallback onCopy,
3521    VoidCallback onCut,
3522    VoidCallback onPaste,
3523    MoveCursorHandler onMoveCursorForwardByCharacter,
3524    MoveCursorHandler onMoveCursorBackwardByCharacter,
3525    MoveCursorHandler onMoveCursorForwardByWord,
3526    MoveCursorHandler onMoveCursorBackwardByWord,
3527    SetSelectionHandler onSetSelection,
3528    VoidCallback onDidGainAccessibilityFocus,
3529    VoidCallback onDidLoseAccessibilityFocus,
3530    Map<CustomSemanticsAction, VoidCallback> customSemanticsActions,
3531  }) : assert(container != null),
3532       _container = container,
3533       _explicitChildNodes = explicitChildNodes,
3534       _excludeSemantics = excludeSemantics,
3535       _enabled = enabled,
3536       _checked = checked,
3537       _toggled = toggled,
3538       _selected = selected,
3539       _button = button,
3540       _header = header,
3541       _textField = textField,
3542       _readOnly = readOnly,
3543       _focused = focused,
3544       _inMutuallyExclusiveGroup = inMutuallyExclusiveGroup,
3545       _obscured = obscured,
3546       _multiline = multiline,
3547       _scopesRoute = scopesRoute,
3548       _namesRoute = namesRoute,
3549       _liveRegion = liveRegion,
3550       _hidden = hidden,
3551       _image = image,
3552       _onDismiss = onDismiss,
3553       _label = label,
3554       _value = value,
3555       _increasedValue = increasedValue,
3556       _decreasedValue = decreasedValue,
3557       _hint = hint,
3558       _hintOverrides = hintOverrides,
3559       _textDirection = textDirection,
3560       _sortKey = sortKey,
3561       _onTap = onTap,
3562       _onLongPress = onLongPress,
3563       _onScrollLeft = onScrollLeft,
3564       _onScrollRight = onScrollRight,
3565       _onScrollUp = onScrollUp,
3566       _onScrollDown = onScrollDown,
3567       _onIncrease = onIncrease,
3568       _onDecrease = onDecrease,
3569       _onCopy = onCopy,
3570       _onCut = onCut,
3571       _onPaste = onPaste,
3572       _onMoveCursorForwardByCharacter = onMoveCursorForwardByCharacter,
3573       _onMoveCursorBackwardByCharacter = onMoveCursorBackwardByCharacter,
3574       _onMoveCursorForwardByWord = onMoveCursorForwardByWord,
3575       _onMoveCursorBackwardByWord = onMoveCursorBackwardByWord,
3576       _onSetSelection = onSetSelection,
3577       _onDidGainAccessibilityFocus = onDidGainAccessibilityFocus,
3578       _onDidLoseAccessibilityFocus = onDidLoseAccessibilityFocus,
3579       _customSemanticsActions = customSemanticsActions,
3580       super(child);
3581
3582  /// If 'container' is true, this [RenderObject] will introduce a new
3583  /// node in the semantics tree. Otherwise, the semantics will be
3584  /// merged with the semantics of any ancestors.
3585  ///
3586  /// Whether descendants of this [RenderObject] can add their semantic information
3587  /// to the [SemanticsNode] introduced by this configuration is controlled by
3588  /// [explicitChildNodes].
3589  bool get container => _container;
3590  bool _container;
3591  set container(bool value) {
3592    assert(value != null);
3593    if (container == value)
3594      return;
3595    _container = value;
3596    markNeedsSemanticsUpdate();
3597  }
3598
3599  /// Whether descendants of this [RenderObject] are allowed to add semantic
3600  /// information to the [SemanticsNode] annotated by this widget.
3601  ///
3602  /// When set to false descendants are allowed to annotate [SemanticNode]s of
3603  /// their parent with the semantic information they want to contribute to the
3604  /// semantic tree.
3605  /// When set to true the only way for descendants to contribute semantic
3606  /// information to the semantic tree is to introduce new explicit
3607  /// [SemanticNode]s to the tree.
3608  ///
3609  /// This setting is often used in combination with [isSemanticBoundary] to
3610  /// create semantic boundaries that are either writable or not for children.
3611  bool get explicitChildNodes => _explicitChildNodes;
3612  bool _explicitChildNodes;
3613  set explicitChildNodes(bool value) {
3614    assert(value != null);
3615    if (_explicitChildNodes == value)
3616      return;
3617    _explicitChildNodes = value;
3618    markNeedsSemanticsUpdate();
3619  }
3620
3621  /// Whether descendants of this [RenderObject] should have their semantic
3622  /// information ignored.
3623  ///
3624  /// When this flag is set to true, all child semantics nodes are ignored.
3625  /// This can be used as a convenience for cases where a child is wrapped in
3626  /// an [ExcludeSemantics] widget and then another [Semantics] widget.
3627  bool get excludeSemantics => _excludeSemantics;
3628  bool _excludeSemantics;
3629  set excludeSemantics(bool value) {
3630    assert(value != null);
3631    if (_excludeSemantics == value)
3632      return;
3633    _excludeSemantics = value;
3634    markNeedsSemanticsUpdate();
3635  }
3636
3637  /// If non-null, sets the [SemanticsNode.hasCheckedState] semantic to true and
3638  /// the [SemanticsNode.isChecked] semantic to the given value.
3639  bool get checked => _checked;
3640  bool _checked;
3641  set checked(bool value) {
3642    if (checked == value)
3643      return;
3644    _checked = value;
3645    markNeedsSemanticsUpdate();
3646  }
3647
3648  /// If non-null, sets the [SemanticsNode.hasEnabledState] semantic to true and
3649  /// the [SemanticsNode.isEnabled] semantic to the given value.
3650  bool get enabled => _enabled;
3651  bool _enabled;
3652  set enabled(bool value) {
3653    if (enabled == value)
3654      return;
3655    _enabled = value;
3656    markNeedsSemanticsUpdate();
3657  }
3658
3659  /// If non-null, sets the [SemanticsNode.isSelected] semantic to the given
3660  /// value.
3661  bool get selected => _selected;
3662  bool _selected;
3663  set selected(bool value) {
3664    if (selected == value)
3665      return;
3666    _selected = value;
3667    markNeedsSemanticsUpdate();
3668  }
3669
3670  /// If non-null, sets the [SemanticsNode.isButton] semantic to the given value.
3671  bool get button => _button;
3672  bool _button;
3673  set button(bool value) {
3674    if (button == value)
3675      return;
3676    _button = value;
3677    markNeedsSemanticsUpdate();
3678  }
3679
3680  /// If non-null, sets the [SemanticsNode.isHeader] semantic to the given value.
3681  bool get header => _header;
3682  bool _header;
3683  set header(bool value) {
3684    if (header == value)
3685      return;
3686    _header = value;
3687    markNeedsSemanticsUpdate();
3688  }
3689
3690  /// If non-null, sets the [SemanticsNode.isTextField] semantic to the given value.
3691  bool get textField => _textField;
3692  bool _textField;
3693  set textField(bool value) {
3694    if (textField == value)
3695      return;
3696    _textField = value;
3697    markNeedsSemanticsUpdate();
3698  }
3699
3700  /// If non-null, sets the [SemanticsNode.isReadOnly] semantic to the given value.
3701  bool get readOnly => _readOnly;
3702  bool _readOnly;
3703  set readOnly(bool value) {
3704    if (readOnly == value)
3705      return;
3706    _readOnly = value;
3707    markNeedsSemanticsUpdate();
3708  }
3709
3710  /// If non-null, sets the [SemanticsNode.isFocused] semantic to the given value.
3711  bool get focused => _focused;
3712  bool _focused;
3713  set focused(bool value) {
3714    if (focused == value)
3715      return;
3716    _focused = value;
3717    markNeedsSemanticsUpdate();
3718  }
3719
3720  /// If non-null, sets the [SemanticsNode.isInMutuallyExclusiveGroup] semantic
3721  /// to the given value.
3722  bool get inMutuallyExclusiveGroup => _inMutuallyExclusiveGroup;
3723  bool _inMutuallyExclusiveGroup;
3724  set inMutuallyExclusiveGroup(bool value) {
3725    if (inMutuallyExclusiveGroup == value)
3726      return;
3727    _inMutuallyExclusiveGroup = value;
3728    markNeedsSemanticsUpdate();
3729  }
3730
3731  /// If non-null, sets the [SemanticsNode.isObscured] semantic to the given
3732  /// value.
3733  bool get obscured => _obscured;
3734  bool _obscured;
3735  set obscured(bool value) {
3736    if (obscured == value)
3737      return;
3738    _obscured = value;
3739    markNeedsSemanticsUpdate();
3740  }
3741
3742  /// If non-null, sets the [SemanticsNode.isMultiline] semantic to the given
3743  /// value.
3744  bool get multiline => _multiline;
3745  bool _multiline;
3746  set multiline(bool value) {
3747    if (multiline == value)
3748      return;
3749    _multiline = value;
3750    markNeedsSemanticsUpdate();
3751  }
3752
3753  /// If non-null, sets the [SemanticsNode.scopesRoute] semantic to the give value.
3754  bool get scopesRoute => _scopesRoute;
3755  bool _scopesRoute;
3756  set scopesRoute(bool value) {
3757    if (scopesRoute == value)
3758      return;
3759    _scopesRoute = value;
3760    markNeedsSemanticsUpdate();
3761  }
3762
3763  /// If non-null, sets the [SemanticsNode.namesRoute] semantic to the give value.
3764  bool get namesRoute => _namesRoute;
3765  bool _namesRoute;
3766  set namesRoute(bool value) {
3767    if (_namesRoute == value)
3768      return;
3769    _namesRoute = value;
3770    markNeedsSemanticsUpdate();
3771  }
3772
3773  /// If non-null, sets the [SemanticsNode.isHidden] semantic to the given
3774  /// value.
3775  bool get hidden => _hidden;
3776  bool _hidden;
3777  set hidden(bool value) {
3778    if (hidden == value)
3779      return;
3780    _hidden = value;
3781    markNeedsSemanticsUpdate();
3782  }
3783
3784  /// If non-null, sets the [SemanticsNode.isImage] semantic to the given
3785  /// value.
3786  bool get image => _image;
3787  bool _image;
3788  set image(bool value) {
3789    if (_image == value)
3790      return;
3791    _image = value;
3792  }
3793
3794  /// If non-null, sets the [SemanticsNode.isLiveRegion] semantic to the given
3795  /// value.
3796  bool get liveRegion => _liveRegion;
3797  bool _liveRegion;
3798  set liveRegion(bool value) {
3799    if (_liveRegion == value)
3800      return;
3801    _liveRegion = value;
3802    markNeedsSemanticsUpdate();
3803  }
3804
3805  /// If non-null, sets the [SemanticsNode.isToggled] semantic to the given
3806  /// value.
3807  bool get toggled => _toggled;
3808  bool _toggled;
3809  set toggled(bool value) {
3810    if (_toggled == value)
3811      return;
3812    _toggled = value;
3813    markNeedsSemanticsUpdate();
3814  }
3815
3816  /// If non-null, sets the [SemanticsNode.label] semantic to the given value.
3817  ///
3818  /// The reading direction is given by [textDirection].
3819  String get label => _label;
3820  String _label;
3821  set label(String value) {
3822    if (_label == value)
3823      return;
3824    _label = value;
3825    markNeedsSemanticsUpdate();
3826  }
3827
3828  /// If non-null, sets the [SemanticsNode.value] semantic to the given value.
3829  ///
3830  /// The reading direction is given by [textDirection].
3831  String get value => _value;
3832  String _value;
3833  set value(String value) {
3834    if (_value == value)
3835      return;
3836    _value = value;
3837    markNeedsSemanticsUpdate();
3838  }
3839
3840  /// If non-null, sets the [SemanticsNode.increasedValue] semantic to the given
3841  /// value.
3842  ///
3843  /// The reading direction is given by [textDirection].
3844  String get increasedValue => _increasedValue;
3845  String _increasedValue;
3846  set increasedValue(String value) {
3847    if (_increasedValue == value)
3848      return;
3849    _increasedValue = value;
3850    markNeedsSemanticsUpdate();
3851  }
3852
3853  /// If non-null, sets the [SemanticsNode.decreasedValue] semantic to the given
3854  /// value.
3855  ///
3856  /// The reading direction is given by [textDirection].
3857  String get decreasedValue => _decreasedValue;
3858  String _decreasedValue;
3859  set decreasedValue(String value) {
3860    if (_decreasedValue == value)
3861      return;
3862    _decreasedValue = value;
3863    markNeedsSemanticsUpdate();
3864  }
3865
3866  /// If non-null, sets the [SemanticsNode.hint] semantic to the given value.
3867  ///
3868  /// The reading direction is given by [textDirection].
3869  String get hint => _hint;
3870  String _hint;
3871  set hint(String value) {
3872    if (_hint == value)
3873      return;
3874    _hint = value;
3875    markNeedsSemanticsUpdate();
3876  }
3877
3878  /// If non-null, sets the [SemanticsNode.hintOverride] to the given value.
3879  SemanticsHintOverrides get hintOverrides => _hintOverrides;
3880  SemanticsHintOverrides _hintOverrides;
3881  set hintOverrides(SemanticsHintOverrides value) {
3882    if (_hintOverrides == value)
3883      return;
3884    _hintOverrides = value;
3885    markNeedsSemanticsUpdate();
3886  }
3887
3888  /// If non-null, sets the [SemanticsNode.textDirection] semantic to the given value.
3889  ///
3890  /// This must not be null if [label], [hint], [value], [increasedValue], or
3891  /// [decreasedValue] are not null.
3892  TextDirection get textDirection => _textDirection;
3893  TextDirection _textDirection;
3894  set textDirection(TextDirection value) {
3895    if (textDirection == value)
3896      return;
3897    _textDirection = value;
3898    markNeedsSemanticsUpdate();
3899  }
3900
3901  /// Sets the [SemanticsNode.sortKey] to the given value.
3902  ///
3903  /// This defines how this node is sorted among the sibling semantics nodes
3904  /// to determine the order in which they are traversed by the accessibility
3905  /// services on the platform (e.g. VoiceOver on iOS and TalkBack on Android).
3906  SemanticsSortKey get sortKey => _sortKey;
3907  SemanticsSortKey _sortKey;
3908  set sortKey(SemanticsSortKey value) {
3909    if (sortKey == value)
3910      return;
3911    _sortKey = value;
3912    markNeedsSemanticsUpdate();
3913  }
3914
3915  /// The handler for [SemanticsAction.tap].
3916  ///
3917  /// This is the semantic equivalent of a user briefly tapping the screen with
3918  /// the finger without moving it. For example, a button should implement this
3919  /// action.
3920  ///
3921  /// VoiceOver users on iOS and TalkBack users on Android can trigger this
3922  /// action by double-tapping the screen while an element is focused.
3923  VoidCallback get onTap => _onTap;
3924  VoidCallback _onTap;
3925  set onTap(VoidCallback handler) {
3926    if (_onTap == handler)
3927      return;
3928    final bool hadValue = _onTap != null;
3929    _onTap = handler;
3930    if ((handler != null) == hadValue)
3931      markNeedsSemanticsUpdate();
3932  }
3933
3934  /// The handler for [SemanticsAction.dismiss].
3935  ///
3936  /// This is a request to dismiss the currently focused node.
3937  ///
3938  /// TalkBack users on Android can trigger this action in the local context
3939  /// menu, and VoiceOver users on iOS can trigger this action with a standard
3940  /// gesture or menu option.
3941  VoidCallback get onDismiss => _onDismiss;
3942  VoidCallback _onDismiss;
3943  set onDismiss(VoidCallback handler) {
3944    if (_onDismiss == handler)
3945      return;
3946    final bool hadValue = _onDismiss != null;
3947    _onDismiss = handler;
3948    if ((handler != null) == hadValue)
3949      markNeedsSemanticsUpdate();
3950  }
3951
3952  /// The handler for [SemanticsAction.longPress].
3953  ///
3954  /// This is the semantic equivalent of a user pressing and holding the screen
3955  /// with the finger for a few seconds without moving it.
3956  ///
3957  /// VoiceOver users on iOS and TalkBack users on Android can trigger this
3958  /// action by double-tapping the screen without lifting the finger after the
3959  /// second tap.
3960  VoidCallback get onLongPress => _onLongPress;
3961  VoidCallback _onLongPress;
3962  set onLongPress(VoidCallback handler) {
3963    if (_onLongPress == handler)
3964      return;
3965    final bool hadValue = _onLongPress != null;
3966    _onLongPress = handler;
3967    if ((handler != null) != hadValue)
3968      markNeedsSemanticsUpdate();
3969  }
3970
3971  /// The handler for [SemanticsAction.scrollLeft].
3972  ///
3973  /// This is the semantic equivalent of a user moving their finger across the
3974  /// screen from right to left. It should be recognized by controls that are
3975  /// horizontally scrollable.
3976  ///
3977  /// VoiceOver users on iOS can trigger this action by swiping left with three
3978  /// fingers. TalkBack users on Android can trigger this action by swiping
3979  /// right and then left in one motion path. On Android, [onScrollUp] and
3980  /// [onScrollLeft] share the same gesture. Therefore, only on of them should
3981  /// be provided.
3982  VoidCallback get onScrollLeft => _onScrollLeft;
3983  VoidCallback _onScrollLeft;
3984  set onScrollLeft(VoidCallback handler) {
3985    if (_onScrollLeft == handler)
3986      return;
3987    final bool hadValue = _onScrollLeft != null;
3988    _onScrollLeft = handler;
3989    if ((handler != null) != hadValue)
3990      markNeedsSemanticsUpdate();
3991  }
3992
3993  /// The handler for [SemanticsAction.scrollRight].
3994  ///
3995  /// This is the semantic equivalent of a user moving their finger across the
3996  /// screen from left to right. It should be recognized by controls that are
3997  /// horizontally scrollable.
3998  ///
3999  /// VoiceOver users on iOS can trigger this action by swiping right with three
4000  /// fingers. TalkBack users on Android can trigger this action by swiping
4001  /// left and then right in one motion path. On Android, [onScrollDown] and
4002  /// [onScrollRight] share the same gesture. Therefore, only on of them should
4003  /// be provided.
4004  VoidCallback get onScrollRight => _onScrollRight;
4005  VoidCallback _onScrollRight;
4006  set onScrollRight(VoidCallback handler) {
4007    if (_onScrollRight == handler)
4008      return;
4009    final bool hadValue = _onScrollRight != null;
4010    _onScrollRight = handler;
4011    if ((handler != null) != hadValue)
4012      markNeedsSemanticsUpdate();
4013  }
4014
4015  /// The handler for [SemanticsAction.scrollUp].
4016  ///
4017  /// This is the semantic equivalent of a user moving their finger across the
4018  /// screen from bottom to top. It should be recognized by controls that are
4019  /// vertically scrollable.
4020  ///
4021  /// VoiceOver users on iOS can trigger this action by swiping up with three
4022  /// fingers. TalkBack users on Android can trigger this action by swiping
4023  /// right and then left in one motion path. On Android, [onScrollUp] and
4024  /// [onScrollLeft] share the same gesture. Therefore, only on of them should
4025  /// be provided.
4026  VoidCallback get onScrollUp => _onScrollUp;
4027  VoidCallback _onScrollUp;
4028  set onScrollUp(VoidCallback handler) {
4029    if (_onScrollUp == handler)
4030      return;
4031    final bool hadValue = _onScrollUp != null;
4032    _onScrollUp = handler;
4033    if ((handler != null) != hadValue)
4034      markNeedsSemanticsUpdate();
4035  }
4036
4037  /// The handler for [SemanticsAction.scrollDown].
4038  ///
4039  /// This is the semantic equivalent of a user moving their finger across the
4040  /// screen from top to bottom. It should be recognized by controls that are
4041  /// vertically scrollable.
4042  ///
4043  /// VoiceOver users on iOS can trigger this action by swiping down with three
4044  /// fingers. TalkBack users on Android can trigger this action by swiping
4045  /// left and then right in one motion path. On Android, [onScrollDown] and
4046  /// [onScrollRight] share the same gesture. Therefore, only on of them should
4047  /// be provided.
4048  VoidCallback get onScrollDown => _onScrollDown;
4049  VoidCallback _onScrollDown;
4050  set onScrollDown(VoidCallback handler) {
4051    if (_onScrollDown == handler)
4052      return;
4053    final bool hadValue = _onScrollDown != null;
4054    _onScrollDown = handler;
4055    if ((handler != null) != hadValue)
4056      markNeedsSemanticsUpdate();
4057  }
4058
4059  /// The handler for [SemanticsAction.increase].
4060  ///
4061  /// This is a request to increase the value represented by the widget. For
4062  /// example, this action might be recognized by a slider control.
4063  ///
4064  /// VoiceOver users on iOS can trigger this action by swiping up with one
4065  /// finger. TalkBack users on Android can trigger this action by pressing the
4066  /// volume up button.
4067  VoidCallback get onIncrease => _onIncrease;
4068  VoidCallback _onIncrease;
4069  set onIncrease(VoidCallback handler) {
4070    if (_onIncrease == handler)
4071      return;
4072    final bool hadValue = _onIncrease != null;
4073    _onIncrease = handler;
4074    if ((handler != null) != hadValue)
4075      markNeedsSemanticsUpdate();
4076  }
4077
4078  /// The handler for [SemanticsAction.decrease].
4079  ///
4080  /// This is a request to decrease the value represented by the widget. For
4081  /// example, this action might be recognized by a slider control.
4082  ///
4083  /// VoiceOver users on iOS can trigger this action by swiping down with one
4084  /// finger. TalkBack users on Android can trigger this action by pressing the
4085  /// volume down button.
4086  VoidCallback get onDecrease => _onDecrease;
4087  VoidCallback _onDecrease;
4088  set onDecrease(VoidCallback handler) {
4089    if (_onDecrease == handler)
4090      return;
4091    final bool hadValue = _onDecrease != null;
4092    _onDecrease = handler;
4093    if ((handler != null) != hadValue)
4094      markNeedsSemanticsUpdate();
4095  }
4096
4097  /// The handler for [SemanticsAction.copy].
4098  ///
4099  /// This is a request to copy the current selection to the clipboard.
4100  ///
4101  /// TalkBack users on Android can trigger this action from the local context
4102  /// menu of a text field, for example.
4103  VoidCallback get onCopy => _onCopy;
4104  VoidCallback _onCopy;
4105  set onCopy(VoidCallback handler) {
4106    if (_onCopy == handler)
4107      return;
4108    final bool hadValue = _onCopy != null;
4109    _onCopy = handler;
4110    if ((handler != null) != hadValue)
4111      markNeedsSemanticsUpdate();
4112  }
4113
4114  /// The handler for [SemanticsAction.cut].
4115  ///
4116  /// This is a request to cut the current selection and place it in the
4117  /// clipboard.
4118  ///
4119  /// TalkBack users on Android can trigger this action from the local context
4120  /// menu of a text field, for example.
4121  VoidCallback get onCut => _onCut;
4122  VoidCallback _onCut;
4123  set onCut(VoidCallback handler) {
4124    if (_onCut == handler)
4125      return;
4126    final bool hadValue = _onCut != null;
4127    _onCut = handler;
4128    if ((handler != null) != hadValue)
4129      markNeedsSemanticsUpdate();
4130  }
4131
4132  /// The handler for [SemanticsAction.paste].
4133  ///
4134  /// This is a request to paste the current content of the clipboard.
4135  ///
4136  /// TalkBack users on Android can trigger this action from the local context
4137  /// menu of a text field, for example.
4138  VoidCallback get onPaste => _onPaste;
4139  VoidCallback _onPaste;
4140  set onPaste(VoidCallback handler) {
4141    if (_onPaste == handler)
4142      return;
4143    final bool hadValue = _onPaste != null;
4144    _onPaste = handler;
4145    if ((handler != null) != hadValue)
4146      markNeedsSemanticsUpdate();
4147  }
4148
4149  /// The handler for [SemanticsAction.onMoveCursorForwardByCharacter].
4150  ///
4151  /// This handler is invoked when the user wants to move the cursor in a
4152  /// text field forward by one character.
4153  ///
4154  /// TalkBack users can trigger this by pressing the volume up key while the
4155  /// input focus is in a text field.
4156  MoveCursorHandler get onMoveCursorForwardByCharacter => _onMoveCursorForwardByCharacter;
4157  MoveCursorHandler _onMoveCursorForwardByCharacter;
4158  set onMoveCursorForwardByCharacter(MoveCursorHandler handler) {
4159    if (_onMoveCursorForwardByCharacter == handler)
4160      return;
4161    final bool hadValue = _onMoveCursorForwardByCharacter != null;
4162    _onMoveCursorForwardByCharacter = handler;
4163    if ((handler != null) != hadValue)
4164      markNeedsSemanticsUpdate();
4165  }
4166
4167  /// The handler for [SemanticsAction.onMoveCursorBackwardByCharacter].
4168  ///
4169  /// This handler is invoked when the user wants to move the cursor in a
4170  /// text field backward by one character.
4171  ///
4172  /// TalkBack users can trigger this by pressing the volume down key while the
4173  /// input focus is in a text field.
4174  MoveCursorHandler get onMoveCursorBackwardByCharacter => _onMoveCursorBackwardByCharacter;
4175  MoveCursorHandler _onMoveCursorBackwardByCharacter;
4176  set onMoveCursorBackwardByCharacter(MoveCursorHandler handler) {
4177    if (_onMoveCursorBackwardByCharacter == handler)
4178      return;
4179    final bool hadValue = _onMoveCursorBackwardByCharacter != null;
4180    _onMoveCursorBackwardByCharacter = handler;
4181    if ((handler != null) != hadValue)
4182      markNeedsSemanticsUpdate();
4183  }
4184
4185  /// The handler for [SemanticsAction.onMoveCursorForwardByWord].
4186  ///
4187  /// This handler is invoked when the user wants to move the cursor in a
4188  /// text field backward by one character.
4189  ///
4190  /// TalkBack users can trigger this by pressing the volume down key while the
4191  /// input focus is in a text field.
4192  MoveCursorHandler get onMoveCursorForwardByWord => _onMoveCursorForwardByWord;
4193  MoveCursorHandler _onMoveCursorForwardByWord;
4194  set onMoveCursorForwardByWord(MoveCursorHandler handler) {
4195    if (_onMoveCursorForwardByWord == handler)
4196      return;
4197    final bool hadValue = _onMoveCursorForwardByWord != null;
4198    _onMoveCursorForwardByWord = handler;
4199    if ((handler != null) != hadValue)
4200      markNeedsSemanticsUpdate();
4201  }
4202
4203  /// The handler for [SemanticsAction.onMoveCursorBackwardByWord].
4204  ///
4205  /// This handler is invoked when the user wants to move the cursor in a
4206  /// text field backward by one character.
4207  ///
4208  /// TalkBack users can trigger this by pressing the volume down key while the
4209  /// input focus is in a text field.
4210  MoveCursorHandler get onMoveCursorBackwardByWord => _onMoveCursorBackwardByWord;
4211  MoveCursorHandler _onMoveCursorBackwardByWord;
4212  set onMoveCursorBackwardByWord(MoveCursorHandler handler) {
4213    if (_onMoveCursorBackwardByWord == handler)
4214      return;
4215    final bool hadValue = _onMoveCursorBackwardByWord != null;
4216    _onMoveCursorBackwardByWord = handler;
4217    if ((handler != null) != hadValue)
4218      markNeedsSemanticsUpdate();
4219  }
4220
4221  /// The handler for [SemanticsAction.setSelection].
4222  ///
4223  /// This handler is invoked when the user either wants to change the currently
4224  /// selected text in a text field or change the position of the cursor.
4225  ///
4226  /// TalkBack users can trigger this handler by selecting "Move cursor to
4227  /// beginning/end" or "Select all" from the local context menu.
4228  SetSelectionHandler get onSetSelection => _onSetSelection;
4229  SetSelectionHandler _onSetSelection;
4230  set onSetSelection(SetSelectionHandler handler) {
4231    if (_onSetSelection == handler)
4232      return;
4233    final bool hadValue = _onSetSelection != null;
4234    _onSetSelection = handler;
4235    if ((handler != null) != hadValue)
4236      markNeedsSemanticsUpdate();
4237  }
4238
4239  /// The handler for [SemanticsAction.didGainAccessibilityFocus].
4240  ///
4241  /// This handler is invoked when the node annotated with this handler gains
4242  /// the accessibility focus. The accessibility focus is the
4243  /// green (on Android with TalkBack) or black (on iOS with VoiceOver)
4244  /// rectangle shown on screen to indicate what element an accessibility
4245  /// user is currently interacting with.
4246  ///
4247  /// The accessibility focus is different from the input focus. The input focus
4248  /// is usually held by the element that currently responds to keyboard inputs.
4249  /// Accessibility focus and input focus can be held by two different nodes!
4250  ///
4251  /// See also:
4252  ///
4253  ///  * [onDidLoseAccessibilityFocus], which is invoked when the accessibility
4254  ///    focus is removed from the node.
4255  ///  * [FocusNode], [FocusScope], [FocusManager], which manage the input focus.
4256  VoidCallback get onDidGainAccessibilityFocus => _onDidGainAccessibilityFocus;
4257  VoidCallback _onDidGainAccessibilityFocus;
4258  set onDidGainAccessibilityFocus(VoidCallback handler) {
4259    if (_onDidGainAccessibilityFocus == handler)
4260      return;
4261    final bool hadValue = _onDidGainAccessibilityFocus != null;
4262    _onDidGainAccessibilityFocus = handler;
4263    if ((handler != null) != hadValue)
4264      markNeedsSemanticsUpdate();
4265  }
4266
4267  /// The handler for [SemanticsAction.didLoseAccessibilityFocus].
4268  ///
4269  /// This handler is invoked when the node annotated with this handler
4270  /// loses the accessibility focus. The accessibility focus is
4271  /// the green (on Android with TalkBack) or black (on iOS with VoiceOver)
4272  /// rectangle shown on screen to indicate what element an accessibility
4273  /// user is currently interacting with.
4274  ///
4275  /// The accessibility focus is different from the input focus. The input focus
4276  /// is usually held by the element that currently responds to keyboard inputs.
4277  /// Accessibility focus and input focus can be held by two different nodes!
4278  ///
4279  /// See also:
4280  ///
4281  ///  * [onDidGainAccessibilityFocus], which is invoked when the node gains
4282  ///    accessibility focus.
4283  ///  * [FocusNode], [FocusScope], [FocusManager], which manage the input focus.
4284  VoidCallback get onDidLoseAccessibilityFocus => _onDidLoseAccessibilityFocus;
4285  VoidCallback _onDidLoseAccessibilityFocus;
4286  set onDidLoseAccessibilityFocus(VoidCallback handler) {
4287    if (_onDidLoseAccessibilityFocus == handler)
4288      return;
4289    final bool hadValue = _onDidLoseAccessibilityFocus != null;
4290    _onDidLoseAccessibilityFocus = handler;
4291    if ((handler != null) != hadValue)
4292      markNeedsSemanticsUpdate();
4293  }
4294
4295  /// The handlers and supported [CustomSemanticsAction]s for this node.
4296  ///
4297  /// These handlers are called whenever the user performs the associated
4298  /// custom accessibility action from a special platform menu. Providing any
4299  /// custom actions here also adds [SemanticsAction.customAction] to the node.
4300  ///
4301  /// See also:
4302  ///
4303  ///  * [CustomSemanticsAction], for an explanation of custom actions.
4304  Map<CustomSemanticsAction, VoidCallback> get customSemanticsActions => _customSemanticsActions;
4305  Map<CustomSemanticsAction, VoidCallback> _customSemanticsActions;
4306  set customSemanticsActions(Map<CustomSemanticsAction, VoidCallback> value) {
4307    if (_customSemanticsActions == value)
4308      return;
4309    _customSemanticsActions = value;
4310    markNeedsSemanticsUpdate();
4311  }
4312
4313  @override
4314  void visitChildrenForSemantics(RenderObjectVisitor visitor) {
4315    if (excludeSemantics)
4316      return;
4317    super.visitChildrenForSemantics(visitor);
4318  }
4319
4320  @override
4321  void describeSemanticsConfiguration(SemanticsConfiguration config) {
4322    super.describeSemanticsConfiguration(config);
4323    config.isSemanticBoundary = container;
4324    config.explicitChildNodes = explicitChildNodes;
4325    assert((scopesRoute == true && explicitChildNodes == true) || scopesRoute != true,
4326      'explicitChildNodes must be set to true if scopes route is true');
4327    assert(!(toggled == true && checked == true),
4328      'A semantics node cannot be toggled and checked at the same time');
4329
4330    if (enabled != null)
4331      config.isEnabled = enabled;
4332    if (checked != null)
4333      config.isChecked = checked;
4334    if (toggled != null)
4335      config.isToggled = toggled;
4336    if (selected != null)
4337      config.isSelected = selected;
4338    if (button != null)
4339      config.isButton = button;
4340    if (header != null)
4341      config.isHeader = header;
4342    if (textField != null)
4343      config.isTextField = textField;
4344    if (readOnly != null)
4345      config.isReadOnly = readOnly;
4346    if (focused != null)
4347      config.isFocused = focused;
4348    if (inMutuallyExclusiveGroup != null)
4349      config.isInMutuallyExclusiveGroup = inMutuallyExclusiveGroup;
4350    if (obscured != null)
4351      config.isObscured = obscured;
4352    if (multiline != null)
4353      config.isMultiline = multiline;
4354    if (hidden != null)
4355      config.isHidden = hidden;
4356    if (image != null)
4357      config.isImage = image;
4358    if (label != null)
4359      config.label = label;
4360    if (value != null)
4361      config.value = value;
4362    if (increasedValue != null)
4363      config.increasedValue = increasedValue;
4364    if (decreasedValue != null)
4365      config.decreasedValue = decreasedValue;
4366    if (hint != null)
4367      config.hint = hint;
4368    if (hintOverrides != null && hintOverrides.isNotEmpty)
4369      config.hintOverrides = hintOverrides;
4370    if (scopesRoute != null)
4371      config.scopesRoute = scopesRoute;
4372    if (namesRoute != null)
4373      config.namesRoute = namesRoute;
4374    if (liveRegion != null)
4375      config.liveRegion = liveRegion;
4376    if (textDirection != null)
4377      config.textDirection = textDirection;
4378    if (sortKey != null)
4379      config.sortKey = sortKey;
4380    // Registering _perform* as action handlers instead of the user provided
4381    // ones to ensure that changing a user provided handler from a non-null to
4382    // another non-null value doesn't require a semantics update.
4383    if (onTap != null)
4384      config.onTap = _performTap;
4385    if (onLongPress != null)
4386      config.onLongPress = _performLongPress;
4387    if (onDismiss != null)
4388      config.onDismiss = _performDismiss;
4389    if (onScrollLeft != null)
4390      config.onScrollLeft = _performScrollLeft;
4391    if (onScrollRight != null)
4392      config.onScrollRight = _performScrollRight;
4393    if (onScrollUp != null)
4394      config.onScrollUp = _performScrollUp;
4395    if (onScrollDown != null)
4396      config.onScrollDown = _performScrollDown;
4397    if (onIncrease != null)
4398      config.onIncrease = _performIncrease;
4399    if (onDecrease != null)
4400      config.onDecrease = _performDecrease;
4401    if (onCopy != null)
4402      config.onCopy = _performCopy;
4403    if (onCut != null)
4404      config.onCut = _performCut;
4405    if (onPaste != null)
4406      config.onPaste = _performPaste;
4407    if (onMoveCursorForwardByCharacter != null)
4408      config.onMoveCursorForwardByCharacter = _performMoveCursorForwardByCharacter;
4409    if (onMoveCursorBackwardByCharacter != null)
4410      config.onMoveCursorBackwardByCharacter = _performMoveCursorBackwardByCharacter;
4411    if (onMoveCursorForwardByWord != null)
4412      config.onMoveCursorForwardByWord = _performMoveCursorForwardByWord;
4413    if (onMoveCursorBackwardByWord != null)
4414      config.onMoveCursorBackwardByWord = _performMoveCursorBackwardByWord;
4415    if (onSetSelection != null)
4416      config.onSetSelection = _performSetSelection;
4417    if (onDidGainAccessibilityFocus != null)
4418      config.onDidGainAccessibilityFocus = _performDidGainAccessibilityFocus;
4419    if (onDidLoseAccessibilityFocus != null)
4420      config.onDidLoseAccessibilityFocus = _performDidLoseAccessibilityFocus;
4421    if (customSemanticsActions != null)
4422      config.customSemanticsActions = _customSemanticsActions;
4423  }
4424
4425  void _performTap() {
4426    if (onTap != null)
4427      onTap();
4428  }
4429
4430  void _performLongPress() {
4431    if (onLongPress != null)
4432      onLongPress();
4433  }
4434
4435  void _performDismiss() {
4436    if (onDismiss != null)
4437      onDismiss();
4438  }
4439
4440  void _performScrollLeft() {
4441    if (onScrollLeft != null)
4442      onScrollLeft();
4443  }
4444
4445  void _performScrollRight() {
4446    if (onScrollRight != null)
4447      onScrollRight();
4448  }
4449
4450  void _performScrollUp() {
4451    if (onScrollUp != null)
4452      onScrollUp();
4453  }
4454
4455  void _performScrollDown() {
4456    if (onScrollDown != null)
4457      onScrollDown();
4458  }
4459
4460  void _performIncrease() {
4461    if (onIncrease != null)
4462      onIncrease();
4463  }
4464
4465  void _performDecrease() {
4466    if (onDecrease != null)
4467      onDecrease();
4468  }
4469
4470  void _performCopy() {
4471    if (onCopy != null)
4472      onCopy();
4473  }
4474
4475  void _performCut() {
4476    if (onCut != null)
4477      onCut();
4478  }
4479
4480  void _performPaste() {
4481    if (onPaste != null)
4482      onPaste();
4483  }
4484
4485  void _performMoveCursorForwardByCharacter(bool extendSelection) {
4486    if (onMoveCursorForwardByCharacter != null)
4487      onMoveCursorForwardByCharacter(extendSelection);
4488  }
4489
4490  void _performMoveCursorBackwardByCharacter(bool extendSelection) {
4491    if (onMoveCursorBackwardByCharacter != null)
4492      onMoveCursorBackwardByCharacter(extendSelection);
4493  }
4494
4495  void _performMoveCursorForwardByWord(bool extendSelection) {
4496    if (onMoveCursorForwardByWord != null)
4497      onMoveCursorForwardByWord(extendSelection);
4498  }
4499
4500  void _performMoveCursorBackwardByWord(bool extendSelection) {
4501    if (onMoveCursorBackwardByWord != null)
4502      onMoveCursorBackwardByWord(extendSelection);
4503  }
4504
4505  void _performSetSelection(TextSelection selection) {
4506    if (onSetSelection != null)
4507      onSetSelection(selection);
4508  }
4509
4510  void _performDidGainAccessibilityFocus() {
4511    if (onDidGainAccessibilityFocus != null)
4512      onDidGainAccessibilityFocus();
4513  }
4514
4515  void _performDidLoseAccessibilityFocus() {
4516    if (onDidLoseAccessibilityFocus != null)
4517      onDidLoseAccessibilityFocus();
4518  }
4519}
4520
4521/// Causes the semantics of all earlier render objects below the same semantic
4522/// boundary to be dropped.
4523///
4524/// This is useful in a stack where an opaque mask should prevent interactions
4525/// with the render objects painted below the mask.
4526class RenderBlockSemantics extends RenderProxyBox {
4527  /// Create a render object that blocks semantics for nodes below it in paint
4528  /// order.
4529  RenderBlockSemantics({
4530    RenderBox child,
4531    bool blocking = true,
4532  }) : _blocking = blocking,
4533       super(child);
4534
4535  /// Whether this render object is blocking semantics of previously painted
4536  /// [RenderObject]s below a common semantics boundary from the semantic tree.
4537  bool get blocking => _blocking;
4538  bool _blocking;
4539  set blocking(bool value) {
4540    assert(value != null);
4541    if (value == _blocking)
4542      return;
4543    _blocking = value;
4544    markNeedsSemanticsUpdate();
4545  }
4546
4547  @override
4548  void describeSemanticsConfiguration(SemanticsConfiguration config) {
4549    super.describeSemanticsConfiguration(config);
4550    config.isBlockingSemanticsOfPreviouslyPaintedNodes = blocking;
4551  }
4552
4553  @override
4554  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
4555    super.debugFillProperties(properties);
4556    properties.add(DiagnosticsProperty<bool>('blocking', blocking));
4557  }
4558}
4559
4560/// Causes the semantics of all descendants to be merged into this
4561/// node such that the entire subtree becomes a single leaf in the
4562/// semantics tree.
4563///
4564/// Useful for combining the semantics of multiple render objects that
4565/// form part of a single conceptual widget, e.g. a checkbox, a label,
4566/// and the gesture detector that goes with them.
4567class RenderMergeSemantics extends RenderProxyBox {
4568  /// Creates a render object that merges the semantics from its descendants.
4569  RenderMergeSemantics({ RenderBox child }) : super(child);
4570
4571  @override
4572  void describeSemanticsConfiguration(SemanticsConfiguration config) {
4573    super.describeSemanticsConfiguration(config);
4574    config
4575      ..isSemanticBoundary = true
4576      ..isMergingSemanticsOfDescendants = true;
4577  }
4578}
4579
4580/// Excludes this subtree from the semantic tree.
4581///
4582/// When [excluding] is true, this render object (and its subtree) is excluded
4583/// from the semantic tree.
4584///
4585/// Useful e.g. for hiding text that is redundant with other text next
4586/// to it (e.g. text included only for the visual effect).
4587class RenderExcludeSemantics extends RenderProxyBox {
4588  /// Creates a render object that ignores the semantics of its subtree.
4589  RenderExcludeSemantics({
4590    RenderBox child,
4591    bool excluding = true,
4592  }) : _excluding = excluding,
4593       super(child) {
4594    assert(_excluding != null);
4595  }
4596
4597  /// Whether this render object is excluded from the semantic tree.
4598  bool get excluding => _excluding;
4599  bool _excluding;
4600  set excluding(bool value) {
4601    assert(value != null);
4602    if (value == _excluding)
4603      return;
4604    _excluding = value;
4605    markNeedsSemanticsUpdate();
4606  }
4607
4608  @override
4609  void visitChildrenForSemantics(RenderObjectVisitor visitor) {
4610    if (excluding)
4611      return;
4612    super.visitChildrenForSemantics(visitor);
4613  }
4614
4615  @override
4616  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
4617    super.debugFillProperties(properties);
4618    properties.add(DiagnosticsProperty<bool>('excluding', excluding));
4619  }
4620}
4621
4622/// A render objects that annotates semantics with an index.
4623///
4624/// Certain widgets will automatically provide a child index for building
4625/// semantics. For example, the [ScrollView] uses the index of the first
4626/// visible child semantics node to determine the
4627/// [SemanticsConfiguration.scrollIndex].
4628///
4629/// See also:
4630///
4631///  * [CustomScrollView], for an explanation of scroll semantics.
4632class RenderIndexedSemantics extends RenderProxyBox {
4633  /// Creates a render object that annotates the child semantics with an index.
4634  RenderIndexedSemantics({
4635    RenderBox child,
4636    @required int index,
4637  }) : assert(index != null),
4638       _index = index,
4639       super(child);
4640
4641  /// The index used to annotated child semantics.
4642  int get index => _index;
4643  int _index;
4644  set index(int value) {
4645    if (value == index)
4646      return;
4647    _index = value;
4648    markNeedsSemanticsUpdate();
4649  }
4650
4651  @override
4652  void describeSemanticsConfiguration(SemanticsConfiguration config) {
4653    super.describeSemanticsConfiguration(config);
4654    config.isSemanticBoundary = true;
4655    config.indexInParent = index;
4656  }
4657
4658  @override
4659  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
4660    super.debugFillProperties(properties);
4661    properties.add(DiagnosticsProperty<int>('index', index));
4662  }
4663}
4664
4665/// Provides an anchor for a [RenderFollowerLayer].
4666///
4667/// See also:
4668///
4669///  * [CompositedTransformTarget], the corresponding widget.
4670///  * [LeaderLayer], the layer that this render object creates.
4671class RenderLeaderLayer extends RenderProxyBox {
4672  /// Creates a render object that uses a [LeaderLayer].
4673  ///
4674  /// The [link] must not be null.
4675  RenderLeaderLayer({
4676    @required LayerLink link,
4677    RenderBox child,
4678  }) : assert(link != null),
4679       super(child) {
4680    this.link = link;
4681  }
4682
4683  /// The link object that connects this [RenderLeaderLayer] with one or more
4684  /// [RenderFollowerLayer]s.
4685  ///
4686  /// This property must not be null. The object must not be associated with
4687  /// another [RenderLeaderLayer] that is also being painted.
4688  LayerLink get link => _link;
4689  LayerLink _link;
4690  set link(LayerLink value) {
4691    assert(value != null);
4692    if (_link == value)
4693      return;
4694    _link = value;
4695    markNeedsPaint();
4696  }
4697
4698  @override
4699  bool get alwaysNeedsCompositing => true;
4700
4701  @override
4702  void paint(PaintingContext context, Offset offset) {
4703    if (layer == null) {
4704      layer = LeaderLayer(link: link, offset: offset);
4705    } else {
4706      final LeaderLayer leaderLayer = layer;
4707      leaderLayer
4708        ..link = link
4709        ..offset = offset;
4710    }
4711    context.pushLayer(layer, super.paint, Offset.zero);
4712    assert(layer != null);
4713  }
4714
4715  @override
4716  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
4717    super.debugFillProperties(properties);
4718    properties.add(DiagnosticsProperty<LayerLink>('link', link));
4719  }
4720}
4721
4722/// Transform the child so that its origin is [offset] from the origin of the
4723/// [RenderLeaderLayer] with the same [LayerLink].
4724///
4725/// The [RenderLeaderLayer] in question must be earlier in the paint order.
4726///
4727/// Hit testing on descendants of this render object will only work if the
4728/// target position is within the box that this render object's parent considers
4729/// to be hittable.
4730///
4731/// See also:
4732///
4733///  * [CompositedTransformFollower], the corresponding widget.
4734///  * [FollowerLayer], the layer that this render object creates.
4735class RenderFollowerLayer extends RenderProxyBox {
4736  /// Creates a render object that uses a [FollowerLayer].
4737  ///
4738  /// The [link] and [offset] arguments must not be null.
4739  RenderFollowerLayer({
4740    @required LayerLink link,
4741    bool showWhenUnlinked = true,
4742    Offset offset = Offset.zero,
4743    RenderBox child,
4744  }) : assert(link != null),
4745       assert(showWhenUnlinked != null),
4746       assert(offset != null),
4747       super(child) {
4748    this.link = link;
4749    this.showWhenUnlinked = showWhenUnlinked;
4750    this.offset = offset;
4751  }
4752
4753  /// The link object that connects this [RenderFollowerLayer] with a
4754  /// [RenderLeaderLayer] earlier in the paint order.
4755  LayerLink get link => _link;
4756  LayerLink _link;
4757  set link(LayerLink value) {
4758    assert(value != null);
4759    if (_link == value)
4760      return;
4761    _link = value;
4762    markNeedsPaint();
4763  }
4764
4765  /// Whether to show the render object's contents when there is no
4766  /// corresponding [RenderLeaderLayer] with the same [link].
4767  ///
4768  /// When the render object is linked, the child is positioned such that it has
4769  /// the same global position as the linked [RenderLeaderLayer].
4770  ///
4771  /// When the render object is not linked, then: if [showWhenUnlinked] is true,
4772  /// the child is visible and not repositioned; if it is false, then child is
4773  /// hidden.
4774  bool get showWhenUnlinked => _showWhenUnlinked;
4775  bool _showWhenUnlinked;
4776  set showWhenUnlinked(bool value) {
4777    assert(value != null);
4778    if (_showWhenUnlinked == value)
4779      return;
4780    _showWhenUnlinked = value;
4781    markNeedsPaint();
4782  }
4783
4784  /// The offset to apply to the origin of the linked [RenderLeaderLayer] to
4785  /// obtain this render object's origin.
4786  Offset get offset => _offset;
4787  Offset _offset;
4788  set offset(Offset value) {
4789    assert(value != null);
4790    if (_offset == value)
4791      return;
4792    _offset = value;
4793    markNeedsPaint();
4794  }
4795
4796  @override
4797  void detach() {
4798    layer = null;
4799    super.detach();
4800  }
4801
4802  @override
4803  bool get alwaysNeedsCompositing => true;
4804
4805  /// The layer we created when we were last painted.
4806  @override
4807  FollowerLayer get layer => super.layer;
4808
4809  /// Return the transform that was used in the last composition phase, if any.
4810  ///
4811  /// If the [FollowerLayer] has not yet been created, was never composited, or
4812  /// was unable to determine the transform (see
4813  /// [FollowerLayer.getLastTransform]), this returns the identity matrix (see
4814  /// [new Matrix4.identity].
4815  Matrix4 getCurrentTransform() {
4816    return layer?.getLastTransform() ?? Matrix4.identity();
4817  }
4818
4819  @override
4820  bool hitTest(BoxHitTestResult result, { Offset position }) {
4821    // RenderFollowerLayer objects don't check if they are
4822    // themselves hit, because it's confusing to think about
4823    // how the untransformed size and the child's transformed
4824    // position interact.
4825    return hitTestChildren(result, position: position);
4826  }
4827
4828  @override
4829  bool hitTestChildren(BoxHitTestResult result, { Offset position }) {
4830    return result.addWithPaintTransform(
4831      transform: getCurrentTransform(),
4832      position: position,
4833      hitTest: (BoxHitTestResult result, Offset position) {
4834        return super.hitTestChildren(result, position: position);
4835      },
4836    );
4837  }
4838
4839  @override
4840  void paint(PaintingContext context, Offset offset) {
4841    assert(showWhenUnlinked != null);
4842    if (layer == null) {
4843      layer = FollowerLayer(
4844        link: link,
4845        showWhenUnlinked: showWhenUnlinked,
4846        linkedOffset: this.offset,
4847        unlinkedOffset: offset,
4848      );
4849    } else {
4850      layer
4851        ..link = link
4852        ..showWhenUnlinked = showWhenUnlinked
4853        ..linkedOffset = this.offset
4854        ..unlinkedOffset = offset;
4855    }
4856    context.pushLayer(
4857      layer,
4858      super.paint,
4859      Offset.zero,
4860      childPaintBounds: const Rect.fromLTRB(
4861        // We don't know where we'll end up, so we have no idea what our cull rect should be.
4862        double.negativeInfinity,
4863        double.negativeInfinity,
4864        double.infinity,
4865        double.infinity,
4866      ),
4867    );
4868  }
4869
4870  @override
4871  void applyPaintTransform(RenderBox child, Matrix4 transform) {
4872    transform.multiply(getCurrentTransform());
4873  }
4874
4875  @override
4876  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
4877    super.debugFillProperties(properties);
4878    properties.add(DiagnosticsProperty<LayerLink>('link', link));
4879    properties.add(DiagnosticsProperty<bool>('showWhenUnlinked', showWhenUnlinked));
4880    properties.add(DiagnosticsProperty<Offset>('offset', offset));
4881    properties.add(TransformProperty('current transform matrix', getCurrentTransform()));
4882  }
4883}
4884
4885/// Render object which inserts an [AnnotatedRegionLayer] into the layer tree.
4886///
4887/// See also:
4888///
4889///  * [Layer.find], for an example of how this value is retrieved.
4890///  * [AnnotatedRegionLayer], the layer this render object creates.
4891class RenderAnnotatedRegion<T> extends RenderProxyBox {
4892
4893  /// Creates a new [RenderAnnotatedRegion] to insert [value] into the
4894  /// layer tree.
4895  ///
4896  /// If [sized] is true, the layer is provided with the size of this render
4897  /// object to clip the results of [Layer.findRegion].
4898  ///
4899  /// Neither [value] nor [sized] can be null.
4900  RenderAnnotatedRegion({
4901    @required T value,
4902    @required bool sized,
4903    RenderBox child,
4904  }) : assert(value != null),
4905       assert(sized != null),
4906       _value = value,
4907       _sized = sized,
4908       super(child);
4909
4910  /// A value which can be retrieved using [Layer.find].
4911  T get value => _value;
4912  T _value;
4913  set value (T newValue) {
4914    if (_value == newValue)
4915      return;
4916    _value = newValue;
4917    markNeedsPaint();
4918  }
4919
4920  /// Whether the render object will pass its [size] to the [AnnotatedRegionLayer].
4921  bool get sized => _sized;
4922  bool _sized;
4923  set sized(bool value) {
4924    if (_sized == value)
4925      return;
4926    _sized = value;
4927    markNeedsPaint();
4928  }
4929
4930  @override
4931  final bool alwaysNeedsCompositing = true;
4932
4933  @override
4934  void paint(PaintingContext context, Offset offset) {
4935    // Annotated region layers are not retained because they do not create engine layers.
4936    final AnnotatedRegionLayer<T> layer = AnnotatedRegionLayer<T>(
4937      value,
4938      size: sized ? size : null,
4939      offset: sized ? offset : null,
4940    );
4941    context.pushLayer(layer, super.paint, offset);
4942  }
4943}
4944