ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Flutter - 코딩마스터하기|파이어스토어를 통한 팔로/언팔로
    개발/Flutter 2021. 11. 17. 21:31
    반응형

    fetch all users except me

    팔로우한 유저의 포스트를 봐야한다.  그래서 나를 제외한 유저를 불러오는 메소드를 만들 것이다. 일단 user_network_repository.dart에서 Stream으로 Firestore.instance를 통해 받아오는 코드를 만들어 준다. Stream<List<UserModel>> getAllUsersWithoutMe(){ return Firestore.instance.collection(COLLECTION_USERS).snapshots().transform(toUsersExceptMe); } 이렇게 collection을 정해주고 snapshots를 하면 해당 콜렉션의 모든것을 다 불러온다. 거기서 toUsersExceptMe를 Transformers에 만들어 준다. 기존 toUser를 복사해서 만들어 주고 기존 내용을 삭제하고 우선 user 리시트를 저장할 부분을 만들어 준다. List<UserModel> users = [];로 해준다. 그리고 내 현재 아이디를 가져오기 위해  FirebaseAuth.instance.currentUser();도 만들어 준다. 그리고 snapshot.documnets.forEach를 이용하요 documentSnapshot 의 내용들을 저장해주면 된다.  그리고 피드에 상단버튼에 userNetworkRepository.getAllUsersWithoutMe().listen((users) { print(users); }); 를 추가해서 debug 해주면 정상적으로 나오는 것을 알 수 있다.


     

    transformers.dart
    
      final toUsersExceptMe = StreamTransformer<QuerySnapshot, List<UserModel>>.fromHandlers(
          handleData: (snapshot, sink) async {
            List<UserModel> users = [];
    
            FirebaseUser _firebaseUser = await FirebaseAuth.instance.currentUser();
    
            snapshot.documents.forEach((documentSnapshot) {
              if(_firebaseUser.uid != documentSnapshot.documentID) {
                users.add(UserModel.fromSnapshot(documentSnapshot));
              }
            });
    
            sink.add(users);
          });
    user_network_repository.dart
      Stream<List<UserModel>> getAllUsersWithoutMe(){
        return Firestore.instance.collection(COLLECTION_USERS).snapshots().transform(toUsersExceptMe);
      }

    show all the users exept me

    확인하기 위한 버튼 부분으 getAllUsersWithoutMe() 부분은 삭제해준다. 그리고 search_screen에서 SafeArea에서 StreamBuilder로 감싸준다. <>안에는 List<UserModel>로 바꿔주고 stream에 userNetworkRepository.getAllUsersWithoutMe()로 해준다. 그리고고 return 부분을 if(snapshot.hasData)로{} 안에 넣어줘서 데이터가 있을때만 나오게 해주고, else 부분에 return MyProgressIndicator로 로딩바가 나오게 해준다. 기존 followings 와 같이 사용하던 부분을 모두 삭제해준다. 그리고  ListView에서 return 위에 UserMode userModel = snapshot.data[index]로 각 인덱스에 snapahot을 불러와서 유저정보를 저장해준다. ListTile의 title인 이름이 나오는 부분을 userModel.username을 해주면 된다. 또 하단에 itemCount에 snapshot.data.length로 해당 인원수만큼 보이게 해주면 된다. 실행해보면 정상적으로 나오는 것을 알 수 있다.


    class _SearchScreenState extends State<SearchScreen> {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("Follow/Unfollow"),
          ),
          body: StreamBuilder<List<UserModel>>(
            stream: userNetworkRepository.getAllUsersWithoutMe(),
            builder: (context, snapshot) {
              if(snapshot.hasData){
              return SafeArea(
                child: ListView.separated(
                  itemBuilder: (context, index) {
                    UserModel userModel = snapshot.data[index];
                    return ListTile(
                      onTap: () {
                        setState(() {
                          // followings[index] = !followings[index];
                        });
                      },
                      leading: RoundedAvatar(
                        index: index,
                      ),
                      title: Text(userModel.username),
                      subtitle: Text('this is user bio of ${userModel.username}'),
                      trailing: Container(
                        height: 30,
                        width: 80,
                        alignment: Alignment.center,
                        decoration: BoxDecoration(
                          color: Colors.red[50],
                          border: Border.all(
                            color: Colors.red,
                            width: 0.5,
                          ),
                          borderRadius: BorderRadius.circular(8),
                        ),
                        child: Text(
                          'Following' ,
                          textAlign: TextAlign.center,
                          style: TextStyle(
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                      ),
                    );
                  }, //(context, index)
                  separatorBuilder: (context, index) {
                    return Divider(
                      color: Colors.grey,
                    );
                  }, //아이템 사이사이 부분
                  itemCount: snapshot.data.length, //아이템 갯수
                ),
              );}
              else{
                return MyProgressIndicator();
              }
            }
          ),
        );
      }
    }

     


    그리고 페이스북 로그인 시에도 register 메소드에 있는 파이어베이스에 저장하는 부분을 넣어주고 따로 users로 하던부분을 합쳐준다.


      void _handleFacebookTokenWithFirebase(
          BuildContext context, String token) async {
        final AuthCredential credential = FacebookAuthProvider.getCredential(
          accessToken: token,
        );
        final AuthResult authResult =
            await _firebaseAuth.signInWithCredential(credential);
    
        _firebaseUser = authResult.user;
        if (_firebaseUser == null) {
          simpleSnackbar(context, '페이스북 로그인이 되지 않았습니다. 다시 시도해주시기 바랍니다.');
        } else {
          userNetworkRepository.attemptCreateUser(
              userKey: _firebaseUser.uid, email: _firebaseUser.email);
        }
        notifyListeners();
      }

    follow-unfollow user method

    팔로우와 언팔로우 메소드를 만들어 보자. user_network_repository에 만들자. Future<void> followUser() async{}로 만들어 주는데 내 유저키와 팔로우 할 사람의 유저키를 반드시 받아오게 하자. 그 뒤 내 refernce와 다른 유저의 reference를 가져오고 둘의 스냅샷도 가져온다. 그 후 post처럼 runTransaction을 해서 두개의 스냅샷이 존재할 경우의 if문을 작성하여 준다. 우선 tx.update()를 만들어 주고 myUserRef와 KEY_FOLLOWINGS: FieldValue.arrayUnion([otherUserKey])로 내 팔로잉 목록에 상대방 유저키를 넣어준다. 그리고 현재 상대방의 팔로워 수를 가져와 준다. otherSnapshot.data[KEY_FOLLOWERS]로 가져올수 있다. 그리고 tx.update로 otherUserRef와 KEY_FOLLOWERS: 에 currentFollowers+1을 해서 숫자를 1 늘려줄 수 잇다. unFollowerUser는 똑같이 복사 해준뒤 arrayUnion을 arrayRemove로, currentFollowers-1로 해주면 완성이다.


    class UserNetworkRepository with Transformers {
      Future<void> attemptCreateUser({String userKey, String email}) async {
        final DocumentReference userRef = Firestore.instance
            .collection(COLLECTION_USERS)
            .document(userKey); //document 위치
        DocumentSnapshot snapshot = await userRef.get(); // 해당 위치에서 데이터를 가져와 보는것
        if (!snapshot.exists) {
          //exists는 존재하지 않는다.
          return await userRef.setData(UserModel.getMapForCreateUser(email));
        }
      }
    
      Stream<UserModel> getUserModelStream(String userKey) {
        //get은 한번만 가져와서 안됨. snapshots는 처음에 똑같이 한번 보내고 변할때마다 보내줌.
        return Firestore.instance
            .collection(COLLECTION_USERS)
            .document(userKey)
            .snapshots()
            .transform(toUser);
      }
    
      Stream<List<UserModel>> getAllUsersWithoutMe(){
        return Firestore.instance.collection(COLLECTION_USERS).snapshots().transform(toUsersExceptMe);
      }
    
    
      Future<void> followUser({String myUserKey, String otherUserKey}) async{
        final DocumentReference myUserRef = Firestore.instance.collection(COLLECTION_USERS).document(myUserKey);
        final DocumentReference otherUserRef = Firestore.instance.collection(COLLECTION_USERS).document(otherUserKey);
    
        final DocumentSnapshot mySnapshot = await myUserRef.get();
        final DocumentSnapshot otherSnapshot = await otherUserRef.get();
    
        Firestore.instance.runTransaction((tx) async {
    
          if(mySnapshot.exists && otherSnapshot.exists){
            await tx.update(myUserRef, {KEY_FOLLOWINGS: FieldValue.arrayUnion([otherUserKey])});
            int currentFollowers = otherSnapshot.data[KEY_FOLLOWERS]; //현재 팔로워 수
            await tx.update(otherUserRef, {KEY_FOLLOWERS: currentFollowers+1});
    
          }
        });
      Future<void> unFollowUser({String myUserKey, String otherUserKey}) async{
        final DocumentReference myUserRef = Firestore.instance.collection(COLLECTION_USERS).document(myUserKey);
        final DocumentReference otherUserRef = Firestore.instance.collection(COLLECTION_USERS).document(otherUserKey);
    
        final DocumentSnapshot mySnapshot = await myUserRef.get();
        final DocumentSnapshot otherSnapshot = await otherUserRef.get();
    
        Firestore.instance.runTransaction((tx) async {
    
          if(mySnapshot.exists && otherSnapshot.exists){
            await tx.update(myUserRef, {KEY_FOLLOWINGS: FieldValue.arrayRemove([otherUserKey])});
            int currentFollowers = otherSnapshot.data[KEY_FOLLOWERS]; //현재 팔로워 수
            await tx.update(otherUserRef, {KEY_FOLLOWERS: currentFollowers-1});
    
          }
        });
    
      }
    }
    
    UserNetworkRepository userNetworkRepository = UserNetworkRepository();

    follow unfollow implementation

    이 부분을 search_screen에서 쓰면된다. ListView 안에 UserModel을 더 명확히 알게 위해 otherUser로 변경해준다. 그리고 나의 유저키를 알기위해서 Provider보다는 consumer를 사용해보자 ListView를 Consumer<UserModelState>로 감싸주고 builder: (BuildContext context, UserModelState myUserModelState, Widget child) {}를 추가 시켜준다. 그리고 child를 없애주고 기존 ListView 부분을 모두 복사해서 return에 붙여넣어주면 된다. 그리고 해당유저를 팔로우 했는지 안했는지 알기위한 메소드를 만들어 보자. user_model_state로 가서 bool로 amIFollowingThisUser(String otherUserKey)로 만들어 준다. _userModel.followings.contains(otherUserKey)로 팔로잉 목록에 유저키가 포함됐는지 확인하면 된다. 이제 다시 search_screen으로 돌아와서 ListView안에 bool amIFollowing = myUserModelState .amIFollowingThisUser(otherUser.userKey);를 만들어 준다. 그리고 setState안에 amIFollowing ? userNetworkRepository.unFollowUser( myUserKey: myUserModelState.userModel.userKey, otherUserKey: otherUser.userKey) : userNetworkRepository.followUser( myUserKey: myUserModelState.userModel.userKey, otherUserKey: otherUser.userKey); });이렇게 넣어주면 된다. 다른 색이나 글씨 부분도 amIFollowing으로 if를 해주면 된다.


    class _SearchScreenState extends State<SearchScreen> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("Follow/Unfollow"),
          ),
          body: StreamBuilder<List<UserModel>>(
              stream: userNetworkRepository.getAllUsersWithoutMe(),
              builder: (context, snapshot) {
                if (snapshot.hasData) {
                  return SafeArea(
                    child: Consumer<UserModelState>(
                      builder: (BuildContext context,
                          UserModelState myUserModelState, Widget child) {
                        return ListView.separated(
                          itemBuilder: (context, index) {
                            UserModel otherUser = snapshot.data[index];
                            bool amIFollowing = myUserModelState
                                .amIFollowingThisUser(otherUser.userKey);
                            return ListTile(
                              onTap: () {
                                setState(() {
                                  amIFollowing
                                      ? userNetworkRepository.unFollowUser(
                                          myUserKey:
                                              myUserModelState.userModel.userKey,
                                          otherUserKey: otherUser.userKey)
                                      : userNetworkRepository.followUser(
                                          myUserKey:
                                              myUserModelState.userModel.userKey,
                                          otherUserKey: otherUser.userKey);
                                });
                              },
                              leading: RoundedAvatar(
                                index: index,
                              ),
                              title: Text(otherUser.username),
                              subtitle:
                                  Text('this is user bio of ${otherUser.username}'),
                              trailing: Container(
                                height: 30,
                                width: 80,
                                alignment: Alignment.center,
                                decoration: BoxDecoration(
                                  color: amIFollowing
                                      ? Colors.blue[50]
                                      : Colors.red[50],
                                  border: Border.all(
                                    color: amIFollowing ? Colors.blue : Colors.red,
                                    width: 0.5,
                                  ),
                                  borderRadius: BorderRadius.circular(8),
                                ),
                                child: FittedBox(
                                  child: Text(
                                    amIFollowing ? 'Following' : 'Unfollowing',
                                    textAlign: TextAlign.center,
                                    style: TextStyle(
                                      fontWeight: FontWeight.bold,
                                    ),
                                  ),
                                ),
                              ),
                            );
                          }, //(context, index)
                          separatorBuilder: (context, index) {
                            return Divider(
                              color: Colors.grey,
                            );
                          }, //아이템 사이사이 부분
                          itemCount: snapshot.data.length, //아이템 갯수
                        );
                      },
                    ),
                  );
                } else {
                  return MyProgressIndicator();
                }
              }),
        );
      }
    }
      bool amIFollowingThisUser(String otherUserKey){
        return _userModel.followings.contains(otherUserKey);
      }

    check null for usermodel

    위에 만들었던 유저를 팔로우 하나 안하나 부분에서 null에대한 부분을 확인해 줘야한다.  아래와 같이 해주면 된다.


      bool amIFollowingThisUser(String otherUserKey){
        if(_userModel == null || _userModel.followings == null || _userModel.followings.isEmpty) return false;
        return _userModel.followings.contains(otherUserKey);
      }

     


     

    반응형

    댓글

Designed by Tistory.