在上一篇文章中發佈了 WPopupMenu
的第一個版本,而且也遺留了兩個問題:git
那在此次發文以前也是解決了上述兩個問題和完善了一些邏輯問題:github
很少說,上圖:canvas
首先,仍是按照正常業務邏輯,先提需求:微信
需求差很少了,就該來實現了。ide
首先迎面來的就是第一個難題,如何在當前頁面彈出?this
這就涉及到我前面所講的幾篇文章:spa
Flutter | 超實用簡單菜單彈出框 PopupMenuButton3d
Flutter 源碼系列:DropdownButton 源碼淺析code
這幾個控件的源碼裏都有一個類:PopupRoute
,該類我也講過:cdn
PopupRoute 是一個浮在當前頁面上的 Route.
看到沒,這就是閱讀源碼的益處!
既然是一個 Route,那麼也能夠經過他來返回值,一箭雙鵰。
瞭解瞭如何在當前頁面彈出頁面,那就能夠自定義樣式了。
樣式也很簡單,大概也能看的出來:
三角形咱們先不說,整個 menu 實際上是有一個背景的,就是一個圓角矩形,使用 ClipRRect
就能實現。
剩下的就是 ListView 和箭頭的組合,我使用了 Row 來組合這些組件,由於箭頭和 ListView 的 item 寬度不同,而且若是都使用 ListView,那麼下標的計算也很煩人。
因此我這裏直接放棄了這種麻煩的方法,選擇了一個相對簡單的方法。
大體邏輯以下:
若是你看過最開始說的那些控件的源碼,那麼這個問題對於你來講應該不是問題,由於...
那些控件的源碼裏給了一個解決方案。直接複製代碼,稍微改一改就能用:
// 使用該控件
CustomSingleChildLayout(
// 這裏計算偏移量
delegate: _PopupMenuRouteLayout(),
child: SizedBox()
)
// ---------------------------------------------
// 使用 SingleChildLayoutDelegate 並複寫 getPositionForChild 方法來計算座標
class _PopupMenuRouteLayout extends SingleChildLayoutDelegate {
@override
Offset getPositionForChild(Size size, Size childSize) {
// ...
return Offset(x, y);
}
}
複製代碼
這裏只是肯定了一個偏移量,那對於彈出位置是在 child 上面仍是下面,我是用 y 來判斷的:
若是 「 y < menu.height * 2 」,那麼則把它放到 child 下面。
這裏的三角形是用 CustomPainter
來畫的,這樣能夠本身隨便定義屬性,簡單又方便。
簡單邏輯以下:
大概的代碼以下:
void paint(Canvas canvas, Size size) {
var path = Path();
// 若是 menu 的長度 大於 child 的長度
if (size.width > this.size.width) {
// 靠右
if (position.left + this.size.width / 2 > position.right) {
path.moveTo(size.width - this.size.width + this.size.width / 2, isInverted ? 0 : size.height);
path.lineTo(
size.width - this.size.width + this.size.width / 2 - radius / 2, isInverted ? size.height : 0);
path.lineTo(
size.width - this.size.width + this.size.width / 2 + radius / 2, isInverted ? size.height : 0);
}else{
// 靠左
path.moveTo(this.size.width / 2, isInverted ? 0 : size.height);
path.lineTo(
this.size.width / 2 - radius / 2, isInverted ? size.height : 0);
path.lineTo(
this.size.width / 2 + radius / 2, isInverted ? size.height : 0);
}
} else {
path.moveTo(size.width / 2, isInverted ? 0 : size.height);
path.lineTo(
size.width / 2 - radius / 2, isInverted ? size.height : 0);
path.lineTo(
size.width / 2 + radius / 2, isInverted ? size.height : 0);
}
path.close();
canvas.drawPath(
path,
_paint,
);
}
複製代碼
一個完整版的 WPopupMenu
就完成了,這裏只是簡單的說了一下邏輯,
但實際寫起來並無那麼簡單。
若是以爲項目還能夠,請點個 star,萬分感謝!
完整代碼已經傳至GitHub:github.com/wanglu1209/…
我也建立了一個微信羣,有興趣的能夠掃碼加羣,若是羣滿,能夠添加我我的微信:17610912320,並註明來意。