How to listen to live changes in firestore document in Flutter using scopeModel

I have a model set up as follows:

// scope.model.dart
mixin AuthModel on CoreModel {
  bool get isLoading => _isLoading;
  get notification => _notification;
  Observable<Map<String, dynamic>> profileUser;

  // constructor
  Future<void> authService() async {
    user = Observable(_auth.onAuthStateChanged);

    profileUser = user.switchMap((FirebaseUser u) {
      if (u != null) {
        authenticated.add(true);
        _isLoading = false;
        notifyListeners();
        return _db
            .collection('users')
            .document(u.uid)
            .snapshots()
            .map((snap) => snap.data);
      } else {
        authenticated.add(false);
        return Observable.just({});
      }
    });
  }

// get profile here
}

To get the user profile, I am doing something of this sort:

get profile {
   profileUser.listen((user) => _profile = user);
    if (_profile == null) {
      _isLoading = true;
      notifyListeners();
    } else {
      return _profile;
    }
  }

_profile is initiazed as an object!

Then in my screens or pages with scopeModel, I simply do

_model.profile['username'];

This works, the problem is when the firestore document information is changed, it doesn't change on the screen view, unless I change routes, or refresh the app.

I am new and struggling with flutter, what would be the right way to do this?

1 answer

  • answered 2019-05-21 11:42 Tinus Jackson

    If you are going to use the FirebaseUser and want to keep track of the state or Logged in user throughout your app, i would suggest that you use the Provider on the highest level of your app.

    For me its the easiest using the Provider Package which is basically ScopedModel v2 according to some tutorials/videos

    Example

    void main() {
    
       runApp(MyApp());
       }
    
    class MyApp extends StatelessWidget {
      MyApp();
    
      @override
      Widget build(BuildContext context) {
        return MultiProvider(
          providers: [
            StreamProvider<FirebaseUser>.value(
              stream: FirebaseAuth.instance.onAuthStateChanged, // Provider here
            ),
          ],
          child: MaterialApp(
            title: 'My App',
            debugShowCheckedModeBanner: false,
            theme: ThemeData(
              primaryColor: Colors.green,
              primarySwatch: Colors.green,
              accentColor: Colors.yellow,
            ),
            home: MainPage(),
          ),
        );
      }
    }
    
    class MainPage extends StatefulWidget {
      MainPage({Key key, this.storage}) : super(key: key);
      final FirebaseStorage storage;
      @override
      _MainPageState createState() => _MainPageState();
    }
    
    class _MainPageState extends State<MainPage>
        with SingleTickerProviderStateMixin {
      @override
      Widget build(BuildContext context) {
        final user = Provider.of<FirebaseUser>(context); // gets the firebase user
        bool loggedIn = user != null;
    
        return loggedIn ? HomePage() : LoginPage(); // Will check if the user is logged in. And will change anywhere in the app if the user logs in
      }
    }
    

    Additional References (This helped me)

    Fireship 185 Provider

    Great Youtube video explaining the code