使用Firebase进行声明式身份验证路由

时间:2020-05-01 18:01:36

标签: flutter

我没有使用Navigator.push来推动用户登录或注销,而是一直使用流来侦听登录和注销事件。

StreamProvider<FirebaseUser>.value(
  value: FirebaseAuth.instance.onAuthStateChanged,
)

它对于本地路由非常有用,因为它可以立即处理仍通过身份验证的用户登录。

Consumer<FirebaseUser>(
  builder: (_, user, __) {
    final isLoggedIn = user != null;

    return MaterialApp(
      home: isLoggedIn ? HomePage() : AuthPage(),
      // ...
    );
  },
);

但是,这仅是本地路线。例如,如果用户随后导航到设置页面,然后单击按钮以注销,则不会以编程方式注销并再次跳转到身份验证屏幕。我要么不得不说Navigator.of(context).pushNamedAndRemoveUntil('/auth', (_) => false),要么会收到关于user为空的错误。

这很有道理。我只是在寻找另一种可能的方式,当他们注销后,我自己不必进行任何堆栈管理。

通过将builder属性添加到MaterialApp

builder: (_, widget) {
  return isLoggedIn ? widget : AuthPage();
},

这使我在未经身份验证后成功地将我移到了身份验证页面,但事实证明,widget实际上是导航器。那意味着当我回到AuthPage时,我无法调用任何依赖于父级导航器的东西。

2 个答案:

答案 0 :(得分:1)

这是怎么回事,您可以使用此小部件包装依赖于此流的所有屏幕,该小部件向您隐藏了侦听该流并进行相应更新的逻辑(您应该像在问题中一样提供流):

class AuthDependentWidget extends StatelessWidget {
  final Widget childWidget;

  const AuthDependentWidget({Key key, @required this.childWidget})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return StreamBuilder(
      stream: FirebaseAuth.instance.onAuthStateChanged,
      builder: (BuildContext context, AsyncSnapshot snapshot) {
        if (snapshot.hasData) {//you handle other cases...
          if (snapshot.currentUser() != null) return childWidget();
        } else {
          return AuthScreen();
        }
      },
    );
  }
}

然后您可以按如下所示从其他页面推送时使用它:

Navigator.of(context).pushReplacement(MaterialPageRoute(
        builder: (ctx) => AuthDependentWidget(
              childWidget: SettingsScreen(),//or any other screen that should listen to the stream
            )));

答案 1 :(得分:1)

我找到了一种方法来解决这个问题(爱的好答案仍然是完全有效的),以防其他人涉足此问题:

您需要利用嵌套的导航器。 Root将是内部导航器,外部导航器是由MaterialApp创建的:

return MaterialApp(
  home: isLoggedIn ? Root() : AuthPage(),
  routes: {
    Root.routeName: (_) => Root(),
    AuthPage.routeName: (_) => AuthPage(),
  },
);

您的Root将保存已认证用户的导航

class Root extends StatefulWidget {
  static const String routeName = '/root';

  @override
  _RootState createState() => _RootState();
}

class _RootState extends State<Root> {
  final _appNavigatorKey = GlobalKey<NavigatorState>();
  
  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async {
        final canPop = _appNavigatorKey.currentState.canPop();

        if (canPop) {
          await _appNavigatorKey.currentState.maybePop();
        }

        return !canPop;
      },
      child: Navigator(
        initialRoute: HomePage.routeName,
        onGenerateRoute: (RouteSettings routeSettings) {
          return MaterialPageRoute(builder: (_) {
            switch (routeSettings.name) {
              case HomePage.routeName:
                return HomePage();
              case AboutPage.routeName:
                return AboutPage();
              case TermsOfUsePage.routeName:
                return TermsOfUsePage();
              case SettingsPage.routeName:
                return SettingsPage();
              case EditorPage.routeName:
                return EditorPage();
              default:
                throw 'Unknown route ${routeSettings.name}';
            }
          });
        },
      ),
    );
  }
}

现在,您可以在设置页面(或任何其他页面)内对(FirebaseAuth.instance.signout())进行身份验证,而无需调用Navigator方法即可立即被踢出身份验证页面。