mirror of
https://github.com/flutter/flutter
synced 2024-10-13 19:52:53 +00:00
Nested InkWells only show the innermost splash (#56611)
This commit is contained in:
parent
0786f29ff2
commit
06adde0bd0
|
@ -174,6 +174,29 @@ abstract class InteractiveInkFeatureFactory {
|
|||
});
|
||||
}
|
||||
|
||||
abstract class _ParentInkResponseState {
|
||||
void markChildInkResponsePressed(_ParentInkResponseState childState, bool value);
|
||||
}
|
||||
|
||||
class _ParentInkResponseProvider extends InheritedWidget {
|
||||
const _ParentInkResponseProvider({
|
||||
this.state,
|
||||
Widget child,
|
||||
}) : super(child: child);
|
||||
|
||||
final _ParentInkResponseState state;
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(_ParentInkResponseProvider oldWidget) => state != oldWidget.state;
|
||||
|
||||
static _ParentInkResponseState of(BuildContext context) {
|
||||
return context.dependOnInheritedWidgetOfExactType<_ParentInkResponseProvider>()?.state;
|
||||
}
|
||||
}
|
||||
|
||||
typedef _GetRectCallback = RectCallback Function(RenderBox referenceBox);
|
||||
typedef _CheckContext = bool Function(BuildContext context);
|
||||
|
||||
/// An area of a [Material] that responds to touch. Has a configurable shape and
|
||||
/// can be configured to clip splashes that extend outside its bounds or not.
|
||||
///
|
||||
|
@ -255,7 +278,7 @@ abstract class InteractiveInkFeatureFactory {
|
|||
/// * [GestureDetector], for listening for gestures without ink splashes.
|
||||
/// * [RaisedButton] and [FlatButton], two kinds of buttons in material design.
|
||||
/// * [IconButton], which combines [InkResponse] with an [Icon].
|
||||
class InkResponse extends StatefulWidget {
|
||||
class InkResponse extends StatelessWidget {
|
||||
/// Creates an area of a [Material] that responds to touch.
|
||||
///
|
||||
/// Must have an ancestor [Material] widget in which to cause ink reactions.
|
||||
|
@ -508,6 +531,40 @@ class InkResponse extends StatefulWidget {
|
|||
/// slightly more efficient).
|
||||
RectCallback getRectCallback(RenderBox referenceBox) => null;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _ParentInkResponseState parentState = _ParentInkResponseProvider.of(context);
|
||||
return _InnerInkResponse(
|
||||
child: child,
|
||||
onTap: onTap,
|
||||
onTapDown: onTapDown,
|
||||
onTapCancel: onTapCancel,
|
||||
onDoubleTap: onDoubleTap,
|
||||
onLongPress: onLongPress,
|
||||
onHighlightChanged: onHighlightChanged,
|
||||
onHover: onHover,
|
||||
containedInkWell: containedInkWell,
|
||||
highlightShape: highlightShape,
|
||||
radius: radius,
|
||||
borderRadius: borderRadius,
|
||||
customBorder: customBorder,
|
||||
focusColor: focusColor,
|
||||
hoverColor: hoverColor,
|
||||
highlightColor: highlightColor,
|
||||
splashColor: splashColor,
|
||||
splashFactory: splashFactory,
|
||||
enableFeedback: enableFeedback,
|
||||
excludeFromSemantics: excludeFromSemantics,
|
||||
focusNode: focusNode,
|
||||
canRequestFocus: canRequestFocus,
|
||||
onFocusChange: onFocusChange,
|
||||
autofocus: autofocus,
|
||||
parentState: parentState,
|
||||
getRectCallback: getRectCallback,
|
||||
debugCheckContext: debugCheckContext,
|
||||
);
|
||||
}
|
||||
|
||||
/// Asserts that the given context satisfies the prerequisites for
|
||||
/// this class.
|
||||
///
|
||||
|
@ -521,9 +578,74 @@ class InkResponse extends StatefulWidget {
|
|||
assert(debugCheckHasDirectionality(context));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class _InnerInkResponse extends StatefulWidget {
|
||||
const _InnerInkResponse({
|
||||
this.child,
|
||||
this.onTap,
|
||||
this.onTapDown,
|
||||
this.onTapCancel,
|
||||
this.onDoubleTap,
|
||||
this.onLongPress,
|
||||
this.onHighlightChanged,
|
||||
this.onHover,
|
||||
this.containedInkWell = false,
|
||||
this.highlightShape = BoxShape.circle,
|
||||
this.radius,
|
||||
this.borderRadius,
|
||||
this.customBorder,
|
||||
this.focusColor,
|
||||
this.hoverColor,
|
||||
this.highlightColor,
|
||||
this.splashColor,
|
||||
this.splashFactory,
|
||||
this.enableFeedback = true,
|
||||
this.excludeFromSemantics = false,
|
||||
this.focusNode,
|
||||
this.canRequestFocus = true,
|
||||
this.onFocusChange,
|
||||
this.autofocus = false,
|
||||
this.parentState,
|
||||
this.getRectCallback,
|
||||
this.debugCheckContext,
|
||||
}) : assert(containedInkWell != null),
|
||||
assert(highlightShape != null),
|
||||
assert(enableFeedback != null),
|
||||
assert(excludeFromSemantics != null),
|
||||
assert(autofocus != null),
|
||||
assert(canRequestFocus != null);
|
||||
|
||||
final Widget child;
|
||||
final GestureTapCallback onTap;
|
||||
final GestureTapDownCallback onTapDown;
|
||||
final GestureTapCallback onTapCancel;
|
||||
final GestureTapCallback onDoubleTap;
|
||||
final GestureLongPressCallback onLongPress;
|
||||
final ValueChanged<bool> onHighlightChanged;
|
||||
final ValueChanged<bool> onHover;
|
||||
final bool containedInkWell;
|
||||
final BoxShape highlightShape;
|
||||
final double radius;
|
||||
final BorderRadius borderRadius;
|
||||
final ShapeBorder customBorder;
|
||||
final Color focusColor;
|
||||
final Color hoverColor;
|
||||
final Color highlightColor;
|
||||
final Color splashColor;
|
||||
final InteractiveInkFeatureFactory splashFactory;
|
||||
final bool enableFeedback;
|
||||
final bool excludeFromSemantics;
|
||||
final ValueChanged<bool> onFocusChange;
|
||||
final bool autofocus;
|
||||
final FocusNode focusNode;
|
||||
final bool canRequestFocus;
|
||||
final _ParentInkResponseState parentState;
|
||||
final _GetRectCallback getRectCallback;
|
||||
final _CheckContext debugCheckContext;
|
||||
|
||||
@override
|
||||
_InkResponseState<InkResponse> createState() => _InkResponseState<InkResponse>();
|
||||
_InkResponseState createState() => _InkResponseState();
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
|
@ -554,7 +676,9 @@ enum _HighlightType {
|
|||
focus,
|
||||
}
|
||||
|
||||
class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKeepAliveClientMixin<T> {
|
||||
class _InkResponseState extends State<_InnerInkResponse>
|
||||
with AutomaticKeepAliveClientMixin<_InnerInkResponse>
|
||||
implements _ParentInkResponseState {
|
||||
Set<InteractiveInkFeature> _splashes;
|
||||
InteractiveInkFeature _currentSplash;
|
||||
bool _hovering = false;
|
||||
|
@ -563,6 +687,23 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe
|
|||
|
||||
bool get highlightsExist => _highlights.values.where((InkHighlight highlight) => highlight != null).isNotEmpty;
|
||||
|
||||
final ObserverList<_ParentInkResponseState> _activeChildren = ObserverList<_ParentInkResponseState>();
|
||||
@override
|
||||
void markChildInkResponsePressed(_ParentInkResponseState childState, bool value) {
|
||||
assert(childState != null);
|
||||
final bool lastAnyPressed = _anyChildInkResponsePressed;
|
||||
if (value) {
|
||||
_activeChildren.add(childState);
|
||||
} else {
|
||||
_activeChildren.remove(childState);
|
||||
}
|
||||
final bool nowAnyPressed = _anyChildInkResponsePressed;
|
||||
if (nowAnyPressed != lastAnyPressed) {
|
||||
widget.parentState?.markChildInkResponsePressed(this, nowAnyPressed);
|
||||
}
|
||||
}
|
||||
bool get _anyChildInkResponsePressed => _activeChildren.isNotEmpty;
|
||||
|
||||
void _handleAction(ActivateIntent intent) {
|
||||
_startSplash(context: context);
|
||||
_handleTap(context);
|
||||
|
@ -578,7 +719,7 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe
|
|||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(T oldWidget) {
|
||||
void didUpdateWidget(_InnerInkResponse oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (_isWidgetEnabled(widget) != _isWidgetEnabled(oldWidget)) {
|
||||
_handleHoverChange(_hovering);
|
||||
|
@ -628,6 +769,9 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe
|
|||
updateKeepAlive();
|
||||
}
|
||||
|
||||
if (type == _HighlightType.pressed) {
|
||||
widget.parentState?.markChildInkResponsePressed(this, value);
|
||||
}
|
||||
if (value == (highlight != null && highlight.active))
|
||||
return;
|
||||
if (value) {
|
||||
|
@ -737,6 +881,8 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe
|
|||
}
|
||||
|
||||
void _handleTapDown(TapDownDetails details) {
|
||||
if (_anyChildInkResponsePressed)
|
||||
return;
|
||||
_startSplash(details: details);
|
||||
if (widget.onTapDown != null) {
|
||||
widget.onTapDown(details);
|
||||
|
@ -813,10 +959,11 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe
|
|||
_highlights[highlight]?.dispose();
|
||||
_highlights[highlight] = null;
|
||||
}
|
||||
widget.parentState?.markChildInkResponsePressed(this, false);
|
||||
super.deactivate();
|
||||
}
|
||||
|
||||
bool _isWidgetEnabled(InkResponse widget) {
|
||||
bool _isWidgetEnabled(_InnerInkResponse widget) {
|
||||
return widget.onTap != null || widget.onDoubleTap != null || widget.onLongPress != null;
|
||||
}
|
||||
|
||||
|
@ -840,25 +987,28 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe
|
|||
}
|
||||
_currentSplash?.color = widget.splashColor ?? Theme.of(context).splashColor;
|
||||
final bool canRequestFocus = enabled && widget.canRequestFocus;
|
||||
return Actions(
|
||||
actions: _actionMap,
|
||||
child: Focus(
|
||||
focusNode: widget.focusNode,
|
||||
canRequestFocus: canRequestFocus,
|
||||
onFocusChange: _handleFocusUpdate,
|
||||
autofocus: widget.autofocus,
|
||||
child: MouseRegion(
|
||||
onEnter: enabled ? _handleMouseEnter : null,
|
||||
onExit: enabled ? _handleMouseExit : null,
|
||||
child: GestureDetector(
|
||||
onTapDown: enabled ? _handleTapDown : null,
|
||||
onTap: enabled ? () => _handleTap(context) : null,
|
||||
onTapCancel: enabled ? _handleTapCancel : null,
|
||||
onDoubleTap: widget.onDoubleTap != null ? _handleDoubleTap : null,
|
||||
onLongPress: widget.onLongPress != null ? () => _handleLongPress(context) : null,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
excludeFromSemantics: widget.excludeFromSemantics,
|
||||
child: widget.child,
|
||||
return _ParentInkResponseProvider(
|
||||
state: this,
|
||||
child: Actions(
|
||||
actions: _actionMap,
|
||||
child: Focus(
|
||||
focusNode: widget.focusNode,
|
||||
canRequestFocus: canRequestFocus,
|
||||
onFocusChange: _handleFocusUpdate,
|
||||
autofocus: widget.autofocus,
|
||||
child: MouseRegion(
|
||||
onEnter: enabled ? _handleMouseEnter : null,
|
||||
onExit: enabled ? _handleMouseExit : null,
|
||||
child: GestureDetector(
|
||||
onTapDown: enabled ? _handleTapDown : null,
|
||||
onTap: enabled ? () => _handleTap(context) : null,
|
||||
onTapCancel: enabled ? _handleTapCancel : null,
|
||||
onDoubleTap: widget.onDoubleTap != null ? _handleDoubleTap : null,
|
||||
onLongPress: widget.onLongPress != null ? () => _handleLongPress(context) : null,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
excludeFromSemantics: widget.excludeFromSemantics,
|
||||
child: widget.child,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -411,4 +411,459 @@ void main() {
|
|||
await tester.pumpAndSettle();
|
||||
expect(focusNode.hasPrimaryFocus, isFalse);
|
||||
});
|
||||
|
||||
testWidgets('When ink wells are nested, only the inner one is triggered by tap splash', (WidgetTester tester) async {
|
||||
final GlobalKey middleKey = GlobalKey();
|
||||
final GlobalKey innerKey = GlobalKey();
|
||||
Widget paddedInkWell({Key key, Widget child}) {
|
||||
return InkWell(
|
||||
key: key,
|
||||
onTap: () {},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(50),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
await tester.pumpWidget(
|
||||
Material(
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Center(
|
||||
child: paddedInkWell(
|
||||
child: paddedInkWell(
|
||||
key: middleKey,
|
||||
child: paddedInkWell(
|
||||
key: innerKey,
|
||||
child: Container(width: 50, height: 50),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
final MaterialInkController material = Material.of(tester.element(find.byKey(innerKey)));
|
||||
|
||||
// Press
|
||||
final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byKey(innerKey)), pointer: 1);
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(material, paintsExactlyCountTimes(#drawCircle, 1));
|
||||
|
||||
// Up
|
||||
await gesture.up();
|
||||
await tester.pumpAndSettle();
|
||||
expect(material, paintsNothing);
|
||||
|
||||
// Press again
|
||||
await gesture.down(tester.getCenter(find.byKey(innerKey)));
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(material, paintsExactlyCountTimes(#drawCircle, 1));
|
||||
|
||||
// Cancel
|
||||
await gesture.cancel();
|
||||
await tester.pumpAndSettle();
|
||||
expect(material, paintsNothing);
|
||||
|
||||
// Press again
|
||||
await gesture.down(tester.getCenter(find.byKey(innerKey)));
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(material, paintsExactlyCountTimes(#drawCircle, 1));
|
||||
|
||||
// Use a second pointer to press
|
||||
final TestGesture gesture2 = await tester.startGesture(tester.getCenter(find.byKey(innerKey)), pointer: 2);
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(material, paintsExactlyCountTimes(#drawCircle, 1));
|
||||
await gesture2.up();
|
||||
});
|
||||
|
||||
testWidgets('Reparenting parent should allow both inkwells to show splash afterwards', (WidgetTester tester) async {
|
||||
final GlobalKey middleKey = GlobalKey();
|
||||
final GlobalKey innerKey = GlobalKey();
|
||||
Widget paddedInkWell({Key key, Widget child}) {
|
||||
return InkWell(
|
||||
key: key,
|
||||
onTap: () {},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(50),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
await tester.pumpWidget(
|
||||
Material(
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: Container(
|
||||
width: 200,
|
||||
height: 100,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
paddedInkWell(
|
||||
key: middleKey,
|
||||
child: paddedInkWell(
|
||||
key: innerKey,
|
||||
),
|
||||
),
|
||||
Container(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
final MaterialInkController material = Material.of(tester.element(find.byKey(innerKey)));
|
||||
|
||||
// Press
|
||||
final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byKey(innerKey)), pointer: 1);
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(material, paintsExactlyCountTimes(#drawCircle, 1));
|
||||
|
||||
// Reparent parent
|
||||
await tester.pumpWidget(
|
||||
Material(
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: Container(
|
||||
width: 200,
|
||||
height: 100,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
paddedInkWell(
|
||||
key: innerKey,
|
||||
),
|
||||
paddedInkWell(
|
||||
key: middleKey,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Up
|
||||
await gesture.up();
|
||||
await tester.pumpAndSettle();
|
||||
expect(material, paintsNothing);
|
||||
|
||||
// Press the previous parent
|
||||
await gesture.down(tester.getCenter(find.byKey(middleKey)));
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(material, paintsExactlyCountTimes(#drawCircle, 1));
|
||||
|
||||
// Use a second pointer to press the previous child
|
||||
await tester.startGesture(tester.getCenter(find.byKey(innerKey)), pointer: 2);
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(material, paintsExactlyCountTimes(#drawCircle, 2));
|
||||
});
|
||||
|
||||
testWidgets('Parent inkwell does not block child inkwells from splashes', (WidgetTester tester) async {
|
||||
final GlobalKey middleKey = GlobalKey();
|
||||
final GlobalKey innerKey = GlobalKey();
|
||||
Widget paddedInkWell({Key key, Widget child}) {
|
||||
return InkWell(
|
||||
key: key,
|
||||
onTap: () {},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(50),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
await tester.pumpWidget(
|
||||
Material(
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Center(
|
||||
child: paddedInkWell(
|
||||
child: paddedInkWell(
|
||||
key: middleKey,
|
||||
child: paddedInkWell(
|
||||
key: innerKey,
|
||||
child: Container(width: 50, height: 50),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
final MaterialInkController material = Material.of(tester.element(find.byKey(innerKey)));
|
||||
|
||||
// Press middle
|
||||
await tester.startGesture(tester.getTopLeft(find.byKey(middleKey)) + const Offset(1, 1), pointer: 1);
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(material, paintsExactlyCountTimes(#drawCircle, 1));
|
||||
|
||||
// Press inner
|
||||
await tester.startGesture(tester.getCenter(find.byKey(innerKey)), pointer: 2);
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(material, paintsExactlyCountTimes(#drawCircle, 2));
|
||||
});
|
||||
|
||||
testWidgets('Parent inkwell can count the number of pressed children to prevent splash', (WidgetTester tester) async {
|
||||
final GlobalKey parentKey = GlobalKey();
|
||||
final GlobalKey leftKey = GlobalKey();
|
||||
final GlobalKey rightKey = GlobalKey();
|
||||
await tester.pumpWidget(
|
||||
Material(
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 100,
|
||||
height: 100,
|
||||
child: InkWell(
|
||||
key: parentKey,
|
||||
onTap: () {},
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 100,
|
||||
height: 50,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: 50,
|
||||
height: 50,
|
||||
child: InkWell(
|
||||
key: leftKey,
|
||||
onTap: () {},
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 50,
|
||||
height: 50,
|
||||
child: InkWell(
|
||||
key: rightKey,
|
||||
onTap: () {},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
final MaterialInkController material = Material.of(tester.element(find.byKey(leftKey)));
|
||||
|
||||
final Offset parentPosition = tester.getTopLeft(find.byKey(parentKey)) + const Offset(1, 1);
|
||||
|
||||
// Press left child
|
||||
final TestGesture gesture1 = await tester.startGesture(tester.getCenter(find.byKey(leftKey)), pointer: 1);
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(material, paintsExactlyCountTimes(#drawCircle, 1));
|
||||
|
||||
// Press right child
|
||||
final TestGesture gesture2 = await tester.startGesture(tester.getCenter(find.byKey(rightKey)), pointer: 2);
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(material, paintsExactlyCountTimes(#drawCircle, 2));
|
||||
|
||||
// Press parent
|
||||
final TestGesture gesture3 = await tester.startGesture(parentPosition, pointer: 3);
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(material, paintsExactlyCountTimes(#drawCircle, 2));
|
||||
await gesture3.up();
|
||||
|
||||
// Release left child
|
||||
await gesture1.up();
|
||||
await tester.pumpAndSettle();
|
||||
expect(material, paintsExactlyCountTimes(#drawCircle, 1));
|
||||
|
||||
// Press parent
|
||||
await gesture3.down(parentPosition);
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(material, paintsExactlyCountTimes(#drawCircle, 1));
|
||||
await gesture3.up();
|
||||
|
||||
// Release right child
|
||||
await gesture2.up();
|
||||
await tester.pumpAndSettle();
|
||||
expect(material, paintsExactlyCountTimes(#drawCircle, 0));
|
||||
|
||||
// Press parent
|
||||
await gesture3.down(parentPosition);
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(material, paintsExactlyCountTimes(#drawCircle, 1));
|
||||
await gesture3.up();
|
||||
});
|
||||
|
||||
testWidgets('When ink wells are reparented, the old parent can display splash while the new parent can not', (WidgetTester tester) async {
|
||||
final GlobalKey innerKey = GlobalKey();
|
||||
final GlobalKey leftKey = GlobalKey();
|
||||
final GlobalKey rightKey = GlobalKey();
|
||||
|
||||
Widget doubleInkWellRow({
|
||||
double leftWidth,
|
||||
double rightWidth,
|
||||
Widget leftChild,
|
||||
Widget rightChild,
|
||||
}) {
|
||||
return Material(
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: Container(
|
||||
width: leftWidth+rightWidth,
|
||||
height: 100,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: leftWidth,
|
||||
height: 100,
|
||||
child: InkWell(
|
||||
key: leftKey,
|
||||
onTap: () {},
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: leftWidth,
|
||||
height: 50,
|
||||
child: leftChild,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: rightWidth,
|
||||
height: 100,
|
||||
child: InkWell(
|
||||
key: rightKey,
|
||||
onTap: () {},
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: leftWidth,
|
||||
height: 50,
|
||||
child: rightChild,
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
await tester.pumpWidget(
|
||||
doubleInkWellRow(
|
||||
leftWidth: 110,
|
||||
rightWidth: 90,
|
||||
leftChild: InkWell(
|
||||
key: innerKey,
|
||||
onTap: () {},
|
||||
),
|
||||
),
|
||||
);
|
||||
final MaterialInkController material = Material.of(tester.element(find.byKey(innerKey)));
|
||||
|
||||
// Press inner
|
||||
final TestGesture gesture = await tester.startGesture(const Offset(100, 50), pointer: 1);
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(material, paintsExactlyCountTimes(#drawCircle, 1));
|
||||
|
||||
// Switch side
|
||||
await tester.pumpWidget(
|
||||
doubleInkWellRow(
|
||||
leftWidth: 90,
|
||||
rightWidth: 110,
|
||||
rightChild: InkWell(
|
||||
key: innerKey,
|
||||
onTap: () {},
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(material, paintsExactlyCountTimes(#drawCircle, 0));
|
||||
|
||||
// A second pointer presses inner
|
||||
final TestGesture gesture2 = await tester.startGesture(const Offset(100, 50), pointer: 2);
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(material, paintsExactlyCountTimes(#drawCircle, 1));
|
||||
|
||||
await gesture.up();
|
||||
await gesture2.up();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Press inner
|
||||
await gesture.down(const Offset(100, 50));
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(material, paintsExactlyCountTimes(#drawCircle, 1));
|
||||
|
||||
// Press left
|
||||
await gesture2.down(const Offset(50, 50));
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(material, paintsExactlyCountTimes(#drawCircle, 2));
|
||||
|
||||
await gesture.up();
|
||||
await gesture2.up();
|
||||
});
|
||||
|
||||
testWidgets("Ink wells's splash starts before tap is confirmed and disappear after tap is canceled", (WidgetTester tester) async {
|
||||
final GlobalKey innerKey = GlobalKey();
|
||||
await tester.pumpWidget(
|
||||
Material(
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: GestureDetector(
|
||||
onHorizontalDragStart: (_) {},
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 100,
|
||||
height: 100,
|
||||
child: InkWell(
|
||||
onTap: () {},
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 50,
|
||||
height: 50,
|
||||
child: InkWell(
|
||||
key: innerKey,
|
||||
onTap: () {},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
final MaterialInkController material = Material.of(tester.element(find.byKey(innerKey)));
|
||||
|
||||
// Press
|
||||
final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byKey(innerKey)), pointer: 1);
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(material, paintsExactlyCountTimes(#drawCircle, 1));
|
||||
|
||||
// Scroll upward
|
||||
await gesture.moveBy(const Offset(0, -100));
|
||||
await tester.pumpAndSettle();
|
||||
expect(material, paintsNothing);
|
||||
|
||||
// Up
|
||||
await gesture.up();
|
||||
await tester.pumpAndSettle();
|
||||
expect(material, paintsNothing);
|
||||
|
||||
// Press again
|
||||
await gesture.down(tester.getCenter(find.byKey(innerKey)));
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(material, paintsExactlyCountTimes(#drawCircle, 1));
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue