Flutter 的 Overlay 機制是一個強大的浮層系統(tǒng)
什么是 Overlay?
想象你的 Flutter 應(yīng)用是一本書:
- 普通 Widget 就像書頁上的文字,按順序排列,受頁面布局限制
- Overlay 就像透明貼紙,可以貼在任何頁面的最上層,不受頁面布局影響
核心概念
1. Overlay(浮層容器)
// 每個 MaterialApp 都自帶一個 Overlay
// 通過 Overlay.of(context) 獲取
final overlay = Overlay.of(context);
2. OverlayEntry(浮層條目)
// 創(chuàng)建一個浮層條目
final entry = OverlayEntry(
builder: (context) => Positioned(
top: 100,
left: 50,
child: Text('我浮在最上層!'),
),
);
// 插入到 Overlay 中顯示
overlay.insert(entry);
// 移除
entry.remove();
工作原理
┌─────────────────────────────┐
│ MaterialApp │
│ ┌─────────────────────┐ │
│ │ Scaffold │ │
│ │ ┌───────────────┐ │ │
│ │ │ AppBar │ │ │
│ │ └───────────────┘ │ │
│ │ ┌───────────────┐ │ │
│ │ │ Body │ │ │ ← 普通 Widget 層
│ │ │ (你的頁面) │ │ │
│ │ └───────────────┘ │ │
│ └─────────────────────┘ │
│ │
│ ┌─────────────────────┐ │
│ │ Overlay Layer │ │ ← Overlay 浮層
│ │ ┌───────────────┐ │ │
│ │ │ Toast 1 │ │ │
│ │ └───────────────┘ │ │
│ │ ┌───────────────┐ │ │
│ │ │ Dialog │ │ │
│ │ └───────────────┘ │ │
│ └─────────────────────┘ │
└─────────────────────────────┘
常見應(yīng)用場景
1. Toast 提示(我們的例子)
Toast.show(context, '操作成功');
// 浮在所有內(nèi)容之上,不影響頁面布局
2. Dialog 對話框
showDialog(context, ...);
// 內(nèi)部就是用 Overlay 實現(xiàn)的
3. Dropdown 下拉菜單
// 下拉菜單展開時,內(nèi)容浮在按鈕上方
4. Tooltip 工具提示
Tooltip(message: '提示文字');
// 長按時浮層顯示提示
為什么用 Overlay?
優(yōu)勢
- 不受布局限制 - 可以顯示在任何位置,不會擠壓其他內(nèi)容
- 層級最高 - 永遠顯示在最上層
- 獨立管理 - 可以隨時插入/移除,不影響原有 Widget 樹
-
全局訪問 - 通過
Overlay.of(context)在任何地方訪問
對比普通 Widget
// ? 普通 Widget - 受布局限制
Column(
children: [
Text('內(nèi)容'),
Container(...), // Toast?會擠壓上面的內(nèi)容
],
)
// ? Overlay - 浮在上層
// Toast 顯示時不影響原有布局
我們的 Toast 實現(xiàn)
static void show(BuildContext context, String message) {
// 1. 獲取 Overlay 容器
final overlay = Overlay.of(context);
// 2. 創(chuàng)建浮層條目
final overlayEntry = OverlayEntry(
builder: (context) => Positioned( // 使用 Positioned 定位
bottom: 100,
left: 20,
right: 20,
child: _ToastWidget(...), // 我們的 Toast Widget
),
);
// 3. 插入到 Overlay 中顯示
overlay.insert(overlayEntry);
// 4. 稍后移除
Future.delayed(duration, () {
overlayEntry.remove();
});
}
關(guān)鍵點
- Positioned 必須 - Overlay 中的 Widget 必須用 Positioned 包裹來定位
-
手動管理生命周期 - 需要自己調(diào)用
insert()和remove() -
單例模式 - 我們用
_currentOverlay確保同時只顯示一個 Toast - Material 包裹 - 需要 Material Widget 提供主題支持
這就是為什么 Toast 能"浮"在頁面上,而不會影響原有布局的原因!