-
Flutter - 코딩마스터하기|사진 업로드,게시하기3 , storage에 저장한 Post의 데이터를 database에 저장하기개발/Flutter 2021. 11. 16. 21:10반응형
create post model
post도 모델을 생성하기 위해서 models - firestore 에 post_model.dart를 생성해준다. 그리고 user_model을 복사해와서 똑같이 만들어 주고 class 명을 PostModel로 바꿔준다. 그리고 상단에 상수들을 선언해 준다.
final String postKey; final String userKey; final String username; final String postImg; final List<dynamic> numOfLikes; final String caption; final String lastCommentor; final String lastComment; final DateTime lastCommentTime; final int numOfComments; final DateTime postTime; final DocumentReference reference;
중간에 fromMap은 다음과 같이 변경 해준다.
PostModel.fromMap(Map<String, dynamic> map, this.postKey, {this.reference}) //fromMap은 Dart 내장 함수 : userKey = map[KEY_USERSKEY], username = map[KEY_USERNAME], postImg = map[KEY_POSTIMG], numOfLikes = map[KEY_NUMOFLIKES], caption = map[KEY_CAPTION], lastCommentor = map[KEY_LASTCOMMENTOR], lastComment = map[KEY_LASTCOMMENT], lastCommentTime = map[KEY_LASTCOMMENTTIME]==null? DateTime.now().toUtc():(map[KEY_LASTCOMMENTTIME] as Timestamp).toDate(), numOfComments = map[KEY_NUMOFCOMMENTS], postTime = map[KEY_POSTTIME]==null? DateTime.now().toUtc():(map[KEY_POSTTIME] as Timestamp).toDate();
여기서 DateTime 부분에서는 시간을 가져올때 null을 해서 에러가 날 수 있으므로 위와같이 처리해 주면 된다.,
fromSnapshot부분은 똑같이 해주면 된다. 그리고 getMapForCreatePost는 다음과 같이 해주면 된다.
static Map<String, dynamic> getMapForCreatePost({String userKey, String username, String caption}) { Map<String, dynamic> map = Map(); map[KEY_USERSKEY] = userKey; map[KEY_USERNAME] = username; map[KEY_POSTIMG] = ""; map[KEY_NUMOFLIKES] = []; map[KEY_CAPTION] = caption; map[KEY_LASTCOMMENTOR] = ""; map[KEY_LASTCOMMENT] = ""; map[KEY_LASTCOMMENTTIME] = DateTime.now().toUtc(); map[KEY_NUMOFCOMMENTS] = 0; map[KEY_POSTTIME] = DateTime.now().toUtc(); return map; }
create transaction to upload post data
우선 repo 폴더에 post_network_repository.dart 를 만들어 준다. 그리고 메소드를 만들어 주는데 우선 Futuer<void> createNewPost를 만들어 준다. 그리고 DocumentReference postRef, DocumnetReference userRef를 해주고 포스트와 유저의 레퍼런스를 가져와준다. post는 Firestore.instance.collection(COLLECTION_POSTS).document(postKey);로 해주지만 userRef는 COLLECTION_USERS를 넣어주고 userKey는 postDate에서[KEY_USERKEY]로 알 수 있으므로 이 방식으로 가져와 준다. 그리고 postSnapahot을 가져온다. DocumentSnapshot postSnapshot = await postRef.get(); 으로 레퍼런스를 통해서 가져올 수 있다. 이제 트랜섹션을 해주면 된다. Firestore.instance에서 .runTransaction을 해주면 된다. 그리고 ()안에 (Transaction tx) async {}를 넣어 주낟. 그리고 if 문에 !post.Snapshot.exists를 해줘서 스냅샷에 대한 정보가 없으면 포스트를 만들어 주고 User에서 my_post에 대한 정보를 업데이트 해주면 된다. await tx.set(postRef,postData);로 포스트 정보를 만들어 주고 await tx.update(userRef,{KEY_MYPOSTS: FieldValue.arrayUnion([postKey])})로 업데이트 해줄 수 있다. 이부분은 그냥 외워야 한다. 우리는 하나만 업데이트 하기때문에 이렇게 썼지만 2개이상일때는 다른 방법을 사용한다. postData가 아닌 {KEY_MYPOSTS: FieldValue.arrayUnion([postKey])}으로 원하는 부분을 업데이트를 하는것을 기억해두자. 여기서 if안에 update에서 실패하면 set부분도 안되고 돌아가게 된다. 충돌이 막아진다.
class PostNetworkRepository { Future<Map<String, dynamic>> createNewPost( String postKey, Map<String, dynamic> postData) async { final DocumentReference postRef = Firestore.instance.collection(COLLECTION_POSTS).document(postKey); final DocumentSnapshot postSnapshot = await postRef.get(); final DocumentReference userRef = Firestore.instance .collection(COLLECTION_USERS) .document(postData[KEY_USERSKEY]); return Firestore.instance.runTransaction((Transaction tx) async { if (!postSnapshot.exists) { await tx.set(postRef, postData); await tx.update(userRef, { KEY_MYPOSTS: FieldValue.arrayUnion([postKey]) }); } }); } } PostNetworkRepository userNetworkRepository = PostNetworkRepository();
use create new post
이미지를 올릴때 포스트가 올라가고 데이터가 올라가는데 이것을 image_network_repository 에서 하는것보다는 share_post_screen에서 하는게 더 나을꺼 같다는 강의에 따라 await imageNetworkRepository.uploadImage(imageFile,postKey: postKey); 아래에 만들어 주자. 우선 uploadImageNCreateNewPost를 rename으로 uploadImage로 바꿔주고, share_post_screen에서 await postNetworkRepository.createNewPost() 를 생성 해준다. 여기에 postKey와 postData에 따라 usermodel이 필요하므로 우선 그 위에 userKey나 username을 불러 올 수 있도록 UserModel userModel = Provider.of<UserModel>(context,listen: false);로 생성 해준다. 그리고 createNewPost()안에 postKey, PostModel.getMapForCreatePost(userKey: userModel.userKey,username: userModel.username,caption: "") 를 넣어준다. caption은 하단에 caption 작성부분에서 가져와야 되는데 이 부분은 다음시간에 해보자.
onPressed: () async { showModalBottomSheet( context: context, builder: (_) => MyProgressIndicator(), //원래 빌더에 ()안에 context가 와야하는데 context를 사용 안하므로 _ 로 해준다 isDismissible: false, //이 창의 바깥 눌렀을때 끝나게 해줄것이냐 enableDrag: false, //드래그를 하게 해줄 것이냐. ); await imageNetworkRepository.uploadImage(imageFile,postKey: postKey); UserModel userModel = Provider.of<UserModel>(context,listen: false); await postNetworkRepository.createNewPost(postKey, PostModel.getMapForCreatePost(userKey: userModel.userKey,username: userModel.username,caption: "")); Navigator.of(context).pop(); //await 되고 나면 pop으로 꺼짐짐 },
use textfield to get caption
캡션을 넣어줘 보자. 우선 statelessWidget를 statefulWidget로 변경 해주자. 그리고 caption부분이 textField므로 state class 상단에 TextEditingController _textEditingController = TextEditingController();을 생성해준다. 그리고 dispose()도 생성해줘서 창이 꺼질때 dispose 하게 해준다. TextField로 가서 controller: _textEditingController,을 넣어주고 바로 캡션을 쓸수 있도록 autoFocus: true, 로 해준다. 그리고 아까 비워뒀던 caption에 await postNetworkRepository.createNewPost(widget.postKey, PostModel.getMapForCreatePost(userKey: userModel.userKey,username: userModel.username,caption: _textEditingController.text)); 이렇게 추가해주면 된다. 여기서 하단에 pop를 통해 모달을 껐는데 중복으로 사진을 올리는것을 막기 위해 똑같이 Navigator.of(context).pop(); 을 한번더 해준다.
title: TextField( controller: _textEditingController, autofocus: true, decoration: InputDecoration( hintText: 'Write a caption....', border: InputBorder.none), ),
TextEditingController _textEditingController = TextEditingController(); @override void dispose() { _textEditingController.dispose(); super.dispose(); } onPressed: () async { showModalBottomSheet( context: context, builder: (_) => MyProgressIndicator(), //원래 빌더에 ()안에 context가 와야하는데 context를 사용 안하므로 _ 로 해준다 isDismissible: false, //이 창의 바깥 눌렀을때 끝나게 해줄것이냐 enableDrag: false, //드래그를 하게 해줄 것이냐. ); await imageNetworkRepository.uploadImage(widget.imageFile,postKey: widget.postKey); UserModel userModel = Provider.of<UserModel>(context,listen: false); await postNetworkRepository.createNewPost(widget.postKey, PostModel.getMapForCreatePost(userKey: userModel.userKey,username: userModel.username,caption: _textEditingController.text)); Navigator.of(context).pop(); //await 되고 나면 모달창이 pop으로 꺼짐 Navigator.of(context).pop(); // sharePost 창이 꺼짐 },
finish share post
이미지를 firestore에 올린 후 그 이미지의 url을 받아서 post 데이터베이스를 업데이트 시켜줘보자. 우선 데이터에 업데이트 해주는 부분을 만들어 주자. post_network_repository에 와서 하단에 Future<void> updatePostImageUrl({String postImg, String postKey})를 만들어 주자. Future이기 때문에 async를 해주고 postRef와 postSnapshot 부분을 그대로 가져오자. 그리고 if(postSnapshot.exists)로 스냅샷이 존재할때 업데이트 시켜주면 된다. await post.updateDate({KEY_POSTIMG: postImg})로 post img 경로가 지정되는 부분만 메소드를 쓰는 곳에서 받아오는 경로로 업데이트 시켜주면 된다. 이제 이 부분을 pop으로 닫히는 부분 위에 String postImgLink = await imageNetworkRepository.getPostImageUrl(widget.postKey); 와 await postNetworkRepository.updatePostImageUrl( postKey: widget.postKey, postImg: postImgLink);와 같이 링크를 받아오고 그 링크와 postKey를 넘겨주면 된다. 실행해보면 정상적으로 다운로드 주소가 올라간것이 보이고, 주소를 실행하면 해당 이미지를 볼 수 있다. 그리고 이 onPressed 부분이 너무 복잡하고 길어서 하단에 메소드로 빼주면 된다.
post_network_repository.dart Future<void> updatePostImageUrl({String postImg, String postKey}) async { final DocumentReference postRef = Firestore.instance.collection(COLLECTION_POSTS).document(postKey); final DocumentSnapshot postSnapshot = await postRef.get(); if (postSnapshot.exists) { await postRef.updateData({ KEY_POSTIMG: postImg, }); } }
share_post_screen.dart void sharPostProcedure() async { showModalBottomSheet( context: context, builder: (_) => MyProgressIndicator(), //원래 빌더에 ()안에 context가 와야하는데 context를 사용 안하므로 _ 로 해준다 isDismissible: false, //이 창의 바깥 눌렀을때 끝나게 해줄것이냐 enableDrag: false, //드래그를 하게 해줄 것이냐. ); await imageNetworkRepository.uploadImage(widget.imageFile, postKey: widget.postKey); UserModel userModel = Provider.of<UserModelState>(context, listen: false).userModel; await postNetworkRepository.createNewPost( widget.postKey, PostModel.getMapForCreatePost( userKey: userModel.userKey, username: userModel.username, caption: _textEditingController.text)); String postImgLink = await imageNetworkRepository.getPostImageUrl(widget.postKey); await postNetworkRepository.updatePostImageUrl( postKey: widget.postKey, postImg: postImgLink); Navigator.of(context).pop(); //await 되고 나면 모달창이 pop으로 꺼짐 Navigator.of(context).pop(); // sharePost 창이 꺼짐 }
반응형'개발 > Flutter' 카테고리의 다른 글
Flutter - 코딩마스터하기|피드페이지도 파이어스토어에 연결하기 (0) 2021.11.19 Flutter - 코딩마스터하기|파이어스토어를 통한 팔로/언팔로 (0) 2021.11.17 Flutter - 코딩마스터하기|사진 업로드,게시하기2 (0) 2021.11.16 Flutter - 코딩마스터하기|사진 업로드,게시하기1 (0) 2021.11.15 Flutter - 코딩마스터하기|Firebase Firestore 사용하기2 (0) 2021.11.15 댓글