我没有使用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
时,我无法调用任何依赖于父级导航器的东西。
答案 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
方法即可立即被踢出身份验证页面。