mirror of
https://github.com/flutter/flutter.git
synced 2026-02-14 23:02:04 +08:00
This follows https://github.com/flutter/flutter/pull/59364 and cl/319911104 FittedBox is still default to hardEdge clip as new FittedBox is added to Google very quickly. Let's first roll other part of changes into Google first.
679 lines
24 KiB
Dart
679 lines
24 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.
|
|
|
|
// @dart = 2.8
|
|
|
|
import 'dart:math' as math;
|
|
import 'package:flutter/gestures.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/widgets.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import '../flutter_test_alternative.dart' show Fake;
|
|
|
|
void main() {
|
|
group('PhysicalShape', () {
|
|
testWidgets('properties', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
const PhysicalShape(
|
|
clipper: ShapeBorderClipper(shape: CircleBorder()),
|
|
elevation: 2.0,
|
|
color: Color(0xFF0000FF),
|
|
shadowColor: Color(0xFF00FF00),
|
|
),
|
|
);
|
|
final RenderPhysicalShape renderObject = tester.renderObject(find.byType(PhysicalShape));
|
|
expect(renderObject.clipper, const ShapeBorderClipper(shape: CircleBorder()));
|
|
expect(renderObject.color, const Color(0xFF0000FF));
|
|
expect(renderObject.shadowColor, const Color(0xFF00FF00));
|
|
expect(renderObject.elevation, 2.0);
|
|
});
|
|
|
|
testWidgets('hit test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
PhysicalShape(
|
|
clipper: const ShapeBorderClipper(shape: CircleBorder()),
|
|
elevation: 2.0,
|
|
color: const Color(0xFF0000FF),
|
|
shadowColor: const Color(0xFF00FF00),
|
|
child: Container(color: const Color(0xFF0000FF)),
|
|
),
|
|
);
|
|
|
|
final RenderPhysicalShape renderPhysicalShape =
|
|
tester.renderObject(find.byType(PhysicalShape));
|
|
|
|
// The viewport is 800x600, the CircleBorder is centered and fits
|
|
// the shortest edge, so we get a circle of radius 300, centered at
|
|
// (400, 300).
|
|
//
|
|
// We test by sampling a few points around the left-most point of the
|
|
// circle (100, 300).
|
|
|
|
expect(tester.hitTestOnBinding(const Offset(99.0, 300.0)), doesNotHit(renderPhysicalShape));
|
|
expect(tester.hitTestOnBinding(const Offset(100.0, 300.0)), hits(renderPhysicalShape));
|
|
expect(tester.hitTestOnBinding(const Offset(100.0, 299.0)), doesNotHit(renderPhysicalShape));
|
|
expect(tester.hitTestOnBinding(const Offset(100.0, 301.0)), doesNotHit(renderPhysicalShape));
|
|
});
|
|
|
|
});
|
|
|
|
group('FractionalTranslation', () {
|
|
testWidgets('hit test - entirely inside the bounding box', (WidgetTester tester) async {
|
|
final GlobalKey key1 = GlobalKey();
|
|
bool _pointerDown = false;
|
|
|
|
await tester.pumpWidget(
|
|
Center(
|
|
child: FractionalTranslation(
|
|
translation: Offset.zero,
|
|
transformHitTests: true,
|
|
child: Listener(
|
|
onPointerDown: (PointerDownEvent event) {
|
|
_pointerDown = true;
|
|
},
|
|
child: SizedBox(
|
|
key: key1,
|
|
width: 100.0,
|
|
height: 100.0,
|
|
child: Container(
|
|
color: const Color(0xFF0000FF)
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
expect(_pointerDown, isFalse);
|
|
await tester.tap(find.byKey(key1));
|
|
expect(_pointerDown, isTrue);
|
|
});
|
|
|
|
testWidgets('hit test - partially inside the bounding box', (WidgetTester tester) async {
|
|
final GlobalKey key1 = GlobalKey();
|
|
bool _pointerDown = false;
|
|
|
|
await tester.pumpWidget(
|
|
Center(
|
|
child: FractionalTranslation(
|
|
translation: const Offset(0.5, 0.5),
|
|
transformHitTests: true,
|
|
child: Listener(
|
|
onPointerDown: (PointerDownEvent event) {
|
|
_pointerDown = true;
|
|
},
|
|
child: SizedBox(
|
|
key: key1,
|
|
width: 100.0,
|
|
height: 100.0,
|
|
child: Container(
|
|
color: const Color(0xFF0000FF)
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
expect(_pointerDown, isFalse);
|
|
await tester.tap(find.byKey(key1));
|
|
expect(_pointerDown, isTrue);
|
|
});
|
|
|
|
testWidgets('hit test - completely outside the bounding box', (WidgetTester tester) async {
|
|
final GlobalKey key1 = GlobalKey();
|
|
bool _pointerDown = false;
|
|
|
|
await tester.pumpWidget(
|
|
Center(
|
|
child: FractionalTranslation(
|
|
translation: const Offset(1.0, 1.0),
|
|
transformHitTests: true,
|
|
child: Listener(
|
|
onPointerDown: (PointerDownEvent event) {
|
|
_pointerDown = true;
|
|
},
|
|
child: SizedBox(
|
|
key: key1,
|
|
width: 100.0,
|
|
height: 100.0,
|
|
child: Container(
|
|
color: const Color(0xFF0000FF)
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
expect(_pointerDown, isFalse);
|
|
await tester.tap(find.byKey(key1));
|
|
expect(_pointerDown, isTrue);
|
|
});
|
|
|
|
testWidgets('semantics bounds are updated', (WidgetTester tester) async {
|
|
final GlobalKey fractionalTranslationKey = GlobalKey();
|
|
final GlobalKey textKey = GlobalKey();
|
|
Offset offset = const Offset(0.4, 0.4);
|
|
|
|
await tester.pumpWidget(
|
|
StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: Center(
|
|
child: Semantics(
|
|
explicitChildNodes: true,
|
|
child: FractionalTranslation(
|
|
key: fractionalTranslationKey,
|
|
translation: offset,
|
|
transformHitTests: true,
|
|
child: GestureDetector(
|
|
onTap: () {
|
|
setState(() {
|
|
offset = const Offset(0.8, 0.8);
|
|
});
|
|
},
|
|
child: SizedBox(
|
|
width: 100.0,
|
|
height: 100.0,
|
|
child: Text(
|
|
'foo',
|
|
key: textKey,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
)
|
|
);
|
|
|
|
expect(
|
|
tester.getSemantics(find.byKey(textKey)).transform,
|
|
Matrix4(
|
|
3.0, 0.0, 0.0, 0.0,
|
|
0.0, 3.0, 0.0, 0.0,
|
|
0.0, 0.0, 1.0, 0.0,
|
|
1170.0, 870.0, 0.0, 1.0,
|
|
),
|
|
);
|
|
|
|
await tester.tap(find.byKey(fractionalTranslationKey));
|
|
await tester.pump();
|
|
expect(
|
|
tester.getSemantics(find.byKey(textKey)).transform,
|
|
Matrix4(
|
|
3.0, 0.0, 0.0, 0.0,
|
|
0.0, 3.0, 0.0, 0.0,
|
|
0.0, 0.0, 1.0, 0.0,
|
|
1290.0, 990.0, 0.0, 1.0,
|
|
),
|
|
);
|
|
});
|
|
});
|
|
|
|
group('Row', () {
|
|
testWidgets('multiple baseline aligned children', (WidgetTester tester) async {
|
|
final UniqueKey key1 = UniqueKey();
|
|
final UniqueKey key2 = UniqueKey();
|
|
const double fontSize1 = 54;
|
|
const double fontSize2 = 14;
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
body: Container(
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.baseline,
|
|
textBaseline: TextBaseline.alphabetic,
|
|
children: <Widget>[
|
|
Text('big text',
|
|
key: key1,
|
|
style: const TextStyle(fontSize: fontSize1),
|
|
),
|
|
Text('one\ntwo\nthree\nfour\nfive\nsix\nseven',
|
|
key: key2,
|
|
style: const TextStyle(fontSize: fontSize2),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final RenderBox textBox1 = tester.renderObject(find.byKey(key1));
|
|
final RenderBox textBox2 = tester.renderObject(find.byKey(key2));
|
|
final RenderBox rowBox = tester.renderObject(find.byType(Row));
|
|
|
|
// The two Texts are baseline aligned, so some portion of them extends
|
|
// both above and below the baseline. The first has a huge font size, so
|
|
// it extends higher above the baseline than usual. The second has many
|
|
// lines, but being aligned by the first line's baseline, they hang far
|
|
// below the baseline. The size of the parent row is just enough to
|
|
// contain both of them.
|
|
const double ahemBaselineLocation = 0.8; // https://web-platform-tests.org/writing-tests/ahem.html
|
|
const double aboveBaseline1 = fontSize1 * ahemBaselineLocation;
|
|
const double belowBaseline1 = fontSize1 * (1 - ahemBaselineLocation);
|
|
const double aboveBaseline2 = fontSize2 * ahemBaselineLocation;
|
|
const double belowBaseline2 = fontSize2 * (1 - ahemBaselineLocation) + fontSize2 * 6;
|
|
final double aboveBaseline = math.max(aboveBaseline1, aboveBaseline2);
|
|
final double belowBaseline = math.max(belowBaseline1, belowBaseline2);
|
|
expect(rowBox.size.height, greaterThan(textBox1.size.height));
|
|
expect(rowBox.size.height, greaterThan(textBox2.size.height));
|
|
expect(rowBox.size.height, moreOrLessEquals(aboveBaseline + belowBaseline, epsilon: .001));
|
|
expect(tester.getTopLeft(find.byKey(key1)).dy, 0);
|
|
expect(
|
|
tester.getTopLeft(find.byKey(key2)).dy,
|
|
moreOrLessEquals(aboveBaseline1 - aboveBaseline2, epsilon: .001),
|
|
);
|
|
});
|
|
|
|
testWidgets('baseline aligned children account for a larger, no-baseline child size', (WidgetTester tester) async {
|
|
// Regression test for https://github.com/flutter/flutter/issues/58898
|
|
final UniqueKey key1 = UniqueKey();
|
|
final UniqueKey key2 = UniqueKey();
|
|
const double fontSize1 = 54;
|
|
const double fontSize2 = 14;
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
body: Container(
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.baseline,
|
|
textBaseline: TextBaseline.alphabetic,
|
|
children: <Widget>[
|
|
Text('big text',
|
|
key: key1,
|
|
style: const TextStyle(fontSize: fontSize1),
|
|
),
|
|
Text('one\ntwo\nthree\nfour\nfive\nsix\nseven',
|
|
key: key2,
|
|
style: const TextStyle(fontSize: fontSize2),
|
|
),
|
|
const FlutterLogo(size: 250),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final RenderBox textBox1 = tester.renderObject(find.byKey(key1));
|
|
final RenderBox textBox2 = tester.renderObject(find.byKey(key2));
|
|
final RenderBox rowBox = tester.renderObject(find.byType(Row));
|
|
|
|
// The two Texts are baseline aligned, so some portion of them extends
|
|
// both above and below the baseline. The first has a huge font size, so
|
|
// it extends higher above the baseline than usual. The second has many
|
|
// lines, but being aligned by the first line's baseline, they hang far
|
|
// below the baseline. The FlutterLogo extends further than both Texts,
|
|
// so the size of the parent row should contain the FlutterLogo as well.
|
|
const double ahemBaselineLocation = 0.8; // https://web-platform-tests.org/writing-tests/ahem.html
|
|
const double aboveBaseline1 = fontSize1 * ahemBaselineLocation;
|
|
const double aboveBaseline2 = fontSize2 * ahemBaselineLocation;
|
|
expect(rowBox.size.height, greaterThan(textBox1.size.height));
|
|
expect(rowBox.size.height, greaterThan(textBox2.size.height));
|
|
expect(rowBox.size.height, 250);
|
|
expect(tester.getTopLeft(find.byKey(key1)).dy, 0);
|
|
expect(
|
|
tester.getTopLeft(find.byKey(key2)).dy,
|
|
moreOrLessEquals(aboveBaseline1 - aboveBaseline2, epsilon: .001),
|
|
);
|
|
});
|
|
});
|
|
|
|
test('UnconstrainedBox toString', () {
|
|
expect(
|
|
const UnconstrainedBox(constrainedAxis: Axis.vertical,).toString(),
|
|
equals('UnconstrainedBox(alignment: center, constrainedAxis: vertical)'),
|
|
);
|
|
expect(
|
|
const UnconstrainedBox(constrainedAxis: Axis.horizontal, textDirection: TextDirection.rtl, alignment: Alignment.topRight).toString(),
|
|
equals('UnconstrainedBox(alignment: topRight, constrainedAxis: horizontal, textDirection: rtl)'),
|
|
);
|
|
});
|
|
|
|
testWidgets('UnconstrainedBox can set and update clipBehavior', (WidgetTester tester) async {
|
|
await tester.pumpWidget(const UnconstrainedBox());
|
|
final RenderUnconstrainedBox renderObject = tester.allRenderObjects.whereType<RenderUnconstrainedBox>().first;
|
|
expect(renderObject.clipBehavior, equals(Clip.none));
|
|
|
|
await tester.pumpWidget(const UnconstrainedBox(clipBehavior: Clip.antiAlias));
|
|
expect(renderObject.clipBehavior, equals(Clip.antiAlias));
|
|
});
|
|
|
|
group('ColoredBox', () {
|
|
_MockCanvas mockCanvas;
|
|
_MockPaintingContext mockContext;
|
|
const Color colorToPaint = Color(0xFFABCDEF);
|
|
|
|
setUp(() {
|
|
mockContext = _MockPaintingContext();
|
|
mockCanvas = mockContext.canvas;
|
|
});
|
|
|
|
testWidgets('ColoredBox - no size, no child', (WidgetTester tester) async {
|
|
await tester.pumpWidget(Flex(
|
|
direction: Axis.horizontal,
|
|
textDirection: TextDirection.ltr,
|
|
children: const <Widget>[
|
|
SizedBox.shrink(
|
|
child: ColoredBox(color: colorToPaint),
|
|
),
|
|
],
|
|
));
|
|
expect(find.byType(ColoredBox), findsOneWidget);
|
|
final RenderObject renderColoredBox = tester.renderObject(find.byType(ColoredBox));
|
|
|
|
renderColoredBox.paint(mockContext, Offset.zero);
|
|
|
|
expect(mockCanvas.rects, isEmpty);
|
|
expect(mockCanvas.paints, isEmpty);
|
|
expect(mockContext.children, isEmpty);
|
|
expect(mockContext.offets, isEmpty);
|
|
});
|
|
|
|
testWidgets('ColoredBox - no size, child', (WidgetTester tester) async {
|
|
const ValueKey<int> key = ValueKey<int>(0);
|
|
const Widget child = SizedBox.expand(key: key);
|
|
await tester.pumpWidget(Flex(
|
|
direction: Axis.horizontal,
|
|
textDirection: TextDirection.ltr,
|
|
children: const <Widget>[
|
|
SizedBox.shrink(
|
|
child: ColoredBox(color: colorToPaint, child: child),
|
|
),
|
|
],
|
|
));
|
|
expect(find.byType(ColoredBox), findsOneWidget);
|
|
final RenderObject renderColoredBox = tester.renderObject(find.byType(ColoredBox));
|
|
final RenderObject renderSizedBox = tester.renderObject(find.byKey(key));
|
|
|
|
renderColoredBox.paint(mockContext, Offset.zero);
|
|
|
|
expect(mockCanvas.rects, isEmpty);
|
|
expect(mockCanvas.paints, isEmpty);
|
|
expect(mockContext.children.single, renderSizedBox);
|
|
expect(mockContext.offets.single, Offset.zero);
|
|
});
|
|
|
|
testWidgets('ColoredBox - size, no child', (WidgetTester tester) async {
|
|
await tester.pumpWidget(const ColoredBox(color: colorToPaint));
|
|
expect(find.byType(ColoredBox), findsOneWidget);
|
|
final RenderObject renderColoredBox = tester.renderObject(find.byType(ColoredBox));
|
|
|
|
renderColoredBox.paint(mockContext, Offset.zero);
|
|
|
|
expect(mockCanvas.rects.single, const Rect.fromLTWH(0, 0, 800, 600));
|
|
expect(mockCanvas.paints.single.color, colorToPaint);
|
|
expect(mockContext.children, isEmpty);
|
|
expect(mockContext.offets, isEmpty);
|
|
});
|
|
|
|
testWidgets('ColoredBox - size, child', (WidgetTester tester) async {
|
|
const ValueKey<int> key = ValueKey<int>(0);
|
|
const Widget child = SizedBox.expand(key: key);
|
|
await tester.pumpWidget(const ColoredBox(color: colorToPaint, child: child));
|
|
expect(find.byType(ColoredBox), findsOneWidget);
|
|
final RenderObject renderColoredBox = tester.renderObject(find.byType(ColoredBox));
|
|
final RenderObject renderSizedBox = tester.renderObject(find.byKey(key));
|
|
|
|
renderColoredBox.paint(mockContext, Offset.zero);
|
|
|
|
expect(mockCanvas.rects.single, const Rect.fromLTWH(0, 0, 800, 600));
|
|
expect(mockCanvas.paints.single.color, colorToPaint);
|
|
expect(mockContext.children.single, renderSizedBox);
|
|
expect(mockContext.offets.single, Offset.zero);
|
|
});
|
|
|
|
testWidgets('ColoredBox - properties', (WidgetTester tester) async {
|
|
const ColoredBox box = ColoredBox(color: colorToPaint);
|
|
final DiagnosticPropertiesBuilder properties = DiagnosticPropertiesBuilder();
|
|
box.debugFillProperties(properties);
|
|
|
|
expect(properties.properties.first.value, colorToPaint);
|
|
});
|
|
});
|
|
testWidgets('Inconsequential golden test', (WidgetTester tester) async {
|
|
// The test validates the Flutter Gold integration. Any changes to the
|
|
// golden file can be approved at any time.
|
|
await tester.pumpWidget(RepaintBoundary(
|
|
child: Container(
|
|
color: const Color(0xFF42A5F5),
|
|
),
|
|
));
|
|
|
|
await tester.pumpAndSettle();
|
|
await expectLater(
|
|
find.byType(RepaintBoundary),
|
|
matchesGoldenFile('inconsequential_golden_file.png'),
|
|
);
|
|
});
|
|
|
|
testWidgets('IgnorePointer ignores pointers', (WidgetTester tester) async {
|
|
final List<String> logs = <String>[];
|
|
Widget target({bool ignoring}) => Align(
|
|
alignment: Alignment.topLeft,
|
|
child: Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: SizedBox(
|
|
width: 100,
|
|
height: 100,
|
|
child: Listener(
|
|
onPointerDown: (_) { logs.add('down1'); },
|
|
child: MouseRegion(
|
|
onEnter: (_) { logs.add('enter1'); },
|
|
onExit: (_) { logs.add('exit1'); },
|
|
cursor: SystemMouseCursors.forbidden,
|
|
child: Stack(
|
|
children: <Widget>[
|
|
Listener(
|
|
onPointerDown: (_) { logs.add('down2'); },
|
|
child: MouseRegion(
|
|
cursor: SystemMouseCursors.click,
|
|
onEnter: (_) { logs.add('enter2'); },
|
|
onExit: (_) { logs.add('exit2'); },
|
|
),
|
|
),
|
|
IgnorePointer(
|
|
ignoring: ignoring,
|
|
child: Listener(
|
|
onPointerDown: (_) { logs.add('down3'); },
|
|
child: MouseRegion(
|
|
cursor: SystemMouseCursors.text,
|
|
onEnter: (_) { logs.add('enter3'); },
|
|
onExit: (_) { logs.add('exit3'); },
|
|
),
|
|
),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final TestGesture gesture = await tester.createGesture(pointer: 1, kind: PointerDeviceKind.mouse);
|
|
await gesture.addPointer(location: const Offset(200, 200));
|
|
addTearDown(gesture.removePointer);
|
|
|
|
await tester.pumpWidget(target(ignoring: true));
|
|
expect(logs, isEmpty);
|
|
|
|
await gesture.moveTo(const Offset(50, 50));
|
|
expect(logs, <String>['enter1', 'enter2']);
|
|
logs.clear();
|
|
|
|
await gesture.down(const Offset(50, 50));
|
|
expect(logs, <String>['down2', 'down1']);
|
|
logs.clear();
|
|
|
|
await gesture.up();
|
|
expect(logs, isEmpty);
|
|
|
|
await tester.pumpWidget(target(ignoring: false));
|
|
expect(logs, <String>['exit2', 'enter3']);
|
|
logs.clear();
|
|
|
|
await gesture.down(const Offset(50, 50));
|
|
expect(logs, <String>['down3', 'down1']);
|
|
logs.clear();
|
|
|
|
await gesture.up();
|
|
expect(logs, isEmpty);
|
|
|
|
await tester.pumpWidget(target(ignoring: true));
|
|
expect(logs, <String>['exit3', 'enter2']);
|
|
logs.clear();
|
|
});
|
|
|
|
testWidgets('AbsorbPointer absorbs pointers', (WidgetTester tester) async {
|
|
final List<String> logs = <String>[];
|
|
Widget target({bool absorbing}) => Align(
|
|
alignment: Alignment.topLeft,
|
|
child: Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: SizedBox(
|
|
width: 100,
|
|
height: 100,
|
|
child: Listener(
|
|
onPointerDown: (_) { logs.add('down1'); },
|
|
child: MouseRegion(
|
|
onEnter: (_) { logs.add('enter1'); },
|
|
onExit: (_) { logs.add('exit1'); },
|
|
cursor: SystemMouseCursors.forbidden,
|
|
child: Stack(
|
|
children: <Widget>[
|
|
Listener(
|
|
onPointerDown: (_) { logs.add('down2'); },
|
|
child: MouseRegion(
|
|
cursor: SystemMouseCursors.click,
|
|
onEnter: (_) { logs.add('enter2'); },
|
|
onExit: (_) { logs.add('exit2'); },
|
|
),
|
|
),
|
|
AbsorbPointer(
|
|
absorbing: absorbing,
|
|
child: Listener(
|
|
onPointerDown: (_) { logs.add('down3'); },
|
|
child: MouseRegion(
|
|
cursor: SystemMouseCursors.text,
|
|
onEnter: (_) { logs.add('enter3'); },
|
|
onExit: (_) { logs.add('exit3'); },
|
|
),
|
|
),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final TestGesture gesture = await tester.createGesture(pointer: 1, kind: PointerDeviceKind.mouse);
|
|
await gesture.addPointer(location: const Offset(200, 200));
|
|
addTearDown(gesture.removePointer);
|
|
|
|
await tester.pumpWidget(target(absorbing: true));
|
|
expect(logs, isEmpty);
|
|
|
|
await gesture.moveTo(const Offset(50, 50));
|
|
expect(logs, <String>['enter1']);
|
|
logs.clear();
|
|
|
|
await gesture.down(const Offset(50, 50));
|
|
expect(logs, <String>['down1']);
|
|
logs.clear();
|
|
|
|
await gesture.up();
|
|
expect(logs, isEmpty);
|
|
|
|
await tester.pumpWidget(target(absorbing: false));
|
|
expect(logs, <String>['enter3']);
|
|
logs.clear();
|
|
|
|
await gesture.down(const Offset(50, 50));
|
|
expect(logs, <String>['down3', 'down1']);
|
|
logs.clear();
|
|
|
|
await gesture.up();
|
|
expect(logs, isEmpty);
|
|
|
|
await tester.pumpWidget(target(absorbing: true));
|
|
expect(logs, <String>['exit3']);
|
|
logs.clear();
|
|
});
|
|
}
|
|
|
|
HitsRenderBox hits(RenderBox renderBox) => HitsRenderBox(renderBox);
|
|
|
|
class HitsRenderBox extends Matcher {
|
|
const HitsRenderBox(this.renderBox);
|
|
|
|
final RenderBox renderBox;
|
|
|
|
@override
|
|
Description describe(Description description) =>
|
|
description.add('hit test result contains ').addDescriptionOf(renderBox);
|
|
|
|
@override
|
|
bool matches(dynamic item, Map<dynamic, dynamic> matchState) {
|
|
final HitTestResult hitTestResult = item as HitTestResult;
|
|
return hitTestResult.path.where(
|
|
(HitTestEntry entry) => entry.target == renderBox
|
|
).isNotEmpty;
|
|
}
|
|
}
|
|
|
|
DoesNotHitRenderBox doesNotHit(RenderBox renderBox) => DoesNotHitRenderBox(renderBox);
|
|
|
|
class DoesNotHitRenderBox extends Matcher {
|
|
const DoesNotHitRenderBox(this.renderBox);
|
|
|
|
final RenderBox renderBox;
|
|
|
|
@override
|
|
Description describe(Description description) =>
|
|
description.add("hit test result doesn't contain ").addDescriptionOf(renderBox);
|
|
|
|
@override
|
|
bool matches(dynamic item, Map<dynamic, dynamic> matchState) {
|
|
final HitTestResult hitTestResult = item as HitTestResult;
|
|
return hitTestResult.path.where(
|
|
(HitTestEntry entry) => entry.target == renderBox
|
|
).isEmpty;
|
|
}
|
|
}
|
|
|
|
class _MockPaintingContext extends Fake implements PaintingContext {
|
|
final List<RenderObject> children = <RenderObject>[];
|
|
final List<Offset> offets = <Offset>[];
|
|
|
|
@override
|
|
final _MockCanvas canvas = _MockCanvas();
|
|
|
|
@override
|
|
void paintChild(RenderObject child, Offset offset) {
|
|
children.add(child);
|
|
offets.add(offset);
|
|
}
|
|
}
|
|
|
|
class _MockCanvas extends Fake implements Canvas {
|
|
final List<Rect> rects = <Rect>[];
|
|
final List<Paint> paints = <Paint>[];
|
|
bool didPaint = false;
|
|
|
|
@override
|
|
void drawRect(Rect rect, Paint paint) {
|
|
rects.add(rect);
|
|
paints.add(paint);
|
|
}
|
|
}
|