1. 程式人生 > 其它 >Flutter自定義Tooltip

Flutter自定義Tooltip

在以前,對Tooltip元件的唯一印象就是白加黑,如下圖所示

後來發現電腦端的軟體,Tooltip都是各種樣式。我正好也要用到這種可以自定義Tooltip的功能,本來想要不用MouseRegion元件和Overlay元件寫一個。想了一下,有點過於繁瑣,於是就去pub.dev找了一圈,看有沒有已經造好的輪子,可惜沒找到。

要不還是先試試Tooltip吧,想著看了一下這個元件的屬性,立馬像發現了新大陸。這些功能原來Tooptip早就已經具有了。

Tooltip

該元件一共具有以下多個屬性:

  • String? message:提示的文字資訊
  • InlineSpan? richMessage:提示的自定義資訊
  • double? height:提示的高度
  • EdgeInsetsGeometry? padding:提示的內邊距
  • EdgeInsetsGeometry? margin:提示的外邊距
  • double? verticalOffset:提示顯示的垂直偏移
  • bool? preferBelow:工具提示是否預設顯示在小部件下方。預設為true。如果沒有足夠的空間以首選方向顯示工具提示,則工具提示將向相反方向顯示
  • bool? excludeFromSemantics:如果Tooltip元件為Semantics的子元件,側設定為true。預設為false
  • Decoration? decoration
    :提示顯示的樣式
  • TextStyle? textStyle:提示顯示的文字樣式
  • Duration? waitDuration:滑鼠懸停多久才顯示提示
  • Duration? showDuration:提示顯示持續的時間
  • Widget? child:提示應用的子元素
  • TooltipTriggerMode? triggerMode:提示觸達的模式(條件)。僅在移動端適用
  • bool? enableFeedback:是否應提供聲音和/或觸覺反饋。在 Android 上,當啟用反饋時,點選會產生點選聲,長按會產生短暫的振動

預設的Tooltip有點醜,我們來把它修改得漂亮一點

Tooltip(
  message: '海綿寶寶',
  padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
  decoration: BoxDecoration(
    color: Colors.white,
    borderRadius: const BorderRadius.all(Radius.circular(4)),
    boxShadow: [
      BoxShadow(blurRadius: 2, color: Colors.black.withOpacity(.2))
    ],
  ),
  textStyle: const TextStyle(color: Colors.black),
  child: Image.asset('assets/images/hmbb.png', width: 250),
)

預設Tooltip一放上去就顯示,一離開就消失,我們來自定義顯示和消失的時間

Tooltip(
  ...
  waitDuration: const Duration(seconds: 2),
  showDuration: const Duration(seconds: 2),
)

預設提示顯示的最大寬度為程式的最大寬度,當提示顯示的資訊更多,就會變成如下樣子

Tooltip(
  message: '海綿寶寶' * 10,
  ...
)

Tooltip只有一個設定高度的值,沒有設定寬度的值。那我們要是想多行顯示資訊怎麼辦?最簡單的就是以下這樣書寫

Tooltip(
  message: '姓名:海綿寶寶\n職務:蟹堡王餐廳大廚',
  ...
)

當然,也可以這樣寫

Tooltip(
  message: '''
姓名:海綿寶寶
職務:蟹堡王餐廳大廚''',
  ...
)

效果一樣,但是不好動態獲取提示資訊。最好的解決方案就是使用richMessage屬性。

richMessage

該屬性傳遞一個InlineSpan物件,所以我們可以在提示中為所欲為,顯示任何內容。

假入我們有以下資訊需要顯示

Role role = Role(name: '海綿寶寶', city: '比奇堡', age: 36, position: '蟹堡王餐廳的高階廚師');

我們可以使用以下程式碼

Tooltip(
  richMessage: WidgetSpan(
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text('姓名:${role.name}'),
        Text('地址:${role.city}'),
        Text('年齡:${role.age}'),
        Text('職位:${role.position}'),
      ],
    ),
  ),
  padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
  decoration: BoxDecoration(
    color: Colors.white,
    borderRadius: const BorderRadius.all(Radius.circular(4)),
    boxShadow: [
      BoxShadow(blurRadius: 2, color: Colors.black.withOpacity(.2))
    ],
  ),
  child: Image.asset('assets/images/hmbb.png', width: 250),
),

顯示是能顯示了,但是這個位置把海綿寶寶給擋住了,我們來更改一下它的位置。我們要是想讓提示顯示在圖片下面,只要把垂直方向向下移動子元素的高度一半距離

Tooltip(
  // 817和806是原圖大小,250是設定顯示的寬度, 817 * 250 / 806計算當前高度
  verticalOffset: 817 * 250 / 806 / 2,
  ...
)

Tooltip中只有一個設定垂直距離的屬性,那要是我們想讓它右邊或左邊顯示怎麼辦?我們可以使用margin這個屬性

Tooltip(
  margin: const EdgeInsets.only(left: 250),
  ...
)

提示雖然顯示到右邊去了,但是還是會遮住圖片(這裡背景是透明,所以不太能看出效果)。我們怎麼才能讓它顯示在圖片外的最右邊呢?把margin值寫大一點當然可行,我們這裡可以計算的精準一點。

要想計算好位置,我們需要知道兩個值。提示顯示的寬度和圖片的寬度。圖片的寬度我們設定成了250,提示的寬度是動態的,我們不好知道,所以我們可以用一個固定寬度包住它

Tooltip(
  richMessage: WidgetSpan(
    child: SizedBox(
      width: 200,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text('姓名:${role.name}'),
          Text('地址:${role.city}'),
          Text('年齡:${role.age}'),
          Text('職位:${role.position}'),
        ],
      ),
    ),
  ),
  ...
),

然後通過以下算式計算

Tooltip(
  ...
  // 250為圖片的寬度,200為提示的寬度
  margin: const EdgeInsets.only(left: 250 * 2 - (250 - 200) / 2),
),