2

I'm trying to create a model class with a few methods to simplify retrieving information from Firebase.

I have a "Unregistered" page where user clicks on the "Login with google" button. If my user is already registered it checks the DB to see if he completed his profile, otherwise it writes basic data to the database with a field profileCompleted: false and redirects the user to the profile page so he can complete his profile.

I'm trying to use a model here, but i'm getting all sorts of errors (just started learning flutter/dart).

I'll share some code, let me know if its not enough!

Unregistered page.

    if (user != null) {
      await prefs.setString('id', user.uid);
      // Check is already sign up
      final QuerySnapshot result = await Firestore.instance
          .collection('users')
          .where('id', isEqualTo: user.uid)
          .getDocuments();
      final List<DocumentSnapshot> documents = result.documents;

      if (documents.length == 0) {
        debugPrint("User not in DB ?");
        //debugPrint(userInfo.toString());
        // Update data to server if new user

        Firestore.instance.collection('users').document(user.uid).setData({
          'id': user.uid,
          'nickname': user.displayName,
          'photoUrl': user.photoUrl,
          'email': user.email,
          'createdAt': DateTime.now(),
          'provider': user.providerId,
          'profileCompleted': false,
          'bloodType': 'A',
          'rhType': 'Negativ',
          'donatedBefore': 'Nu',
          'lastDonation': '0'
        });
        Navigator.push(
            context,
            MaterialPageRoute(
                builder: (context) => CompleteProfile(
                      currentUserId: user.uid,
                      userInfo: documents.single,
                    )));
      } else if (documents.single["profileCompleted"] == false) {
        Navigator.push(
            context,
            MaterialPageRoute(
                builder: (context) => CompleteProfile(
                      currentUserId: user.uid,
                      userInfo: documents.single,
                    )));
      } else {
        Navigator.push(
            context,
            MaterialPageRoute(
                builder: (context) => HomePage(
                      currentUserId: user.uid,
                      userInfo: documents.single,
                    )));
      }
    }

Complete profile page

class CompleteProfile extends StatefulWidget {
  final String currentUserId;
  final userInfo;
  static const routeName = '/profile';
  final User user;

  CompleteProfile(
      {Key key,
      this.title,
      @required this.currentUserId,
      @required this.userInfo,
      this.user})
      : super(key: key);

  final String title;

  @override
  _CompleteProfileState createState() => _CompleteProfileState(user,
      currentUserId: this.currentUserId, userInfo: this.userInfo);
}

class _CompleteProfileState extends State<CompleteProfile> {
  User user;

  _CompleteProfileState(this.user,
      {Key key, @required this.currentUserId, @required this.userInfo});
  final String currentUserId;
  final userInfo;

My issue here is that if i try to set a dropdown like this ->

child: ListTile(
title: DropdownButton<String>(
items: _bloodTypes.map((String value) {
return DropdownMenuItem<String>(
value: value, child: Text(value));
}).toList(),
style: textStyle,
value: retrieveBloodType(user.bloodType),
onChanged: (value) => updateBloodType(value),

And on default value->

  String retrieveBloodType(String value) {
    return _bloodType;
  }

I get an error that i'm getting on null.

My idea was to initialize the User model with something like User user = new User(userInfo["id"],userInfo["email"].... in which userInfo is an object retrieved from firebase. However, that throws another error that only static members can be accessed in initalizers.

My model is quite simple so here's a sneak peek (few lines, not the entire model).

class User {
  int _id;
  String _nickname;
  int get id => _id;
  String get nickname => _nickname;
  set bloodType(String newBloodType) {
    if (newBloodType.length <= 50 && newBloodType != null) {
      _bloodType = newBloodType;
    }
  }

Any idea on how id have to change this to work? The only reason i'm thinking of using models is to simplify screen widgets (so i can add all firebase logic to the model instead of the widgets).

Dante R.
  • 902
  • 3
  • 13
  • 41
  • Have a look at packages `json_annotation` and `json_serializable`. They are your best friends here. – Chenna Reddy Sep 28 '19 at 12:23
  • That does seem like it would help me, however...since i dont have enough experience with dart (or similar languages) i'm having a hard time imagining how i'd implement those packages. Do you have any detailed tutorial/example that explains their use or something like that? Thanks! – Dante R. Sep 29 '19 at 01:12
  • @ChennaReddy had a bit of a headache, but i managed to sort it out, those packages are indeed life savers, thanks ! – Dante R. Sep 30 '19 at 11:08

2 Answers2

7

This is how I would do it

import 'package:cloud_firestore/cloud_firestore.dart';

class User {
  final String userName;
  final String email;
  final String name;
  final String phoneNumber;
  final String profilePictureUrl;
  final Timestamp creationDate;

  const User(
    this.userName,
    this.email,
    this.name,
    this.phoneNumber,
    this.profilePictureUrl,
    this.creationDate
  );

  factory User.fromDocument(DocumentSnapshot document) {
    return User(
      document['userName'],
      document['email'],
      document['name'],
      document['phoneNumber'],
      document['profilePictureUrl'],
      document['creationDate']
    );
  }
Taiyr Begeyev
  • 547
  • 1
  • 9
  • 12
  • can you further explain your code the .fromDocument method? Is this where you put the retrieval/querying from firebase? I'm trying to replicate it so that for example i can already get the call the info from one place. like email = User.email; thanks! – NinFudj Mar 19 '22 at 13:23
2

it would be better if your model looks like that:

class User {
  final String userName;
  final String email;


  User({
    this.userName,
    this.email,
 
  });

 User.fromMap(Map map)
  : this(
     userName : map['userName'],
     email : map['email'],
   );

 Map<Stringm dynmic> asMap() => {
   'userName' : userName,
   'email' : email,
  }

then you can use this model to set data to firebase as follow

Firestore.instance.collection('users').document(user.uid).setData(user.asMap)