Доступ к данным currentUser в плагине firebase_auth версии 0.2.0
В моем приложении у меня есть ящик с UserAccountsDrawerHeader, который я передаю его свойства, просто получая свойство x изFirebaseAuth.instance.currentUser.x
В последней версии 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];
}
Мой метод
Я только что связал состояние аутентификации с моим ранее реализованным состоянием 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
Обновление 2:
Вот как я изменил функцию входа в Google из примеров Firebase:
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.
2 ответа
Есть несколько возможностей.
Во-первых: используйте виджет с отслеживанием состояния. Переопределите метод 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
будет вызван, когда будущее закончится. И они оба .then
а также .whenComplete
передаст результат вашего будущего в качестве параметра вашему myFunction
,
"Как правильно осуществить аутентификацию"? Вы определенно не должны делать это таким образом. Вы будете иметь тонны дублирования кода.
Самый идеальный способ организовать слои, такие как Аутентификация, выглядит следующим образом:
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();
}
В этом так много преимуществ, что нет причин не делать этого. Такие как "Код один раз, используйте везде". Или возможность иметь общее значение и переопределить его для определенного виджета. Вы поймете, что многие виджеты Flutter следуют этой идее. Такие как Навигатор, Эшафот, Тема,...
Но "Как это сделать?" Это все благодаря 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();
}