Unverified Commit 06adde0b authored by Tong Mu's avatar Tong Mu Committed by GitHub

Nested InkWells only show the innermost splash (#56611)

parent 0786f29f
...@@ -174,6 +174,29 @@ abstract class InteractiveInkFeatureFactory { ...@@ -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 /// 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. /// can be configured to clip splashes that extend outside its bounds or not.
/// ///
...@@ -255,7 +278,7 @@ abstract class InteractiveInkFeatureFactory { ...@@ -255,7 +278,7 @@ abstract class InteractiveInkFeatureFactory {
/// * [GestureDetector], for listening for gestures without ink splashes. /// * [GestureDetector], for listening for gestures without ink splashes.
/// * [RaisedButton] and [FlatButton], two kinds of buttons in material design. /// * [RaisedButton] and [FlatButton], two kinds of buttons in material design.
/// * [IconButton], which combines [InkResponse] with an [Icon]. /// * [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. /// Creates an area of a [Material] that responds to touch.
/// ///
/// Must have an ancestor [Material] widget in which to cause ink reactions. /// Must have an ancestor [Material] widget in which to cause ink reactions.
...@@ -508,6 +531,40 @@ class InkResponse extends StatefulWidget { ...@@ -508,6 +531,40 @@ class InkResponse extends StatefulWidget {
/// slightly more efficient). /// slightly more efficient).
RectCallback getRectCallback(RenderBox referenceBox) => null; 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 /// Asserts that the given context satisfies the prerequisites for
/// this class. /// this class.
/// ///
...@@ -521,9 +578,74 @@ class InkResponse extends StatefulWidget { ...@@ -521,9 +578,74 @@ class InkResponse extends StatefulWidget {
assert(debugCheckHasDirectionality(context)); assert(debugCheckHasDirectionality(context));
return true; 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 @override
_InkResponseState<InkResponse> createState() => _InkResponseState<InkResponse>(); _InkResponseState createState() => _InkResponseState();
@override @override
void debugFillProperties(DiagnosticPropertiesBuilder properties) { void debugFillProperties(DiagnosticPropertiesBuilder properties) {
...@@ -554,7 +676,9 @@ enum _HighlightType { ...@@ -554,7 +676,9 @@ enum _HighlightType {
focus, 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; Set<InteractiveInkFeature> _splashes;
InteractiveInkFeature _currentSplash; InteractiveInkFeature _currentSplash;
bool _hovering = false; bool _hovering = false;
...@@ -563,6 +687,23 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe ...@@ -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; 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) { void _handleAction(ActivateIntent intent) {
_startSplash(context: context); _startSplash(context: context);
_handleTap(context); _handleTap(context);
...@@ -578,7 +719,7 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe ...@@ -578,7 +719,7 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe
} }
@override @override
void didUpdateWidget(T oldWidget) { void didUpdateWidget(_InnerInkResponse oldWidget) {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
if (_isWidgetEnabled(widget) != _isWidgetEnabled(oldWidget)) { if (_isWidgetEnabled(widget) != _isWidgetEnabled(oldWidget)) {
_handleHoverChange(_hovering); _handleHoverChange(_hovering);
...@@ -628,6 +769,9 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe ...@@ -628,6 +769,9 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe
updateKeepAlive(); updateKeepAlive();
} }
if (type == _HighlightType.pressed) {
widget.parentState?.markChildInkResponsePressed(this, value);
}
if (value == (highlight != null && highlight.active)) if (value == (highlight != null && highlight.active))
return; return;
if (value) { if (value) {
...@@ -737,6 +881,8 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe ...@@ -737,6 +881,8 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe
} }
void _handleTapDown(TapDownDetails details) { void _handleTapDown(TapDownDetails details) {
if (_anyChildInkResponsePressed)
return;
_startSplash(details: details); _startSplash(details: details);
if (widget.onTapDown != null) { if (widget.onTapDown != null) {
widget.onTapDown(details); widget.onTapDown(details);
...@@ -813,10 +959,11 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe ...@@ -813,10 +959,11 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe
_highlights[highlight]?.dispose(); _highlights[highlight]?.dispose();
_highlights[highlight] = null; _highlights[highlight] = null;
} }
widget.parentState?.markChildInkResponsePressed(this, false);
super.deactivate(); super.deactivate();
} }
bool _isWidgetEnabled(InkResponse widget) { bool _isWidgetEnabled(_InnerInkResponse widget) {
return widget.onTap != null || widget.onDoubleTap != null || widget.onLongPress != null; return widget.onTap != null || widget.onDoubleTap != null || widget.onLongPress != null;
} }
...@@ -840,7 +987,9 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe ...@@ -840,7 +987,9 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe
} }
_currentSplash?.color = widget.splashColor ?? Theme.of(context).splashColor; _currentSplash?.color = widget.splashColor ?? Theme.of(context).splashColor;
final bool canRequestFocus = enabled && widget.canRequestFocus; final bool canRequestFocus = enabled && widget.canRequestFocus;
return Actions( return _ParentInkResponseProvider(
state: this,
child: Actions(
actions: _actionMap, actions: _actionMap,
child: Focus( child: Focus(
focusNode: widget.focusNode, focusNode: widget.focusNode,
...@@ -862,6 +1011,7 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe ...@@ -862,6 +1011,7 @@ class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKe
), ),
), ),
), ),
),
); );
} }
} }
......
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