我有一个特殊的用例,它使我头痛了几个小时。
我确实有一个使用Firebase和社交登录进行身份验证的应用程序。另外,我实现了redux以管理应用程序状态。
一切工作都很好,但是只有当我尝试在需要FirebaseUser
类数据的屏幕上注销时,渲染将失败,因为导航器的过渡仍在运行(触发重新渲染)我正从屏幕上移开),最后到达所需的位置,但是转换时屏幕出现了问题。
我的中间件代码是这样的:
Middleware<RootState> _signOutUser(GlobalKey navigatorKey) {
return (Store<RootState> store, dynamic dynamicAction, NextDispatcher next) {
final SignOut action = dynamicAction;
final NavigatorState nav = navigatorKey.currentState;
next(action);
store.dispatch(AppIsLoading());
Future<dynamic>(() async {
try {
await auth.signOut();
} on PlatformException catch (error) {
return Future<void>.error(error);
}
}).then<void>((dynamic _) {
nav.pushNamedAndRemoveUntil(Routes.home, (Route<dynamic> route) => false);
store.dispatch(UnsetUser());
store.dispatch(AppIsLoaded());
action.completer.complete();
}).catchError((Object response) {
nav.maybePop(nav.context);
action.completer.completeError(response);
store.dispatch(AppIsLoaded());
});
};
}
我最好这样做:
Middleware<RootState> _signOutUser(GlobalKey navigatorKey) {
return (Store<RootState> store, dynamic dynamicAction, NextDispatcher next) {
final SignOut action = dynamicAction;
final NavigatorState nav = navigatorKey.currentState;
next(action);
store.dispatch(AppIsLoading());
Future<dynamic>(() async {
try {
await auth.signOut();
} on PlatformException catch (error) {
return Future<void>.error(error);
}
}).then<void>((dynamic _) {
return nav.pushNamedAndRemoveUntil(Routes.home, (Route<dynamic> route) => false);
}).then<void>((dynamic _) {
store.dispatch(UnsetUser());
store.dispatch(AppIsLoaded());
action.completer.complete();
}).catchError((Object response) {
nav.maybePop(nav.context);
action.completer.completeError(response);
store.dispatch(AppIsLoaded());
});
};
}
但是也可以通过这种方式进行过渡时的渲染。
我可以在屏幕上添加保护装置,但这会导致屏幕看起来不像我刚才查看的屏幕。
还是在小部件内部有一种方法可以防止在特殊事件时重新渲染,从而使其保持原样?
我有点迷失了atm,因此我们将不胜感激。
谢谢!
答案 0 :(得分:0)
如您所愿,要获得理想的结果可能非常棘手,因为您正在等待登出(FirebaseUser
被删除的地方)并且只有 then 推送转到新屏幕。
问题在于,与此同时,删除用户后,您仍然需要当前屏幕上的一些数据(我相信吗?),并且在过渡期间会遇到异常,因为您尝试执行以下操作:访问当前不可用的数据。我说的对吗?
一个简单的解决方案(也许是最正确的解决方案)是在屏幕上让用户引用,直到您退出注销为止,直到仅在退出时才将其删除。我们无权访问您的窗口小部件,但是您可以创建一个获取用户引用的变量,然后注销即可注销该用户,但您仍然可以通过窗口小部件上的引用访问以前的数据。
另一种解决方案是仅在推送到新屏幕后才退出,但是如果出于某种原因退出失败,则您将无法正确回退,但是,通常,此退出是在客户端进行的尽力而为方法,它将始终为用户注销,无论是否失败或不远程进行。因此,通过这种方式,您可以简单地从await
中删除signOut
,因为它将同步结束该方法并随后调用注销,或者可以像这样更明确地使其:
try {
await nav.pushNamedAndRemoveUntil(
Routes.home, (Route<dynamic> route) => false).then((_) => auth.signOut());
store.dispatch(UnsetUser());
store.dispatch(AppIsLoaded());
action.completer.complete();
} on PlatformException catch (error) {
var response = await Future<void>.error(error);
await nav.maybePop(nav.context);
action.completer.completeError(response);
store.dispatch(AppIsLoaded());
}
答案 1 :(得分:0)
我找到了解决方案……这显然是flutter
框架中的错误。
我在flutter软件包的Navigator
和NavigatorState
类中进行了挖掘,并找到了未来从未完成的原因。
方法pushNamedAndRemoveUntil
(以及其他几个方法)确实返回一个完成器。到目前为止(正确)。但是,仅当从堆栈中弹出Route
时,它才完成。那是不正确的行为。
就我而言,它应该在新Route被推入堆栈时完成(并且过渡/动画确实完成了)。路线上实际上存在一个名为didPush
的方法,该方法从Animation中返回一个TickerFuture:
TickerFuture didPush() => TickerFuture.complete();
这与我对这种方法的期望一样多。
目前,我直接将其固定在包装中,但之后将提出问题,以便将来可以修复。