mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Make InkInteractiveFeature customBorder updatable (#123536)
This commit is contained in:
parent
48bb12dfbe
commit
94f8772cea
@ -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');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user