前言:
這是我參與8月更文挑戰的第 31 天,活動詳情檢視:8月更文挑戰。為應掘金的八月更文挑戰
,我準備在本月挑選 31
個以前沒有介紹過的元件,進行全面分析和屬性介紹。這些文章將來會作為 Flutter 元件集錄
的重要素材。希望可以堅持下去,你的支援將是我最大的動力~
1. 認識 Tooltip 及使用
今天是八月更文的最後一天,帶大家看一下 Tooltip
元件的實現,從而引出 Overlay
元件的使用方式。 Tooltip
元件主要的作用是在滑鼠懸浮
或長按手勢
下觸發訊息提示。它繼承自 StatefulWidget
,其中必須傳入 String
型別的 message
,還有很多其他的引數用於配置。
final String message;
複製程式碼
如下是 Tooltip
預設的效果,可以套在任意元件上,當滑鼠懸浮
或長按手勢
時,會在下方顯示提示資訊。
Tooltip(
message: "寶塔鎮河妖",
child: Icon(Icons.info_outline)
);
複製程式碼
當 preferBelow
屬性為 false
,提示資訊就會顯示在上方。
Tooltip(
preferBelow: false,
message: "寶塔鎮河妖",
child: Icon(Icons.info_outline)
);
複製程式碼
通過 verticalOffset
可以設定豎直偏移,此偏移量可為負數。此值為 0
時,提示框底部與元件中心對齊
Tooltip(
preferBelow: false,
verticalOffset: 12,
message: "寶塔鎮河妖",
child: Icon(Icons.info_outline)
);
複製程式碼
通過 padding
和 margin
可以設定內外邊距:
Tooltip(
preferBelow: false,
padding: EdgeInsets.symmetric(horizontal: 40,vertical: 5),
margin: EdgeInsets.all(10),
message: "寶塔鎮河妖",
child: Icon(Icons.info_outline)
);
複製程式碼
通過 decoration
和 textStyle
可以設定 盒子裝飾
和 文字樣式
。
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
表示長按時,需要等待多長時間再顯示提示框。
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
方法中可以看出提示框的預設表現會受 Theme
、TooltipTheme
的資料影響,對暗黑主體也進行了適配。
會通過 GestureDetector
來監聽長按事件,如果檢測到滑鼠的連線,外層會套上 MouseRegion
進行監聽滑鼠的移入移出事件。
最終顯示的是使用者傳入的 child
元件,那提示框是如何彈出和消失的呢?現在焦點就可以放在 _showTooltip
和 _hideTooltip
如何控制提示框的顯隱。
3.Overlay 在 Tooltip 原始碼的應用
在移動端中,長按會彈出提示框,從原始碼中可以看出,核心的方法是 ensureTooltipVisible
。
void _handleLongPress() {
_longPressActivated = true;
final bool tooltipCreated = ensureTooltipVisible();
if (tooltipCreated)
Feedback.forLongPress(context);
}
複製程式碼
_TooltipState
中維護了兩個計時器 _hideTimer
和 _showTimer
來處理延遲。開始會取消並置空 _showTimer
計時器,這樣保證不會在計時器完成時再出現一個框。如果 _entry
非空,表示提示框已經存在,會取消並置空 _hideTimer
計時器,並執行動畫。此處返回 false
,表示已經存在,開啟失敗。
否則會執行 _createNewEntry
建立新的 Entry
並執行動畫。
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
元件。
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();
即可。
void _removeEntry() {
_hideTimer?.cancel();
_hideTimer = null;
_showTimer?.cancel();
_showTimer = null;
_entry?.remove();
_entry = null;
}
複製程式碼
Overlay
元件本身的使用並不複雜,這是 Tooltip
中有延遲和動畫的處理,讓顯隱的邏輯複雜了一些。這些在計時器的控制人常開發中也是值得我們學習的。雖然是很小的一個元件,但其中包含了很多知識,這種小巧的元件很適合我們去細細品讀。到這裡本系列就完結了,在毫無存稿的情況下,連更 31 天實屬不易,感謝大家的支援,後會有期~