mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
973 lines
30 KiB
Dart
973 lines
30 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// reduced-test-set:
|
|
// This file is run as part of a reduced test set in CI on Mac and Windows
|
|
// machines.
|
|
@Tags(<String>['reduced-test-set'])
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import '../rendering/mock_canvas.dart';
|
|
import 'test_border.dart' show TestBorder;
|
|
|
|
final List<String> log = <String>[];
|
|
|
|
class PathClipper extends CustomClipper<Path> {
|
|
@override
|
|
Path getClip(Size size) {
|
|
log.add('getClip');
|
|
return Path()
|
|
..addRect(const Rect.fromLTWH(50.0, 50.0, 100.0, 100.0));
|
|
}
|
|
@override
|
|
bool shouldReclip(PathClipper oldClipper) => false;
|
|
}
|
|
|
|
class ValueClipper<T> extends CustomClipper<T> {
|
|
ValueClipper(this.message, this.value);
|
|
|
|
final String message;
|
|
final T value;
|
|
|
|
@override
|
|
T getClip(Size size) {
|
|
log.add(message);
|
|
return value;
|
|
}
|
|
|
|
@override
|
|
bool shouldReclip(ValueClipper<T> oldClipper) {
|
|
return oldClipper.message != message || oldClipper.value != value;
|
|
}
|
|
}
|
|
|
|
class NotifyClipper<T> extends CustomClipper<T> {
|
|
NotifyClipper({required this.clip}) : super(reclip: clip);
|
|
|
|
final ValueNotifier<T> clip;
|
|
|
|
@override
|
|
T getClip(Size size) => clip.value;
|
|
|
|
@override
|
|
bool shouldReclip(NotifyClipper<T> oldClipper) => clip != oldClipper.clip;
|
|
}
|
|
|
|
void main() {
|
|
testWidgets('ClipRect with a FittedBox child sized to zero works with semantics', (WidgetTester tester) async {
|
|
await tester.pumpWidget(Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: ClipRect(
|
|
child: FittedBox(
|
|
child: SizedBox.fromSize(
|
|
size: Size.zero,
|
|
child: Semantics(
|
|
image: true,
|
|
label: 'Image',
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
expect(find.byType(FittedBox), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('ClipRect updates clipBehavior in updateRenderObject', (WidgetTester tester) async {
|
|
await tester.pumpWidget(const ClipRect());
|
|
|
|
final RenderClipRect renderClip = tester.allRenderObjects.whereType<RenderClipRect>().first;
|
|
|
|
expect(renderClip.clipBehavior, equals(Clip.hardEdge));
|
|
|
|
await tester.pumpWidget(const ClipRect(clipBehavior: Clip.antiAlias));
|
|
|
|
expect(renderClip.clipBehavior, equals(Clip.antiAlias));
|
|
|
|
await tester.pumpWidget(const ClipRect(clipBehavior: Clip.none));
|
|
|
|
expect(renderClip.clipBehavior, equals(Clip.none));
|
|
});
|
|
|
|
test('ClipRRect constructs with the right default values', () {
|
|
const ClipRRect clipRRect = ClipRRect();
|
|
expect(clipRRect.clipBehavior, equals(Clip.antiAlias));
|
|
expect(clipRRect.borderRadius, equals(BorderRadius.zero));
|
|
});
|
|
|
|
testWidgets('ClipRRect updates clipBehavior in updateRenderObject', (WidgetTester tester) async {
|
|
await tester.pumpWidget(const ClipRRect());
|
|
|
|
final RenderClipRRect renderClip = tester.allRenderObjects.whereType<RenderClipRRect>().first;
|
|
|
|
expect(renderClip.clipBehavior, equals(Clip.antiAlias));
|
|
|
|
await tester.pumpWidget(const ClipRRect(clipBehavior: Clip.hardEdge));
|
|
|
|
expect(renderClip.clipBehavior, equals(Clip.hardEdge));
|
|
|
|
await tester.pumpWidget(const ClipRRect(clipBehavior: Clip.none));
|
|
|
|
expect(renderClip.clipBehavior, equals(Clip.none));
|
|
});
|
|
|
|
testWidgets('ClipOval updates clipBehavior in updateRenderObject', (WidgetTester tester) async {
|
|
await tester.pumpWidget(const ClipOval());
|
|
|
|
final RenderClipOval renderClip = tester.allRenderObjects.whereType<RenderClipOval>().first;
|
|
|
|
expect(renderClip.clipBehavior, equals(Clip.antiAlias));
|
|
|
|
await tester.pumpWidget(const ClipOval(clipBehavior: Clip.hardEdge));
|
|
|
|
expect(renderClip.clipBehavior, equals(Clip.hardEdge));
|
|
|
|
await tester.pumpWidget(const ClipOval(clipBehavior: Clip.none));
|
|
|
|
expect(renderClip.clipBehavior, equals(Clip.none));
|
|
});
|
|
|
|
testWidgets('ClipPath updates clipBehavior in updateRenderObject', (WidgetTester tester) async {
|
|
await tester.pumpWidget(const ClipPath());
|
|
|
|
final RenderClipPath renderClip = tester.allRenderObjects.whereType<RenderClipPath>().first;
|
|
|
|
expect(renderClip.clipBehavior, equals(Clip.antiAlias));
|
|
|
|
await tester.pumpWidget(const ClipPath(clipBehavior: Clip.hardEdge));
|
|
|
|
expect(renderClip.clipBehavior, equals(Clip.hardEdge));
|
|
|
|
await tester.pumpWidget(const ClipPath(clipBehavior: Clip.none));
|
|
|
|
expect(renderClip.clipBehavior, equals(Clip.none));
|
|
});
|
|
|
|
testWidgets('ClipPath', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
ClipPath(
|
|
clipper: PathClipper(),
|
|
child: GestureDetector(
|
|
behavior: HitTestBehavior.opaque,
|
|
onTap: () { log.add('tap'); },
|
|
),
|
|
),
|
|
);
|
|
expect(log, equals(<String>['getClip']));
|
|
|
|
await tester.tapAt(const Offset(10.0, 10.0));
|
|
expect(log, equals(<String>['getClip']));
|
|
log.clear();
|
|
|
|
await tester.tapAt(const Offset(100.0, 100.0));
|
|
expect(log, equals(<String>['tap']));
|
|
log.clear();
|
|
});
|
|
|
|
testWidgets('ClipOval', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
ClipOval(
|
|
child: GestureDetector(
|
|
behavior: HitTestBehavior.opaque,
|
|
onTap: () { log.add('tap'); },
|
|
),
|
|
),
|
|
);
|
|
expect(log, equals(<String>[]));
|
|
|
|
await tester.tapAt(const Offset(10.0, 10.0));
|
|
expect(log, equals(<String>[]));
|
|
log.clear();
|
|
|
|
await tester.tapAt(const Offset(400.0, 300.0));
|
|
expect(log, equals(<String>['tap']));
|
|
log.clear();
|
|
});
|
|
|
|
testWidgets('Transparent ClipOval hit test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
Opacity(
|
|
opacity: 0.0,
|
|
child: ClipOval(
|
|
child: GestureDetector(
|
|
behavior: HitTestBehavior.opaque,
|
|
onTap: () { log.add('tap'); },
|
|
),
|
|
),
|
|
),
|
|
);
|
|
expect(log, equals(<String>[]));
|
|
|
|
await tester.tapAt(const Offset(10.0, 10.0));
|
|
expect(log, equals(<String>[]));
|
|
log.clear();
|
|
|
|
await tester.tapAt(const Offset(400.0, 300.0));
|
|
expect(log, equals(<String>['tap']));
|
|
log.clear();
|
|
});
|
|
|
|
testWidgets('ClipRect', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
Align(
|
|
alignment: Alignment.topLeft,
|
|
child: SizedBox(
|
|
width: 100.0,
|
|
height: 100.0,
|
|
child: ClipRect(
|
|
clipper: ValueClipper<Rect>('a', const Rect.fromLTWH(5.0, 5.0, 10.0, 10.0)),
|
|
child: GestureDetector(
|
|
behavior: HitTestBehavior.opaque,
|
|
onTap: () { log.add('tap'); },
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
expect(log, equals(<String>['a']));
|
|
|
|
await tester.tapAt(const Offset(10.0, 10.0));
|
|
expect(log, equals(<String>['a', 'tap']));
|
|
|
|
await tester.tapAt(const Offset(100.0, 100.0));
|
|
expect(log, equals(<String>['a', 'tap']));
|
|
|
|
await tester.pumpWidget(
|
|
Align(
|
|
alignment: Alignment.topLeft,
|
|
child: SizedBox(
|
|
width: 100.0,
|
|
height: 100.0,
|
|
child: ClipRect(
|
|
clipper: ValueClipper<Rect>('a', const Rect.fromLTWH(5.0, 5.0, 10.0, 10.0)),
|
|
child: GestureDetector(
|
|
behavior: HitTestBehavior.opaque,
|
|
onTap: () { log.add('tap'); },
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
expect(log, equals(<String>['a', 'tap']));
|
|
|
|
await tester.pumpWidget(
|
|
Align(
|
|
alignment: Alignment.topLeft,
|
|
child: SizedBox(
|
|
width: 200.0,
|
|
height: 200.0,
|
|
child: ClipRect(
|
|
clipper: ValueClipper<Rect>('a', const Rect.fromLTWH(5.0, 5.0, 10.0, 10.0)),
|
|
child: GestureDetector(
|
|
behavior: HitTestBehavior.opaque,
|
|
onTap: () { log.add('tap'); },
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
expect(log, equals(<String>['a', 'tap', 'a']));
|
|
|
|
await tester.pumpWidget(
|
|
Align(
|
|
alignment: Alignment.topLeft,
|
|
child: SizedBox(
|
|
width: 200.0,
|
|
height: 200.0,
|
|
child: ClipRect(
|
|
clipper: ValueClipper<Rect>('a', const Rect.fromLTWH(5.0, 5.0, 10.0, 10.0)),
|
|
child: GestureDetector(
|
|
behavior: HitTestBehavior.opaque,
|
|
onTap: () { log.add('tap'); },
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
expect(log, equals(<String>['a', 'tap', 'a']));
|
|
|
|
await tester.pumpWidget(
|
|
Align(
|
|
alignment: Alignment.topLeft,
|
|
child: SizedBox(
|
|
width: 200.0,
|
|
height: 200.0,
|
|
child: ClipRect(
|
|
clipper: ValueClipper<Rect>('b', const Rect.fromLTWH(5.0, 5.0, 10.0, 10.0)),
|
|
child: GestureDetector(
|
|
behavior: HitTestBehavior.opaque,
|
|
onTap: () { log.add('tap'); },
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
expect(log, equals(<String>['a', 'tap', 'a', 'b']));
|
|
|
|
await tester.pumpWidget(
|
|
Align(
|
|
alignment: Alignment.topLeft,
|
|
child: SizedBox(
|
|
width: 200.0,
|
|
height: 200.0,
|
|
child: ClipRect(
|
|
clipper: ValueClipper<Rect>('c', const Rect.fromLTWH(25.0, 25.0, 10.0, 10.0)),
|
|
child: GestureDetector(
|
|
behavior: HitTestBehavior.opaque,
|
|
onTap: () { log.add('tap'); },
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
expect(log, equals(<String>['a', 'tap', 'a', 'b', 'c']));
|
|
|
|
await tester.tapAt(const Offset(30.0, 30.0));
|
|
expect(log, equals(<String>['a', 'tap', 'a', 'b', 'c', 'tap']));
|
|
|
|
await tester.tapAt(const Offset(100.0, 100.0));
|
|
expect(log, equals(<String>['a', 'tap', 'a', 'b', 'c', 'tap']));
|
|
log.clear();
|
|
});
|
|
|
|
testWidgets('debugPaintSizeEnabled', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
const ClipRect(
|
|
child: Placeholder(),
|
|
),
|
|
);
|
|
expect(tester.renderObject(find.byType(ClipRect)).paint, paints
|
|
..save()
|
|
..clipRect(rect: const Rect.fromLTRB(0.0, 0.0, 800.0, 600.0))
|
|
..save()
|
|
..path() // Placeholder
|
|
..restore()
|
|
..restore(),
|
|
);
|
|
debugPaintSizeEnabled = true;
|
|
expect(tester.renderObject(find.byType(ClipRect)).debugPaint, paints
|
|
..rect(rect: const Rect.fromLTRB(0.0, 0.0, 800.0, 600.0))
|
|
..paragraph(),
|
|
);
|
|
debugPaintSizeEnabled = false;
|
|
});
|
|
|
|
testWidgets('ClipRect painting', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
Center(
|
|
child: RepaintBoundary(
|
|
child: Container(
|
|
color: Colors.white,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(100.0),
|
|
child: SizedBox(
|
|
height: 100.0,
|
|
width: 100.0,
|
|
child: Transform.rotate(
|
|
angle: 1.0, // radians
|
|
child: ClipRect(
|
|
child: Container(
|
|
color: Colors.red,
|
|
child: Container(
|
|
color: Colors.white,
|
|
child: RepaintBoundary(
|
|
child: Center(
|
|
child: Container(
|
|
color: Colors.black,
|
|
height: 10.0,
|
|
width: 10.0,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
await expectLater(
|
|
find.byType(RepaintBoundary).first,
|
|
matchesGoldenFile('clip.ClipRect.png'),
|
|
);
|
|
});
|
|
|
|
testWidgets('ClipRect save, overlay, and antialiasing', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
RepaintBoundary(
|
|
child: Stack(
|
|
textDirection: TextDirection.ltr,
|
|
children: <Widget>[
|
|
Positioned(
|
|
top: 0.0,
|
|
left: 0.0,
|
|
width: 100.0,
|
|
height: 100.0,
|
|
child: ClipRect(
|
|
child: Container(
|
|
color: Colors.blue,
|
|
),
|
|
),
|
|
),
|
|
Positioned(
|
|
top: 50.0,
|
|
left: 50.0,
|
|
width: 100.0,
|
|
height: 100.0,
|
|
child: Transform.rotate(
|
|
angle: 1.0,
|
|
child: Container(
|
|
color: Colors.red,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
await expectLater(
|
|
find.byType(RepaintBoundary).first,
|
|
matchesGoldenFile('clip.ClipRectOverlay.png'),
|
|
);
|
|
});
|
|
|
|
testWidgets('ClipRRect painting', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
Center(
|
|
child: RepaintBoundary(
|
|
child: Container(
|
|
color: Colors.white,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(100.0),
|
|
child: SizedBox(
|
|
height: 100.0,
|
|
width: 100.0,
|
|
child: Transform.rotate(
|
|
angle: 1.0, // radians
|
|
child: ClipRRect(
|
|
borderRadius: const BorderRadius.only(
|
|
topLeft: Radius.elliptical(10.0, 20.0),
|
|
topRight: Radius.elliptical(5.0, 30.0),
|
|
bottomLeft: Radius.elliptical(2.5, 12.0),
|
|
bottomRight: Radius.elliptical(15.0, 6.0),
|
|
),
|
|
child: Container(
|
|
color: Colors.red,
|
|
child: Container(
|
|
color: Colors.white,
|
|
child: RepaintBoundary(
|
|
child: Center(
|
|
child: Container(
|
|
color: Colors.black,
|
|
height: 10.0,
|
|
width: 10.0,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
await expectLater(
|
|
find.byType(RepaintBoundary).first,
|
|
matchesGoldenFile('clip.ClipRRect.png'),
|
|
);
|
|
});
|
|
|
|
testWidgets('ClipOval painting', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
Center(
|
|
child: RepaintBoundary(
|
|
child: Container(
|
|
color: Colors.white,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(100.0),
|
|
child: SizedBox(
|
|
height: 100.0,
|
|
width: 100.0,
|
|
child: Transform.rotate(
|
|
angle: 1.0, // radians
|
|
child: ClipOval(
|
|
child: Container(
|
|
color: Colors.red,
|
|
child: Container(
|
|
color: Colors.white,
|
|
child: RepaintBoundary(
|
|
child: Center(
|
|
child: Container(
|
|
color: Colors.black,
|
|
height: 10.0,
|
|
width: 10.0,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
await expectLater(
|
|
find.byType(RepaintBoundary).first,
|
|
matchesGoldenFile('clip.ClipOval.png'),
|
|
);
|
|
});
|
|
|
|
testWidgets('ClipPath painting', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
Center(
|
|
child: RepaintBoundary(
|
|
child: Container(
|
|
color: Colors.white,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(100.0),
|
|
child: SizedBox(
|
|
height: 100.0,
|
|
width: 100.0,
|
|
child: Transform.rotate(
|
|
angle: 1.0, // radians
|
|
child: ClipPath(
|
|
clipper: const ShapeBorderClipper(
|
|
shape: BeveledRectangleBorder(
|
|
borderRadius: BorderRadius.all(Radius.circular(20.0)),
|
|
),
|
|
),
|
|
child: Container(
|
|
color: Colors.red,
|
|
child: Container(
|
|
color: Colors.white,
|
|
child: RepaintBoundary(
|
|
child: Center(
|
|
child: Container(
|
|
color: Colors.black,
|
|
height: 10.0,
|
|
width: 10.0,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
await expectLater(
|
|
find.byType(RepaintBoundary).first,
|
|
matchesGoldenFile('clip.ClipPath.png'),
|
|
);
|
|
});
|
|
|
|
Center genPhysicalModel(Clip clipBehavior) {
|
|
return Center(
|
|
child: RepaintBoundary(
|
|
child: Container(
|
|
color: Colors.white,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(100.0),
|
|
child: SizedBox(
|
|
height: 100.0,
|
|
width: 100.0,
|
|
child: Transform.rotate(
|
|
angle: 1.0, // radians
|
|
child: PhysicalModel(
|
|
borderRadius: const BorderRadius.all(Radius.circular(20.0)),
|
|
color: Colors.red,
|
|
clipBehavior: clipBehavior,
|
|
child: Container(
|
|
color: Colors.white,
|
|
child: RepaintBoundary(
|
|
child: Center(
|
|
child: Container(
|
|
color: Colors.black,
|
|
height: 10.0,
|
|
width: 10.0,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
testWidgets('PhysicalModel painting with Clip.antiAlias', (WidgetTester tester) async {
|
|
await tester.pumpWidget(genPhysicalModel(Clip.antiAlias));
|
|
await expectLater(
|
|
find.byType(RepaintBoundary).first,
|
|
matchesGoldenFile('clip.PhysicalModel.antiAlias.png'),
|
|
);
|
|
});
|
|
|
|
testWidgets('PhysicalModel painting with Clip.hardEdge', (WidgetTester tester) async {
|
|
await tester.pumpWidget(genPhysicalModel(Clip.hardEdge));
|
|
await expectLater(
|
|
find.byType(RepaintBoundary).first,
|
|
matchesGoldenFile('clip.PhysicalModel.hardEdge.png'),
|
|
);
|
|
});
|
|
|
|
// There will be bleeding edges on the rect edges, but there shouldn't be any bleeding edges on the
|
|
// round corners.
|
|
testWidgets('PhysicalModel painting with Clip.antiAliasWithSaveLayer', (WidgetTester tester) async {
|
|
await tester.pumpWidget(genPhysicalModel(Clip.antiAliasWithSaveLayer));
|
|
await expectLater(
|
|
find.byType(RepaintBoundary).first,
|
|
matchesGoldenFile('clip.PhysicalModel.antiAliasWithSaveLayer.png'),
|
|
);
|
|
});
|
|
|
|
testWidgets('Default PhysicalModel painting', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
Center(
|
|
child: RepaintBoundary(
|
|
child: Container(
|
|
color: Colors.white,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(100.0),
|
|
child: SizedBox(
|
|
height: 100.0,
|
|
width: 100.0,
|
|
child: Transform.rotate(
|
|
angle: 1.0, // radians
|
|
child: PhysicalModel(
|
|
borderRadius: const BorderRadius.all(Radius.circular(20.0)),
|
|
color: Colors.red,
|
|
child: Container(
|
|
color: Colors.white,
|
|
child: RepaintBoundary(
|
|
child: Center(
|
|
child: Container(
|
|
color: Colors.black,
|
|
height: 10.0,
|
|
width: 10.0,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
await expectLater(
|
|
find.byType(RepaintBoundary).first,
|
|
matchesGoldenFile('clip.PhysicalModel.default.png'),
|
|
);
|
|
});
|
|
|
|
Center genPhysicalShape(Clip clipBehavior) {
|
|
return Center(
|
|
child: RepaintBoundary(
|
|
child: Container(
|
|
color: Colors.white,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(100.0),
|
|
child: SizedBox(
|
|
height: 100.0,
|
|
width: 100.0,
|
|
child: Transform.rotate(
|
|
angle: 1.0, // radians
|
|
child: PhysicalShape(
|
|
clipper: const ShapeBorderClipper(
|
|
shape: BeveledRectangleBorder(
|
|
borderRadius: BorderRadius.all(Radius.circular(20.0)),
|
|
),
|
|
),
|
|
clipBehavior: clipBehavior,
|
|
color: Colors.red,
|
|
child: Container(
|
|
color: Colors.white,
|
|
child: RepaintBoundary(
|
|
child: Center(
|
|
child: Container(
|
|
color: Colors.black,
|
|
height: 10.0,
|
|
width: 10.0,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
testWidgets('PhysicalShape painting with Clip.antiAlias', (WidgetTester tester) async {
|
|
await tester.pumpWidget(genPhysicalShape(Clip.antiAlias));
|
|
await expectLater(
|
|
find.byType(RepaintBoundary).first,
|
|
matchesGoldenFile('clip.PhysicalShape.antiAlias.png'),
|
|
);
|
|
});
|
|
|
|
testWidgets('PhysicalShape painting with Clip.hardEdge', (WidgetTester tester) async {
|
|
await tester.pumpWidget(genPhysicalShape(Clip.hardEdge));
|
|
await expectLater(
|
|
find.byType(RepaintBoundary).first,
|
|
matchesGoldenFile('clip.PhysicalShape.hardEdge.png'),
|
|
);
|
|
});
|
|
|
|
testWidgets('PhysicalShape painting with Clip.antiAliasWithSaveLayer', (WidgetTester tester) async {
|
|
await tester.pumpWidget(genPhysicalShape(Clip.antiAliasWithSaveLayer));
|
|
await expectLater(
|
|
find.byType(RepaintBoundary).first,
|
|
matchesGoldenFile('clip.PhysicalShape.antiAliasWithSaveLayer.png'),
|
|
);
|
|
});
|
|
|
|
testWidgets('PhysicalShape painting', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
Center(
|
|
child: RepaintBoundary(
|
|
child: Container(
|
|
color: Colors.white,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(100.0),
|
|
child: SizedBox(
|
|
height: 100.0,
|
|
width: 100.0,
|
|
child: Transform.rotate(
|
|
angle: 1.0, // radians
|
|
child: PhysicalShape(
|
|
clipper: const ShapeBorderClipper(
|
|
shape: BeveledRectangleBorder(
|
|
borderRadius: BorderRadius.all(Radius.circular(20.0)),
|
|
),
|
|
),
|
|
color: Colors.red,
|
|
child: Container(
|
|
color: Colors.white,
|
|
child: RepaintBoundary(
|
|
child: Center(
|
|
child: Container(
|
|
color: Colors.black,
|
|
height: 10.0,
|
|
width: 10.0,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
await expectLater(
|
|
find.byType(RepaintBoundary).first,
|
|
matchesGoldenFile('clip.PhysicalShape.default.png'),
|
|
);
|
|
});
|
|
|
|
testWidgets('ClipPath.shape', (WidgetTester tester) async {
|
|
final List<String> logs = <String>[];
|
|
final ShapeBorder shape = TestBorder((String message) { logs.add(message); });
|
|
Widget buildClipPath() {
|
|
return ClipPath.shape(
|
|
shape: shape,
|
|
child: const SizedBox(width: 100.0, height: 100.0),
|
|
);
|
|
}
|
|
final Widget clipPath = buildClipPath();
|
|
// verify that a regular clip works as one would expect
|
|
logs.add('--0');
|
|
await tester.pumpWidget(clipPath);
|
|
// verify that pumping again doesn't recompute the clip
|
|
// even though the widget itself is new (the shape doesn't change identity)
|
|
logs.add('--1');
|
|
await tester.pumpWidget(buildClipPath());
|
|
// verify that ClipPath passes the TextDirection on to its shape
|
|
logs.add('--2');
|
|
await tester.pumpWidget(Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: clipPath,
|
|
));
|
|
// verify that changing the text direction from LTR to RTL has an effect
|
|
// even though the widget itself is identical
|
|
logs.add('--3');
|
|
await tester.pumpWidget(Directionality(
|
|
textDirection: TextDirection.rtl,
|
|
child: clipPath,
|
|
));
|
|
// verify that pumping again with a text direction has no effect
|
|
logs.add('--4');
|
|
await tester.pumpWidget(Directionality(
|
|
textDirection: TextDirection.rtl,
|
|
child: buildClipPath(),
|
|
));
|
|
logs.add('--5');
|
|
// verify that changing the text direction and the widget at the same time
|
|
// works as expected
|
|
await tester.pumpWidget(Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: clipPath,
|
|
));
|
|
expect(logs, <String>[
|
|
'--0',
|
|
'getOuterPath Rect.fromLTRB(0.0, 0.0, 800.0, 600.0) null',
|
|
'--1',
|
|
'--2',
|
|
'getOuterPath Rect.fromLTRB(0.0, 0.0, 800.0, 600.0) TextDirection.ltr',
|
|
'--3',
|
|
'getOuterPath Rect.fromLTRB(0.0, 0.0, 800.0, 600.0) TextDirection.rtl',
|
|
'--4',
|
|
'--5',
|
|
'getOuterPath Rect.fromLTRB(0.0, 0.0, 800.0, 600.0) TextDirection.ltr',
|
|
]);
|
|
});
|
|
|
|
testWidgets('CustomClipper reclips when notified', (WidgetTester tester) async {
|
|
final ValueNotifier<Rect> clip = ValueNotifier<Rect>(const Rect.fromLTWH(50.0, 50.0, 100.0, 100.0));
|
|
|
|
await tester.pumpWidget(
|
|
ClipRect(
|
|
clipper: NotifyClipper<Rect>(clip: clip),
|
|
child: const Placeholder(),
|
|
),
|
|
);
|
|
|
|
expect(tester.renderObject(find.byType(ClipRect)).paint, paints
|
|
..save()
|
|
..clipRect(rect: const Rect.fromLTWH(50.0, 50.0, 100.0, 100.0))
|
|
..save()
|
|
..path() // Placeholder
|
|
..restore()
|
|
..restore(),
|
|
);
|
|
|
|
expect(tester.renderObject(find.byType(ClipRect)).debugNeedsPaint, isFalse);
|
|
clip.value = const Rect.fromLTWH(50.0, 50.0, 150.0, 100.0);
|
|
expect(tester.renderObject(find.byType(ClipRect)).debugNeedsPaint, isTrue);
|
|
|
|
expect(tester.renderObject(find.byType(ClipRect)).paint, paints
|
|
..save()
|
|
..clipRect(rect: const Rect.fromLTWH(50.0, 50.0, 150.0, 100.0))
|
|
..save()
|
|
..path() // Placeholder
|
|
..restore()
|
|
..restore(),
|
|
);
|
|
});
|
|
|
|
testWidgets('ClipRRect supports BorderRadiusDirectional', (WidgetTester tester) async {
|
|
const Radius startRadius = Radius.circular(15.0);
|
|
const Radius endRadius = Radius.circular(30.0);
|
|
|
|
Widget buildClipRRect(TextDirection textDirection) {
|
|
return Directionality(
|
|
textDirection: textDirection,
|
|
child: const ClipRRect(
|
|
borderRadius: BorderRadiusDirectional.horizontal(
|
|
start: startRadius,
|
|
end: endRadius,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
for (final TextDirection textDirection in TextDirection.values) {
|
|
await tester.pumpWidget(buildClipRRect(textDirection));
|
|
final RenderClipRRect renderClip = tester.allRenderObjects.whereType<RenderClipRRect>().first;
|
|
final bool isRtl = textDirection == TextDirection.rtl;
|
|
expect(renderClip.borderRadius.resolve(textDirection).topLeft, isRtl ? endRadius : startRadius);
|
|
expect(renderClip.borderRadius.resolve(textDirection).topRight, isRtl ? startRadius : endRadius);
|
|
expect(renderClip.borderRadius.resolve(textDirection).bottomLeft, isRtl ? endRadius : startRadius);
|
|
expect(renderClip.borderRadius.resolve(textDirection).bottomRight, isRtl ? startRadius : endRadius);
|
|
}
|
|
});
|
|
|
|
testWidgets('ClipRRect is direction-aware', (WidgetTester tester) async {
|
|
const Radius startRadius = Radius.circular(15.0);
|
|
const Radius endRadius = Radius.circular(30.0);
|
|
TextDirection textDirection = TextDirection.ltr;
|
|
|
|
Widget buildClipRRect(TextDirection textDirection) {
|
|
return Directionality(
|
|
textDirection: textDirection,
|
|
child: const ClipRRect(
|
|
borderRadius: BorderRadiusDirectional.horizontal(
|
|
start: startRadius,
|
|
end: endRadius,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildStatefulClipRRect() {
|
|
return StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Directionality(
|
|
textDirection: textDirection,
|
|
child: SizedBox(
|
|
width: 100.0,
|
|
height: 100.0,
|
|
child: ClipRRect(
|
|
borderRadius: const BorderRadiusDirectional.horizontal(
|
|
start: startRadius,
|
|
end: endRadius,
|
|
),
|
|
child: GestureDetector(
|
|
onTap: () {
|
|
setState(() {
|
|
textDirection = TextDirection.rtl;
|
|
});
|
|
},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
for (final TextDirection textDirection in TextDirection.values) {
|
|
await tester.pumpWidget(buildClipRRect(textDirection));
|
|
final RenderClipRRect renderClip = tester.allRenderObjects.whereType<RenderClipRRect>().first;
|
|
final bool isRtl = textDirection == TextDirection.rtl;
|
|
expect(renderClip.textDirection, isRtl ? TextDirection.rtl : TextDirection.ltr);
|
|
}
|
|
|
|
await tester.pumpWidget(buildStatefulClipRRect());
|
|
final RenderClipRRect renderClip = tester.allRenderObjects.whereType<RenderClipRRect>().first;
|
|
expect(renderClip.textDirection, TextDirection.ltr);
|
|
await tester.tapAt(Offset.zero);
|
|
await tester.pump();
|
|
expect(renderClip.textDirection, TextDirection.rtl);
|
|
});
|
|
}
|