前语

最近闲下来在回顾常识点的时分,发现用了很久的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的完成也很简单,一个GlobalKeykeyElementvaluemap,供给了增加移除的办法。这两个办法又是何时调用的呢。

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撤销挂载,只有传入的keyGlobalKey时才会履行。


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中获取的,接下来咱们来看看在GetMaterialControllerkey是怎样界说的。

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,而GetMaterialApp对其做一层封装便是为了履行自己的一系列操作逻辑。

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原理便是GlobalKeyGet仅仅对这种方法的一种封装

关于BuildOwner的常识假如有不了解的,Flutter框架剖析 — runApp初始化里面有对WidgetBindingBuildOwner有的介绍。