概述
Ink翻译为墨水,墨汁的意思,就是点击某一组件时具有水波纹的效果。Android中MaterialButton中有类似的效果,Flutter中和Ink相关的有Ink、InkWell、InkFeature、Ink.image、InkSplash、InkRipple、InkResponse、InkDecoration、InkHighlight,有些是组件,有些是效果,有些是属性。
Ink
Ink的构造如下:
Ink({
Key? key,
this.padding,
Color? color,
Decoration? decoration,
this.width,
this.height,
this.child,
})
Ink和Container最大的不同在于使用InkWell时,Ink可以展示点击出现的水波纹的效果,而Contaier没有任何点击效果。除此之外,Ink和Container相似,只是比Container少了一些参数而已,若无明确指明,官方和民间都建议使用Container。Ink一般搭配InkWell使用,主要是设置InkWell的样式。decoration和Container中的decoration一样,使用BoxDecoration就好,若BoxDecoration不是很清楚,可以看看Flutter-Container详解。
效果 | 说明 |
没有使用decoration | |
使用decoration设置圆角 | |
使用Container |
通过观察效果图可以发现,使用Container确实无法展示InkWell的点击效果。当使用decoration设置圆角的时候,虽然按钮形状是由圆角的,但水波纹扩散的范围依然是矩形,想要使水波纹扩散的范围是圆角矩阵,在InkWell中把borderRadius的圆角弧度设置为和decoration中圆角弧度一致即可。当在decoration中设置了color,InkWell中自带的color就不要设置了,否则报错。
InkWell,InkResponse
class InkWell extends InkResponse { ... }
InkWell属于完全继承InkResponse,没有一点自己属于自己的属性,只是比InkResponse 少了一些属性,估计那些属性不常用,所以InkWell相对简单,才会导致InkWell使用的比较多,而InkResponse使用的相对较少吧。InkResponse的构造如下:
const InkResponse({
Key? key,
this.child,
this.onTap,
this.onTapDown,
this.onTapCancel,
this.onDoubleTap,
this.onLongPress,
this.onHighlightChanged,
this.onHover,
this.mouseCursor,
this.containedInkWell = false,
this.highlightShape = BoxShape.circle,
this.radius,
this.borderRadius,
this.customBorder,
this.focusColor,
this.hoverColor,
this.highlightColor,
this.overlayColor,
this.splashColor,
this.splashFactory,
this.enableFeedback = true,
this.excludeFromSemantics = false,
this.focusNode,
this.canRequestFocus = true,
this.onFocusChange,
this.autofocus = false,
})
下表则对其中属性一一进行说明和比较:
属性 | 说明 |
child | 子组件 |
onTap | 点击 |
onTapDown | 按下 |
onTapCancel | 点击取消 |
onDoubleTap | 双击 |
onLongPress | 长按 |
onHighlightChanged | 高亮色变化 |
onHover | 悬停改变 |
mouseCursor | 光标 |
containedInkWell | 范围裁切 |
highlightShape | 高亮色形状 |
radius | 水波纹半径 |
borderRadius | 边框弧度 |
customBorder | 边框 |
focusColor | 焦点色 |
hoverColor | 悬停色 |
highlightColor | 高亮色 |
overlayColor | 覆盖色 |
splashColor | 散点色 |
splashFactory | splash形状 |
enableFeedback | 是否反馈 |
excludeFromSemantics | 是否删除语义 |
focusNode | 焦点结点 |
canRequestFocus | 是否可以请求焦点 |
onFocusChange | 焦点改变 |
autofocus | 自动获取焦点 |
针对这些属性直接翻译很多不通顺,也不好说清楚,没有效果图直观,下面则对部分属性进行效果展示
- onTap
点击事件,当用户点击此组件时,onTap会被调用,通常会在onTap中进行点击事件处理。 - onTapDown
按下动作,准确来讲应该是当用户执行点击动作,手机接触到屏幕的一瞬间,该方法会被调用,通常用来对用户动作进行监听来执行较为复杂的交互,比如特效、游戏、动画、计算等以手机接触为起点。 - onTapCancel
点击取消,当用户点击在屏幕上,手指还没有离开屏幕,此时又后悔了,不想进行目前的操作了,手指随意滑动一下后松开,该方法会被调用,代表此次点击取消了,无效。 - onDoubleTap
双击,连续点击两次,时间尽可能短,此时该方法会被调用。 - onLongPress
长按,手指按下之后在屏幕上停留一段时间,此时该方法会被调用。
-highlightColor 、onHighlightChanged 、highlightShape
当设置高亮色,点击时onHighlightChanged会被调用。当在InkWell中设置的时候:
当在InkResponse中设置的时候:
对比发现,同意属性在不同组件中设置的时候,效果不同。highlightShape默认为BoxShape.circle,也可以选择BoxShape.rectangle,当InkResponse中设置BoxShape.rectangle时效果如下:
效果和InkWell类似,因为InkWell的highlightShape(InkWell中该属性不可设置)为BoxShape.rectangle,而InkResponse的highlightShape默认为BoxShape.circle,效果的差异主要是BoxShape形状的差异。 - hoverColor,onHover
悬停状态是由用户使用光标在交互式元素上暂停时发起的。它们可以应用于所有的交互组件,并且应该被淡化以避免从内容上分散注意力。它可以应用于整个组件、组件内的元素,或者作为组件部分上的圆形形状。悬停状态可以由以下组件继承:按钮、切换按钮、选择控件、网格列表项、列表项、卡片、芯片、文本字段和图标。这里还没有设置出明显的效果所以无法展示,后续还需仔细研究。 - mouseCursor
光标样式,这里默认为MaterialStateMouseCursor.clickable,当然还有MaterialStateMouseCursor.textable供选择,主要是一个是点击光标,一个是文本光标。 - splashColor,splashFactory
在InkWell中设置splashColor为Colors.red时,splashFactory 不同效果不同:
属性 | 效果 |
InkSplash.splashFactory | |
InkRipple.splashFactory |
InkSplash.splashFactory 和InkRipple.splashFactory不同之处可能在于水波纹的开始及结束状态。
- containedInkWell
InkWill中不可设置该属性,但是在继承InkResponse时被设置为true,InkResponse中默认为false:
属性 | 效果 |
true | |
false |
containedInkWell 设为true时,超出容器的水波纹会被裁剪,false的时候不会被裁剪。
-radius,borderRadius
当水波纹为圆形的时候,radius为水波纹半径。borderRadius为InkWell或InkResponse的边框圆角,当设置后水波纹扩散有角的地方,会被才切成有弧度的,而不是以直角的显示:
属性 | 效果 |
radius: 22.0 | |
radius: 66.0 | |
borderRadius: BorderRadius.circular(16.0),radius: 66.0 |
radius值不同,水波纹的半径是有区别的。borderRadius需要仔细观察第二幅和第三幅图,在点击的时候,四角是否有灰色的直角背景显示出来,如果有就没有设置,如果没有就是设置了。
- customBorder
边框。 - overlayColor
覆盖层颜色,覆盖层是指元素上显示其状态的半透明覆盖层。overlay提供了一种使用不透明度来可视化状态的系统方法。一个覆盖层可以应用到整个元素或在一个圆形的形状。覆盖颜色与它所应用的元素上的文本或图标的颜色相匹配。如果文本或图标在状态改变期间改变颜色,覆盖层应该匹配结束状态的颜色。一次只应用一个状态层。例如,如果一个元素首先被聚焦然后被悬停,悬停状态层将只显示悬停完成,然后返回到焦点状态层如果元素仍然被聚焦。详细效果还没有弄出来。 - enableFeedback
默认为false,没有反馈,有时点击按钮需要手机震动一下,就是这个意思。 - focusNode,canRequestFocus ,onFocusChange ,autofocus
这些都是字面意思,不需要什么解释。
InkFeature、Ink.image、InkSplash、InkRipple、InkDecoration、InkHighlight
- InkDecoration继承自InkFeature,在Ink中进行装饰。
if (_ink == null) {
_ink = InkDecoration(
decoration: widget.decoration,
configuration: createLocalImageConfiguration(context),
controller: Material.of(context)!,
referenceBox: context.findRenderObject()! as RenderBox,
onRemoved: _handleRemoved,
);
} else {
_ink!.decoration = widget.decoration;
_ink!.configuration = createLocalImageConfiguration(context);
}
InkDecoration直接将Ink中设置的decoration进一步包装,添加一些配置。
- Ink.image
Ink.image是decoraton中配置的一种,用于给Ink装饰图片。
Ink.image({
Key? key,
this.padding,
required ImageProvider image,
ImageErrorListener? onImageError,
ColorFilter? colorFilter,
BoxFit? fit,
AlignmentGeometry alignment = Alignment.center,
Rect? centerSlice,
ImageRepeat repeat = ImageRepeat.noRepeat,
bool matchTextDirection = false,
this.width,
this.height,
this.child,
})
观察发现,Ink.image和BoxDecoration中的image指定的DecorationImage是一模一样,用法也一样。若不太清楚,可以参考Flutter-Container详解。
- InkSplash、InkRipple、InkHighlight
InkSplash、InkRipple、InkHighlight都是继承自InteractiveInkFeature,而InteractiveInkFeature继承自InkFeature,所以InkSplash、InkRipple和InkDecoration都是属于Ink装饰。以InkRipple.splashFactory为例
class _InkRippleFactory extends InteractiveInkFeatureFactory {
const _InkRippleFactory();
@override
InteractiveInkFeature create({
required MaterialInkController controller,
required RenderBox referenceBox,
required Offset position,
required Color color,
required TextDirection textDirection,
bool containedInkWell = false,
RectCallback? rectCallback,
BorderRadius? borderRadius,
ShapeBorder? customBorder,
double? radius,
VoidCallback? onRemoved,
}) {
return InkRipple(
controller: controller,
referenceBox: referenceBox,
position: position,
color: color,
containedInkWell: containedInkWell,
rectCallback: rectCallback,
borderRadius: borderRadius,
customBorder: customBorder,
radius: radius,
onRemoved: onRemoved,
textDirection: textDirection,
);
}
}
...
/// Used to specify this type of ink splash for an [InkWell], [InkResponse]
/// or material [Theme].
static const InteractiveInkFeatureFactory splashFactory = _InkRippleFactory();
...
所以InkSplash、InkRipple是上文中提到的InkSplash.splashFactory 和InkRipple.splashFactory工厂具体实现。
工厂模式就是定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行,主要解决接口选择的问题,是 Java 中最常用的设计模式之一。在任何需要生成复杂对象的地方,都可以使用工厂方法模式。复杂对象适合使用工厂模式,而简单对象只需简单通过构造创建对象即可,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
注
Flutter中IButton、BottomNavigationBar、CalendarDatePicker、DataTable、NavigationRail、Stepperden等使用了InkResponse或InkWell,所以很多组件也有水波纹效果。