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 {
BoxShape shape = BoxShape.rectangle,
double? radius,
BorderRadius? borderRadius,
ShapeBorder? customBorder,
super.customBorder,
RectCallback? rectCallback,
super.onRemoved,
Duration fadeDuration = _kDefaultHighlightFadeDuration,
}) : _shape = shape,
_radius = radius,
_borderRadius = borderRadius ?? BorderRadius.zero,
_customBorder = customBorder,
_textDirection = textDirection,
_rectCallback = rectCallback {
_alphaController = AnimationController(duration: fadeDuration, vsync: controller.vsync)
......@@ -69,7 +69,6 @@ class InkHighlight extends InteractiveInkFeature {
final BoxShape _shape;
final double? _radius;
final BorderRadius _borderRadius;
final ShapeBorder? _customBorder;
final RectCallback? _rectCallback;
final TextDirection _textDirection;
......@@ -106,8 +105,8 @@ class InkHighlight extends InteractiveInkFeature {
void _paintHighlight(Canvas canvas, Rect rect, Paint paint) {
canvas.save();
if (_customBorder != null) {
canvas.clipPath(_customBorder!.getOuterPath(rect, textDirection: _textDirection));
if (customBorder != null) {
canvas.clipPath(customBorder!.getOuterPath(rect, textDirection: _textDirection));
}
switch (_shape) {
case BoxShape.circle:
......
......@@ -116,12 +116,11 @@ class InkRipple extends InteractiveInkFeature {
bool containedInkWell = false,
RectCallback? rectCallback,
BorderRadius? borderRadius,
ShapeBorder? customBorder,
super.customBorder,
double? radius,
super.onRemoved,
}) : _position = position,
_borderRadius = borderRadius ?? BorderRadius.zero,
_customBorder = customBorder,
_textDirection = textDirection,
_targetRadius = radius ?? _getTargetRadius(referenceBox, containedInkWell, rectCallback, position),
_clipCallback = _getClipCallback(referenceBox, containedInkWell, rectCallback),
......@@ -166,7 +165,6 @@ class InkRipple extends InteractiveInkFeature {
final Offset _position;
final BorderRadius _borderRadius;
final ShapeBorder? _customBorder;
final double _targetRadius;
final RectCallback? _clipCallback;
final TextDirection _textDirection;
......@@ -245,7 +243,7 @@ class InkRipple extends InteractiveInkFeature {
center: center,
textDirection: _textDirection,
radius: _radius.value,
customBorder: _customBorder,
customBorder: customBorder,
borderRadius: _borderRadius,
clipCallback: _clipCallback,
);
......
......@@ -106,7 +106,7 @@ class InkSparkle extends InteractiveInkFeature {
bool containedInkWell = true,
RectCallback? rectCallback,
BorderRadius? borderRadius,
ShapeBorder? customBorder,
super.customBorder,
double? radius,
super.onRemoved,
double? turbulenceSeed,
......@@ -114,7 +114,6 @@ class InkSparkle extends InteractiveInkFeature {
_color = color,
_position = position,
_borderRadius = borderRadius ?? BorderRadius.zero,
_customBorder = customBorder,
_textDirection = textDirection,
_targetRadius = (radius ?? _getTargetRadius(
referenceBox,
......@@ -236,7 +235,6 @@ class InkSparkle extends InteractiveInkFeature {
final Color _color;
final Offset _position;
final BorderRadius _borderRadius;
final ShapeBorder? _customBorder;
final double _targetRadius;
final RectCallback? _clipCallback;
final TextDirection _textDirection;
......@@ -292,7 +290,7 @@ class InkSparkle extends InteractiveInkFeature {
canvas: canvas,
clipCallback: _clipCallback!,
textDirection: _textDirection,
customBorder: _customBorder,
customBorder: customBorder,
borderRadius: _borderRadius,
);
}
......
......@@ -122,12 +122,11 @@ class InkSplash extends InteractiveInkFeature {
bool containedInkWell = false,
RectCallback? rectCallback,
BorderRadius? borderRadius,
ShapeBorder? customBorder,
super.customBorder,
double? radius,
super.onRemoved,
}) : _position = position,
_borderRadius = borderRadius ?? BorderRadius.zero,
_customBorder = customBorder,
_targetRadius = radius ?? _getTargetRadius(referenceBox, containedInkWell, rectCallback, position!),
_clipCallback = _getClipCallback(referenceBox, containedInkWell, rectCallback),
_repositionToReferenceBox = !containedInkWell,
......@@ -153,7 +152,6 @@ class InkSplash extends InteractiveInkFeature {
final Offset? _position;
final BorderRadius _borderRadius;
final ShapeBorder? _customBorder;
final double _targetRadius;
final RectCallback? _clipCallback;
final bool _repositionToReferenceBox;
......@@ -211,7 +209,7 @@ class InkSplash extends InteractiveInkFeature {
center: center!,
textDirection: _textDirection,
radius: _radius.value,
customBorder: _customBorder,
customBorder: customBorder,
borderRadius: _borderRadius,
clipCallback: _clipCallback,
);
......
......@@ -41,8 +41,10 @@ abstract class InteractiveInkFeature extends InkFeature {
required super.controller,
required super.referenceBox,
required Color color,
ShapeBorder? customBorder,
super.onRemoved,
}) : _color = color;
}) : _color = color,
_customBorder = customBorder;
/// Called when the user input that triggered this feature's appearance was confirmed.
///
......@@ -67,6 +69,17 @@ abstract class InteractiveInkFeature extends InkFeature {
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].
///
/// The [transform] argument is the [Matrix4] transform that typically
......@@ -854,10 +867,9 @@ class _InkResponseState extends State<_InkResponseStateWidget>
}
initStatesController();
}
if (widget.customBorder != oldWidget.customBorder ||
widget.radius != oldWidget.radius ||
widget.borderRadius != oldWidget.borderRadius ||
widget.highlightShape != oldWidget.highlightShape) {
if (widget.radius != oldWidget.radius ||
widget.highlightShape != oldWidget.highlightShape ||
widget.borderRadius != oldWidget.borderRadius) {
final InkHighlight? hoverHighlight = _highlights[_HighlightType.hover];
if (hoverHighlight != null) {
hoverHighlight.dispose();
......@@ -869,6 +881,9 @@ class _InkResponseState extends State<_InkResponseStateWidget>
// Do not call updateFocusHighlights() here because it is called below
}
}
if (widget.customBorder != oldWidget.customBorder) {
_updateHighlightsAndSplashes();
}
if (enabled != isWidgetEnabled(oldWidget)) {
statesController.update(MaterialState.disabled, !enabled);
if (!enabled) {
......@@ -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 RenderBox referenceBox = context.findRenderObject()! as RenderBox;
final Offset position = referenceBox.globalToLocal(globalPosition);
......@@ -1103,7 +1134,7 @@ class _InkResponseState extends State<_InkResponseStateWidget>
globalPosition = details!.globalPosition;
}
statesController.update(MaterialState.pressed, true); // ... before creating the splash
final InteractiveInkFeature splash = _createInkFeature(globalPosition);
final InteractiveInkFeature splash = _createSplash(globalPosition);
_splashes ??= HashSet<InteractiveInkFeature>();
_splashes!.add(splash);
_currentSplash?.cancel();
......
......@@ -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 {
FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTouch;
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