【问题标题】:Declarative auth routing with Firebase使用 Firebase 进行声明式身份验证路由
【发布时间】:2020-05-01 18:01:36
【问题描述】:

我没有在用户登录或退出时使用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', (_) =&gt; false),要么得到一个关于user 为空的错误。

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

我通过将builder 属性添加到MaterialApp 来接近

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

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

【问题讨论】:

  • 用Consumer 包裹你的设置屏幕,就像你在第一个屏幕上所做的那样看起来不适合你?我的意思是在 SettingPage.dart 中是这样的:isLoggedIn ? SettingPage() : AuthPage()
  • 嗨,LoVe,这是一种方法,但我必须对每条路线都这样做,不是吗?假设我有一个关于页面或验证电子邮件页面等。

标签: flutter


【解决方案1】:

我找到了一种方法来实现这一点(LoVe 的好答案仍然完全有效),以防其他人遇到这个问题:

您需要利用嵌套导航器。 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 方法。

【讨论】:

  • 刚刚遇到这个,您能解释一下自动导航是如何进行的吗?请提供更多代码
【解决方案2】:

怎么样,你用这个小部件包装所有依赖于这个流的屏幕,它向你隐藏了监听流并相应更新的逻辑(你应该像你在问题中所做的那样提供流):

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
            )));

【讨论】:

  • 感谢LoVe,我现在肯定会使用它。我仍然很好奇是否有办法在树的更高处做到这一点,但我喜欢你的方法
猜你喜欢
  • 2017-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-09
  • 2017-03-11
  • 2021-01-22
  • 2018-10-19
相关资源
最近更新 更多