概述

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详解

效果

说明

flutter InputDecorationTheme设置 flutter ink_圆角

没有使用decoration

flutter InputDecorationTheme设置 flutter ink_悬停_02

使用decoration设置圆角

flutter InputDecorationTheme设置 flutter ink_圆角_03

使用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

flutter InputDecorationTheme设置 flutter ink_工厂模式_04

InkRipple.splashFactory

flutter InputDecorationTheme设置 flutter ink_悬停_05

InkSplash.splashFactory 和InkRipple.splashFactory不同之处可能在于水波纹的开始及结束状态。

  • containedInkWell
    InkWill中不可设置该属性,但是在继承InkResponse时被设置为true,InkResponse中默认为false:

属性

效果

true

flutter InputDecorationTheme设置 flutter ink_圆角_06

false

flutter InputDecorationTheme设置 flutter ink_圆角_07

containedInkWell 设为true时,超出容器的水波纹会被裁剪,false的时候不会被裁剪。

-radius,borderRadius
当水波纹为圆形的时候,radius为水波纹半径。borderRadius为InkWell或InkResponse的边框圆角,当设置后水波纹扩散有角的地方,会被才切成有弧度的,而不是以直角的显示:

属性

效果

radius: 22.0

flutter InputDecorationTheme设置 flutter ink_圆角_08

radius: 66.0

flutter InputDecorationTheme设置 flutter ink_圆角_09

borderRadius: BorderRadius.circular(16.0),radius: 66.0

flutter InputDecorationTheme设置 flutter ink_圆角_10

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,所以很多组件也有水波纹效果。