依赖注入
AI解释:
依赖注入的核心思想是将对象的创建和维护责任从对象本身转移到其他组件,例如容器或框架。这样,对象只需要关心如何使用依赖对象,而不需要关心依赖对象的创建和维护。
在Flutter中一般是注册回调函数实现注入的。 为了能够在任何地方快速的访问,合理的设计依赖注入框架是必须的,由于Flutter的开放性,我们可以巧妙的利用路由回调来完成初始步骤,并为依赖提供路由的生命周期功能。
路由NavigatorObserver
监听路由的变化:
@override
void didPush(Route route, Route? previousRoute) {
}
@override
void didRemove(Route route, Route? previousRoute) {
}
@override
void didReplace({Route? newRoute, Route? oldRoute}) {
}
@override
void didPop(Route route, Route? previousRoute) {
}
利用路由监听回调函数可以很方便的管理路由的生命周期,在这里一般只需要disRemove``didPop这两个方法。
是否要依赖BuildContext
在我看来BuildContext必不可少,在context可见的情况下,一般可以根据MediaQuery.of(context)得到当前页面Route,依赖就可以轻松注入了,并且在Route.dispose之后依赖也跟着销毁;
abstract mixin class DependenceManager<T extends RouteNode> {
void didPop(Route route) {
_pop(route);
}
void didRemove(Route route) {
_pop(route);
}
final _caches = <Object, T>{};
T? _currentDependence;
void clear() {
_caches.clear();
_currentDependence = null;
}
void _pop(Route route) {
final dependence = _caches[route];
if (dependence == null) {
if (route is TransitionRoute) {
_caches[route] = createNode(route)..onPop();
route.completed.whenComplete(() {
final dependence = _caches.remove(route);
dependence?.completed();
});
}
return;
}
if (identical(_currentDependence, dependence)) {
_currentDependence = (dependence.child ?? dependence.parent) as T?;
}
dependence.onPop();
}
T createNode(Route route);
T? getRouteDependence(BuildContext? context) {
if (context == null) return _currentDependence;
final route = ModalRoute.of(context);
if (route == null) return _currentDependence;
final value = _caches[route];
if (value != null) return value;
final currentRouteDep = createNode(route);
_caches[route] = currentRouteDep;
route.completed.whenComplete(() {
final dependence = _caches.remove(route);
dependence?.completed();
});
if (_currentDependence != null) {
_currentDependence!.insertChild(currentRouteDep);
}
return _currentDependence = currentRouteDep;
}
}
abstract class RouteNode {
RouteNode? get parent;
RouteNode? get child;
// 这里存储当前路由所有的依赖,以`Type`为`Key`,`Controller`为依赖类型或更复杂的类型
final _deps = <Type, Controller>{};
// ...
// 在 pop 时立即调用
void onPop();
void insertChild(RouteNode newChild);
// route.completed 异步调用,确保页面不再可用,放心销毁
void completed();
// ...
}
为什么要这样设计有以下几个原因:
- 严格遵守路由生命周期
- 路由分离,依赖也要分离,在未调用
Route.dispose之前页面是激活状态的,其原因在于过渡动画,在动画期间是必须支持可访问的。 RouteNode是一个链表结构,在多个页面共享依赖时方便查找,getRouteDependence支持没有BuildContext的情况。
至此可以看出RouteNode才是真正管理依赖的地方,之前的步骤只是为了配置路由的生命周期并且与依赖关联;
具体实现可以看github:flutter_nop,针对Controller类型作了更复杂的设计,解决依赖之间难以查找的问题。