I'm trying to test my firebase auth methods. Auth methods are signin, signout , register, etc.
this are methods i want to perform unit test.
I'm getting error No Firebase App '[DEFAULT]' has been created - call Firebase.initializeApp()
I tried to initialize Firebase.initializeApp in test main method its also doesn't work.
class MockUserRepository extends Mock implements AuthService {
final MockFirebaseAuth auth;
MockUserRepository({this.auth});
}
class MockFirebaseAuth extends Mock implements FirebaseAuth{}
class MockFirebaseUser extends Mock implements FirebaseUser{}
class MockFirebase extends Mock implements Firebase{}
void main() {
MockFirebase firebase=MockFirebase();
MockFirebaseAuth _auth = MockFirebaseAuth();
BehaviorSubject<MockFirebaseUser> _user = BehaviorSubject<MockFirebaseUser>();
when(_auth.onAuthStateChanged).thenAnswer((_){
return _user;
});
AuthService _repo = AuthService.instance(auth: _auth);
group('user repository test', (){
when(_auth.signInWithEmailAndPassword(email: "email",password: "password")).thenAnswer((_)async{
_user.add(MockFirebaseUser());
});
when(_auth.signInWithEmailAndPassword(email: "mail",password: "pass")).thenThrow((){
return null;
});
test("sign in with email and password", () async {
var signedIn = await _repo.onLogin(email:"testuser#test.com",password: "123456");
expect(signedIn, isNotNull);
expect(_repo.status, Status.Authenticated);
});
test("sing in fails with incorrect email and password",() async {
var signedIn = await _repo.onLogin(email:"testuser#test.com",password: "666666");
expect(signedIn, false);
expect(_repo.status, Status.Unauthenticated);
});
test('sign out', ()async{
await _repo.signout();
expect(_repo.status, Status.Unauthenticated);
});
});
}
AuthService class
enum Status { Uninitialized, Authenticated, Authenticating,
Unauthenticated }
class AuthService with ChangeNotifier {
FirebaseAuth auth = FirebaseAuth.instance;
FirebaseUser _user;
FirebaseUser get user => _user;
set user(FirebaseUser value) {
_user = value;
}
Status _status = Status.Uninitialized;
Future<User> getCurrentUser() async {
User currentUser;
await FirebaseAuth.instance.authStateChanges().listen((User user) {
currentUser = user;
});
return currentUser;
}
AuthService();
AuthService.instance({this.auth}) {
// auth.onAuthStateChanged.listen((user) {
// onAuthStateChanged(user);
// });
}
Future<void> signout() async {
await auth.signOut();
}
Future<User> createAccount({String email, String password}) async {
try {
UserCredential userCredential = await
auth.createUserWithEmailAndPassword(
email: email, password: password);
return userCredential != null ? userCredential.user : null;
} on FirebaseAuthException catch (e) {
showToast(e.message);
} catch (e) {
log(e.toString());
return null;
}
}
Future<User> onLogin({String email, String password}) async {
try {
User user;
await auth
.signInWithEmailAndPassword(email: email, password: password)
.then((value) {
showToast("Login sucessful");
user = value != null ? value.user : null;
});
return user;
} on FirebaseAuthException catch (e) {
showToast(e.message);
}
}
sendResetPassword({String email}) async {
bool isSent = false;
try {
await auth.sendPasswordResetEmail(email: email).then((value) {
showToast("Reset password email sent");
isSent = true;
});
return isSent;
} on FirebaseAuthException catch (e) {
showToast(e.message);
}
}
Future<void> onAuthStateChanged(FirebaseUser user) async {
if (user == null) {
_status = Status.Unauthenticated;
} else {
_user = user;
_status = Status.Authenticated;
}
notifyListeners();
}
Status get status => _status;
set status(Status value) {
_status = value;
}
}
Have a look at how they tested directly in firebase_auth code : https://github.com/FirebaseExtended/flutterfire/tree/master/packages/firebase_auth/firebase_auth/test
Call the setupFirebaseAuthMocks (you can adapt the code from here) method at the beginning of your main method and call await Firebase.initializeApp(); in a setUpAll method.
Firebase.initializeApp() is the solution for this. I am pretty sure. Try calling it in the file your are doing all these functions in their initState().
Related
import 'package:brew_crew/models/user.dart';
import 'package:firebase_auth/firebase_auth.dart';
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
Userr? _userFromFirebaseUser(User? user) {
return user != null ? Userr(uid: user.uid) : null;
}
Stream<User?> get user {
return _auth.authStateChanges().map(_userFromFirebaseUser);
}
Future signInAnon() async {
try {
UserCredential result = await _auth.signInAnonymously();
User? user = result.user;
return _userFromFirebaseUser(user!);
} catch (e) {
print(e);
return null;
}
}
}
you have written double rr in the function return type remove the one r
User? _userFromFirebaseUser(User? user) {
return user != null ? User(uid: user.uid) : null;
}
I am building an application, and I would like to show a progress bar while the user is waiting to be logged-in in the platform.
How can I add a circle progress bar while I am awaiting?
Future<void> validateAndSubmit() async {
if (validateAndSave()) {
try {
final Auth auth = await AuthFactory.getAuth();
final String userId = await auth
.signInWithEmailAndPassword(_email, _password)
.whenComplete(() =>
Navigator.of(context).popAndPushNamed(RootPage.routeName));
print('Signed In:$userId');
} catch (e) {
print('Error:$e');
print(e.toString());
}
}
}
Since you use future, the best way would be to control a CircularProgressIndicator() with a boolean inside your method like this:
Future<void> validateAndSubmit() async {
setState(( {
_isLoading = true;
});
if (validateAndSave()) {
try {
final Auth auth = await AuthFactory.getAuth();
final String userId = await auth
.signInWithEmailAndPassword(_email, _password)
.whenComplete(() =>
{
setState(() {
_isLoading = false;
});
Navigator.of(context).popAndPushNamed(RootPage.routeName));
}
print('Signed In:$userId');
} catch (e) {
print('Error:$e');
print(e.toString());
}
}
}
and then somewhere in your widget tree:
_isLoading ? CircularProgressIndicator() : Other widget...
I tried using conditional operator to check whether the user is logged in or not and according set the home to first screen or login screen but the problem is whenever I start my app it shows the login screen first for few milliseconds before moving to the first screen ! Can someone suggest a fix!
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
void initState() {
super.initState();
getUserInfo();
}
Future getUserInfo() async {
await getUser();
setState(() {});
print(uid);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Login',
home:
(uid != null && authSignedIn != false) ? FirstScreen() : LoginPage(),
);
}
}
I have added the getuser() function below:-
final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();
bool authSignedIn;
String uid;
String name;
String imageUrl;
Future getUser() async {
// Initialize Firebase
await Firebase.initializeApp();
SharedPreferences prefs = await SharedPreferences.getInstance();
bool authSignedIn = prefs.getBool('auth') ?? false;
final User user = _auth.currentUser;
if (authSignedIn == true) {
if (user != null) {
uid = user.uid;
name = user.displayName;
imageUrl = user.photoURL;
}
}
}
Future<String> signInWithGoogle() async {
// Initialize Firebase
await Firebase.initializeApp();
final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
final GoogleSignInAuthentication googleSignInAuthentication = await googleSignInAccount.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
final UserCredential userCredential = await _auth.signInWithCredential(credential);
final User user = userCredential.user;
if (user != null) {
// Checking if email and name is null
assert(user.uid != null);
assert(user.email != null);
assert(user.displayName != null);
assert(user.photoURL != null);
uid = user.uid;
name = user.displayName;
imageUrl = user.photoURL;
assert(!user.isAnonymous);
assert(await user.getIdToken() != null);
final User currentUser = _auth.currentUser;
assert(user.uid == currentUser.uid);
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool('auth', true);
return 'Google sign in successful, User UID: ${user.uid}';
}
return null;
}
void signOutGoogle() async {
await googleSignIn.signOut();
await _auth.signOut();
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool('auth', false);
uid = null;
name = null;
imageUrl = null;
print("User signed out of Google account");
}
You can try to use routing. There really good tutorials about routing and navigation in flutter on youtube. Also I put here a little example for routing.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Code Sample for Navigator',
// MaterialApp contains our top-level Navigator
initialRoute: '/',
routes: {
'/': (BuildContext context) => HomePage(),
'/signup': (BuildContext context) => SignUpPage(),
},
);
}
In my Flutter App I'm using Firebase Auth to authenticate the users. I have login_service with Provider and in the main.dart I check the state of the login.
My flow is:
Check if the user is logged with trackUserState()
If the user is logged I check the database: if the user exist I go to the HomeScreen, if not to the registration screen
My problem is that where I'm checking if the user is logged or not (on the main.dart, inside the routes object) generates a problem: the function is being executed all the time, nonstop.
How can I refactor this to only execute the function on init and after that notify only the changes (logout, a logged but non registered user being registered...) with notifyListeners()?
This are the codes of both files, main.dart and login_service.dart:
main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
MyApp(),
);
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<LoginState>(
create: (context) => LoginState(),
child: MaterialApp(
title: 'MyApp',
debugShowCheckedModeBanner: false,
routes: {
// ignore: missing_return
'/': (BuildContext context) {
var state = Provider.of<LoginState>(context);
state.trackUserState();
if(state.userExist() == false) {
return RegisterScreen();
}
if(state.isLoggedIn() == true) {
return HomeScreen();
}
if (state.isLoggedIn() == false) {
return LoginScreen();
}
},
...here the other routes
},
theme: brightTheme,
),
);
}
}
login_service.dart
class LoginState with ChangeNotifier {
// Instances
final GoogleSignIn _googleSignIn = GoogleSignIn();
final FirebaseAuth _auth = FirebaseAuth.instance;
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
// Variables
User _user;
bool _userExist;
bool _loading = false;
bool _isLoggedIn = false;
String _userEmail;
// Functions to track
bool isLoggedIn() => _isLoggedIn;
bool isLoading() => _loading;
bool userExist() => _userExist;
User currentUser() => _user;
String getUserEmail() => _userEmail;
// Registration Flow
void goAheadAfterRegistration() async {
if (_user != null) {
var userExist = await _firestore.collection('users').where('email', isEqualTo: _user.email).get();
if (userExist.docs.length == 1) {
} else {
print('Este user aun no se ha registrado');
_userExist = false;
}
notifyListeners();
} else {
_loading = false;
notifyListeners();
}
}
void checkUserOnDatabase() async {
var userExistOnDatabase = await _firestore.collection('users').where('email', isEqualTo: _user.email).get();
if (userExistOnDatabase.docs.length == 1) {
_isLoggedIn = true;
_userExist = true;
notifyListeners();
} else {
_userExist = false;
notifyListeners();
}
notifyListeners();
}
// LogOut the user
void logout() async {
_googleSignIn.signOut();
_auth.signOut();
_loading = false;
_isLoggedIn = false;
notifyListeners();
}
// Tracking the user state
void trackUserState() {
_auth.authStateChanges()
.listen((User user) {
if (user == null) {
_isLoggedIn = false;
notifyListeners();
} else {
_user = user;
checkUserOnDatabase();
notifyListeners();
}
});
}
// Third party login providers
void googleLogin() async {
_loading = true;
notifyListeners();
_user = await _handleSignIn();
checkUserOnDatabase();
}
Future<User> _handleSignIn() async {
final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
final User user = (await _auth.signInWithCredential(credential)).user;
return user;
}
// TODO on login true circular progress indicator while we check if the user exist to prevent new logins
}
Thank you very much guys!
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
*handle all that 1 trigger stuff here*
runApp(
MyApp(),
);
}
I have a signup button which sends an http request to my API. I want to navigate off of the signup page after the request is completed. Using bloc pattern, I add an event to the bloc to navigate to the login page but the signup page isn't waiting for http request to be completed before adding event to bloc.
Signup Button code :-
class _SignUpButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocBuilder<SignUpCubit, SignUpState>(
buildWhen: (previous, current) => previous.status != current.status,
builder: (context, state) {
return state.status.isSubmissionInProgress
? const CircularProgressIndicator()
: IconButton(
key: const Key('loginForm_continue_raisedButton'),
onPressed: state.status.isValidated
? () async {
await context.bloc<SignUpCubit>().signUpFormSubmitted();
context.bloc<AuthenticationBloc>().add(AuthenticationUserChanged(User.empty));
}
: () => print('invalid'),
icon: Icon(Icons.keyboard_arrow_right),
color: Colors.white,
iconSize: 40,
);
},
);
}
}
Signup cubit :-
part 'signup_state.dart';
class SignUpCubit extends Cubit<SignUpState> {
SignUpCubit(this._authenticationRepository)
: assert(_authenticationRepository != null),
super(const SignUpState());
final AuthenticationRepository _authenticationRepository;
//irrelevant code omitted
Future<void> signUpFormSubmitted() async {
if (!state.status.isValidated) return;
emit(state.copyWith(status: FormzStatus.submissionInProgress));
try {
await _authenticationRepository.signUp(
name: state.name,
password: state.password.value,
mobile: state.mobile.value,
village: state.village,
email: state.email.value,
pincode: state.pincode);
emit(state.copyWith(status: FormzStatus.submissionSuccess));
} on Exception {
emit(state.copyWith(status: FormzStatus.submissionFailure));
}
}
}
Authentication repository :-
/// Thrown if during the sign up process if a failure occurs.
class SignUpFailure implements Exception {}
class AuthenticationRepository {
/// {#macro authentication_repository}
AuthenticationRepository({
FirebaseAuth firebaseAuth,
GoogleSignIn googleSignIn,
}) : _firebaseAuth = firebaseAuth ?? FirebaseAuth.instance,
_googleSignIn = googleSignIn ?? GoogleSignIn.standard();
final FirebaseAuth _firebaseAuth;
final GoogleSignIn _googleSignIn;
/// Stream of [User] which will emit the current user when
/// the authentication state changes.
///
/// Emits [User.empty] if the user is not authenticated.
Stream<User> get user {
return _firebaseAuth.onAuthStateChanged.map((firebaseUser) {
return firebaseUser == null ? User.empty : firebaseUser.toUser;
});
}
//irrelevant code omitted
Future<void> signUp(
{#required String name,
#required String password,
#required String mobile,
#required String village,
#required String pincode,
#required String email}) async {
assert(
name != null && mobile != null && village != null && pincode != null);
try {
if (email != '' && password != '') {
await _firebaseAuth.createUserWithEmailAndPassword(
email: email, password: password);
}
final params = {
'name': name,
'phone': mobile,
'village': village + '_' + pincode,
'api': 't',
'login': 'NA'
};
Response res = await Dio().get(
"https://us-central1-greensat-9087a.cloudfunctions.net/addUser",
queryParameters: params);
print(res.data);
} on Exception {
throw SignUpFailure();
}
}
}
Authentication bloc :-
class AuthenticationBloc
extends Bloc<AuthenticationEvent, AuthenticationState> {
AuthenticationBloc({
#required AuthenticationRepository authenticationRepository,
}) : assert(authenticationRepository != null),
_authenticationRepository = authenticationRepository,
super(const AuthenticationState.unknown()) {
_userSubscription = _authenticationRepository.user.listen(
(user) => add(AuthenticationUserChanged(user)),
);
}
final AuthenticationRepository _authenticationRepository;
StreamSubscription<User> _userSubscription;
#override
Stream<AuthenticationState> mapEventToState(
AuthenticationEvent event,
) async* {
if (event is AuthenticationUserChanged) {
yield _mapAuthenticationUserChangedToState(event);
} else if (event is AuthenticationLogoutRequested) {
unawaited(_authenticationRepository.logOut());
} else if (event is AuthenticationOTPRequested) {
yield _mapAuthenticationUserOTPRequested(event);
} else if (event is AuthenticationSignupStarted) {
yield _mapAuthenticationUserSignupStarted(event);
}
}
#override
Future<void> close() {
_userSubscription?.cancel();
return super.close();
}
AuthenticationState _mapAuthenticationUserChangedToState(
AuthenticationUserChanged event,
) {
print('user changed state');
print(event.user);
return event.user != User.empty
? AuthenticationState.authenticated(event.user)
: const AuthenticationState.unauthenticated();
}
AuthenticationState _mapAuthenticationUserOTPRequested(
AuthenticationOTPRequested event,
) {
return const AuthenticationState.unauthenticatedOTP();
}
AuthenticationState _mapAuthenticationUserSignupStarted(
AuthenticationSignupStarted event,
) {
return const AuthenticationState.signupStarted();
}
}
you can't use async in a cubit emitter function
INCORRECT: ⛔️
void increment() async => emit(state+1)
CORRECT: ✅
void increment() => emit(state+1)