前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Flutter 组件集录】Tooltip 与 Overlay

【Flutter 组件集录】Tooltip 与 Overlay

作者头像
张风捷特烈
发布2022-03-18 15:54:40
1.6K0
发布2022-03-18 15:54:40
举报
1. 认识 Tooltip 及使用

今天是八月更文的最后一天,带大家看一下 Tooltip 组件的实现,从而引出 Overlay 组件的使用方式。 Tooltip 组件主要的作用是在鼠标悬浮长按手势下触发消息提示。它继承自 StatefulWidget ,其中必须传入 String 类型的 message ,还有很多其他的参数用于配置。

代码语言:javascript
复制
final String message;

如下是 Tooltip 默认的效果,可以套在任意组件上,当鼠标悬浮长按手势时,会在下方显示提示信息。

代码语言:javascript
复制
Tooltip(
  message: "宝塔镇河妖",
  child: Icon(Icons.info_outline)
);
preferBelow 属性为 false ,提示信息就会显示在上方。
代码语言:javascript
复制
Tooltip(
  preferBelow: false,
  message: "宝塔镇河妖",
  child: Icon(Icons.info_outline)
);

通过 verticalOffset 可以设置竖直偏移,此偏移量可为负数。此值为 0 时,提示框底部与组件中心对齐

代码语言:javascript
复制
Tooltip(
  preferBelow: false,
  verticalOffset: 12,
  message: "宝塔镇河妖",
  child: Icon(Icons.info_outline)
);

通过 paddingmargin 可以设置内外边距:

代码语言:javascript
复制
Tooltip(
  preferBelow: false,
  padding: EdgeInsets.symmetric(horizontal: 40,vertical: 5),
  margin: EdgeInsets.all(10),
  message: "宝塔镇河妖",
  child: Icon(Icons.info_outline)
);

通过 decorationtextStyle 可以设置 盒子装饰文字样式

代码语言:javascript
复制
Tooltip(
  preferBelow: false,
  verticalOffset: 15,
  message: "宝塔镇河妖",
  textStyle: TextStyle(
    color: Colors.red,
    shadows: [
      Shadow(
        color: Colors.white,
        offset: Offset(1, 1),
      ),
    ],
  ),
  decoration: BoxDecoration(boxShadow: [
    BoxShadow(
      color: Colors.orangeAccent,
      offset: Offset(1, 1),
      blurRadius: 8,
    )
  ]),
  child: Icon(Icons.info_outline)
);

有时候我们并不希望鼠标一进入就显示提示,waitDuration 表示鼠标进入时,需要等待多长时间再显示提示框。showDuration 表示长按时,需要等待多长时间再显示提示框。

代码语言:javascript
复制
Tooltip(
  // 略同...
  waitDuration:const Duration(seconds: 2),
  showDuration:const Duration(seconds: 2),
  child: Icon(Icons.info_outline)
);

Tooltip 组件的属性就是这些,下面我们来看一下它的源码实现。

2. Tooltip 源码简看

Tooltip 作为一个 StatefulWidget,自然是会维护一个状态类进行组件构建,状态周期等逻辑处理。如下是 _TooltipState 的类定义。看到它混入了 SingleTickerProviderStateMixin,表示该状态类中会使用动画。

initState 回调中,会初始化 _controller 动画控制器,可以看出 Tooltip 的提示框会伴随一个透明度的渐变动画。然后对鼠标 mouseTracker 和触点 pointerRouter 进行监听。

dispose 回调中移除监听和销毁动画控制器。

build 方法中可以看出提示框的默认表现会受 ThemeTooltipTheme 的数据影响,对暗黑主体也进行了适配。

会通过 GestureDetector 来监听长按事件,如果检测到鼠标的连接,外层会套上 MouseRegion 进行监听鼠标的移入移出事件。

最终显示的是用户传入的 child 组件,那提示框是如何弹出和消失的呢?现在焦点就可以放在 _showTooltip_hideTooltip 如何控制提示框的显隐。

3.Overlay 在 Tooltip 源码的应用

在移动端中,长按会弹出提示框,从源码中可以看出,核心的方法是 ensureTooltipVisible

代码语言:javascript
复制
void _handleLongPress() {
  _longPressActivated = true;
  final bool tooltipCreated = ensureTooltipVisible();
  if (tooltipCreated)
    Feedback.forLongPress(context);
}

_TooltipState 中维护了两个计时器 _hideTimer_showTimer 来处理延迟。开始会取消并置空 _showTimer 计时器,这样保证不会在计时器完成时再出现一个框。如果 _entry 非空,表示提示框已经存在,会取消并置空 _hideTimer 计时器,并执行动画。此处返回 false ,表示已经存在,开启失败。 否则会执行 _createNewEntry 创建新的 Entry 并执行动画。

代码语言:javascript
复制
Timer? _hideTimer;
Timer? _showTimer;
OverlayEntry? _entry;

bool ensureTooltipVisible() {
  _showTimer?.cancel();
  _showTimer = null;
  if (_entry != null) {
    // Stop trying to hide, if we were.
    _hideTimer?.cancel();
    _hideTimer = null;
    _controller.forward();
    return false; // Already visible.
  }
  _createNewEntry();
  _controller.forward();
  return true;
}

_createNewEntry 中,先通过 Overlay.of 获取 OverlayState 对象,再通过 context.findRenderObject 获取 RenderBox 得到组件的位置。最后创建 OverlayEntry_entry 赋值,并将_entry通过 overlayState 插入,其中主体的内容就是 overlay 组件。

代码语言:javascript
复制
void _createNewEntry() {
  final OverlayState overlayState = Overlay.of( //1. 得到 overlayState
    context,
    debugRequiredFor: widget,
  )!;
  final RenderBox box = context.findRenderObject()! as RenderBox;
  final Offset target = box.localToGlobal(
    box.size.center(Offset.zero),
    ancestor: overlayState.context.findRenderObject(),
  );

  final Widget overlay = Directionality( 
    textDirection: Directionality.of(context),
    child: _TooltipOverlay(//...组件暂略
  );
   //2. 创建 _entry
  _entry = OverlayEntry(builder: (BuildContext context) => overlay);
   //3. 插入 _entry
  overlayState.insert(_entry!);
  SemanticsService.tooltip(widget.message);
}

我们为 Tooltip 传入的大多数参数都是用于构建 _TooltipOverlay 的,下面是它的源码。可以看出,它通过 FadeTransition 进行透明渐变动画,通过 CustomSingleChildLayout 进行提示框的定位。并且 IgnorePointer 表示提示框是忽略点击事件的。

这样 Overlay 的弹出就看完了,至于 Overlay 的移除,只需要 _entry?.remove(); 即可。

代码语言:javascript
复制
void _removeEntry() {
  _hideTimer?.cancel();
  _hideTimer = null;
  _showTimer?.cancel();
  _showTimer = null;
  _entry?.remove();
  _entry = null;
}

Overlay 组件本身的使用并不复杂,这是 Tooltip 中有延迟和动画的处理,让显隐的逻辑复杂了一些。这些在计时器的控制人常开发中也是值得我们学习的。虽然是很小的一个组件,但其中包含了很多知识,这种小巧的组件很适合我们去细细品读。到这里本系列就完结了,在毫无存稿的情况下,连更 31 天实属不易,感谢大家的支持,后会有期~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/08/31 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 认识 Tooltip 及使用
  • 2. Tooltip 源码简看
  • 3.Overlay 在 Tooltip 源码的应用
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档