mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Improve ShapeDecoration performance. (#108648)
This commit is contained in:
parent
0de1ca2744
commit
01f3fca3f5
@ -324,9 +324,9 @@ class MiniIconWithText extends StatelessWidget {
|
||||
child: Container(
|
||||
width: 16.0,
|
||||
height: 16.0,
|
||||
decoration: BoxDecoration(
|
||||
decoration: ShapeDecoration(
|
||||
color: Theme.of(context).primaryColor,
|
||||
shape: BoxShape.circle,
|
||||
shape: const CircleBorder(),
|
||||
),
|
||||
child: Icon(icon, color: Colors.white, size: 12.0),
|
||||
),
|
||||
|
||||
@ -108,6 +108,14 @@ class _NoInputBorder extends InputBorder {
|
||||
return Path()..addRect(rect);
|
||||
}
|
||||
|
||||
@override
|
||||
void paintInterior(Canvas canvas, Rect rect, Paint paint, { TextDirection? textDirection }) {
|
||||
canvas.drawRect(rect, paint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get preferPaintInterior => true;
|
||||
|
||||
@override
|
||||
void paint(
|
||||
Canvas canvas,
|
||||
@ -194,6 +202,14 @@ class UnderlineInputBorder extends InputBorder {
|
||||
return Path()..addRRect(borderRadius.resolve(textDirection).toRRect(rect));
|
||||
}
|
||||
|
||||
@override
|
||||
void paintInterior(Canvas canvas, Rect rect, Paint paint, { TextDirection? textDirection }) {
|
||||
canvas.drawRRect(borderRadius.resolve(textDirection).toRRect(rect), paint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get preferPaintInterior => true;
|
||||
|
||||
@override
|
||||
ShapeBorder? lerpFrom(ShapeBorder? a, double t) {
|
||||
if (a is UnderlineInputBorder) {
|
||||
@ -387,6 +403,14 @@ class OutlineInputBorder extends InputBorder {
|
||||
..addRRect(borderRadius.resolve(textDirection).toRRect(rect));
|
||||
}
|
||||
|
||||
@override
|
||||
void paintInterior(Canvas canvas, Rect rect, Paint paint, { TextDirection? textDirection }) {
|
||||
canvas.drawRRect(borderRadius.resolve(textDirection).toRRect(rect), paint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get preferPaintInterior => true;
|
||||
|
||||
Path _gapBorderPath(Canvas canvas, RRect center, double start, double extent) {
|
||||
// When the corner radii on any side add up to be greater than the
|
||||
// given height, each radius has to be scaled to not exceed the
|
||||
|
||||
@ -533,6 +533,78 @@ abstract class ShapeBorder {
|
||||
/// * [Path.contains], which can tell if an [Offset] is within a [Path].
|
||||
Path getInnerPath(Rect rect, { TextDirection? textDirection });
|
||||
|
||||
/// Paint a canvas with the appropriate shape.
|
||||
///
|
||||
/// On [ShapeBorder] subclasses whose [preferPaintInterior] method returns
|
||||
/// true, this should be faster than using [Canvas.drawPath] with the path
|
||||
/// provided by [getOuterPath]. (If [preferPaintInterior] returns false,
|
||||
/// then this method asserts in debug mode and does nothing in release mode.)
|
||||
///
|
||||
/// Subclasses are expected to implement this method when the [Canvas] API
|
||||
/// has a dedicated method to draw the relevant shape. For example,
|
||||
/// [CircleBorder] uses this to call [Canvas.drawCircle], and
|
||||
/// [RoundedRectangleBorder] uses this to call [Canvas.drawRRect].
|
||||
///
|
||||
/// Subclasses that implement this must ensure that calling [paintInterior]
|
||||
/// is semantically equivalent to (i.e. renders the same pixels as) calling
|
||||
/// [Canvas.drawPath] with the same [Paint] and the [Path] returned from
|
||||
/// [getOuterPath], and must also override [preferPaintInterior] to
|
||||
/// return true.
|
||||
///
|
||||
/// For example, a shape that draws a rectangle might implement
|
||||
/// [getOuterPath], [paintInterior], and [preferPaintInterior] as follows:
|
||||
///
|
||||
/// ```dart
|
||||
/// class RectangleBorder extends OutlinedBorder {
|
||||
/// // ...
|
||||
///
|
||||
/// @override
|
||||
/// Path getOuterPath(Rect rect, { TextDirection? textDirection }) {
|
||||
/// return Path()
|
||||
/// ..addRect(rect);
|
||||
/// }
|
||||
///
|
||||
/// @override
|
||||
/// void paintInterior(Canvas canvas, Rect rect, Paint paint, {TextDirection? textDirection}) {
|
||||
/// canvas.drawRect(rect, paint);
|
||||
/// }
|
||||
///
|
||||
/// @override
|
||||
/// bool get preferPaintInterior => true;
|
||||
///
|
||||
/// // ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// When a shape can only be drawn using path, [preferPaintInterior] must
|
||||
/// return false. In that case, classes such as [ShapeDecoration] will cache
|
||||
/// the path from [getOuterPath] and call [Canvas.drawPath] directly.
|
||||
void paintInterior(Canvas canvas, Rect rect, Paint paint, {TextDirection? textDirection}) {
|
||||
assert(!preferPaintInterior, '$runtimeType.preferPaintInterior returns true but $runtimeType.paintInterior is not implemented.');
|
||||
assert(false, '$runtimeType.preferPaintInterior returns false, so it is an error to call its paintInterior method.');
|
||||
}
|
||||
|
||||
/// Reports whether [paintInterior] is implemented.
|
||||
///
|
||||
/// Classes such as [ShapeDecoration] prefer to use [paintInterior] if this
|
||||
/// getter returns true. This is intended to enable faster painting; instead
|
||||
/// of computing a shape using [getOuterPath] and then drawing it using
|
||||
/// [Canvas.drawPath], the path can be drawn directly to the [Canvas] using
|
||||
/// dedicated methods such as [Canvas.drawRect] or [Canvas.drawCircle].
|
||||
///
|
||||
/// By default, this getter returns false.
|
||||
///
|
||||
/// Subclasses that implement [paintInterior] should override this to return
|
||||
/// true. Subclasses should only override [paintInterior] if doing so enables
|
||||
/// faster rendering than is possible with [Canvas.drawPath] (so, in
|
||||
/// particular, subclasses should not call [Canvas.drawPath] in
|
||||
/// [paintInterior]).
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [paintInterior], whose API documentation has an example implementation.
|
||||
bool get preferPaintInterior => false;
|
||||
|
||||
/// Paints the border within the given [Rect] on the given [Canvas].
|
||||
///
|
||||
/// The `textDirection` argument must be provided and non-null if the border
|
||||
@ -719,6 +791,14 @@ class _CompoundBorder extends ShapeBorder {
|
||||
return borders.first.getOuterPath(rect, textDirection: textDirection);
|
||||
}
|
||||
|
||||
@override
|
||||
void paintInterior(Canvas canvas, Rect rect, Paint paint, { TextDirection? textDirection }) {
|
||||
borders.first.paintInterior(canvas, rect, paint, textDirection: textDirection);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get preferPaintInterior => true;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Rect rect, { TextDirection? textDirection }) {
|
||||
for (final ShapeBorder border in borders) {
|
||||
|
||||
@ -180,6 +180,18 @@ abstract class BoxBorder extends ShapeBorder {
|
||||
..addRect(rect);
|
||||
}
|
||||
|
||||
@override
|
||||
void paintInterior(Canvas canvas, Rect rect, Paint paint, { TextDirection? textDirection }) {
|
||||
// For `ShapeDecoration(shape: Border.all())`, a rectangle with sharp edges
|
||||
// is always painted. There is no borderRadius parameter for
|
||||
// ShapeDecoration or Border, only for BoxDecoration, which doesn't call
|
||||
// this method.
|
||||
canvas.drawRect(rect, paint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get preferPaintInterior => true;
|
||||
|
||||
/// Paints the border within the given [Rect] on the given [Canvas].
|
||||
///
|
||||
/// This is an extension of the [ShapeBorder.paint] method. It allows
|
||||
|
||||
@ -105,6 +105,18 @@ class CircleBorder extends OutlinedBorder {
|
||||
return Path()..addOval(_adjustRect(rect));
|
||||
}
|
||||
|
||||
@override
|
||||
void paintInterior(Canvas canvas, Rect rect, Paint paint, { TextDirection? textDirection }) {
|
||||
if (eccentricity == 0.0) {
|
||||
canvas.drawCircle(rect.center, rect.shortestSide / 2.0, paint);
|
||||
} else {
|
||||
canvas.drawOval(_adjustRect(rect), paint);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool get preferPaintInterior => true;
|
||||
|
||||
@override
|
||||
CircleBorder copyWith({ BorderSide? side, double? eccentricity }) {
|
||||
return CircleBorder(side: side ?? this.side, eccentricity: eccentricity ?? this.eccentricity);
|
||||
|
||||
@ -132,6 +132,18 @@ class RoundedRectangleBorder extends OutlinedBorder {
|
||||
..addRRect(borderRadius.resolve(textDirection).toRRect(rect));
|
||||
}
|
||||
|
||||
@override
|
||||
void paintInterior(Canvas canvas, Rect rect, Paint paint, { TextDirection? textDirection }) {
|
||||
if (borderRadius == BorderRadius.zero) {
|
||||
canvas.drawRect(rect, paint);
|
||||
} else {
|
||||
canvas.drawRRect(borderRadius.resolve(textDirection).toRRect(rect), paint);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool get preferPaintInterior => true;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Rect rect, { TextDirection? textDirection }) {
|
||||
switch (side.style) {
|
||||
@ -352,6 +364,19 @@ class _RoundedRectangleToCircleBorder extends OutlinedBorder {
|
||||
..addRRect(_adjustBorderRadius(rect, textDirection)!.toRRect(_adjustRect(rect)));
|
||||
}
|
||||
|
||||
@override
|
||||
void paintInterior(Canvas canvas, Rect rect, Paint paint, { TextDirection? textDirection }) {
|
||||
final BorderRadius adjustedBorderRadius = _adjustBorderRadius(rect, textDirection)!;
|
||||
if (adjustedBorderRadius == BorderRadius.zero) {
|
||||
canvas.drawRect(_adjustRect(rect), paint);
|
||||
} else {
|
||||
canvas.drawRRect(adjustedBorderRadius.toRRect(_adjustRect(rect)), paint);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool get preferPaintInterior => true;
|
||||
|
||||
@override
|
||||
_RoundedRectangleToCircleBorder copyWith({ BorderSide? side, BorderRadiusGeometry? borderRadius, double? circleness, double? eccentricity }) {
|
||||
return _RoundedRectangleToCircleBorder(
|
||||
|
||||
@ -310,6 +310,7 @@ class _ShapeDecorationPainter extends BoxPainter {
|
||||
Path? _innerPath;
|
||||
Paint? _interiorPaint;
|
||||
int? _shadowCount;
|
||||
late List<Rect> _shadowBounds;
|
||||
late List<Path> _shadowPaths;
|
||||
late List<Paint> _shadowPaints;
|
||||
|
||||
@ -342,13 +343,21 @@ class _ShapeDecorationPainter extends BoxPainter {
|
||||
..._decoration.shadows!.map((BoxShadow shadow) => shadow.toPaint()),
|
||||
];
|
||||
}
|
||||
_shadowPaths = <Path>[
|
||||
..._decoration.shadows!.map((BoxShadow shadow) {
|
||||
return _decoration.shape.getOuterPath(rect.shift(shadow.offset).inflate(shadow.spreadRadius), textDirection: textDirection);
|
||||
}),
|
||||
];
|
||||
if (_decoration.shape.preferPaintInterior) {
|
||||
_shadowBounds = <Rect>[
|
||||
..._decoration.shadows!.map((BoxShadow shadow) {
|
||||
return rect.shift(shadow.offset).inflate(shadow.spreadRadius);
|
||||
}),
|
||||
];
|
||||
} else {
|
||||
_shadowPaths = <Path>[
|
||||
..._decoration.shadows!.map((BoxShadow shadow) {
|
||||
return _decoration.shape.getOuterPath(rect.shift(shadow.offset).inflate(shadow.spreadRadius), textDirection: textDirection);
|
||||
}),
|
||||
];
|
||||
}
|
||||
}
|
||||
if (_interiorPaint != null || _shadowCount != null) {
|
||||
if (!_decoration.shape.preferPaintInterior && (_interiorPaint != null || _shadowCount != null)) {
|
||||
_outerPath = _decoration.shape.getOuterPath(rect, textDirection: textDirection);
|
||||
}
|
||||
if (_decoration.image != null) {
|
||||
@ -359,17 +368,27 @@ class _ShapeDecorationPainter extends BoxPainter {
|
||||
_lastTextDirection = textDirection;
|
||||
}
|
||||
|
||||
void _paintShadows(Canvas canvas) {
|
||||
void _paintShadows(Canvas canvas, Rect rect, TextDirection? textDirection) {
|
||||
if (_shadowCount != null) {
|
||||
for (int index = 0; index < _shadowCount!; index += 1) {
|
||||
canvas.drawPath(_shadowPaths[index], _shadowPaints[index]);
|
||||
if (_decoration.shape.preferPaintInterior) {
|
||||
for (int index = 0; index < _shadowCount!; index += 1) {
|
||||
_decoration.shape.paintInterior(canvas, _shadowBounds[index], _shadowPaints[index], textDirection: textDirection);
|
||||
}
|
||||
} else {
|
||||
for (int index = 0; index < _shadowCount!; index += 1) {
|
||||
canvas.drawPath(_shadowPaths[index], _shadowPaints[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _paintInterior(Canvas canvas) {
|
||||
void _paintInterior(Canvas canvas, Rect rect, TextDirection? textDirection) {
|
||||
if (_interiorPaint != null) {
|
||||
canvas.drawPath(_outerPath, _interiorPaint!);
|
||||
if (_decoration.shape.preferPaintInterior) {
|
||||
_decoration.shape.paintInterior(canvas, rect, _interiorPaint!, textDirection: textDirection);
|
||||
} else {
|
||||
canvas.drawPath(_outerPath, _interiorPaint!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -395,8 +414,8 @@ class _ShapeDecorationPainter extends BoxPainter {
|
||||
final Rect rect = offset & configuration.size!;
|
||||
final TextDirection? textDirection = configuration.textDirection;
|
||||
_precache(rect, textDirection);
|
||||
_paintShadows(canvas);
|
||||
_paintInterior(canvas);
|
||||
_paintShadows(canvas, rect, textDirection);
|
||||
_paintInterior(canvas, rect, textDirection);
|
||||
_paintImage(canvas, configuration);
|
||||
_decoration.shape.paint(canvas, rect, textDirection: textDirection);
|
||||
}
|
||||
|
||||
@ -123,6 +123,15 @@ class StadiumBorder extends OutlinedBorder {
|
||||
..addRRect(RRect.fromRectAndRadius(rect, radius));
|
||||
}
|
||||
|
||||
@override
|
||||
void paintInterior(Canvas canvas, Rect rect, Paint paint, { TextDirection? textDirection }) {
|
||||
final Radius radius = Radius.circular(rect.shortestSide / 2.0);
|
||||
canvas.drawRRect(RRect.fromRectAndRadius(rect, radius), paint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get preferPaintInterior => true;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Rect rect, { TextDirection? textDirection }) {
|
||||
switch (side.style) {
|
||||
@ -312,6 +321,14 @@ class _StadiumToCircleBorder extends OutlinedBorder {
|
||||
..addRRect(_adjustBorderRadius(rect).toRRect(_adjustRect(rect)));
|
||||
}
|
||||
|
||||
@override
|
||||
void paintInterior(Canvas canvas, Rect rect, Paint paint, { TextDirection? textDirection }) {
|
||||
canvas.drawRRect(_adjustBorderRadius(rect).toRRect(_adjustRect(rect)), paint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get preferPaintInterior => true;
|
||||
|
||||
@override
|
||||
_StadiumToCircleBorder copyWith({ BorderSide? side, double? circleness, double? eccentricity }) {
|
||||
return _StadiumToCircleBorder(
|
||||
@ -486,6 +503,19 @@ class _StadiumToRoundedRectangleBorder extends OutlinedBorder {
|
||||
..addRRect(_adjustBorderRadius(rect).toRRect(rect));
|
||||
}
|
||||
|
||||
@override
|
||||
void paintInterior(Canvas canvas, Rect rect, Paint paint, { TextDirection? textDirection }) {
|
||||
final BorderRadius adjustedBorderRadius = _adjustBorderRadius(rect);
|
||||
if (adjustedBorderRadius == BorderRadius.zero) {
|
||||
canvas.drawRect(rect, paint);
|
||||
} else {
|
||||
canvas.drawRRect(adjustedBorderRadius.toRRect(rect), paint);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool get preferPaintInterior => true;
|
||||
|
||||
@override
|
||||
_StadiumToRoundedRectangleBorder copyWith({ BorderSide? side, BorderRadius? borderRadius, double? rectness }) {
|
||||
return _StadiumToRoundedRectangleBorder(
|
||||
|
||||
@ -269,7 +269,7 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(Material), paints..path(color: tileColor));
|
||||
expect(find.byType(Material), paints..rect(color: tileColor));
|
||||
});
|
||||
|
||||
testWidgets('CheckboxListTile respects selectedTileColor', (WidgetTester tester) async {
|
||||
@ -289,7 +289,7 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(Material), paints..path(color: selectedTileColor));
|
||||
expect(find.byType(Material), paints..rect(color: selectedTileColor));
|
||||
});
|
||||
|
||||
testWidgets('CheckboxListTile selected item text Color', (WidgetTester tester) async {
|
||||
|
||||
@ -79,7 +79,7 @@ void main() {
|
||||
}
|
||||
|
||||
await tester.pumpWidget(buildFrame(Brightness.light));
|
||||
expect(getMaterialBox(tester), paints..path(color: const Color(0x3d000000)));
|
||||
expect(getMaterialBox(tester), paints..rrect(color: const Color(0x3d000000)));
|
||||
expect(tester.getSize(find.byType(ChoiceChip)), const Size(108.0, 48.0));
|
||||
expect(getMaterial(tester).color, null);
|
||||
expect(getMaterial(tester).elevation, 0);
|
||||
@ -88,7 +88,7 @@ void main() {
|
||||
|
||||
await tester.pumpWidget(buildFrame(Brightness.dark));
|
||||
await tester.pumpAndSettle(); // Theme transition animation
|
||||
expect(getMaterialBox(tester), paints..path(color: const Color(0x3dffffff)));
|
||||
expect(getMaterialBox(tester), paints..rrect(color: const Color(0x3dffffff)));
|
||||
expect(tester.getSize(find.byType(ChoiceChip)), const Size(108.0, 48.0));
|
||||
expect(getMaterial(tester).color, null);
|
||||
expect(getMaterial(tester).elevation, 0);
|
||||
|
||||
@ -66,11 +66,11 @@ void expectCheckmarkColor(Finder finder, Color color) {
|
||||
paints
|
||||
// Physical model path
|
||||
..path()
|
||||
// The first path that is painted is the selection overlay. We do not care
|
||||
// The first layer that is painted is the selection overlay. We do not care
|
||||
// how it is painted but it has to be added it to this pattern so that the
|
||||
// check mark can be checked next.
|
||||
..path()
|
||||
// The second path that is painted is the check mark.
|
||||
..rrect()
|
||||
// The second layer that is painted is the check mark.
|
||||
..path(color: color),
|
||||
);
|
||||
}
|
||||
|
||||
@ -66,11 +66,11 @@ void expectCheckmarkColor(Finder finder, Color color) {
|
||||
paints
|
||||
// Physical model layer path
|
||||
..path()
|
||||
// The first path that is painted is the selection overlay. We do not care
|
||||
// The first layer that is painted is the selection overlay. We do not care
|
||||
// how it is painted but it has to be added it to this pattern so that the
|
||||
// check mark can be checked next.
|
||||
..path()
|
||||
// The second path that is painted is the check mark.
|
||||
..rrect()
|
||||
// The second layer that is painted is the check mark.
|
||||
..path(color: color),
|
||||
);
|
||||
}
|
||||
|
||||
@ -239,7 +239,7 @@ void main() {
|
||||
}
|
||||
|
||||
await tester.pumpWidget(buildFrame(Brightness.light));
|
||||
expect(getMaterialBox(tester), paints..path(color: const Color(0x1f000000)));
|
||||
expect(getMaterialBox(tester), paints..rrect()..circle(color: const Color(0xff1976d2)));
|
||||
expect(tester.getSize(find.byType(Chip)), const Size(156.0, 48.0));
|
||||
expect(getMaterial(tester).color, null);
|
||||
expect(getMaterial(tester).elevation, 0);
|
||||
@ -266,7 +266,7 @@ void main() {
|
||||
|
||||
await tester.pumpWidget(buildFrame(Brightness.dark));
|
||||
await tester.pumpAndSettle(); // Theme transition animation
|
||||
expect(getMaterialBox(tester), paints..path(color: const Color(0x1fffffff)));
|
||||
expect(getMaterialBox(tester), paints..rrect(color: const Color(0x1fffffff)));
|
||||
expect(tester.getSize(find.byType(Chip)), const Size(156.0, 48.0));
|
||||
expect(getMaterial(tester).color, null);
|
||||
expect(getMaterial(tester).elevation, 0);
|
||||
@ -1650,7 +1650,7 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
expect(materialBox, paints..path(color: chipTheme.disabledColor));
|
||||
expect(materialBox, paints..rrect(color: chipTheme.disabledColor));
|
||||
});
|
||||
|
||||
testWidgets('Chip merges ChipThemeData label style with the provided label style', (WidgetTester tester) async {
|
||||
@ -1823,13 +1823,13 @@ void main() {
|
||||
DefaultTextStyle labelStyle = getLabelStyle(tester, 'false');
|
||||
|
||||
// Check default theme for enabled widget.
|
||||
expect(materialBox, paints..path(color: defaultChipTheme.backgroundColor));
|
||||
expect(materialBox, paints..rrect(color: defaultChipTheme.backgroundColor));
|
||||
expect(iconData.color, equals(const Color(0xde000000)));
|
||||
expect(labelStyle.style.color, equals(Colors.black.withAlpha(0xde)));
|
||||
await tester.tap(find.byType(RawChip));
|
||||
await tester.pumpAndSettle();
|
||||
materialBox = getMaterialBox(tester);
|
||||
expect(materialBox, paints..path(color: defaultChipTheme.selectedColor));
|
||||
expect(materialBox, paints..rrect(color: defaultChipTheme.selectedColor));
|
||||
await tester.tap(find.byType(RawChip));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
@ -1838,7 +1838,7 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
materialBox = getMaterialBox(tester);
|
||||
labelStyle = getLabelStyle(tester, 'false');
|
||||
expect(materialBox, paints..path(color: defaultChipTheme.disabledColor));
|
||||
expect(materialBox, paints..rrect(color: defaultChipTheme.disabledColor));
|
||||
expect(labelStyle.style.color, equals(Colors.black.withAlpha(0xde)));
|
||||
|
||||
// Apply a custom theme.
|
||||
@ -1860,13 +1860,13 @@ void main() {
|
||||
labelStyle = getLabelStyle(tester, 'false');
|
||||
|
||||
// Check custom theme for enabled widget.
|
||||
expect(materialBox, paints..path(color: customTheme.backgroundColor));
|
||||
expect(materialBox, paints..rrect(color: customTheme.backgroundColor));
|
||||
expect(iconData.color, equals(customTheme.deleteIconColor));
|
||||
expect(labelStyle.style.color, equals(Colors.black.withAlpha(0xde)));
|
||||
await tester.tap(find.byType(RawChip));
|
||||
await tester.pumpAndSettle();
|
||||
materialBox = getMaterialBox(tester);
|
||||
expect(materialBox, paints..path(color: customTheme.selectedColor));
|
||||
expect(materialBox, paints..rrect(color: customTheme.selectedColor));
|
||||
await tester.tap(find.byType(RawChip));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
@ -1878,7 +1878,7 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
materialBox = getMaterialBox(tester);
|
||||
labelStyle = getLabelStyle(tester, 'false');
|
||||
expect(materialBox, paints..path(color: customTheme.disabledColor));
|
||||
expect(materialBox, paints..rrect(color: customTheme.disabledColor));
|
||||
expect(labelStyle.style.color, equals(Colors.black.withAlpha(0xde)));
|
||||
});
|
||||
|
||||
@ -2677,17 +2677,17 @@ void main() {
|
||||
|
||||
// Default, not disabled.
|
||||
await tester.pumpWidget(chipWidget());
|
||||
expect(find.byType(RawChip), paints..rrect(color: defaultColor));
|
||||
expect(find.byType(RawChip), paints..rrect()..rrect(color: defaultColor));
|
||||
|
||||
// Selected.
|
||||
await tester.pumpWidget(chipWidget(selected: true));
|
||||
expect(find.byType(RawChip), paints..rrect(color: selectedColor));
|
||||
expect(find.byType(RawChip), paints..rrect()..rrect(color: selectedColor));
|
||||
|
||||
// Focused.
|
||||
final FocusNode chipFocusNode = focusNode.children.first;
|
||||
chipFocusNode.requestFocus();
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(RawChip), paints..rrect(color: focusedColor));
|
||||
expect(find.byType(RawChip), paints..rrect()..rrect(color: focusedColor));
|
||||
|
||||
// Hovered.
|
||||
final Offset center = tester.getCenter(find.byType(ChoiceChip));
|
||||
@ -2697,17 +2697,17 @@ void main() {
|
||||
await gesture.addPointer();
|
||||
await gesture.moveTo(center);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(RawChip), paints..rrect(color: hoverColor));
|
||||
expect(find.byType(RawChip), paints..rrect()..rrect(color: hoverColor));
|
||||
|
||||
// Pressed.
|
||||
await gesture.down(center);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(RawChip), paints..rrect(color: pressedColor));
|
||||
expect(find.byType(RawChip), paints..rrect()..rrect(color: pressedColor));
|
||||
|
||||
// Disabled.
|
||||
await tester.pumpWidget(chipWidget(enabled: false));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(RawChip), paints..rrect(color: disabledColor));
|
||||
expect(find.byType(RawChip), paints..rrect()..rrect(color: disabledColor));
|
||||
});
|
||||
|
||||
testWidgets('Chip uses stateful border side color from resolveWith', (WidgetTester tester) async {
|
||||
@ -2756,17 +2756,17 @@ void main() {
|
||||
|
||||
// Default, not disabled.
|
||||
await tester.pumpWidget(chipWidget());
|
||||
expect(find.byType(RawChip), paints..rrect(color: defaultColor));
|
||||
expect(find.byType(RawChip), paints..rrect()..rrect(color: defaultColor));
|
||||
|
||||
// Selected.
|
||||
await tester.pumpWidget(chipWidget(selected: true));
|
||||
expect(find.byType(RawChip), paints..rrect(color: selectedColor));
|
||||
expect(find.byType(RawChip), paints..rrect()..rrect(color: selectedColor));
|
||||
|
||||
// Focused.
|
||||
final FocusNode chipFocusNode = focusNode.children.first;
|
||||
chipFocusNode.requestFocus();
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(RawChip), paints..rrect(color: focusedColor));
|
||||
expect(find.byType(RawChip), paints..rrect()..rrect(color: focusedColor));
|
||||
|
||||
// Hovered.
|
||||
final Offset center = tester.getCenter(find.byType(ChoiceChip));
|
||||
@ -2776,17 +2776,17 @@ void main() {
|
||||
await gesture.addPointer();
|
||||
await gesture.moveTo(center);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(RawChip), paints..rrect(color: hoverColor));
|
||||
expect(find.byType(RawChip), paints..rrect()..rrect(color: hoverColor));
|
||||
|
||||
// Pressed.
|
||||
await gesture.down(center);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(RawChip), paints..rrect(color: pressedColor));
|
||||
expect(find.byType(RawChip), paints..rrect()..rrect(color: pressedColor));
|
||||
|
||||
// Disabled.
|
||||
await tester.pumpWidget(chipWidget(enabled: false));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(RawChip), paints..rrect(color: disabledColor));
|
||||
expect(find.byType(RawChip), paints..rrect()..rrect(color: disabledColor));
|
||||
|
||||
});
|
||||
|
||||
@ -2843,19 +2843,19 @@ void main() {
|
||||
|
||||
// Default, not disabled.
|
||||
await tester.pumpWidget(chipWidget());
|
||||
expect(find.byType(RawChip), paints..rrect(color: defaultColor));
|
||||
expect(find.byType(RawChip), paints..rrect()..rrect(color: defaultColor));
|
||||
|
||||
// Selected.
|
||||
await tester.pumpWidget(chipWidget(selected: true));
|
||||
// Because the resolver returns `null` for this value, we should fall back
|
||||
// to the theme
|
||||
expect(find.byType(RawChip), paints..rrect(color: fallbackThemeColor));
|
||||
expect(find.byType(RawChip), paints..rrect()..rrect(color: fallbackThemeColor));
|
||||
|
||||
// Focused.
|
||||
final FocusNode chipFocusNode = focusNode.children.first;
|
||||
chipFocusNode.requestFocus();
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(RawChip), paints..rrect(color: focusedColor));
|
||||
expect(find.byType(RawChip), paints..rrect()..rrect(color: focusedColor));
|
||||
|
||||
// Hovered.
|
||||
final Offset center = tester.getCenter(find.byType(ChoiceChip));
|
||||
@ -2865,17 +2865,17 @@ void main() {
|
||||
await gesture.addPointer();
|
||||
await gesture.moveTo(center);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(RawChip), paints..rrect(color: hoverColor));
|
||||
expect(find.byType(RawChip), paints..rrect()..rrect(color: hoverColor));
|
||||
|
||||
// Pressed.
|
||||
await gesture.down(center);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(RawChip), paints..rrect(color: pressedColor));
|
||||
expect(find.byType(RawChip), paints..rrect()..rrect(color: pressedColor));
|
||||
|
||||
// Disabled.
|
||||
await tester.pumpWidget(chipWidget(enabled: false));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(RawChip), paints..rrect(color: disabledColor));
|
||||
expect(find.byType(RawChip), paints..rrect()..rrect(color: disabledColor));
|
||||
});
|
||||
|
||||
testWidgets('Chip uses stateful shape in different states', (WidgetTester tester) async {
|
||||
@ -2991,12 +2991,12 @@ void main() {
|
||||
// Default, not disabled. Defer to theme.
|
||||
await tester.pumpWidget(chipWidget());
|
||||
expect(getMaterial(tester).shape, isA<StadiumBorder>());
|
||||
expect(find.byType(RawChip), paints..rrect(color: themeBorderSide.color));
|
||||
expect(find.byType(RawChip), paints..rrect()..rrect(color: themeBorderSide.color));
|
||||
|
||||
// Selected.
|
||||
await tester.pumpWidget(chipWidget(selected: true));
|
||||
expect(getMaterial(tester).shape, isA<RoundedRectangleBorder>());
|
||||
expect(find.byType(RawChip), paints..drrect(color: selectedBorderSide.color));
|
||||
expect(find.byType(RawChip), paints..rect()..drrect(color: selectedBorderSide.color));
|
||||
});
|
||||
|
||||
testWidgets('Chip responds to density changes.', (WidgetTester tester) async {
|
||||
|
||||
@ -159,7 +159,7 @@ void main() {
|
||||
);
|
||||
|
||||
final RenderBox materialBox = getMaterialBox(tester);
|
||||
expect(materialBox, paints..path(color: chipTheme.backgroundColor));
|
||||
expect(materialBox, paints..rect(color: chipTheme.backgroundColor));
|
||||
expect(getMaterial(tester).elevation, chipTheme.elevation);
|
||||
expect(tester.getSize(find.byType(RawChip)), const Size(250, 250)); // label + padding + labelPadding
|
||||
expect(getMaterial(tester).shape, chipTheme.shape);
|
||||
@ -212,7 +212,7 @@ void main() {
|
||||
);
|
||||
|
||||
final RenderBox materialBox = getMaterialBox(tester);
|
||||
expect(materialBox, paints..path(color: chipTheme.backgroundColor));
|
||||
expect(materialBox, paints..rect(color: chipTheme.backgroundColor));
|
||||
expect(tester.getSize(find.byType(RawChip)), const Size(250, 250)); // label + padding + labelPadding
|
||||
expect(getMaterial(tester).elevation, chipTheme.elevation);
|
||||
expect(getMaterial(tester).shape, chipTheme.shape);
|
||||
@ -264,7 +264,7 @@ void main() {
|
||||
);
|
||||
|
||||
final RenderBox materialBox = getMaterialBox(tester);
|
||||
expect(materialBox, paints..path(color: backgroundColor));
|
||||
expect(materialBox, paints..circle(color: backgroundColor));
|
||||
expect(tester.getSize(find.byType(RawChip)), const Size(250, 250)); // label + padding + labelPadding
|
||||
expect(getMaterial(tester).elevation, elevation);
|
||||
expect(getMaterial(tester).shape, shape);
|
||||
@ -660,11 +660,11 @@ void main() {
|
||||
|
||||
// Default.
|
||||
await tester.pumpWidget(chipWidget());
|
||||
expect(find.byType(RawChip), paints..rrect(color: defaultColor));
|
||||
expect(find.byType(RawChip), paints..rrect()..rrect(color: defaultColor));
|
||||
|
||||
// Selected.
|
||||
await tester.pumpWidget(chipWidget(selected: true));
|
||||
expect(find.byType(RawChip), paints..rrect(color: selectedColor));
|
||||
expect(find.byType(RawChip), paints..rrect()..rrect(color: selectedColor));
|
||||
});
|
||||
|
||||
testWidgets('Chip uses stateful border side from chip theme', (WidgetTester tester) async {
|
||||
@ -702,11 +702,11 @@ void main() {
|
||||
|
||||
// Default.
|
||||
await tester.pumpWidget(chipWidget());
|
||||
expect(find.byType(RawChip), paints..rrect(color: defaultColor));
|
||||
expect(find.byType(RawChip), paints..rrect()..rrect(color: defaultColor));
|
||||
|
||||
// Selected.
|
||||
await tester.pumpWidget(chipWidget(selected: true));
|
||||
expect(find.byType(RawChip), paints..rrect(color: selectedColor));
|
||||
expect(find.byType(RawChip), paints..rrect()..rrect(color: selectedColor));
|
||||
});
|
||||
|
||||
testWidgets('Chip uses stateful shape from chip theme', (WidgetTester tester) async {
|
||||
|
||||
@ -1149,6 +1149,7 @@ void main() {
|
||||
expect(
|
||||
find.byType(Material),
|
||||
paints
|
||||
..rect()
|
||||
..rect(
|
||||
color: Colors.orange[500],
|
||||
rect: const Rect.fromLTRB(350.0, 250.0, 450.0, 350.0),
|
||||
@ -1166,6 +1167,7 @@ void main() {
|
||||
expect(
|
||||
find.byType(Material),
|
||||
paints
|
||||
..rect()
|
||||
..rect(
|
||||
color: const Color(0xffffffff),
|
||||
rect: const Rect.fromLTRB(350.0, 250.0, 450.0, 350.0),
|
||||
@ -1202,6 +1204,7 @@ void main() {
|
||||
expect(
|
||||
find.byType(Material),
|
||||
paints
|
||||
..rect()
|
||||
..rect(
|
||||
color: const Color(0x1f000000),
|
||||
rect: const Rect.fromLTRB(350.0, 250.0, 450.0, 350.0),
|
||||
@ -1222,6 +1225,7 @@ void main() {
|
||||
expect(
|
||||
find.byType(Material),
|
||||
paints
|
||||
..rect()
|
||||
..rect(
|
||||
color: const Color(0x1f000000),
|
||||
rect: const Rect.fromLTRB(350.0, 250.0, 450.0, 350.0),
|
||||
@ -1242,6 +1246,7 @@ void main() {
|
||||
expect(
|
||||
find.byType(Material),
|
||||
paints
|
||||
..rect()
|
||||
..rect(
|
||||
color: Colors.orange[500],
|
||||
rect: const Rect.fromLTRB(350.0, 250.0, 450.0, 350.0),
|
||||
@ -1349,51 +1354,22 @@ void main() {
|
||||
await tester.pumpWidget(buildListTile(rectShape));
|
||||
Rect rect = tester.getRect(find.byType(ListTile));
|
||||
|
||||
// Check if a path was painted with the correct color and shape
|
||||
// Check if a rounded rectangle was painted with the correct color and shape
|
||||
expect(
|
||||
find.byType(Material),
|
||||
paints..path(
|
||||
color: tileColor,
|
||||
// Corners should be included
|
||||
includes: <Offset>[
|
||||
Offset(rect.left, rect.top),
|
||||
Offset(rect.right, rect.top),
|
||||
Offset(rect.left, rect.bottom),
|
||||
Offset(rect.right, rect.bottom),
|
||||
],
|
||||
// Points outside rect should be excluded
|
||||
excludes: <Offset>[
|
||||
Offset(rect.left - 1, rect.top - 1),
|
||||
Offset(rect.right + 1, rect.top - 1),
|
||||
Offset(rect.left - 1, rect.bottom + 1),
|
||||
Offset(rect.right + 1, rect.bottom + 1),
|
||||
],
|
||||
),
|
||||
paints..rect(color: tileColor, rect: rect),
|
||||
);
|
||||
|
||||
// Test stadium shape
|
||||
await tester.pumpWidget(buildListTile(stadiumShape));
|
||||
rect = tester.getRect(find.byType(ListTile));
|
||||
|
||||
// Check if a path was painted with the correct color and shape
|
||||
// Check if a rounded rectangle was painted with the correct color and shape
|
||||
expect(
|
||||
find.byType(Material),
|
||||
paints..path(
|
||||
paints..clipRect()..rrect(
|
||||
color: tileColor,
|
||||
// Center points of sides should be included
|
||||
includes: <Offset>[
|
||||
Offset(rect.left + rect.width / 2, rect.top),
|
||||
Offset(rect.left, rect.top + rect.height / 2),
|
||||
Offset(rect.right, rect.top + rect.height / 2),
|
||||
Offset(rect.left + rect.width / 2, rect.bottom),
|
||||
],
|
||||
// Corners should be excluded
|
||||
excludes: <Offset>[
|
||||
Offset(rect.left, rect.top),
|
||||
Offset(rect.right, rect.top),
|
||||
Offset(rect.left, rect.bottom),
|
||||
Offset(rect.right, rect.bottom),
|
||||
],
|
||||
rrect: RRect.fromRectAndRadius(rect, Radius.circular(rect.shortestSide / 2.0)),
|
||||
),
|
||||
);
|
||||
});
|
||||
@ -1504,14 +1480,14 @@ void main() {
|
||||
);
|
||||
|
||||
// Initially, when isSelected is false, the ListTile should respect tileColor.
|
||||
expect(find.byType(Material), paints..path(color: tileColor));
|
||||
expect(find.byType(Material), paints..rect(color: tileColor));
|
||||
|
||||
// Tap on tile to change isSelected.
|
||||
await tester.tap(find.byType(ListTile));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// When isSelected is true, the ListTile should respect selectedTileColor.
|
||||
expect(find.byType(Material), paints..path(color: selectedTileColor));
|
||||
expect(find.byType(Material), paints..rect(color: selectedTileColor));
|
||||
});
|
||||
|
||||
testWidgets('ListTile shows Material ripple effects on top of tileColor', (WidgetTester tester) async {
|
||||
@ -1533,7 +1509,7 @@ void main() {
|
||||
);
|
||||
|
||||
// Before ListTile is tapped, it should be tileColor
|
||||
expect(find.byType(Material), paints..path(color: tileColor));
|
||||
expect(find.byType(Material), paints..rect(color: tileColor));
|
||||
|
||||
// Tap on tile to trigger ink effect and wait for it to be underway.
|
||||
await tester.tap(find.byType(ListTile));
|
||||
@ -1543,7 +1519,7 @@ void main() {
|
||||
expect(
|
||||
find.byType(Material),
|
||||
paints
|
||||
..path(color: tileColor)
|
||||
..rect(color: tileColor)
|
||||
..circle(),
|
||||
);
|
||||
});
|
||||
@ -1572,13 +1548,13 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(Material), paints..path(color: defaultColor));
|
||||
expect(find.byType(Material), paints..rect(color: defaultColor));
|
||||
|
||||
// Tap on tile to change isSelected.
|
||||
await tester.tap(find.byType(ListTile));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(Material), paints..path(color: defaultColor));
|
||||
expect(find.byType(Material), paints..rect(color: defaultColor));
|
||||
});
|
||||
|
||||
testWidgets('ListTile layout at zero size', (WidgetTester tester) async {
|
||||
|
||||
@ -396,13 +396,13 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(Material), paints..path(color: theme.tileColor));
|
||||
expect(find.byType(Material), paints..rect(color: theme.tileColor));
|
||||
|
||||
// Tap on tile to change isSelected.
|
||||
await tester.tap(find.byType(ListTile));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(Material), paints..path(color: theme.selectedTileColor));
|
||||
expect(find.byType(Material), paints..rect(color: theme.selectedTileColor));
|
||||
});
|
||||
|
||||
testWidgets("ListTileTheme's tileColor & selectedTileColor are overridden by ListTile properties", (WidgetTester tester) async {
|
||||
@ -438,13 +438,13 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(Material), paints..path(color: tileColor));
|
||||
expect(find.byType(Material), paints..rect(color: tileColor));
|
||||
|
||||
// Tap on tile to change isSelected.
|
||||
await tester.tap(find.byType(ListTile));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(Material), paints..path(color: selectedTileColor));
|
||||
expect(find.byType(Material), paints..rect(color: selectedTileColor));
|
||||
});
|
||||
|
||||
testWidgets('ListTile uses ListTileTheme shape in a drawer', (WidgetTester tester) async {
|
||||
|
||||
@ -681,7 +681,7 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(Material), paints..path(color: tileColor));
|
||||
expect(find.byType(Material), paints..rect(color: tileColor));
|
||||
});
|
||||
|
||||
testWidgets('RadioListTile respects selectedTileColor', (WidgetTester tester) async {
|
||||
@ -702,7 +702,7 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(Material), paints..path(color: selectedTileColor));
|
||||
expect(find.byType(Material), paints..rect(color: selectedTileColor));
|
||||
});
|
||||
|
||||
testWidgets('RadioListTile selected item text Color', (WidgetTester tester) async {
|
||||
|
||||
@ -372,7 +372,7 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(Material), paints..path(color: tileColor));
|
||||
expect(find.byType(Material), paints..rect(color: tileColor));
|
||||
});
|
||||
|
||||
testWidgets('SwitchListTile respects selectedTileColor', (WidgetTester tester) async {
|
||||
@ -392,7 +392,7 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(Material), paints..path(color: selectedTileColor));
|
||||
expect(find.byType(Material), paints..rect(color: selectedTileColor));
|
||||
});
|
||||
|
||||
testWidgets('SwitchListTile selected item text Color', (WidgetTester tester) async {
|
||||
@ -553,6 +553,7 @@ void main() {
|
||||
expect(
|
||||
Material.of(tester.element(find.byKey(key))),
|
||||
paints
|
||||
..rect()
|
||||
..rect(
|
||||
color: Colors.orange[500],
|
||||
rect: const Rect.fromLTRB(350.0, 250.0, 450.0, 350.0),
|
||||
|
||||
@ -878,9 +878,7 @@ void main() {
|
||||
);
|
||||
expect(tip.size.height, equals(32.0));
|
||||
expect(tip.size.width, equals(74.0));
|
||||
expect(tip, paints..path(
|
||||
color: const Color(0x80800000),
|
||||
));
|
||||
expect(tip, paints..rrect(color: const Color(0x80800000)));
|
||||
});
|
||||
|
||||
testWidgets('Tooltip stays after long press', (WidgetTester tester) async {
|
||||
|
||||
@ -734,9 +734,7 @@ void main() {
|
||||
|
||||
expect(tip.size.height, equals(32.0));
|
||||
expect(tip.size.width, equals(74.0));
|
||||
expect(tip, paints..path(
|
||||
color: const Color(0x80800000),
|
||||
));
|
||||
expect(tip, paints..rrect(color: const Color(0x80800000)));
|
||||
});
|
||||
|
||||
testWidgets('Tooltip decoration - TooltipTheme', (WidgetTester tester) async {
|
||||
@ -776,9 +774,7 @@ void main() {
|
||||
|
||||
expect(tip.size.height, equals(32.0));
|
||||
expect(tip.size.width, equals(74.0));
|
||||
expect(tip, paints..path(
|
||||
color: const Color(0x80800000),
|
||||
));
|
||||
expect(tip, paints..rrect(color: const Color(0x80800000)));
|
||||
});
|
||||
|
||||
testWidgets('Tooltip height and padding - ThemeData.tooltipTheme', (WidgetTester tester) async {
|
||||
|
||||
@ -2024,7 +2024,7 @@ void main() {
|
||||
|
||||
await tester.pumpWidget(buildFrame(600.1));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(RawScrollbar), paints..rect()..rect()); // Show the bar.
|
||||
expect(find.byType(RawScrollbar), paints..rect()); // Show the bar.
|
||||
|
||||
await tester.pumpWidget(buildFrame(600.0));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
@ -55,7 +55,7 @@ Future<void> main() async {
|
||||
expect(
|
||||
find.byType(DecoratedBox),
|
||||
paints
|
||||
..path(color: Color(Colors.blue.value))
|
||||
..rect(color: Color(Colors.blue.value))
|
||||
..rect(color: Colors.black)
|
||||
..rect(color: Colors.white),
|
||||
);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user