颤动:等待导航过渡结束

时间:2019-11-13 09:32:11

标签: flutter dart

我有一个特殊的用例,它使我头痛了几个小时。

我确实有一个使用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,因此我们将不胜感激。

谢谢!

2 个答案:

答案 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软件包的NavigatorNavigatorState类中进行了挖掘,并找到了未来从未完成的原因。

方法pushNamedAndRemoveUntil(以及其他几个方法)确实返回一个完成器。到目前为止(正确)。但是,仅当从堆栈中弹出Route时,它才完成。那是不正确的行为。

就我而言,它应该在新Route被推入堆栈时完成(并且过渡/动画确实完成了)。路线上实际上存在一个名为didPush的方法,该方法从Animation中返回一个TickerFuture:

TickerFuture didPush() => TickerFuture.complete();

这与我对这种方法的期望一样多。

目前,我直接将其固定在包装中,但之后将提出问题,以便将来可以修复。