Flutter中Globalkey和Get是如何获取BuildContext的
前言
最近闲下来在回顾知识点的时候,发现用了很久的Get.context
却并不知道它的具体实现逻辑,和GlobalKey
获取的BuildContext
的方式又有什么共同点和差异点。今天就来分析一下它们。
GlobalKey的实现
@optionalTypeArgs
abstract class GlobalKey<T extends State<StatefulWidget>> extends Key {
factory GlobalKey({ String? debugLabel }) => LabeledGlobalKey<T>(debugLabel);
const GlobalKey.constructor() : super.empty();
Element? get _currentElement => WidgetsBinding.instance.buildOwner!._globalKeyRegistry[this];
BuildContext? get currentContext => _currentElement;
Widget? get currentWidget => _currentElement?.widget;
T? get currentState {
final Element? element = _currentElement;
if (element is StatefulElement) {
final StatefulElement statefulElement = element;
final State state = statefulElement.state;
if (state is T) {
return state;
}
}
return null;
}
}
GlobalKey
是一个抽象类,我们创建的GlobalKey
实际返回的是LabeledGlobalKey
,这里我们不去探究。
GlobalKey
内部实现非常简单,提供了三个getter
,这里我们要探究的是currentContext
。
currentContext从何而来
Element? get _currentElement => WidgetsBinding.instance.buildOwner!._globalKeyRegistry[this];
BuildContext? get currentContext => _currentElement;
可以看到currentContext
获取的是_currentElement
,_currentElement
又是从_globalKeyRegistry
中获取的传入当前this
对象。
class BuildOwner {
final Map<GlobalKey, Element> _globalKeyRegistry = <GlobalKey, Element>{};
void _registerGlobalKey(GlobalKey key, Element element) {
_globalKeyRegistry[key] = element;
}
void _unregisterGlobalKey(GlobalKey key, Element element) {
if (_globalKeyRegistry[key] == element) {
_globalKeyRegistry.remove(key);
}
}
}
可以看到在BuildOwner
关于_globakKeyRegistry
的实现也很简单,一个GlobalKey
做key
,Element
做value
的map
,提供了添加
与移除
的方法。这两个方法又是何时调用的呢。
GlobalKey添加、取消
我们都知道Globalkey
是作为key
传入Widget
的,我们随便找一个Widget
一探究竟。
abstract class StatefulWidget extends Widget {
const StatefulWidget({ super.key });
}
abstract class Widget extends DiagnosticableTree {
const Widget({ this.key });
final Key? key;
}
我们在Widget
传入的key
最终会赋值给Widget
,而Widget
只是作为配置项最终创建的是Element
,所以我们进入Element
查看。
abstract class Element extends DiagnosticableTree implements BuildContext {
void mount(Element? parent, Object? newSlot) {
if (parent != null) {
_owner = parent.owner;
}
final Key? key = widget.key;
if (key is GlobalKey) {
owner!._registerGlobalKey(key, this);
}
}
void unmount() {
final Key? key = _widget?.key;
if (key is GlobalKey) {
owner!._unregisterGlobalKey(key, this);
}
}
去掉无关代码,ELement
中添加
和移除GlobalKey
的地方有两处,mount
挂载和unmount
取消挂载,只有传入的key
为GlobalKey
时才会执行。
abstract class GlobalKey<T extends State<StatefulWidget>> extends Key {
Element? get _currentElement => WidgetsBinding.instance.buildOwner!._globalKeyRegistry[this];
BuildContext? get currentContext => _currentElement;
}
再回过头来看GlobalKey
内部实现,就能明白GlobakKey
是如何拿到当前当前BuildContext
的了。
Get.context的实现
extension GetNavigation on GetInterface {
GlobalKey<NavigatorState> get key => _getxController.key;
BuildContext? get context => key.currentContext;
static GetMaterialController _getxController = GetMaterialController();
}
点击查看Get.context
源码,我们可以找到上面三行代码,可以看到Get
获取context
的方式也是用的GlobalKey
,它只是对这个方式做了一层封装供我们使用。
这里的key
是从GetMaterialController
中获取的,接下来我们来看看在GetMaterialController
中key
是怎么定义的。
Get中GlobalKey是如何创建的
class GetMaterialController extends SuperController {
var _key = GlobalKey<NavigatorState>(debugLabel: 'Key Created by default');
GlobalKey<NavigatorState> get key => _key;
GlobalKey<NavigatorState>? addKey(GlobalKey<NavigatorState> newKey) {
_key = newKey;
return key;
}
}
在GetMaterialController
中有一个默认的key
,同时也能调用addKey
替换默认的key
,这个默认的key
又是何时生效的呢。
GlobakKey如何起作用
我们把目光放回到main.dart
中,再使用了Get
我们都会把MaterialApp
替换成GetMaterialApp
,而Get
对MaterialApp
对其做一层封装就是为了执行自己的一系列操作逻辑。
class GetMaterialApp extends StatelessWidget {
final GlobalKey<NavigatorState>? navigatorKey;
const GetMaterialApp({
Key? key,
this.navigatorKey,
});
@override
Widget build(BuildContext context) => GetBuilder<GetMaterialController>(
builder: (_) =>
MaterialApp(
navigatorKey: (navigatorKey == null
? Get.key
: Get.addKey(navigatorKey!)),
)
}
可以看到,在Get
中如果我们传入了navigatorKey
就调用addKey
替换它的默认key
,如果没有传入就使用Get
的默认key
。
看到这里我们也就能明白Get.context
是如何起作用和获取到了。
总结
GloblKey
最终赋值给Widget
、在对应的Element
中调用BuildOwner
进行添加和取消GlobalKey
获取到的key
是在BuildOwner
中添加时传入的当前Element
Get
中有默认的GlobalKey
,在GetMaterialApp
中如果我们传入了navigatorKey
,就会替代默认的key
Get.context
原理就是GlobalKey
,Get
只是对这种方式的一种封装
关于BuildOwner
的知识如果有不了解的,Flutter框架分析 -- runApp初始化里面有对WidgetBinding
和BuildOwner
有的介绍。
转载自:https://juejin.cn/post/7221359467618893884