blob: 46355c30f7e4add2481bd3e6bc7fa9bd86b19f3b [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'ink_well.dart' show InteractiveInkFeature;
import 'material.dart';
const Duration _kDefaultHighlightFadeDuration = Duration(milliseconds: 200);
/// A visual emphasis on a part of a [Material] receiving user interaction.
///
/// This object is rarely created directly. Instead of creating an ink highlight
/// directly, consider using an [InkResponse] or [InkWell] widget, which uses
/// gestures (such as tap and long-press) to trigger ink highlights.
///
/// See also:
///
/// * [InkResponse], which uses gestures to trigger ink highlights and ink
/// splashes in the parent [Material].
/// * [InkWell], which is a rectangular [InkResponse] (the most common type of
/// ink response).
/// * [Material], which is the widget on which the ink highlight is painted.
/// * [InkSplash], which is an ink feature that shows a reaction to user input
/// on a [Material].
class InkHighlight extends InteractiveInkFeature {
/// Begin a highlight animation.
///
/// The [controller] argument is typically obtained via
/// `Material.of(context)`.
///
/// If a `rectCallback` is given, then it provides the highlight rectangle,
/// otherwise, the highlight rectangle is coincident with the [referenceBox].
///
/// When the highlight is removed, `onRemoved` will be called.
InkHighlight({
@required MaterialInkController controller,
@required RenderBox referenceBox,
@required Color color,
@required TextDirection textDirection,
BoxShape shape = BoxShape.rectangle,
BorderRadius borderRadius,
ShapeBorder customBorder,
RectCallback rectCallback,
VoidCallback onRemoved,
Duration fadeDuration = _kDefaultHighlightFadeDuration,
}) : assert(color != null),
assert(shape != null),
assert(textDirection != null),
assert(fadeDuration != null),
_shape = shape,
_borderRadius = borderRadius ?? BorderRadius.zero,
_customBorder = customBorder,
_textDirection = textDirection,
_rectCallback = rectCallback,
super(controller: controller, referenceBox: referenceBox, color: color, onRemoved: onRemoved) {
_alphaController = AnimationController(duration: fadeDuration, vsync: controller.vsync)
..addListener(controller.markNeedsPaint)
..addStatusListener(_handleAlphaStatusChanged)
..forward();
_alpha = _alphaController.drive(IntTween(
begin: 0,
end: color.alpha,
));
controller.addInkFeature(this);
}
final BoxShape _shape;
final BorderRadius _borderRadius;
final ShapeBorder _customBorder;
final RectCallback _rectCallback;
final TextDirection _textDirection;
Animation<int> _alpha;
AnimationController _alphaController;
/// Whether this part of the material is being visually emphasized.
bool get active => _active;
bool _active = true;
/// Start visually emphasizing this part of the material.
void activate() {
_active = true;
_alphaController.forward();
}
/// Stop visually emphasizing this part of the material.
void deactivate() {
_active = false;
_alphaController.reverse();
}
void _handleAlphaStatusChanged(AnimationStatus status) {
if (status == AnimationStatus.dismissed && !_active)
dispose();
}
@override
void dispose() {
_alphaController.dispose();
super.dispose();
}
void _paintHighlight(Canvas canvas, Rect rect, Paint paint) {
assert(_shape != null);
canvas.save();
if (_customBorder != null) {
canvas.clipPath(_customBorder.getOuterPath(rect, textDirection: _textDirection));
}
switch (_shape) {
case BoxShape.circle:
canvas.drawCircle(rect.center, Material.defaultSplashRadius, paint);
break;
case BoxShape.rectangle:
if (_borderRadius != BorderRadius.zero) {
final RRect clipRRect = RRect.fromRectAndCorners(
rect,
topLeft: _borderRadius.topLeft, topRight: _borderRadius.topRight,
bottomLeft: _borderRadius.bottomLeft, bottomRight: _borderRadius.bottomRight,
);
canvas.drawRRect(clipRRect, paint);
} else {
canvas.drawRect(rect, paint);
}
break;
}
canvas.restore();
}
@override
void paintFeature(Canvas canvas, Matrix4 transform) {
final Paint paint = Paint()..color = color.withAlpha(_alpha.value);
final Offset originOffset = MatrixUtils.getAsTranslation(transform);
final Rect rect = _rectCallback != null ? _rectCallback() : Offset.zero & referenceBox.size;
if (originOffset == null) {
canvas.save();
canvas.transform(transform.storage);
_paintHighlight(canvas, rect, paint);
canvas.restore();
} else {
_paintHighlight(canvas, rect.shift(originOffset), paint);
}
}
}