Shady Aziza
Shady Aziza

Reputation: 53327

Accessing currentUser data in firebase_auth plugin version 0.2.0

In my app, I have a drawer with a UserAccountsDrawerHeader, which I feed its properties by simply getting the x property from FirebaseAuth.instance.currentUser.x

In the latest firebase_auth 0.2.0 version , where currentUser() is async.

I have been trying for several hours to store the information of the currently logged user and have not yet reached the correct way to do this.

I understand that I can access them by something like the following:

   Future<String> _getCurrentUserName() async {
  FirebaseUser user = await FirebaseAuth.instance.currentUser();
  return user.displayName;
}

...

new UserAccountsDrawerHeader(accountName: new Text(_getCurrentUserName()))

I understand that these code snippets will give type mismatch, but I am just trying to illustrate what I am trying to do.

What am I missing exactly that is preventing me from reaching a solution?

Update

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];
  }

My method

I just coupled the auth state with my previously implemented TabBar state

   _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];
    });
  }

My Drawer

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),
              ),

Here is the exception I get from the debug console

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

Update 2:

This is how I modified the google sign in function from the firebase examples:

    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;
    }
    }

Update 3:

My main function

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()},

  ));
}

And then my SignIn contains a google button that when pressed:

onPressed: () {  _testSignInWithGoogle(). //async returns FirebaseUser
                          whenComplete(()=>Navigator.of(context).pushNamed("/MyTabs")
                          );
                        }

and the Drawer from update 1 is included within MyTabs build.

Upvotes: 3

Views: 1923

Answers (2)

R&#233;mi Rousselet
R&#233;mi Rousselet

Reputation: 277067

"How to properly implement authentification" ? You should definitely not do it this way. You'll have tons of code duplication.

The most ideal way to organise layers such as Authentification is like this :

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);
            }
          ),
        ],
      ),
    ),
  ),
));

And then, when you need a configuration or the current user inside a widget, you'd do this :

@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();
}

There are so many advantages about doing this, that there's no reason not to do it. Such as "Code once, use everywhere". Or the ability to have a generic value and override it for a specific widget. You'll realise that a lot of Flutter widgets are following this idea. Such as Navigator, Scaffold, Theme, ...

But "How to do this ??" It's all thanks to the BuildContext context parameter. Which provides a few helpers to do it. For exemple, the code of Authentification.of(context) would be the following :

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();
}

Upvotes: 1

R&#233;mi Rousselet
R&#233;mi Rousselet

Reputation: 277067

There are several possibilities.

First : Use a stateful widget Override the initState method like this :

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);
  }
}

Second : Use the FutureBuilder widget Basically, it's a wrapper for those who don't want to use a stateful widget. It does the same in the end. But you won't be able to reuse your future somewhere else.

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();
      },
    );
  }
}

Explanation : Your getCurrentUserName is asynchronous. You can't just directly mix it with other synchronous functions. Asynchronous functions are quite useful. But if you want to use them, just remember two things :

Inside another async function, you can var x = await myFuture, which will wait until myFuture finish to get it's result.

But you can't use await inside a sync function. Instead, you can use myFuture.then(myFunction) or myFuture.whenComplete(myFunction). myFunction will be called when the future is finished. And they both .then and .whenComplete will pass the result of your future as parameter to your myFunction.

Upvotes: 2

Related Questions