我有一个WelcomeScreen
,其中包含注册和登录,以及HomeScreen
,我想在用户登录后重定向到该位置。要管理身份验证数据,我创建了一个auth.dart
static
属性和方法,这样我就可以在所有具有相同数据的页面上访问它们。
import 'package:firebase_auth/firebase_auth.dart';
class Auth {
static final auth = FirebaseAuth.instance;
static Future<void> logout() async {
await auth.signOut();
}
static Future<void> loginUser(String userEmail, String userPassword) async {
await auth.signInWithEmailAndPassword(email: userEmail, password: userPassword);
}
static Future<FirebaseUser> getCurrentUser() async {
return await auth.currentUser();
}
}
在main.dart
文件中,我正在使用StreamBuilder
基于更改身份验证数据来更改当前屏幕。我从this answer获得了这个StreamBuilder
代码。
home: StreamBuilder<FirebaseUser>(
stream: Auth.auth.onAuthStateChanged,
builder: (context, snapshot) {
if (snapshot.hasData) {
return HomeScreen();
} else {
return WelcomeScreen();
}
},
),
在我的登录屏幕中,我正在使用以下代码触发登录:
Future<void> login() async {
...
try {
await Auth.loginUser(userEmail, userPassword);
var user = await Auth.getCurrentUser();
print(user.displayName); // This works
} catch (error) {
print(error.message);
}
}
我不知道我使用的静态方法是否是处理Firebase身份验证的正确方法,但它似乎有效。登录后,我可以显示已登录用户的名称,但是StreamBuilder
中的main.dart
不能反映更新的身份验证数据,即不更改页面。
是因为静态方法还是在StreamBuilder
的实现中出现问题?
答案 0 :(得分:9)
[我将其添加为单独的答案,因为它使用了提供程序包]
您应该使用static
或其他一些模型来代替在Auth
类中创建变量和方法Provider
。我在bool
类中使用Auth
变量来跟踪登录,您可以根据需要修改该类。
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(
ChangeNotifierProvider<Auth>(
create: (_) => Auth(),
child: MaterialApp(home: MyApp()),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<Auth>(
builder: (_, auth, __) {
if (auth.loggedIn) return HomeScreen();
return WelcomeScreen();
},
);
}
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Home Screen')),
floatingActionButton: FloatingActionButton.extended(
label: Text('Sign out'),
onPressed: () async {
final auth = Provider.of<Auth>(context, listen: false);
await auth.logout();
},
),
);
}
}
class WelcomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Welcome Screen')),
body: Center(
child: RaisedButton(
onPressed: () => Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => LoginPage())),
child: Text('Go to Login Page'),
),
),
);
}
}
class LoginPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Login Page')),
body: Center(
child: RaisedButton(
onPressed: () async {
final auth = Provider.of<Auth>(context, listen: false);
await auth.loginUser('test@test.com', 'test1234');
await Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => MyApp()));
},
child: Text('Login'),
),
),
);
}
}
class Auth with ChangeNotifier {
final _auth = FirebaseAuth.instance;
bool _loggedIn = false;
bool get loggedIn => _loggedIn;
Future<void> logout() async {
await _auth.signOut();
_loggedIn = false;
notifyListeners();
}
Future<void> loginUser(String userEmail, String userPassword) async {
await _auth.signInWithEmailAndPassword(email: userEmail, password: userPassword);
_loggedIn = true;
notifyListeners();
}
Future<FirebaseUser> getCurrentUser() async {
return await _auth.currentUser();
}
}
现在您可以看到,即使您不是从LoginPage
的直接子代Consumer
登录,也可以看到它的builder
仍在被调用登录状态更改。但是,当您导航到LoginPage
时,由于窗口小部件树仅显示LoginPage
,因此需要弹出弹出窗口以返回上一页。在上一个示例中,我导航到HomeScreen
,但在此示例中,我弹出。
答案 1 :(得分:3)
屏幕截图:
我不确定您的工作方式,因此我添加了一个最小的工作代码,没有对您的Auth
类进行任何更改。尽管使用Provider
是个好主意,但是您也可以使用static
方法来完成工作。
修改后的代码:
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(MaterialApp(home: MyApp()));
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamBuilder<FirebaseUser>(
stream: Auth.auth.onAuthStateChanged,
builder: (context, snapshot) {
if (snapshot.hasData) return HomeScreen();
else return WelcomeScreen();
},
);
}
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Home Screen')),
floatingActionButton: FloatingActionButton.extended(
label: Text('Sign out'),
onPressed: Auth.logout,
),
);
}
}
class WelcomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Welcome Screen')),
body: Center(
child: RaisedButton(
onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (_) => LoginPage())),
child: Text('Go to Login Page'),
),
),
);
}
}
class LoginPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Login Page')),
body: Center(
child: RaisedButton(
onPressed: () async {
await Auth.loginUser('test@test.com', 'test1234');
await Navigator.pushAndRemoveUntil(context, MaterialPageRoute(builder: (_) => MyApp()), (_) => false);
},
child: Text('Login'),
),
),
);
}
}
答案 2 :(得分:1)
我制作了一个视频(https://youtu.be/iqy7xareuAI),讨论了这笔赏金,并带您完成了实现所需应用程序的步骤。它所需要的只是一个简单的StreamBuilder
和一个FutureBuilder
。
像provider
和singleton pattern
这样的更复杂的工具(您试图通过静态类来实现)可以应用于更复杂的应用程序,但此处不需要。
这是WelcomeScreen的代码:
import 'package:ctfultterfireexperiments/src/screens/home_screen.dart';
import 'package:ctfultterfireexperiments/src/screens/login_signup_screen.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
class WelcomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamBuilder<FirebaseUser>(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (BuildContext _, AsyncSnapshot<FirebaseUser> snapshot) {
//if the snapshot is null, or not has data it is signed out
if(! snapshot.hasData) return LoginSignupScreen();
// if the snapshot is having data it is signed in, show the homescreen
return HomeScreen();
},
);
}
}
这是HomeScreen.dart的代码
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Container(
child: Center(
child: FutureBuilder(
builder: (BuildContext context, AsyncSnapshot<FirebaseUser> snapshot) {
if(!snapshot.hasData) return LinearProgressIndicator();
return Text("Home Screen: ${snapshot.data.displayName}");
},
future: FirebaseAuth.instance.currentUser(),
)),
),
Spacer(),
RaisedButton(onPressed: () {FirebaseAuth.instance.signOut();})
],
);
}
}
这是LoginSignupScreen.dart的代码:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart';
class LoginSignupScreen extends StatelessWidget {
login() async{
final GoogleSignIn _googleSignIn = GoogleSignIn();
final _auth = FirebaseAuth.instance;
final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
final FirebaseUser user = (await _auth.signInWithCredential(credential)).user;
print("signed in " + user.displayName);
}
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Spacer(flex: 1,),
Text("Login/Signup Screen"),
Spacer(flex: 2,),
RaisedButton(onPressed: login)
],
);
}
}
这将作为最小的工作示例。
答案 3 :(得分:0)
我认为在Flutter中管理Firebase身份验证的最佳方法是使用提供程序包。您的Auth类缺少一件重要的事情,即onAuthStateChnaged方法。您可以在Auth类中创建流作为onAuthStateChanged的获取器。 Auth类将扩展ChangeNotifier类。 ChangeNotifier类是Flutter API的一部分。
class Auth extends ChangeNotifier {
final FirebaseAuth _auth = FirebaseAuth.instance;
// create a getter stream
Stream<FirebaseUser> get onAuthStateChanged => _auth.onAuthStateChanged;
//Sign in async functions here ..
}
用ChangeNotifierProvider(提供程序包的一部分)包装MaterialApp,并在create方法中返回Auth类的实例,如下所示:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => Auth(),
child: new MaterialApp(
home: Landing(),
),
);
}
}
现在将登录页面创建为无状态窗口小部件。使用Consumer或Provider.of(上下文)和流构建器来侦听auth更改,并根据需要呈现登录页面或主页。
class Landing extends StatelessWidget {
@override
Widget build(BuildContext context) {
Auth auth = Provider.of<Auth>(context);
return StreamBuilder<FirebaseUser>(
stream: auth.onAuthStateChanged,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
FirebaseUser user = snapshot.data;
if (user == null) {
return LogIn();
}
return Home();
} else {
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
},
);
}
}
您可以从Flutter官方文档中了解有关提供者的状态管理的更多信息。请点击以下链接:https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple