skip to Main Content

I’m developing a flutter app with firestore as my db and provider for state management. The idea is basically that I fetch data from firestore only in certain scenarios (such as the user’s name to display) and then store it locally using flutter secure storage. Then, I using provider to handle this and in certain cases fetch new data. I’m now running into an issue where despite the secure storage being updated, the provider instance seems to be holding on to some stale data, and I have no way of changing it. This is fixed by running flutter restart, but obviously that is not what I want.
Here is the provider:


class UserData {
  final String uid;
  final String name;
  final String profilePictureUrl;
  final bool verified;

  UserData(
      {required this.uid,
      required this.name,
      required this.verified,
      required this.profilePictureUrl});

  Map<String, dynamic> toJson() => {
        'uid': uid,
        'name': name,
        'profilePictureUrl': profilePictureUrl,
        'verified': verified,
      };

  factory UserData.fromJson(Map<String, dynamic> json) {
    return UserData(
      uid: json['uid'],
      name: json['name'],
      profilePictureUrl: json['profilePictureUrl'],
      verified: json['verified'],
    );
  }
}

class AuthenticationProvider with ChangeNotifier {
  final FirebaseAuth _auth = FirebaseAuth.instance;
  final FirebaseFirestore _firestore = FirebaseFirestore.instance;
  final FlutterSecureStorage _secureStorage = FlutterSecureStorage();
  UserData? _userData;

  AuthenticationProvider() {
    _auth.authStateChanges().listen(_onAuthStateChanged);
  }

  UserData? get userData => _userData;

  Stream<User?> get authStateChanges => _auth.authStateChanges();

  Future<void> verified(bool verified) async {
    User? user = _auth.currentUser;
    if (user != null) {
      await _firestore.collection('users').doc(user.uid).update({
        'verified': verified,
      });
      _userData = await _getUserData(user.uid);
      await _cacheUserData(_userData!);
      notifyListeners();
    }
  }

  

 

  Future<UserData> _getUserData(String uid) async {
    DocumentSnapshot doc = await _firestore.collection('users').doc(uid).get();
    if (doc.exists) {
      return UserData(
          uid: uid,
          name: doc['name'],
          profilePictureUrl: (doc.data() as Map<String, dynamic>?)
                      ?.containsKey('profilePictureUrl') ??
                  false
              ? doc['profilePictureUrl']
              : '',
          verified: doc['verified']);
    } else {
      throw Exception('Document does not exist');
    }
  }

  Future<void> _cacheUserData(UserData userData) async {
    String userDataJson = jsonEncode(userData.toJson());
    await _secureStorage.write(key: 'user_data', value: userDataJson);
  }

  Future<UserData?> getCachedUserData() async {
    String? userDataJson = await _secureStorage.read(key: 'user_data');
    if (userDataJson != null) {
      Map<String, dynamic> userDataMap = jsonDecode(userDataJson);
      return UserData.fromJson(userDataMap);
    }
    return null;
  }

  Future<void> _clearCachedUserData() async {
    await _secureStorage.delete(key: 'user_data');
  }

  void _onAuthStateChanged(User? user) async {
    if (user == null) {
      _userData = null;
      await _clearCachedUserData();
    } else {
      _userData = await _getUserData(user.uid);
      await _cacheUserData(_userData!);
    }
    notifyListeners();
  }

The problem has to do with verified(bool verified). When i call it,
The firstore is correctly updated, teh secure storage is correctly updated, but then _userData seems to be outdated when accessed by consumers as follows:

Widget TopOfPage(BuildContext context, double screenHeight) {
  return Row(
      mainAxisAlignment: MainAxisAlignment.start,
      mainAxisSize: MainAxisSize.min,
      children: [
        Consumer<AuthenticationProvider>(
          builder: (context, authenticationProvider, _) {
            if (authenticationProvider.userData != null) {
              print("user is authenticated");
              // User is authenticated, retrieve their first name from Firestore

              if (authenticationProvider.userData!.verified) {
                return ProfileShowcase(
                    context, screenHeight, authenticationProvider);
              } else {

Note that when having:

if (Provider.of<AuthenticationProvider>(context,
                            listen: false)
                        .userData!
                        .verified

The problem is the same.

Does anybody have any insight on how to fix this? I’m at a loss and have tried what feels like everything. Thanks!

Tried many debug prints, and they all support what I said, _userData seems to have stale values.

2

Answers


  1. Chosen as BEST ANSWER

    Fixed it! Your suggestion made me check where i was calling it. Making this change:

    // await AuthenticationProvider().verified(true); 
    await context.read<AuthenticationProvider>().verified(true); 
    

    Fixed the issue. Not really sure why but it did. Thanks!


  2. The code you provided earlier was just passing the method, but not executing it. By calling:

    await context.read<AuthenticationProvider>().verified(true);
    

    You actually execute the function and pass control to its body

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search