Mirror before scaling in _AnimatedIconPainter (#93312)
diff --git a/AUTHORS b/AUTHORS
index d30ad28..b11eaf4 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -91,3 +91,4 @@
TheOneWithTheBraid <the-one@with-the-braid.cf>
Alberto Miola <betoman96@gmail.com>
Twin Sun, LLC <google-contrib@twinsunsolutions.com>
+Taskulu LDA <contributions@taskulu.com>
diff --git a/packages/flutter/lib/src/material/animated_icons/animated_icons.dart b/packages/flutter/lib/src/material/animated_icons/animated_icons.dart
index 08dfe2b..9187c90 100644
--- a/packages/flutter/lib/src/material/animated_icons/animated_icons.dart
+++ b/packages/flutter/lib/src/material/animated_icons/animated_icons.dart
@@ -156,11 +156,11 @@
void paint(ui.Canvas canvas, Size size) {
// The RenderCustomPaint render object performs canvas.save before invoking
// this and canvas.restore after, so we don't need to do it here.
- canvas.scale(scale, scale);
if (shouldMirror) {
canvas.rotate(math.pi);
canvas.translate(-size.width, -size.height);
}
+ canvas.scale(scale, scale);
final double clampedProgress = progress.value.clamp(0.0, 1.0);
for (final _PathFrames path in paths)
diff --git a/packages/flutter/test/material/animated_icons_test.dart b/packages/flutter/test/material/animated_icons_test.dart
index 5e0dffd..91d7439 100644
--- a/packages/flutter/test/material/animated_icons_test.dart
+++ b/packages/flutter/test/material/animated_icons_test.dart
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+@Tags(<String>['reduced-test-set'])
+
import 'dart:math' as math show pi;
import 'package:flutter/material.dart';
@@ -26,6 +28,7 @@
void scale(double sx, [double? sy]) {
capturedSx = sx;
capturedSy = sy!;
+ invocations.add(RecordedScale(sx, sy));
}
final List<RecordedCanvasCall> invocations = <RecordedCanvasCall>[];
@@ -75,6 +78,21 @@
int get hashCode => hashValues(dx, dy);
}
+class RecordedScale extends RecordedCanvasCall {
+ const RecordedScale(this.sx, this.sy);
+
+ final double sx;
+ final double sy;
+
+ @override
+ bool operator ==(Object other) {
+ return other is RecordedScale && other.sx == sx && other.sy == sy;
+ }
+
+ @override
+ int get hashCode => hashValues(sx, sy);
+}
+
void main() {
testWidgets('IconTheme color', (WidgetTester tester) async {
await tester.pumpWidget(
@@ -218,9 +236,11 @@
data: IconThemeData(
color: Color(0xFF666666),
),
- child: AnimatedIcon(
- progress: AlwaysStoppedAnimation<double>(0.0),
- icon: AnimatedIcons.arrow_menu,
+ child: RepaintBoundary(
+ child: AnimatedIcon(
+ progress: AlwaysStoppedAnimation<double>(0.0),
+ icon: AnimatedIcons.arrow_menu,
+ ),
),
),
),
@@ -231,7 +251,10 @@
expect(canvas.invocations, const <RecordedCanvasCall>[
RecordedRotate(math.pi),
RecordedTranslate(-48, -48),
+ RecordedScale(0.5, 0.5),
]);
+ await expectLater(find.byType(AnimatedIcon),
+ matchesGoldenFile('animated_icons_test.icon.rtl.png'));
});
testWidgets('Inherited text direction ltr', (WidgetTester tester) async {
@@ -242,9 +265,11 @@
data: IconThemeData(
color: Color(0xFF666666),
),
- child: AnimatedIcon(
- progress: AlwaysStoppedAnimation<double>(0.0),
- icon: AnimatedIcons.arrow_menu,
+ child: RepaintBoundary(
+ child: AnimatedIcon(
+ progress: AlwaysStoppedAnimation<double>(0.0),
+ icon: AnimatedIcons.arrow_menu,
+ ),
),
),
),
@@ -252,7 +277,11 @@
final CustomPaint customPaint = tester.widget(find.byType(CustomPaint));
final MockCanvas canvas = MockCanvas();
customPaint.painter!.paint(canvas, const Size(48.0, 48.0));
- expect(canvas.invocations, isEmpty);
+ expect(canvas.invocations, const <RecordedCanvasCall>[
+ RecordedScale(0.5, 0.5),
+ ]);
+ await expectLater(find.byType(AnimatedIcon),
+ matchesGoldenFile('animated_icons_test.icon.ltr.png'));
});
testWidgets('Inherited text direction overridden', (WidgetTester tester) async {
@@ -277,8 +306,25 @@
expect(canvas.invocations, const <RecordedCanvasCall>[
RecordedRotate(math.pi),
RecordedTranslate(-48, -48),
+ RecordedScale(0.5, 0.5),
]);
});
+
+ testWidgets('Direction has no effect on position of widget', (WidgetTester tester) async {
+ const AnimatedIcon icon = AnimatedIcon(
+ progress: AlwaysStoppedAnimation<double>(0.0),
+ icon: AnimatedIcons.arrow_menu,
+ );
+ await tester.pumpWidget(
+ const Directionality(textDirection: TextDirection.rtl, child: icon),
+ );
+ final Rect rtlRect = tester.getRect(find.byType(AnimatedIcon));
+ await tester.pumpWidget(
+ const Directionality(textDirection: TextDirection.ltr, child: icon),
+ );
+ final Rect ltrRect = tester.getRect(find.byType(AnimatedIcon));
+ expect(rtlRect, ltrRect);
+ });
}
PaintColorMatcher hasColor(int color) {
diff --git a/packages/flutter/test_private/test/animated_icons_private_test.dart.tmpl b/packages/flutter/test_private/test/animated_icons_private_test.dart.tmpl
index 8e2cc2e..482f76d 100644
--- a/packages/flutter/test_private/test/animated_icons_private_test.dart.tmpl
+++ b/packages/flutter/test_private/test/animated_icons_private_test.dart.tmpl
@@ -187,9 +187,9 @@
);
painter.paint(mockCanvas, size);
mockCanvas.verifyCallsInOrder(<MockCall>[
- MockCall('scale', <dynamic>[1.0, 1.0]),
MockCall('rotate', <dynamic>[math.pi]),
MockCall('translate', <dynamic>[-48.0, -48.0]),
+ MockCall('scale', <dynamic>[1.0, 1.0]),
MockCall.any('drawPath'),
]);
});