Unverified Commit 94f8772c authored by Bruno Leroux's avatar Bruno Leroux Committed by GitHub

Make InkInteractiveFeature customBorder updatable (#123536)

parent 48bb12df
...@@ -44,14 +44,14 @@ class InkHighlight extends InteractiveInkFeature { ...@@ -44,14 +44,14 @@ class InkHighlight extends InteractiveInkFeature {
BoxShape shape = BoxShape.rectangle, BoxShape shape = BoxShape.rectangle,
double? radius, double? radius,
BorderRadius? borderRadius, BorderRadius? borderRadius,
ShapeBorder? customBorder, super.customBorder,
RectCallback? rectCallback, RectCallback? rectCallback,
super.onRemoved, super.onRemoved,
Duration fadeDuration = _kDefaultHighlightFadeDuration, Duration fadeDuration = _kDefaultHighlightFadeDuration,
}) : _shape = shape, }) : _shape = shape,
_radius = radius, _radius = radius,
_borderRadius = borderRadius ?? BorderRadius.zero, _borderRadius = borderRadius ?? BorderRadius.zero,
_customBorder = customBorder,
_textDirection = textDirection, _textDirection = textDirection,
_rectCallback = rectCallback { _rectCallback = rectCallback {
_alphaController = AnimationController(duration: fadeDuration, vsync: controller.vsync) _alphaController = AnimationController(duration: fadeDuration, vsync: controller.vsync)
...@@ -69,7 +69,6 @@ class InkHighlight extends InteractiveInkFeature { ...@@ -69,7 +69,6 @@ class InkHighlight extends InteractiveInkFeature {
final BoxShape _shape; final BoxShape _shape;
final double? _radius; final double? _radius;
final BorderRadius _borderRadius; final BorderRadius _borderRadius;
final ShapeBorder? _customBorder;
final RectCallback? _rectCallback; final RectCallback? _rectCallback;
final TextDirection _textDirection; final TextDirection _textDirection;
...@@ -106,8 +105,8 @@ class InkHighlight extends InteractiveInkFeature { ...@@ -106,8 +105,8 @@ class InkHighlight extends InteractiveInkFeature {
void _paintHighlight(Canvas canvas, Rect rect, Paint paint) { void _paintHighlight(Canvas canvas, Rect rect, Paint paint) {
canvas.save(); canvas.save();
if (_customBorder != null) { if (customBorder != null) {
canvas.clipPath(_customBorder!.getOuterPath(rect, textDirection: _textDirection)); canvas.clipPath(customBorder!.getOuterPath(rect, textDirection: _textDirection));
} }
switch (_shape) { switch (_shape) {
case BoxShape.circle: case BoxShape.circle:
......
...@@ -116,12 +116,11 @@ class InkRipple extends InteractiveInkFeature { ...@@ -116,12 +116,11 @@ class InkRipple extends InteractiveInkFeature {
bool containedInkWell = false, bool containedInkWell = false,
RectCallback? rectCallback, RectCallback? rectCallback,
BorderRadius? borderRadius, BorderRadius? borderRadius,
ShapeBorder? customBorder, super.customBorder,
double? radius, double? radius,
super.onRemoved, super.onRemoved,
}) : _position = position, }) : _position = position,
_borderRadius = borderRadius ?? BorderRadius.zero, _borderRadius = borderRadius ?? BorderRadius.zero,
_customBorder = customBorder,
_textDirection = textDirection, _textDirection = textDirection,
_targetRadius = radius ?? _getTargetRadius(referenceBox, containedInkWell, rectCallback, position), _targetRadius = radius ?? _getTargetRadius(referenceBox, containedInkWell, rectCallback, position),
_clipCallback = _getClipCallback(referenceBox, containedInkWell, rectCallback), _clipCallback = _getClipCallback(referenceBox, containedInkWell, rectCallback),
...@@ -166,7 +165,6 @@ class InkRipple extends InteractiveInkFeature { ...@@ -166,7 +165,6 @@ class InkRipple extends InteractiveInkFeature {
final Offset _position; final Offset _position;
final BorderRadius _borderRadius; final BorderRadius _borderRadius;
final ShapeBorder? _customBorder;
final double _targetRadius; final double _targetRadius;
final RectCallback? _clipCallback; final RectCallback? _clipCallback;
final TextDirection _textDirection; final TextDirection _textDirection;
...@@ -245,7 +243,7 @@ class InkRipple extends InteractiveInkFeature { ...@@ -245,7 +243,7 @@ class InkRipple extends InteractiveInkFeature {
center: center, center: center,
textDirection: _textDirection, textDirection: _textDirection,
radius: _radius.value, radius: _radius.value,
customBorder: _customBorder, customBorder: customBorder,
borderRadius: _borderRadius, borderRadius: _borderRadius,
clipCallback: _clipCallback, clipCallback: _clipCallback,
); );
......
...@@ -106,7 +106,7 @@ class InkSparkle extends InteractiveInkFeature { ...@@ -106,7 +106,7 @@ class InkSparkle extends InteractiveInkFeature {
bool containedInkWell = true, bool containedInkWell = true,
RectCallback? rectCallback, RectCallback? rectCallback,
BorderRadius? borderRadius, BorderRadius? borderRadius,
ShapeBorder? customBorder, super.customBorder,
double? radius, double? radius,
super.onRemoved, super.onRemoved,
double? turbulenceSeed, double? turbulenceSeed,
...@@ -114,7 +114,6 @@ class InkSparkle extends InteractiveInkFeature { ...@@ -114,7 +114,6 @@ class InkSparkle extends InteractiveInkFeature {
_color = color, _color = color,
_position = position, _position = position,
_borderRadius = borderRadius ?? BorderRadius.zero, _borderRadius = borderRadius ?? BorderRadius.zero,
_customBorder = customBorder,
_textDirection = textDirection, _textDirection = textDirection,
_targetRadius = (radius ?? _getTargetRadius( _targetRadius = (radius ?? _getTargetRadius(
referenceBox, referenceBox,
...@@ -236,7 +235,6 @@ class InkSparkle extends InteractiveInkFeature { ...@@ -236,7 +235,6 @@ class InkSparkle extends InteractiveInkFeature {
final Color _color; final Color _color;
final Offset _position; final Offset _position;
final BorderRadius _borderRadius; final BorderRadius _borderRadius;
final ShapeBorder? _customBorder;
final double _targetRadius; final double _targetRadius;
final RectCallback? _clipCallback; final RectCallback? _clipCallback;
final TextDirection _textDirection; final TextDirection _textDirection;
...@@ -292,7 +290,7 @@ class InkSparkle extends InteractiveInkFeature { ...@@ -292,7 +290,7 @@ class InkSparkle extends InteractiveInkFeature {
canvas: canvas, canvas: canvas,
clipCallback: _clipCallback!, clipCallback: _clipCallback!,
textDirection: _textDirection, textDirection: _textDirection,
customBorder: _customBorder, customBorder: customBorder,
borderRadius: _borderRadius, borderRadius: _borderRadius,
); );
} }
......
...@@ -122,12 +122,11 @@ class InkSplash extends InteractiveInkFeature { ...@@ -122,12 +122,11 @@ class InkSplash extends InteractiveInkFeature {
bool containedInkWell = false, bool containedInkWell = false,
RectCallback? rectCallback, RectCallback? rectCallback,
BorderRadius? borderRadius, BorderRadius? borderRadius,
ShapeBorder? customBorder, super.customBorder,
double? radius, double? radius,
super.onRemoved, super.onRemoved,
}) : _position = position, }) : _position = position,
_borderRadius = borderRadius ?? BorderRadius.zero, _borderRadius = borderRadius ?? BorderRadius.zero,
_customBorder = customBorder,
_targetRadius = radius ?? _getTargetRadius(referenceBox, containedInkWell, rectCallback, position!), _targetRadius = radius ?? _getTargetRadius(referenceBox, containedInkWell, rectCallback, position!),
_clipCallback = _getClipCallback(referenceBox, containedInkWell, rectCallback), _clipCallback = _getClipCallback(referenceBox, containedInkWell, rectCallback),
_repositionToReferenceBox = !containedInkWell, _repositionToReferenceBox = !containedInkWell,
...@@ -153,7 +152,6 @@ class InkSplash extends InteractiveInkFeature { ...@@ -153,7 +152,6 @@ class InkSplash extends InteractiveInkFeature {
final Offset? _position; final Offset? _position;
final BorderRadius _borderRadius; final BorderRadius _borderRadius;
final ShapeBorder? _customBorder;
final double _targetRadius; final double _targetRadius;
final RectCallback? _clipCallback; final RectCallback? _clipCallback;
final bool _repositionToReferenceBox; final bool _repositionToReferenceBox;
...@@ -211,7 +209,7 @@ class InkSplash extends InteractiveInkFeature { ...@@ -211,7 +209,7 @@ class InkSplash extends InteractiveInkFeature {
center: center!, center: center!,
textDirection: _textDirection, textDirection: _textDirection,
radius: _radius.value, radius: _radius.value,
customBorder: _customBorder, customBorder: customBorder,
borderRadius: _borderRadius, borderRadius: _borderRadius,
clipCallback: _clipCallback, clipCallback: _clipCallback,
); );
......
...@@ -41,8 +41,10 @@ abstract class InteractiveInkFeature extends InkFeature { ...@@ -41,8 +41,10 @@ abstract class InteractiveInkFeature extends InkFeature {
required super.controller, required super.controller,
required super.referenceBox, required super.referenceBox,
required Color color, required Color color,
ShapeBorder? customBorder,
super.onRemoved, super.onRemoved,
}) : _color = color; }) : _color = color,
_customBorder = customBorder;
/// Called when the user input that triggered this feature's appearance was confirmed. /// Called when the user input that triggered this feature's appearance was confirmed.
/// ///
...@@ -67,6 +69,17 @@ abstract class InteractiveInkFeature extends InkFeature { ...@@ -67,6 +69,17 @@ abstract class InteractiveInkFeature extends InkFeature {
controller.markNeedsPaint(); controller.markNeedsPaint();
} }
/// The ink's optional custom border.
ShapeBorder? get customBorder => _customBorder;
ShapeBorder? _customBorder;
set customBorder(ShapeBorder? value) {
if (value == _customBorder) {
return;
}
_customBorder = value;
controller.markNeedsPaint();
}
/// Draws an ink splash or ink ripple on the passed in [Canvas]. /// Draws an ink splash or ink ripple on the passed in [Canvas].
/// ///
/// The [transform] argument is the [Matrix4] transform that typically /// The [transform] argument is the [Matrix4] transform that typically
...@@ -854,10 +867,9 @@ class _InkResponseState extends State<_InkResponseStateWidget> ...@@ -854,10 +867,9 @@ class _InkResponseState extends State<_InkResponseStateWidget>
} }
initStatesController(); initStatesController();
} }
if (widget.customBorder != oldWidget.customBorder || if (widget.radius != oldWidget.radius ||
widget.radius != oldWidget.radius || widget.highlightShape != oldWidget.highlightShape ||
widget.borderRadius != oldWidget.borderRadius || widget.borderRadius != oldWidget.borderRadius) {
widget.highlightShape != oldWidget.highlightShape) {
final InkHighlight? hoverHighlight = _highlights[_HighlightType.hover]; final InkHighlight? hoverHighlight = _highlights[_HighlightType.hover];
if (hoverHighlight != null) { if (hoverHighlight != null) {
hoverHighlight.dispose(); hoverHighlight.dispose();
...@@ -869,6 +881,9 @@ class _InkResponseState extends State<_InkResponseStateWidget> ...@@ -869,6 +881,9 @@ class _InkResponseState extends State<_InkResponseStateWidget>
// Do not call updateFocusHighlights() here because it is called below // Do not call updateFocusHighlights() here because it is called below
} }
} }
if (widget.customBorder != oldWidget.customBorder) {
_updateHighlightsAndSplashes();
}
if (enabled != isWidgetEnabled(oldWidget)) { if (enabled != isWidgetEnabled(oldWidget)) {
statesController.update(MaterialState.disabled, !enabled); statesController.update(MaterialState.disabled, !enabled);
if (!enabled) { if (!enabled) {
...@@ -986,7 +1001,23 @@ class _InkResponseState extends State<_InkResponseStateWidget> ...@@ -986,7 +1001,23 @@ class _InkResponseState extends State<_InkResponseStateWidget>
} }
} }
InteractiveInkFeature _createInkFeature(Offset globalPosition) { void _updateHighlightsAndSplashes() {
for (final InkHighlight? highlight in _highlights.values) {
if (highlight != null) {
highlight.customBorder = widget.customBorder;
}
}
if (_currentSplash != null) {
_currentSplash!.customBorder = widget.customBorder;
}
if (_splashes != null && _splashes!.isNotEmpty) {
for (final InteractiveInkFeature inkFeature in _splashes!) {
inkFeature.customBorder = widget.customBorder;
}
}
}
InteractiveInkFeature _createSplash(Offset globalPosition) {
final MaterialInkController inkController = Material.of(context); final MaterialInkController inkController = Material.of(context);
final RenderBox referenceBox = context.findRenderObject()! as RenderBox; final RenderBox referenceBox = context.findRenderObject()! as RenderBox;
final Offset position = referenceBox.globalToLocal(globalPosition); final Offset position = referenceBox.globalToLocal(globalPosition);
...@@ -1103,7 +1134,7 @@ class _InkResponseState extends State<_InkResponseStateWidget> ...@@ -1103,7 +1134,7 @@ class _InkResponseState extends State<_InkResponseStateWidget>
globalPosition = details!.globalPosition; globalPosition = details!.globalPosition;
} }
statesController.update(MaterialState.pressed, true); // ... before creating the splash statesController.update(MaterialState.pressed, true); // ... before creating the splash
final InteractiveInkFeature splash = _createInkFeature(globalPosition); final InteractiveInkFeature splash = _createSplash(globalPosition);
_splashes ??= HashSet<InteractiveInkFeature>(); _splashes ??= HashSet<InteractiveInkFeature>();
_splashes!.add(splash); _splashes!.add(splash);
_currentSplash?.cancel(); _currentSplash?.cancel();
......
...@@ -816,6 +816,91 @@ testWidgets('InkResponse radius can be updated', (WidgetTester tester) async { ...@@ -816,6 +816,91 @@ testWidgets('InkResponse radius can be updated', (WidgetTester tester) async {
); );
}); });
testWidgets('InkWell splash customBorder can be updated', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/121626.
final FocusNode focusNode = FocusNode(debugLabel: 'Ink Focus');
Widget boilerplate(BorderRadius borderRadius) {
return Material(
child: Directionality(
textDirection: TextDirection.ltr,
child: Align(
alignment: Alignment.topLeft,
child: SizedBox(
width: 100,
height: 100,
child: MouseRegion(
child: InkWell(
focusNode: focusNode,
customBorder: RoundedRectangleBorder(borderRadius: borderRadius),
onTap: () { },
),
),
),
),
),
);
}
await tester.pumpWidget(boilerplate(BorderRadius.circular(20)));
await tester.pumpAndSettle();
final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
expect(inkFeatures, paintsExactlyCountTimes(#clipPath, 0));
final TestGesture gesture = await tester.startGesture(tester.getRect(find.byType(InkWell)).center);
await tester.pump(const Duration(milliseconds: 200)); // Unconfirmed splash is well underway.
expect(inkFeatures, paintsExactlyCountTimes(#clipPath, 2)); // Splash and highlight.
const Rect expectedClipRect = Rect.fromLTRB(0, 0, 100, 100);
Path expectedClipPath = Path()
..addRRect(RRect.fromRectAndRadius(
expectedClipRect,
const Radius.circular(20),
));
// Check that the splash and the highlight are correctly clipped.
expect(
inkFeatures,
paints
..clipPath(pathMatcher: coversSameAreaAs(
expectedClipPath,
areaToCompare: expectedClipRect.inflate(20.0),
sampleSize: 100,
))
..clipPath(pathMatcher: coversSameAreaAs(
expectedClipPath,
areaToCompare: expectedClipRect.inflate(20.0),
sampleSize: 100,
)),
);
await tester.pumpWidget(boilerplate(BorderRadius.circular(40)));
await tester.pumpAndSettle();
expectedClipPath = Path()
..addRRect(RRect.fromRectAndRadius(
expectedClipRect,
const Radius.circular(40),
));
// Check that the splash and the highlight are correctly clipped.
expect(
inkFeatures,
paints
..clipPath(pathMatcher: coversSameAreaAs(
expectedClipPath,
areaToCompare: expectedClipRect.inflate(20.0),
sampleSize: 100,
))
..clipPath(pathMatcher: coversSameAreaAs(
expectedClipPath,
areaToCompare: expectedClipRect.inflate(20.0),
sampleSize: 100,
)),
);
await gesture.up();
});
testWidgets("ink response doesn't change color on focus when on touch device", (WidgetTester tester) async { testWidgets("ink response doesn't change color on focus when on touch device", (WidgetTester tester) async {
FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTouch; FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTouch;
final FocusNode focusNode = FocusNode(debugLabel: 'Ink Focus'); final FocusNode focusNode = FocusNode(debugLabel: 'Ink Focus');
......
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