Add missing properties to _ArcPaintPredicate. (#162572)

Fixes https://github.com/flutter/flutter/issues/162317

### Description
- Adds `startAngle`, `sweepAngle` and `useCenter` properties to
`_ArcPaintPredicate`
- Adds tests for `_ArcPaintPredicate`

## Pre-launch Checklist

- [X] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [X] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [X] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [X] I signed the [CLA].
- [X] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant documentation (doc comments with `///`).
- [X] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [X] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [X] All existing and new tests are passing.

Co-authored-by: Tong Mu <dkwingsmt@users.noreply.github.com>
This commit is contained in:
Kostia Sokolovskyi 2025-02-18 18:49:33 +01:00 committed by GitHub
parent 7e81af2b45
commit c12330fd21
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 206 additions and 0 deletions

View File

@ -388,6 +388,9 @@ abstract class PaintPattern {
/// arguments as they were seen by the method.
void arc({
Rect? rect,
double? startAngle,
double? sweepAngle,
bool? useCenter,
Color? color,
double? strokeWidth,
bool? hasMaskFilter,
@ -968,6 +971,9 @@ class _TestRecordingCanvasPatternMatcher extends _TestRecordingCanvasMatcher
@override
void arc({
Rect? rect,
double? startAngle,
double? sweepAngle,
bool? useCenter,
Color? color,
double? strokeWidth,
bool? hasMaskFilter,
@ -977,6 +983,9 @@ class _TestRecordingCanvasPatternMatcher extends _TestRecordingCanvasMatcher
_predicates.add(
_ArcPaintPredicate(
rect: rect,
startAngle: startAngle,
sweepAngle: sweepAngle,
useCenter: useCenter,
color: color,
strokeWidth: strokeWidth,
hasMaskFilter: hasMaskFilter,
@ -1598,6 +1607,9 @@ class _LinePaintPredicate extends _DrawCommandPaintPredicate {
class _ArcPaintPredicate extends _DrawCommandPaintPredicate {
_ArcPaintPredicate({
this.rect,
this.startAngle,
this.sweepAngle,
this.useCenter,
super.color,
super.strokeWidth,
super.hasMaskFilter,
@ -1607,6 +1619,12 @@ class _ArcPaintPredicate extends _DrawCommandPaintPredicate {
final Rect? rect;
final double? startAngle;
final double? sweepAngle;
final bool? useCenter;
@override
void verifyArguments(List<dynamic> arguments) {
super.verifyArguments(arguments);
@ -1617,6 +1635,27 @@ class _ArcPaintPredicate extends _DrawCommandPaintPredicate {
'exactly the expected rect ($rect).',
);
}
final double startAngleArgument = arguments[1] as double;
if (startAngle != null && startAngleArgument != startAngle) {
throw FlutterError(
'It called $methodName with a start angle, $startAngleArgument, which '
'was not exactly the expected start angle ($startAngle).',
);
}
final double sweepAngleArgument = arguments[2] as double;
if (sweepAngle != null && sweepAngleArgument != sweepAngle) {
throw FlutterError(
'It called $methodName with a sweep angle, $sweepAngleArgument, which '
'was not exactly the expected sweep angle ($sweepAngle).',
);
}
final bool useCenterArgument = arguments[3] as bool;
if (useCenter != null && useCenterArgument != useCenter) {
throw FlutterError(
'It called $methodName with a useCenter value, $useCenterArgument, '
'which was not exactly the expected value ($useCenter).',
);
}
}
@override
@ -1625,6 +1664,15 @@ class _ArcPaintPredicate extends _DrawCommandPaintPredicate {
if (rect != null) {
description.add('rect $rect');
}
if (startAngle != null) {
description.add('startAngle $startAngle');
}
if (sweepAngle != null) {
description.add('sweepAngle $sweepAngle');
}
if (useCenter != null) {
description.add('useCenter $useCenter');
}
}
}

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
@ -220,4 +222,160 @@ void main() {
]);
});
});
group('arc', () {
final Rect rect = Offset.zero & const Size.square(50);
const double startAngle = math.pi / 4;
const double sweepAngle = math.pi / 2;
const bool useCenter = false;
final Paint paint = Paint()..color = Colors.blue;
Future<void> pumpPainter(WidgetTester tester) async {
await tester.pumpWidget(
Center(
child: CustomPaint(
painter: _ArcPainter(
startAngle: startAngle,
sweepAngle: sweepAngle,
useCenter: useCenter,
paint: paint,
),
size: rect.size,
),
),
);
}
testWidgets('matches when rect is correct', (WidgetTester tester) async {
await pumpPainter(tester);
expect(tester.renderObject(find.byType(CustomPaint)), paints..arc(rect: rect));
});
testWidgets('does not match when rect is incorrect', (WidgetTester tester) async {
await pumpPainter(tester);
expect(
() => expect(
tester.renderObject(find.byType(CustomPaint)),
paints..arc(rect: rect.deflate(10)),
),
throwsA(
isA<TestFailure>().having(
(TestFailure failure) => failure.message,
'message',
contains(
'It called drawArc with a paint whose rect, '
'Rect.fromLTRB(0.0, 0.0, 50.0, 50.0), was not exactly the '
'expected rect (Rect.fromLTRB(10.0, 10.0, 40.0, 40.0)).',
),
),
),
);
});
testWidgets('matches when startAngle is correct', (WidgetTester tester) async {
await pumpPainter(tester);
expect(tester.renderObject(find.byType(CustomPaint)), paints..arc(startAngle: startAngle));
});
testWidgets('does not match when startAngle is incorrect', (WidgetTester tester) async {
await pumpPainter(tester);
expect(
() => expect(
tester.renderObject(find.byType(CustomPaint)),
paints..arc(startAngle: startAngle * 2),
),
throwsA(
isA<TestFailure>().having(
(TestFailure failure) => failure.message,
'message',
contains(
'It called drawArc with a start angle, 0.7853981633974483, which '
'was not exactly the expected start angle (1.5707963267948966).',
),
),
),
);
});
testWidgets('matches when sweepAngle is correct', (WidgetTester tester) async {
await pumpPainter(tester);
expect(tester.renderObject(find.byType(CustomPaint)), paints..arc(sweepAngle: sweepAngle));
});
testWidgets('does not match when sweepAngle is incorrect', (WidgetTester tester) async {
await pumpPainter(tester);
expect(
() => expect(
tester.renderObject(find.byType(CustomPaint)),
paints..arc(sweepAngle: sweepAngle * 2),
),
throwsA(
isA<TestFailure>().having(
(TestFailure failure) => failure.message,
'message',
contains(
'It called drawArc with a sweep angle, 1.5707963267948966, which '
'was not exactly the expected sweep angle (3.141592653589793).',
),
),
),
);
});
testWidgets('matches when useCenter is correct', (WidgetTester tester) async {
await pumpPainter(tester);
expect(tester.renderObject(find.byType(CustomPaint)), paints..arc(useCenter: useCenter));
});
testWidgets('does not match when useCenter is incorrect', (WidgetTester tester) async {
await pumpPainter(tester);
expect(
() => expect(
tester.renderObject(find.byType(CustomPaint)),
paints..arc(useCenter: !useCenter),
),
throwsA(
isA<TestFailure>().having(
(TestFailure failure) => failure.message,
'message',
contains(
'It called drawArc with a useCenter value, false, which was not '
'exactly the expected value (true)',
),
),
),
);
});
});
}
class _ArcPainter extends CustomPainter {
const _ArcPainter({
required this.startAngle,
required this.sweepAngle,
required this.useCenter,
required Paint paint,
}) : _paint = paint;
final double startAngle;
final double sweepAngle;
final bool useCenter;
final Paint _paint;
@override
void paint(Canvas canvas, Size size) {
canvas.drawArc(Offset.zero & size, startAngle, sweepAngle, useCenter, _paint);
}
@override
bool shouldRepaint(MyPainter oldDelegate) {
return true;
}
}