Unverified Commit 0a383fce authored by Hans Muller's avatar Hans Muller Committed by GitHub

Fix InkRipple.cancel(), added paints..everything (#17787)

parent dfc0244e
......@@ -198,15 +198,13 @@ class InkRipple extends InteractiveInkFeature {
@override
void cancel() {
_fadeInController.stop();
// Watch out: setting _fadeOutController's value to 1.0 would
// trigger a call to _handleAlphaStatusChanged() which would
// Watch out: setting _fadeOutController's value to 1.0 will
// trigger a call to _handleAlphaStatusChanged() which will
// dispose _fadeOutController.
final double _fadeOutValue = 1.0 - _fadeInController.value;
if (_fadeOutValue < 1.0) {
_fadeOutController
..value = _fadeOutValue
..animateTo(1.0, duration: _kCancelDuration);
}
final double fadeOutValue = 1.0 - _fadeInController.value;
_fadeOutController.value = fadeOutValue;
if (fadeOutValue < 1.0)
_fadeOutController.animateTo(1.0, duration: _kCancelDuration);
}
void _handleAlphaStatusChanged(AnimationStatus status) {
......
......@@ -278,4 +278,50 @@ void main() {
await gesture.up(); // generates a tap cancel
await tester.pumpAndSettle();
});
testWidgets('Cancel an InkRipple that was disposed when its animation ended', (WidgetTester tester) async {
const Color highlightColor = const Color(0xAAFF0000);
const Color splashColor = const Color(0xB40000FF);
// Regression test for https://github.com/flutter/flutter/issues/14391
await tester.pumpWidget(
new Material(
child: new Center(
child: new Container(
width: 100.0,
height: 100.0,
child: new InkWell(
splashColor: splashColor,
highlightColor: highlightColor,
onTap: () { },
radius: 100.0,
splashFactory: InkRipple.splashFactory,
),
),
),
),
);
final Offset tapDownOffset = tester.getTopLeft(find.byType(InkWell));
await tester.tapAt(tapDownOffset);
await tester.pump(); // start splash
// No delay here so _fadeInController.value=1.0 (InkRipple.dart)
// Generate a tap cancel; Will cancel the ink splash before it started
final TestGesture gesture = await tester.startGesture(tapDownOffset);
await tester.pump(); // start gesture
await gesture.moveTo(const Offset(0.0, 0.0));
await gesture.up(); // generates a tap cancel
final RenderBox box = Material.of(tester.element(find.byType(InkWell))) as dynamic;
expect(box, paints..everything((Symbol method, List<dynamic> arguments) {
if (method != #drawCircle)
return true;
final Paint paint = arguments[2];
if (paint.color.alpha == 0)
return true;
throw 'Expected: paint.color.alpha == 0, found: ${paint.color.alpha}';
}));
});
}
......@@ -48,7 +48,8 @@ Matcher get paintsNothing => new _TestRecordingCanvasPaintsNothingMatcher();
/// Matches objects or functions that assert when they try to paint.
Matcher get paintsAssertion => new _TestRecordingCanvasPaintsAssertionMatcher();
/// Signature for [PaintPattern.something] predicate argument.
/// Signature for the [PaintPattern.something] and [PaintPattern.everything]
/// predicate argument.
///
/// Used by the [paints] matcher.
///
......@@ -398,6 +399,22 @@ abstract class PaintPattern {
/// displayed from the test framework and should be complete sentence
/// describing the problem.
void something(PaintPatternPredicate predicate);
/// Provides a custom matcher.
///
/// Each method call after the last matched call (if any) will be passed to
/// the given predicate, along with the values of its (positional) arguments.
///
/// For each one, the predicate must either return a boolean or throw a [String].
///
/// The predicate will be applied to each [Canvas] call until it returns false
/// or all of the method calls have been tested.
///
/// If the predicate throws a [String], then the [paints] [Matcher] is
/// considered to have failed. The thrown string is used in the message
/// displayed from the test framework and should be complete sentence
/// describing the problem.
void everything(PaintPatternPredicate predicate);
}
/// Matches a [Path] that contains (as defined by [Path.contains]) the given
......@@ -728,6 +745,11 @@ class _TestRecordingCanvasPatternMatcher extends _TestRecordingCanvasMatcher imp
_predicates.add(new _SomethingPaintPredicate(predicate));
}
@override
void everything(PaintPatternPredicate predicate) {
_predicates.add(new _EverythingPaintPredicate(predicate));
}
@override
Description describe(Description description) {
if (_predicates.isEmpty)
......@@ -1303,6 +1325,36 @@ class _SomethingPaintPredicate extends _PaintPredicate {
String toString() => 'a "something" step';
}
class _EverythingPaintPredicate extends _PaintPredicate {
_EverythingPaintPredicate(this.predicate);
final PaintPatternPredicate predicate;
@override
void match(Iterator<RecordedInvocation> call) {
assert(predicate != null);
while (call.moveNext()) {
final RecordedInvocation currentCall = call.current;
if (!currentCall.invocation.isMethod)
throw 'It called $currentCall, which was not a method, when the paint pattern expected a method call';
if (!_runPredicate(currentCall.invocation.memberName, currentCall.invocation.positionalArguments))
return;
}
}
bool _runPredicate(Symbol methodName, List<dynamic> arguments) {
try {
return predicate(methodName, arguments);
} on String catch (s) {
throw 'It painted something that the predicate passed to an "everything" step '
'in the paint pattern considered incorrect:\n $s\n ';
}
}
@override
String toString() => 'an "everything" step';
}
class _FunctionPaintPredicate extends _PaintPredicate {
_FunctionPaintPredicate(this.symbol, this.arguments);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment