背景
不管是剛寫Flutter開發還是Flutter開發的老將,相信對BuildContext一定不陌生,那么BuildContext是什么呢?為什么我們每次StatelessWidget.build(context),State.build(context)在呼叫build的時候都需要傳入一個BuildContext呢?
本文主要講下BuildContext來龍去脈,已經它的使用場景,
常見的BuildContext場景
首先我們一起來看看BuildContext的使用場景,相信做過Flutter開發的同學也知道,BuildContext的使用場景最多的無非就是各種of方法:Object.of(context)查找特定的物件,事實功能也確實是如此,比如:
// flutter路由2.0里面通過of方法查找到最近的Router物件
Router.of(context).routerDelegate.setNewRoutePath(routerName);
// flutter路由1.0里面查找最近的NavigatorState物件
Navigator.of(context).push
// 向上查找最近的ThemeData物件
Theme.of(context).textTheme
// .... 各種各樣的of(context)方法
還有我們在創建widget的時候,build里面傳參:
Widget build(BuildContext context)
BuildContext從哪來
我們通過撰寫widget知道BuildContext是來自于widget或者state的build(BuildContext context)方法,所以我們需要從Widget的build出發,這里以StatelessWidget為例一步步來看
- 先看
StatelessWidget原始碼
abstract class StatelessWidget extends Widget {
@protected
Widget build(BuildContext context);
}
StatelessWidget只是一個抽象方法,定義了一個build方法,再看看build方法在哪呼叫到:
- 定位到了’StatelessElement’方法呼叫了
class StatelessElement extends ComponentElement {
@override
Widget build() => widget.build(this);
}
我們看到Element的build呼叫到了widget.build方法并且傳入了this作為引數,這個引數型別就是BuildContext,
StatefulWidget也是如此,就是通過Element的直接呼叫了state.build(Context context)并且將自己(this)作為引數傳遞進去:
class StatefulElement extends ComponentElement {
@override
Widget build() => state.build(this);
}
我們傳入的明明是Element,怎么又會變成BuildContext(肯定是繼承關系啦),我們再來看看BuildContext原始碼,看看它又是何方神圣:
abstract class BuildContext {}
這里我們只需要知道它是一個抽象類,所以:
通過上面分析不容易總結出,Element必定是BuildContext的子類(去看Element繼承關系就知道),BuildContext其實就是對應的Element物件,將BuildContext作為引數傳遞也就是為了阻止開發人員直接操作Element(Element一般是不能夠直接操作的,如果直接將context as Element編譯也是能通過的(最好別這么完)),講到這里我們就知道了BuildContext是個什么玩意兒了吧
解決什么問題
通過上面的分析我們知道BuildContext的本尊其實就是Element,Element的來龍去買已經作用可以看之前的文章【Flutter原理】三棵樹的誕生與核心流程,Element作為widget和renderObject的橋梁,這就意味著我們可以拿到三棵樹中的widget,element,renderObject三個節點,所以可以做的事情有很多,我們主要來看看官方提供給了我們哪些功能,來解決哪些問題:
看看BuildContext原始碼
abstract class BuildContext {
/// 當前BuildContext的Element的配置資訊Widget
Widget get widget;
// Widget管理器
BuildOwner? get owner;
// 當前widget是否正在更新
bool get debugDoingBuild;
// 獲取當前對應widget的RenderObject
RenderObject? findRenderObject();
// findRenderObject 回傳的 RenderBox 的大小
Size? get size;
// 注冊依賴祖先InheritWigdet
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect });
// 注冊并獲取指定型別的祖先InheritWidget(內部使用的是Set集合,所以不存在重復注冊問題)
T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({ Object? aspect });
// 獲取指定型別祖先InteritWidget的Element
InheritedElement? getElementForInheritedWidgetOfExactType<T extends InheritedWidget>();
// 獲取指定型別的最近祖先Widget
T? findAncestorWidgetOfExactType<T extends Widget>();
// 獲取指定型別的祖先State
T? findAncestorStateOfType<T extends State>();
// 獲取指定型別的最遠祖先State
T? findRootAncestorStateOfType<T extends State>();
// 獲取指定型別的RendObject
T? findAncestorRenderObjectOfType<T extends RenderObject>();
// 訪問祖先Element
void visitAncestorElements(bool visitor(Element element));
// 訪問孩子Element
void visitChildElements(ElementVisitor visitor);
}
通過上面原始碼部分的方法作用可以知道,BuildContext的主要作用就是訪問三個樹中間的指定節點,那么拿到這些節點有什么用呢?
首先我們知道widget是一個immutable物件,element和renderObject一般情況下是需要對開發者屏蔽的,那么他的最大意義對于開發者來說也就是獲取指定的widget,我們知道flutter開發中都是各種各樣的widget層級嵌套,我們要使用跨級訪問某些widget獲取資料時候,這個時候BuildContext的作用就來了,也就是我們所說的Widget資料共享需要使用到BuildContext,比如,上面各種Object.of(BuildContext context)獲取指定的類,拿到需要的物件,在進行對應的操作,
那么我們能不能在自己定義的widget方法中也利用BuildContext實作資料共享呢,肯定是可以的,下面我們簡單來寫個實體:
簡單的例子
添加靜態of方法
class CommonWidgets extends StatefulWidget {
final String routerName;
// 查找指定型別的最近祖先CommonWidgets
static CommonWidgets of(BuildContext context) {
CommonWidgets widget =
context.findAncestorWidgetOfExactType<CommonWidgets>();
return widget;
}
CommonWidgets({this.routerName, Key key}) : super(key: key);
@override
_CommonWidgetsState createState() => _CommonWidgetsState();
}
使用
//使用方法跟系統的諸多widget類似,通過of方法獲取到CommonWidgets,讀取routerName
CommonWidgets.of(context).routerName
總結
- BuildContext底層原理實作實際上就是Element
- of(context)原理,其實就是通過呼叫BuildContext各種實作方法遍歷widget tree和Element tree 從而獲取到指定的物件來達到資料共享的目的,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/354676.html
標籤:其他
上一篇:IOS技術分享| WebRTC iOS原始碼下載&編譯
下一篇:Flutter 容器類組件
