@@ -641,7 +641,7 @@ class _RenderInkFeatures extends RenderProxyBox implements MaterialInkController
641641 }
642642
643643 void _didChangeLayout () {
644- if (_inkFeatures != null && _inkFeatures ! .isNotEmpty) {
644+ if (_inkFeatures? .isNotEmpty ?? false ) {
645645 markNeedsPaint ();
646646 }
647647 }
@@ -651,16 +651,18 @@ class _RenderInkFeatures extends RenderProxyBox implements MaterialInkController
651651
652652 @override
653653 void paint (PaintingContext context, Offset offset) {
654- if (_inkFeatures != null && _inkFeatures! .isNotEmpty) {
654+ final List <InkFeature >? inkFeatures = _inkFeatures;
655+ if (inkFeatures != null && inkFeatures.isNotEmpty) {
655656 final Canvas canvas = context.canvas;
656657 canvas.save ();
657658 canvas.translate (offset.dx, offset.dy);
658659 canvas.clipRect (Offset .zero & size);
659- for (final InkFeature inkFeature in _inkFeatures ! ) {
660+ for (final InkFeature inkFeature in inkFeatures ) {
660661 inkFeature._paint (canvas);
661662 }
662663 canvas.restore ();
663664 }
665+ assert (inkFeatures == _inkFeatures);
664666 super .paint (context, offset);
665667 }
666668}
@@ -740,32 +742,71 @@ abstract class InkFeature {
740742 onRemoved? .call ();
741743 }
742744
745+ // Returns the paint transform that allows `fromRenderObject` to perform paint
746+ // in `toRenderObject`'s coordinate space.
747+ //
748+ // Returns null if either `fromRenderObject` or `toRenderObject` is not in the
749+ // same render tree, or either of them is in an offscreen subtree (see
750+ // RenderObject.paintsChild).
751+ static Matrix4 ? _getPaintTransform (
752+ RenderObject fromRenderObject,
753+ RenderObject toRenderObject,
754+ ) {
755+ // The paths to fromRenderObject and toRenderObject's common ancestor.
756+ final List <RenderObject > fromPath = < RenderObject > [fromRenderObject];
757+ final List <RenderObject > toPath = < RenderObject > [toRenderObject];
758+
759+ RenderObject from = fromRenderObject;
760+ RenderObject to = toRenderObject;
761+
762+ while (! identical (from, to)) {
763+ final int fromDepth = from.depth;
764+ final int toDepth = to.depth;
765+
766+ if (fromDepth >= toDepth) {
767+ final AbstractNode ? fromParent = from.parent;
768+ // Return early if the 2 render objects are not in the same render tree,
769+ // or either of them is offscreen and thus won't get painted.
770+ if (fromParent is ! RenderObject || ! fromParent.paintsChild (from)) {
771+ return null ;
772+ }
773+ fromPath.add (fromParent);
774+ from = fromParent;
775+ }
776+
777+ if (fromDepth <= toDepth) {
778+ final AbstractNode ? toParent = to.parent;
779+ if (toParent is ! RenderObject || ! toParent.paintsChild (to)) {
780+ return null ;
781+ }
782+ toPath.add (toParent);
783+ to = toParent;
784+ }
785+ }
786+ assert (identical (from, to));
787+
788+ final Matrix4 transform = Matrix4 .identity ();
789+ final Matrix4 inverseTransform = Matrix4 .identity ();
790+
791+ for (int index = toPath.length - 1 ; index > 0 ; index -= 1 ) {
792+ toPath[index].applyPaintTransform (toPath[index - 1 ], transform);
793+ }
794+ for (int index = fromPath.length - 1 ; index > 0 ; index -= 1 ) {
795+ fromPath[index].applyPaintTransform (fromPath[index - 1 ], inverseTransform);
796+ }
797+
798+ final double det = inverseTransform.invert ();
799+ return det != 0 ? (inverseTransform..multiply (transform)) : null ;
800+ }
801+
743802 void _paint (Canvas canvas) {
744803 assert (referenceBox.attached);
745804 assert (! _debugDisposed);
746- // find the chain of renderers from us to the feature's referenceBox
747- final List <RenderObject > descendants = < RenderObject > [referenceBox];
748- RenderObject node = referenceBox;
749- while (node != _controller) {
750- final RenderObject childNode = node;
751- node = node.parent! as RenderObject ;
752- if (! node.paintsChild (childNode)) {
753- // Some node between the reference box and this would skip painting on
754- // the reference box, so bail out early and avoid unnecessary painting.
755- // Some cases where this can happen are the reference box being
756- // offstage, in a fully transparent opacity node, or in a keep alive
757- // bucket.
758- return ;
759- }
760- descendants.add (node);
761- }
762805 // determine the transform that gets our coordinate system to be like theirs
763- final Matrix4 transform = Matrix4 .identity ();
764- assert (descendants.length >= 2 );
765- for (int index = descendants.length - 1 ; index > 0 ; index -= 1 ) {
766- descendants[index].applyPaintTransform (descendants[index - 1 ], transform);
806+ final Matrix4 ? transform = _getPaintTransform (_controller, referenceBox);
807+ if (transform != null ) {
808+ paintFeature (canvas, transform);
767809 }
768- paintFeature (canvas, transform);
769810 }
770811
771812 /// Override this method to paint the ink feature.
0 commit comments