Доступ к данным 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();
}
Другие вопросы по тегам