在 firebase_auth 插件版本 0.2.0 中访问 currentUser 数据
Accessing currentUser data in firebase_auth plugin version 0.2.0
在我的应用程序中,我有一个带有 UserAccountsDrawerHeader 的抽屉,我通过简单地从 [= 获取 x 属性 来提供它的属性73=]
在最新的 firebase_auth 0.2.0 版本中,其中 currentUser() 是异步的。
我已经尝试了几个小时来存储当前登录用户的信息,但还没有找到正确的方法。
我知道我可以通过以下方式访问它们:
Future<String> _getCurrentUserName() async {
FirebaseUser user = await FirebaseAuth.instance.currentUser();
return user.displayName;
}
...
new UserAccountsDrawerHeader(accountName: new Text(_getCurrentUserName()))
我知道这些代码片段会导致类型不匹配,但我只是想说明我正在尝试做什么。
我究竟遗漏了什么阻碍我找到解决方案的原因?
更新
class _MyTabsState extends State<MyTabs> with TickerProviderStateMixin {
TabController controller;
Pages _page;
String _currentUserName;
String _currentUserEmail;
String _currentUserPhoto;
@override
void initState() {
super.initState();
_states();
controller = new TabController(length: 5, vsync: this);
controller.addListener(_select);
_page = pages[0];
}
我的方法
我刚刚将 auth 状态与我之前实现的 TabBar 状态结合起来
_states() async{
var user = await FirebaseAuth.instance.currentUser();
var name = user.displayName;
var email = user.email;
var photoUrl = user.photoUrl;
setState(() {
this._currentUserName=name;
this._currentUserEmail=email;
this._currentUserPhoto=photoUrl;
_page = pages[controller.index];
});
}
我的抽屉
drawer: new Drawer(
child: new ListView(
children: <Widget>[
new UserAccountsDrawerHeader(accountName: new Text(_currentUserName) ,
accountEmail: new Text (_currentUserEmail),
currentAccountPicture: new CircleAvatar(
backgroundImage: new NetworkImage(_currentUserPhoto),
),
这是我从调试控制台得到的异常
I/flutter (14926): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (14926): The following assertion was thrown building MyTabs(dirty, state: _MyTabsState#f49aa(tickers:
I/flutter (14926): tracking 1 ticker)):
I/flutter (14926): 'package:flutter/src/widgets/text.dart': Failed assertion: line 207 pos 15: 'data != null': is not
I/flutter (14926): true.
I/flutter (14926): Either the assertion indicates an error in the framework itself, or we should provide substantially
更新二:
这就是我修改 firebase 示例中的 google 登录功能的方式:
Future <FirebaseUser> _testSignInWithGoogle() async {
final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
//checking if there is a current user
var check = await FirebaseAuth.instance.currentUser();
if (check!=null){
final FirebaseUser user = check;
return user;
}
else{
final FirebaseUser user = await _auth.signInWithGoogle(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
assert(user.email != null);
assert(user.displayName != null);
assert(!user.isAnonymous);
assert(await user.getToken() != null);
return user;
}
}
更新 3:
我的主要功能
void main() {
runApp(
new MaterialApp(
home: new SignIn(),
routes: <String, WidgetBuilder>{
"/SignUp":(BuildContext context)=> new SignUp(),
"/Login": (BuildContext context)=> new SignIn(),
"/MyTabs": (BuildContext context)=> new MyTabs()},
));
}
然后我的登录包含一个 google 按钮,按下时:
onPressed: () { _testSignInWithGoogle(). //async returns FirebaseUser
whenComplete(()=>Navigator.of(context).pushNamed("/MyTabs")
);
}
更新 1 中的抽屉包含在 MyTabs 构建中。
有几种可能性。
第一:使用有状态的小部件
像这样重写 initState 方法:
class Test extends StatefulWidget {
@override
_TestState createState() => new _TestState();
}
class _TestState extends State<Test> {
String _currentUserName;
@override
initState() {
super.initState();
doAsyncStuff();
}
doAsyncStuff() async {
var name = await _getCurrentUserName();
setState(() {
this._currentUserName = name;
});
}
@override
Widget build(BuildContext context) {
if (_currentUserName == null)
return new Container();
return new Text(_currentUserName);
}
}
第二:使用 FutureBuilder 小部件
基本上,它是那些不想使用有状态小部件的人的包装器。它最终做同样的事情。
但是你将无法在其他地方重用你的未来。
class Test extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new FutureBuilder(
future: _getCurrentUserName(),
builder: (context, AsyncSnapshot<int> snapshot) {
if (snapshot.hasData)
return new Text(snapshot.data.toString());
else
return new Container();
},
);
}
}
说明:
您的 getCurrentUserName 是异步的。
您不能直接将它与其他同步函数混合使用。
异步函数非常有用。但是如果你想使用它们,只要记住两件事:
在另一个异步函数中,您可以 var x = await myFuture
,它会等到 myFuture
完成才能得到它的结果。
但是您不能在同步函数中使用 await
。
相反,您可以使用
myFuture.then(myFunction)
或 myFuture.whenComplete(myFunction)
。 myFunction
将在 future 完成时调用。他们 .then
和 .whenComplete
都会将你未来的结果作为参数传递给你的 myFunction
。
"How to properly implement authentification" ?
你绝对不应该这样做。你会有大量的代码重复。
Authentification等层最理想的组织方式是这样的:
runApp(new Configuration.fromFile("confs.json",
child: new Authentification(
child: new MaterialApp(
home: new Column(
children: <Widget>[
new Text("Hello"),
new AuthentifiedBuilder(
inRoles: [UserRole.admin],
builder: (context, user) {
return new Text(user.name);
}
),
],
),
),
),
));
然后,当您需要配置或小部件内的当前用户时,您可以这样做:
@override
Widget build(BuildContext context) {
var user = Authentification.of(context).user;
var host = Configuration.of(context).host;
// do stuff with host and the user
return new Container();
}
这样做有很多好处,没有理由不这样做。
如"Code once, use everywhere"。或者具有通用值并为特定小部件覆盖它的能力。
你会发现很多 Flutter 小部件都遵循这个想法。
比如 Navigator、Scaffold、Theme、...
但是"How to do this ??"
这都归功于 BuildContext context
参数。它提供了一些帮手来做到这一点。
例如,Authentification.of(context)
的代码如下:
class Authentification extends StatefulWidget {
final Widget child;
static AuthentificationData of(BuildContext context) {
final AuthentificationData auth = context.inheritFromWidgetOfExactType(AuthentificationData);
assert(auth != null);
return auth;
}
Authentification({this.child});
@override
AuthentificationState createState() => new AuthentificationState();
}
在我的应用程序中,我有一个带有 UserAccountsDrawerHeader 的抽屉,我通过简单地从 [= 获取 x 属性 来提供它的属性73=]
在最新的 firebase_auth 0.2.0 版本中,其中 currentUser() 是异步的。
我已经尝试了几个小时来存储当前登录用户的信息,但还没有找到正确的方法。
我知道我可以通过以下方式访问它们:
Future<String> _getCurrentUserName() async {
FirebaseUser user = await FirebaseAuth.instance.currentUser();
return user.displayName;
}
...
new UserAccountsDrawerHeader(accountName: new Text(_getCurrentUserName()))
我知道这些代码片段会导致类型不匹配,但我只是想说明我正在尝试做什么。
我究竟遗漏了什么阻碍我找到解决方案的原因?
更新
class _MyTabsState extends State<MyTabs> with TickerProviderStateMixin {
TabController controller;
Pages _page;
String _currentUserName;
String _currentUserEmail;
String _currentUserPhoto;
@override
void initState() {
super.initState();
_states();
controller = new TabController(length: 5, vsync: this);
controller.addListener(_select);
_page = pages[0];
}
我的方法
我刚刚将 auth 状态与我之前实现的 TabBar 状态结合起来
_states() async{
var user = await FirebaseAuth.instance.currentUser();
var name = user.displayName;
var email = user.email;
var photoUrl = user.photoUrl;
setState(() {
this._currentUserName=name;
this._currentUserEmail=email;
this._currentUserPhoto=photoUrl;
_page = pages[controller.index];
});
}
我的抽屉
drawer: new Drawer(
child: new ListView(
children: <Widget>[
new UserAccountsDrawerHeader(accountName: new Text(_currentUserName) ,
accountEmail: new Text (_currentUserEmail),
currentAccountPicture: new CircleAvatar(
backgroundImage: new NetworkImage(_currentUserPhoto),
),
这是我从调试控制台得到的异常
I/flutter (14926): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (14926): The following assertion was thrown building MyTabs(dirty, state: _MyTabsState#f49aa(tickers:
I/flutter (14926): tracking 1 ticker)):
I/flutter (14926): 'package:flutter/src/widgets/text.dart': Failed assertion: line 207 pos 15: 'data != null': is not
I/flutter (14926): true.
I/flutter (14926): Either the assertion indicates an error in the framework itself, or we should provide substantially
更新二:
这就是我修改 firebase 示例中的 google 登录功能的方式:
Future <FirebaseUser> _testSignInWithGoogle() async {
final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
//checking if there is a current user
var check = await FirebaseAuth.instance.currentUser();
if (check!=null){
final FirebaseUser user = check;
return user;
}
else{
final FirebaseUser user = await _auth.signInWithGoogle(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
assert(user.email != null);
assert(user.displayName != null);
assert(!user.isAnonymous);
assert(await user.getToken() != null);
return user;
}
}
更新 3:
我的主要功能
void main() {
runApp(
new MaterialApp(
home: new SignIn(),
routes: <String, WidgetBuilder>{
"/SignUp":(BuildContext context)=> new SignUp(),
"/Login": (BuildContext context)=> new SignIn(),
"/MyTabs": (BuildContext context)=> new MyTabs()},
));
}
然后我的登录包含一个 google 按钮,按下时:
onPressed: () { _testSignInWithGoogle(). //async returns FirebaseUser
whenComplete(()=>Navigator.of(context).pushNamed("/MyTabs")
);
}
更新 1 中的抽屉包含在 MyTabs 构建中。
有几种可能性。
第一:使用有状态的小部件 像这样重写 initState 方法:
class Test extends StatefulWidget {
@override
_TestState createState() => new _TestState();
}
class _TestState extends State<Test> {
String _currentUserName;
@override
initState() {
super.initState();
doAsyncStuff();
}
doAsyncStuff() async {
var name = await _getCurrentUserName();
setState(() {
this._currentUserName = name;
});
}
@override
Widget build(BuildContext context) {
if (_currentUserName == null)
return new Container();
return new Text(_currentUserName);
}
}
第二:使用 FutureBuilder 小部件 基本上,它是那些不想使用有状态小部件的人的包装器。它最终做同样的事情。 但是你将无法在其他地方重用你的未来。
class Test extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new FutureBuilder(
future: _getCurrentUserName(),
builder: (context, AsyncSnapshot<int> snapshot) {
if (snapshot.hasData)
return new Text(snapshot.data.toString());
else
return new Container();
},
);
}
}
说明: 您的 getCurrentUserName 是异步的。 您不能直接将它与其他同步函数混合使用。 异步函数非常有用。但是如果你想使用它们,只要记住两件事:
在另一个异步函数中,您可以 var x = await myFuture
,它会等到 myFuture
完成才能得到它的结果。
但是您不能在同步函数中使用 await
。
相反,您可以使用
myFuture.then(myFunction)
或 myFuture.whenComplete(myFunction)
。 myFunction
将在 future 完成时调用。他们 .then
和 .whenComplete
都会将你未来的结果作为参数传递给你的 myFunction
。
"How to properly implement authentification" ? 你绝对不应该这样做。你会有大量的代码重复。
Authentification等层最理想的组织方式是这样的:
runApp(new Configuration.fromFile("confs.json",
child: new Authentification(
child: new MaterialApp(
home: new Column(
children: <Widget>[
new Text("Hello"),
new AuthentifiedBuilder(
inRoles: [UserRole.admin],
builder: (context, user) {
return new Text(user.name);
}
),
],
),
),
),
));
然后,当您需要配置或小部件内的当前用户时,您可以这样做:
@override
Widget build(BuildContext context) {
var user = Authentification.of(context).user;
var host = Configuration.of(context).host;
// do stuff with host and the user
return new Container();
}
这样做有很多好处,没有理由不这样做。 如"Code once, use everywhere"。或者具有通用值并为特定小部件覆盖它的能力。 你会发现很多 Flutter 小部件都遵循这个想法。 比如 Navigator、Scaffold、Theme、...
但是"How to do this ??"
这都归功于 BuildContext context
参数。它提供了一些帮手来做到这一点。
例如,Authentification.of(context)
的代码如下:
class Authentification extends StatefulWidget {
final Widget child;
static AuthentificationData of(BuildContext context) {
final AuthentificationData auth = context.inheritFromWidgetOfExactType(AuthentificationData);
assert(auth != null);
return auth;
}
Authentification({this.child});
@override
AuthentificationState createState() => new AuthentificationState();
}