← 開發日常

BuildContext in Flutter

BuildContext在flutter開發中很常出現,是build方法的唯一參數。我們時常需要透過of方法取得某些Widget時,此時就會使用到context,例如:

dart
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這個抽象類別。

Article image

各式各樣的找祖先方法

Element實作BuildContext,並實作了BuildContext裡的眾多方法。在這些方法中,有一系列找祖先的方法,讓我們可以透過context找到往上好幾層的Widget或State。

dart
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的。

Article image

Build方法中的唯一參數

為了讓我們能更靈活的控制UI,BuildContext作為build方法唯一參數,讓我們可以透過它取得祖先Widget,並應用在一些常見的場景中

  1. 取得祖先Widget的狀態,來做相對應的顯示,例如:我們需要根據手機的寬高來顯示不一樣的UI設計。
  2. 操作祖先Widget並改變他的狀態,例如:當我們想顯示自訂訊息在SnackBar時,我們必須透過BuildContext取得ScaffoldState,才能用它顯示SnackBar。

小結

我們都會從StackOverflow上找某個UI如何實作,這些解答中多多少少會用到BuildContext。BuildContext作為Flutter核心設計之一,理解BuildContext能有效讓我們更清楚Flutter是如何運作的,也能在發生error時,更清楚其背後的根本原因。