
Thanks @NikoYuwono for getting this working! We appreciate your help! * Implement Double Tap Handling in TextField and Editable * Fix test broken by the change and add test for double tap * Fix affected tests * Remove unnecessary new * Fix test
209 lines
6.0 KiB
Dart
209 lines
6.0 KiB
Dart
// Copyright 2017 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'package:flutter/gestures.dart' show kPressTimeout;
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
|
|
int confirmCount = 0;
|
|
int cancelCount = 0;
|
|
|
|
class TestInkSplash extends InkSplash {
|
|
TestInkSplash({
|
|
MaterialInkController controller,
|
|
RenderBox referenceBox,
|
|
Offset position,
|
|
Color color,
|
|
bool containedInkWell = false,
|
|
RectCallback rectCallback,
|
|
BorderRadius borderRadius,
|
|
ShapeBorder customBorder,
|
|
double radius,
|
|
VoidCallback onRemoved,
|
|
TextDirection textDirection
|
|
}) : super(
|
|
controller: controller,
|
|
referenceBox: referenceBox,
|
|
position: position,
|
|
color: color,
|
|
containedInkWell: containedInkWell,
|
|
rectCallback: rectCallback,
|
|
borderRadius: borderRadius,
|
|
customBorder: customBorder,
|
|
radius: radius,
|
|
onRemoved: onRemoved,
|
|
textDirection: textDirection,
|
|
);
|
|
|
|
@override
|
|
void confirm() {
|
|
confirmCount += 1;
|
|
super.confirm();
|
|
}
|
|
|
|
@override
|
|
void cancel() {
|
|
cancelCount += 1;
|
|
super.cancel();
|
|
}
|
|
}
|
|
|
|
class TestInkSplashFactory extends InteractiveInkFeatureFactory {
|
|
const TestInkSplashFactory();
|
|
|
|
@override
|
|
InteractiveInkFeature create({
|
|
MaterialInkController controller,
|
|
RenderBox referenceBox,
|
|
Offset position,
|
|
Color color,
|
|
bool containedInkWell = false,
|
|
RectCallback rectCallback,
|
|
BorderRadius borderRadius,
|
|
ShapeBorder customBorder,
|
|
double radius,
|
|
VoidCallback onRemoved,
|
|
TextDirection textDirection,
|
|
}) {
|
|
return TestInkSplash(
|
|
controller: controller,
|
|
referenceBox: referenceBox,
|
|
position: position,
|
|
color: color,
|
|
containedInkWell: containedInkWell,
|
|
rectCallback: rectCallback,
|
|
borderRadius: borderRadius,
|
|
customBorder: customBorder,
|
|
radius: radius,
|
|
onRemoved: onRemoved,
|
|
textDirection: textDirection,
|
|
);
|
|
}
|
|
}
|
|
|
|
void main() {
|
|
testWidgets('Tap and no focus causes a splash', (WidgetTester tester) async {
|
|
final Key textField1 = UniqueKey();
|
|
final Key textField2 = UniqueKey();
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Theme(
|
|
data: ThemeData.light().copyWith(splashFactory: const TestInkSplashFactory()),
|
|
child: Material(
|
|
child: Container(
|
|
alignment: Alignment.topLeft,
|
|
child: Column(
|
|
children: <Widget>[
|
|
TextField(
|
|
key: textField1,
|
|
decoration: const InputDecoration(
|
|
labelText: 'label',
|
|
),
|
|
),
|
|
TextField(
|
|
key: textField2,
|
|
decoration: const InputDecoration(
|
|
labelText: 'label',
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
)
|
|
);
|
|
|
|
confirmCount = 0;
|
|
cancelCount = 0;
|
|
|
|
await tester.tap(find.byKey(textField1));
|
|
await tester.pumpAndSettle(Duration(milliseconds: 300));
|
|
expect(confirmCount, 1);
|
|
expect(cancelCount, 0);
|
|
|
|
// textField1 already has the focus, no new splash
|
|
await tester.tap(find.byKey(textField1));
|
|
await tester.pumpAndSettle(Duration(milliseconds: 300));
|
|
expect(confirmCount, 1);
|
|
expect(cancelCount, 0);
|
|
|
|
// textField2 gets the focus and a splash
|
|
await tester.tap(find.byKey(textField2));
|
|
await tester.pumpAndSettle(Duration(milliseconds: 300));
|
|
expect(confirmCount, 2);
|
|
expect(cancelCount, 0);
|
|
|
|
// Tap outside of textField1's editable. It still gets focus and splash.
|
|
await tester.tapAt(tester.getTopLeft(find.byKey(textField1)));
|
|
await tester.pumpAndSettle(Duration(milliseconds: 300));
|
|
expect(confirmCount, 3);
|
|
expect(cancelCount, 0);
|
|
|
|
// Tap in the center of textField2's editable. It still gets the focus
|
|
// and the splash. There is no splash cancel.
|
|
await tester.tap(find.byKey(textField2));
|
|
await tester.pumpAndSettle(Duration(milliseconds: 300));
|
|
expect(confirmCount, 4);
|
|
expect(cancelCount, 0);
|
|
});
|
|
|
|
testWidgets('Splash cancel', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Theme(
|
|
data: ThemeData.light().copyWith(splashFactory: const TestInkSplashFactory()),
|
|
child: Material(
|
|
child: ListView(
|
|
children: <Widget>[
|
|
const TextField(
|
|
decoration: InputDecoration(
|
|
labelText: 'label1',
|
|
),
|
|
),
|
|
const TextField(
|
|
decoration: InputDecoration(
|
|
labelText: 'label2',
|
|
),
|
|
),
|
|
Container(
|
|
height: 1000.0,
|
|
color: const Color(0xFF00FF00),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
)
|
|
);
|
|
|
|
confirmCount = 0;
|
|
cancelCount = 0;
|
|
|
|
// Pointer is dragged below the textfield, splash is canceled.
|
|
final TestGesture gesture1 = await tester.startGesture(tester.getCenter(find.text('label1')));
|
|
|
|
// Splashes start on tapDown.
|
|
// If the timeout is less than kPressTimeout the recognizer will just trigger
|
|
// the onTapCancel callback. If the timeout is greater or equal to kPressTimeout
|
|
// and less than kLongPressTimeout then onTapDown, onCancel will be called.
|
|
await tester.pump(kPressTimeout);
|
|
|
|
await gesture1.moveTo(const Offset(400.0, 300.0));
|
|
await gesture1.up();
|
|
expect(confirmCount, 0);
|
|
expect(cancelCount, 1);
|
|
|
|
// Pointer is dragged upwards causing a scroll, splash is canceled.
|
|
final TestGesture gesture2 = await tester.startGesture(tester.getCenter(find.text('label2')));
|
|
await tester.pump(kPressTimeout);
|
|
await gesture2.moveBy(const Offset(0.0, -200.0));
|
|
await gesture2.up();
|
|
expect(confirmCount, 0);
|
|
expect(cancelCount, 2);
|
|
});
|
|
}
|