BuildContext在flutter開發中很常出現,是build方法的唯一參數。我們時常需要透過of方法取得某些Widget時,此時就會使用到context,例如:
MediaQuery.of(context).size // 取得畫面寬高
Scaffold.of(context).showSnackBar(snackBar); // 顯示Snack Bar 但是實際上context到底是什麼呢?今天就來聊聊這個話題吧。
Flutter的三棵樹
如果有稍微研究過Flutter的話,多多少少都知道Flutter有三顆樹,分別是Widget Tree, Element Tree, RenderObject Tree。而當中的Element Tree是由各式各樣的Element組成的,例如:SatelessElement、StatefulElement、InheritedElement...等,每個Widget都有他自己相對應的element。
Element主要職責是橫向連接Widget與RenderObject,縱向連結parent Element與child Element,並在Widget發生變化時,根據情況更新RenderObject或重新建立子樹。如果追著這些Element的繼承一路往上,會發現最終Element實作了BuildContext這個抽象類別。
各式各樣的找祖先方法
Element實作BuildContext,並實作了BuildContext裡的眾多方法。在這些方法中,有一系列找祖先的方法,讓我們可以透過context找到往上好幾層的Widget或State。
abstract class BuildContext {
...
T findAncestorWidgetOfExactType<T extends Widget>();
T findAncestorStateOfType<T extends State>();
T findRootAncestorStateOfType<T extends State>();
T findAncestorRenderObjectOfType<T extends RenderObject>();
void visitAncestorElements(bool visitor(Element element));
void visitChildElements(ElementVisitor visitor);
...
} 這些找祖先方法被廣泛的運用在各種of方法中,例如Scaffold.of(context)就是透過context.findAncestorStateOfType<T>()來取得ScaffoldState的。
Build方法中的唯一參數
為了讓我們能更靈活的控制UI,BuildContext作為build方法唯一參數,讓我們可以透過它取得祖先Widget,並應用在一些常見的場景中
- 取得祖先Widget的狀態,來做相對應的顯示,例如:我們需要根據手機的寬高來顯示不一樣的UI設計。
- 操作祖先Widget並改變他的狀態,例如:當我們想顯示自訂訊息在SnackBar時,我們必須透過BuildContext取得ScaffoldState,才能用它顯示SnackBar。
小結
我們都會從StackOverflow上找某個UI如何實作,這些解答中多多少少會用到BuildContext。BuildContext作為Flutter核心設計之一,理解BuildContext能有效讓我們更清楚Flutter是如何運作的,也能在發生error時,更清楚其背後的根本原因。