Hi
I have a working example of Firebase Auth Flow.
When I try to replace MaterialApp with GetMaterialApp, then suddenly got an error and navigation not working anymore.
I/BiChannelGoogleApi(19411): [FirebaseAuth: ] getGoogleApiForMethod() returned Gms: com.google.firebase.auth.api.internal.zzaq@7a7dc93
D/FirebaseAuth(19411): Notifying id token listeners about user ( 1Sf6z0hAQIUC1wXyleKHj2Nb7Jv1 ).
D/FirebaseAuth(19411): Notifying auth state listeners about user ( 1Sf6z0hAQIUC1wXyleKHj2Nb7Jv1 ).
I/flutter (19411): [GET] onClose of GetMaterialController called
I/flutter (19411): [GET] GetMaterialController deleted from memory
This is my code
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:jazapos/models/firebase_user_info.dart';
import 'package:jazapos/services/auth_service.dart';
import 'package:jazapos/services/firebase_auth_service.dart';
import 'package:provider/provider.dart';
void main() {
Get.lazyPut<AuthService>(() => FirebaseAuthService());
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AuthWidgetBuilder(
builder: (context, snapshot) {
return MaterialApp(
home: Landing(userSnapshot: snapshot),
);
},
);
}
}
class AuthWidgetBuilder extends StatelessWidget {
AuthWidgetBuilder({
Key key,
@required this.builder,
}) : super(key: key);
final Widget Function(BuildContext, AsyncSnapshot<FirebaseUserInfo>) builder;
final _auth = AuthService.to;
@override
Widget build(BuildContext context) {
return StreamBuilder<FirebaseUserInfo>(
stream: _auth.onAuthStateChanged,
builder: (context, snapshot) {
final FirebaseUserInfo user = snapshot.data;
if (user != null) {
return Provider(
create: (context) => Provider<FirebaseUserInfo>.value(value: user),
child: builder(context, snapshot),
);
}
return builder(context, snapshot);
},
);
}
}
class Landing extends StatelessWidget {
final AsyncSnapshot<FirebaseUserInfo> userSnapshot;
const Landing({Key key, this.userSnapshot}) : super(key: key);
@override
Widget build(BuildContext context) {
if (userSnapshot.connectionState == ConnectionState.active) {
if (userSnapshot.hasData) {
return Home();
} else {
return Login();
}
}
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
}
class Login extends StatelessWidget {
final _auth = AuthService.to;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Login'),
FlatButton(
child: Text('Go Anonymous'),
onPressed: _auth.signInAnonymously,
)
],
),
),
);
}
}
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Home'),
FlatButton(
child: Text('Go to Account'),
onPressed: () {
// Get.to(Account());
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
return Account();
},
),
);
},
)
],
),
),
);
}
}
class Account extends StatelessWidget {
@override
Widget build(BuildContext context) {
final _auth = AuthService.to;
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Account'),
FlatButton(
onPressed: _auth.signOut,
child: Text('Sign out'),
)
],
),
),
);
}
}
I really want to use Get because it looks easier to navigate and doing dependency injection.
Thanks for your help.
Easy way:
MaterialApp(
navigatorKey: Get.key,
navigatorObserver: [GetObserver()]
Correct way:
Exclude all of your classes, you just need a Controller to control the auth, with the example I gave you using ever.
You can do what you are trying to do in literally 5 lines of code.
MaterialApp(
navigatorKey: Get.key,
navigatorObserver: [GetObserver()]
After adding code above into MaterialApp then snapshot.connectionState always return ConnectionState.waiting. Any idea whats happen?
For the correct way, I still not fully understand. I'll try to test more about it.
MaterialApp(
navigatorKey: Get.key,
navigatorObserver: [GetObserver()]After adding code above into
MaterialAppthensnapshot.connectionStatealways returnConnectionState.waiting. Any idea whats happen?For the correct way, I still not fully understand. I'll try to test more about it.
Adding the key will not make any difference in your application, if this error appeared with the key, without it the same error should be displayed.
I will try in the future to insert an example of how to login with Get.
Ok, I'll try to check my code again. For now, this is close. Thanks
@jonataslaw I've tried using controller, but I stuck at how to check the user state part.
I try using _.user.value?.userId but it gives me The getter 'userId' isn't defined for the type 'Object'.
For reference, I've included firebase_user_info.dart too at below.
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:jazapos/models/firebase_user_info.dart';
import 'package:jazapos/services/auth_service.dart';
import 'package:jazapos/services/firebase_auth_service.dart';
void main() {
Get.lazyPut<AuthService>(() => FirebaseAuthService());
runApp(App());
}
class App extends StatelessWidget {
final _auth = AuthService.to;
@override
Widget build(BuildContext context) {
return GetMaterialApp(
home: GetBuilder<LandingController>(
init: LandingController(),
builder: (_) {
print('user2: ${_.user.value}'); // initial value => uid: null, photoUrl: null, displayName: null
// print(_.user.value?.userId); // The getter 'userId' isn't defined for the type 'Object'.
if (_.user == null) {
return Login();
}
return Home();
},
),
);
}
}
class LandingController extends GetController {
static LandingController get to => Get.find();
final _auth = AuthService.to;
final user = FirebaseUserInfo().obs;
@override
void onInit() {
user.bindStream(_auth.onAuthStateChanged);
ever(user, (value) {
print('user: $value');
});
super.onInit();
}
}
firebase_user_info.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/foundation.dart';
@immutable
class FirebaseUserInfo {
const FirebaseUserInfo({
this.uid,
this.photoUrl,
this.displayName,
});
final String uid;
final String photoUrl;
final String displayName;
factory FirebaseUserInfo.fromFirebaseUser(FirebaseUser user) {
if (user == null) {
return null;
}
return FirebaseUserInfo(
uid: user.uid,
displayName: user.displayName,
photoUrl: user.photoUrl,
);
}
String get userId => uid;
@override
String toString() =>
'uid: $uid, photoUrl: $photoUrl, displayName: $displayName';
}
nb: should I open new issue for this?
Thanks
@jonataslaw
Ok, so basically, now I'm able to get userId inside my builder. Then the new problem is when I sign in, I can see that running well, but the page not changed, it still shows the Login page.
How to make it reactive? Also since we use FirebaseUserInfo().obs, do we really need to user ever(user) ?
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:jazapos/models/firebase_user_info.dart';
import 'package:jazapos/services/auth_service.dart';
import 'package:jazapos/services/firebase_auth_service.dart';
void main() {
Get.lazyPut<AuthService>(() => FirebaseAuthService());
runApp(App());
}
class App extends StatelessWidget {
final _auth = AuthService.to;
@override
Widget build(BuildContext context) {
return GetMaterialApp(
home: GetBuilder<LandingController>(
init: LandingController(),
builder: (_) {
FirebaseUserInfo user = _.user.value;
print('userId ${user?.userId}');
if (user?.userId == null) {
return Login();
}
return Home();
},
),
);
}
}
class LandingController extends GetController {
static LandingController get to => Get.find();
final _auth = AuthService.to;
final user = FirebaseUserInfo().obs;
@override
void onInit() {
user.bindStream(_auth.onAuthStateChanged);
ever(user, (value) {
print('user: $value');
});
super.onInit();
}
}
Thanks
class LandingController extends GetController {
static LandingController get to => Get.find();
final _auth = AuthService.to;
final user = FirebaseUserInfo().obs;
@override
void onInit() {
user.bindStream(_auth.onAuthStateChanged);
ever(user, checkUser);
}
checkUser(user) {
if (user?.userId == null) {
Get.off(Login());
} else {
Get.off(Home());
}
// if you have error, try this:
// SchedulerBinding.instance.addPostFrameCallback((_) {
// if (user?.userId == null) {
// Get.off(Login());
// } else {
// Get.off(Home());
// }
// });
}
}
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
home: GetBuilder<LandingController>(
init: LandingController(),
builder: (_) {
return Scaffold();
},
),
);
}
}
Thanks a lot @jonataslaw
It's working, now, the code is much clearer if we compared it with previous (Provider + StreamBuilder).
void main() {
Get.lazyPut<AuthService>(() => FirebaseAuthService());
runApp(App());
}
class App extends StatelessWidget {
final _auth = AuthService.to;
@override
Widget build(BuildContext context) {
return GetMaterialApp(
home: GetBuilder<LandingController>(
init: LandingController(),
builder: (_) {
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
},
),
);
}
}
class LandingController extends GetController {
static LandingController get to => Get.find();
final _auth = AuthService.to;
final user = FirebaseUserInfo().obs;
@override
void onInit() {
user.bindStream(_auth.onAuthStateChanged);
ever(user, checkUser);
super.onInit();
}
void checkUser(user) {
if (user?.userId == null) {
Get.off(Login());
} else {
Get.off(Home());
}
}
}
I'm so happy :dancing_men:
@ghprod hello :) mind sharing the AuthService and FirebaseService? I'm trying to replicated this :)
@ghprod actually... what does Get.lazyPut
Why T is different from C?
@ghprod actually... what does Get.lazyPut( () => C()); does?
Why T is different from C?
You can register an abstract class with an implementation class.
Get.lazyPut<AuthService>(() => FirebaseAuthService());
AuthService is an abstract class; FirebaseAuthService is the implemented class.
@stefandevo thanks!
I'm still confused with the ".to" method from the example above D:
Most helpful comment
Thanks a lot @jonataslaw
It's working, now, the code is much clearer if we compared it with previous (Provider + StreamBuilder).
I'm so happy :dancing_men: