delay / flutter_starter

MIT License
422 stars 146 forks source link

Updating to Flutter 1.22.3 causes looping calls to handleAuthChanged #10

Closed trof-app closed 3 years ago

trof-app commented 3 years ago

Thanks for creating the starter project, it was working brilliantly for me and far more elegant than I could of hoped to build myself. I have found an issue however since upgrading to flutter version 1.22.3 where the ever() method in the onReady function is repeadly called on a loop, the behaviour causes the get.offAll(HomeUI()) to be constantly hit maxing out the CPU and the app becomes unresponsive. Pubspec.yaml: ` name: trofng_core description: Next Generation Project.

publish_to: 'none' # Remove this line if you wish to publish to pub.dev

version: 1.0.0+1

environment: sdk: ">=2.7.0 <3.0.0"

dependencies: get: ^3.17.1 flutter: sdk: flutter flutter_localizations: sdk: flutter cupertino_icons: ^1.0.0 json_annotation: ^3.1.0 firebase_core: '^0.5.0' firebase_analytics: '^6.0.0' firebase_auth: '^0.18.0+1' cloud_firestore: '^0.14.0+2' get_storage: ^1.3.2 uuid: ^2.2.2 simple_gravatar: ^1.0.5 flutter_sheet_localization: ^1.0.0

dev_dependencies: flutter_test: sdk: flutter json_serializable: ^3.5.0 build_runner: ^1.0.0

flutter_sheet_localization_generator: ^1.0.0

flutter: uses-material-design: true

assets:

[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.2) [✓] Xcode - develop for iOS and macOS (Xcode 12.2) [!] Android Studio (version 4.1) ✗ Flutter plugin not installed; this adds Flutter specific functionality. ✗ Dart plugin not installed; this adds Dart specific functionality. [✓] VS Code (version 1.51.1) [✓] Connected device (2 available)`

The code in my auth_controller: `

class AuthController extends GetxController { static AuthController to = Get.find(); TextEditingController nameController = TextEditingController(); TextEditingController emailController = TextEditingController(); TextEditingController passwordController = TextEditingController(); final FirebaseAuth _auth = FirebaseAuth.instance; final FirebaseFirestore _db = FirebaseFirestore.instance; Rx firebaseUser = Rx(); Rx firestoreUser = Rx(); final RxBool admin = false.obs;

@override void onReady() async { //run every time auth state changes ever(firebaseUser, handleAuthChanged); firebaseUser.value = await getUser; firebaseUser.bindStream(_auth.authStateChanges()); super.onReady(); }

@override void onClose() { nameController?.dispose(); emailController?.dispose(); passwordController?.dispose(); super.onClose(); }

handleAuthChanged(_firebaseUser) async { //get user data from firestore if (_firebaseUser?.uid != null) { firestoreUser.bindStream(streamFirestoreUser()); //await isAdmin(); }

if (_firebaseUser == null) {
  Get.offAll(SignInUI());
} else {
  Get.offAll(HomeUI());
}

}

// Firebase user one-time fetch Future get getUser async => _auth.currentUser;

// Firebase user a realtime stream Stream get user => _auth.authStateChanges();

//Streams the firestore user from the firestore collection Stream streamFirestoreUser() { if (firebaseUser?.value?.uid != null) { return _db .doc('/users/${firebaseUser.value.uid}') .snapshots() .map((snapshot) => TrofUser.fromJson(snapshot.data())); } return null; }

//get the firestore user from the firestore collection Future getFirestoreUser() { if (firebaseUser?.value?.uid != null) { return _db.doc('/users/${firebaseUser.value.uid}').get().then( (documentSnapshot) => TrofUser.fromJson(documentSnapshot.data())); } return null; }

//Method to handle user sign in using email and password signInWithEmailAndPassword(BuildContext context) async { showLoadingIndicator(); try { await _auth.signInWithEmailAndPassword( email: emailController.text.trim(), password: passwordController.text.trim()); emailController.clear(); passwordController.clear(); hideLoadingIndicator(); } catch (error) { hideLoadingIndicator(); Get.snackbar('Sign in Error', 'Error in Sign on', snackPosition: SnackPosition.BOTTOM, duration: Duration(seconds: 7), backgroundColor: Get.theme.snackBarTheme.backgroundColor, colorText: Get.theme.snackBarTheme.actionTextColor); } }

// User registration using email and password registerWithEmailAndPassword(BuildContext context) async { showLoadingIndicator(); try { await _auth .createUserWithEmailAndPassword( email: emailController.text, password: passwordController.text) .then((result) async { print('uID: ' + result.user.uid); print('email: ' + result.user.email); //get photo url from gravatar if user has one Gravatar gravatar = Gravatar(emailController.text); String gravatarUrl = gravatar.imageUrl( size: 200, defaultImage: GravatarImage.retro, rating: GravatarRating.pg, fileExtension: true, ); //create the new user object TrofUser _newUser = TrofUser( uid: result.user.uid, userEmail: result.user.email, firstName: nameController.text, photoUrl: gravatarUrl); //create the user in firestore _createUserFirestore(_newUser, result.user); emailController.clear(); passwordController.clear(); hideLoadingIndicator(); }); } catch (error) { hideLoadingIndicator(); Get.snackbar('Sign Up Error', error.message, snackPosition: SnackPosition.BOTTOM, duration: Duration(seconds: 10), backgroundColor: Get.theme.snackBarTheme.backgroundColor, colorText: Get.theme.snackBarTheme.actionTextColor); } }

//handles updating the user when updating profile Future updateUser(BuildContext context, TrofUser user, String oldEmail, String password) async { try { showLoadingIndicator(); await _auth .signInWithEmailAndPassword(email: oldEmail, password: password) .then((_firebaseUser) { _firebaseUser.user .updateEmail(user.userEmail) .then((value) => _updateUserFirestore(user, _firebaseUser.user)); }); hideLoadingIndicator(); Get.snackbar( 'Update Profile Success', 'Profile update has been successful', snackPosition: SnackPosition.BOTTOM, duration: Duration(seconds: 5), backgroundColor: Get.theme.snackBarTheme.backgroundColor, colorText: Get.theme.snackBarTheme.actionTextColor); } on PlatformException catch (error) { //List errors = error.toString().split(','); // print("Error: " + errors[1]); hideLoadingIndicator(); print(error.code); String authError; switch (error.code) { case 'ERROR_WRONG_PASSWORD': authError = 'Wrong Password'; break; default: authError = 'Auth Error'; break; } Get.snackbar('Wrong Password', authError, snackPosition: SnackPosition.BOTTOM, duration: Duration(seconds: 10), backgroundColor: Get.theme.snackBarTheme.backgroundColor, colorText: Get.theme.snackBarTheme.actionTextColor); } }

//updates the firestore user in users collection void _updateUserFirestore(TrofUser user, User _firebaseUser) { _db.doc('/users/${_firebaseUser.uid}').update(user.toJson()); update(); }

//create the firestore user in users collection void _createUserFirestore(TrofUser user, User _firebaseUser) { _db.doc('/users/${_firebaseUser.uid}').set(user.toJson()); update(); }

//password reset email Future sendPasswordResetEmail(BuildContext context) async { showLoadingIndicator(); try { await _auth.sendPasswordResetEmail(email: emailController.text); hideLoadingIndicator(); Get.snackbar('Password Reset Sent', 'Check your email for the reset link', snackPosition: SnackPosition.BOTTOM, duration: Duration(seconds: 5), backgroundColor: Get.theme.snackBarTheme.backgroundColor, colorText: Get.theme.snackBarTheme.actionTextColor); } catch (error) { hideLoadingIndicator(); Get.snackbar('Reset Password Error', error.message, snackPosition: SnackPosition.BOTTOM, duration: Duration(seconds: 10), backgroundColor: Get.theme.snackBarTheme.backgroundColor, colorText: Get.theme.snackBarTheme.actionTextColor); } }

//check if user is an admin user isAdmin() async { await getUser.then((user) async { DocumentSnapshot adminRef = await _db.collection('admin').doc(user?.uid).get(); if (adminRef.exists) { admin.value = true; } else { admin.value = false; } update(); }); }

// Sign out Future signOut() { nameController.clear(); emailController.clear(); passwordController.clear(); return _auth.signOut(); } } `

delay commented 3 years ago

This bug should have been fixed a couple of weeks ago. https://github.com/delay/flutter_starter/commit/793b5654f82ae5f2d3f28b441c7011f7210d388f

Basically you need to change super.onInit to super.onReady in auth_controller.dart

  void onReady() async {
    //run every time auth state changes
    ever(firebaseUser, handleAuthChanged);
    firebaseUser.value = await getUser;
    firebaseUser.bindStream(user);
    super.onReady();
  }
delay commented 3 years ago

I am going to close this issue. Let me know if the above fix does not work for you.