코딩하는 제리

[Flutter/Project](Instagram Clone) 포스트 데이터 업로드/업데이트 파일 작성 본문

Flutter/Project_InstaClone(완)

[Flutter/Project](Instagram Clone) 포스트 데이터 업로드/업데이트 파일 작성

JerryCho 2021. 3. 1. 20:34

models/repo/post_network_repository.dart
Share 버튼 실행 설정

StatefulWidget으로 변경한 후 TextEditingController 작성

screems/share_post_screen.dart
캡션 텍스트필드 컨트롤러 지정
user_key 확인
my_posts의 포스트 키 확인


소스코드 및 pubspec.yaml

// models/repo/post_network_repository.dart

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter_project_IJ/constants/firestore_keys.dart';

/*
포스트 데이터 업로드 과정
1. post reference를 가져옴(생성)
2. 어떤 포스트가 해당 유저에 포함되어 있는지 user reference를 가져옴
3. post collection과 user collection 두가지를 업데이트 해야하기에 transaction 사용.
4. user data에 postKey를 업데이트
5. post reference를 통해 post data를 업로드
*/

class PostNetworkRepository {
  Future<void> createNewPost(
      String postKey, Map<String, dynamic> postData) async {
    // 1. post reference를 가져옴(생성)
    final DocumentReference postRef =
        FirebaseFirestore.instance.collection(COLLECTION_POSTS).doc(postKey);
    // post reference를 통해 해당 document의 snapshot을 가져옴.
    final DocumentSnapshot postSnapshot = await postRef.get();
    // 2. 어떤 포스트가 해당 유저에 포함되어 있는지 user reference를 가져옴
    final DocumentReference userRef = FirebaseFirestore.instance
        .collection(COLLECTION_USERS)
        .doc(postData[KEY_USERKEY]);

    // 3. transaction -> 데이터 업로드에 하나라도 실패하면 이전의 데이터로 바꿔줌.
    return FirebaseFirestore.instance.runTransaction((Transaction tx) async {
      // 해당 postSnapshot이 존재하지 않으면
      if (!postSnapshot.exists) {
        // 4. 포스트 데이터 업로드
        await tx.set(postRef, postData);
        // 5. user의 mypost를 업데이트
        // user reference에 postKey 추가
        await tx.update(userRef, {
          KEY_MYPOSTS: FieldValue.arrayUnion([postKey])
        });
      }
    });
  }
}

PostNetworkRepository postNetworkRepository = PostNetworkRepository();
// screens/share_post_screen.dart

import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_project_IJ/constants/common_size.dart';
import 'package:flutter_project_IJ/constants/screen_size.dart';
import 'package:flutter_project_IJ/models/firestore/post_model.dart';
import 'package:flutter_project_IJ/models/firestore/user_model.dart';
import 'package:flutter_project_IJ/models/user_model_state.dart';
import 'package:flutter_project_IJ/repo/image_network_repository.dart';
import 'package:flutter_project_IJ/repo/post_network_repository.dart';
import 'package:flutter_project_IJ/widgets/my_progress_indicator.dart';
import 'package:flutter_tags/flutter_tags.dart';
import 'package:provider/provider.dart';

// TextEditingController를 위해 StatefulWidget로 변경
class SharePostScreen extends StatefulWidget {
  final File imageFile;
  final String postKey;

  SharePostScreen(this.imageFile, {Key key, @required this.postKey})
      : super(key: key);

  @override
  _SharePostScreenState createState() => _SharePostScreenState();
}

class _SharePostScreenState extends State<SharePostScreen> {
  // 캡션 텍스트박스 컨트롤러
  TextEditingController _textEditingController = TextEditingController();

  List<String> _tagItems = [
    "approval",
    "pigeon",
    "brown",
    "expenditure",
    "compromise",
    "citizen",
    "inspire",
    "relieve",
    "grave",
    "incredible",
    "voucher",
    "girl",
    "relax",
  ];

  @override
  void dispose() {
    // 컨트롤러 종료
    _textEditingController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('New Post'),
        actions: [
          TextButton(
            onPressed: () async {
              showModalBottomSheet(
                context: context,
                builder: (_) => MyProgressIndicator(),
                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.getMapForeCreatePost(
                      userKey: usermodel.userKey,
                      username: usermodel.username,
                      caption: _textEditingController.text));
              // 팝업 창을 닫음
              Navigator.of(context).pop();
              // share_post_screen을 닫음
              Navigator.of(context).pop();
            },
            child: Text(
              'Share',
              textScaleFactor: 1.4,
              style: TextStyle(color: Colors.blue),
            ),
          )
        ],
      ),
      body: ListView(
        children: [
          _captionWithImage(),
          _divider,
          _sectionButton('Tag People'),
          _divider,
          _sectionButton('Add Location'),
          _tags(),
          SizedBox(height: common_s_gap),
          _divider,
          SectionSwitch('Facebook'),
          SectionSwitch('Instagram'),
          SectionSwitch('Tumblr'),
          _divider,
        ],
      ),
    );
  }

  Tags _tags() {
    return Tags(
      horizontalScroll: true,
      itemCount: _tagItems.length,
      heightHorizontalScroll: 30 /* 높이 */,
      itemBuilder: (index) => ItemTags(
        index: index,
        title: _tagItems[index],
        activeColor: Colors.grey[200],
        textActiveColor: Colors.black87,
        borderRadius: BorderRadius.circular(4) /* 테두리 둥글기 */,
        elevation: 2 /* 그림자 */,
        splashColor: Colors.grey[800] /* 선택효과 색상 */,
        color: Colors.red /* 선택 색상 */,
      ),
    );
  }

  Divider get _divider => Divider(
        color: Colors.grey[300],
        height: 1,
        thickness: 1,
      );

  ListTile _sectionButton(String title) {
    return ListTile(
      title: Text(
        title,
        style: TextStyle(fontWeight: FontWeight.w400),
      ),
      trailing: Icon(Icons.navigate_next),
      dense: true,
      /* 작게 만듦. */
      contentPadding: EdgeInsets.symmetric(horizontal: common_gap),
    );
  }

  ListTile _captionWithImage() {
    return ListTile(
      contentPadding: EdgeInsets.symmetric(
        vertical: common_gap,
        horizontal: common_gap,
      ),
      leading: Image.file(
        widget.imageFile,
        width: size.width / 6,
        height: size.width / 6,
        fit: BoxFit.cover,
      ),
      title: TextField(
        controller: _textEditingController,
        autofocus: true,
        decoration: InputDecoration(
          hintText: 'Wirte a caption...',
          border: InputBorder.none,
        ),
      ),
    );
  }
}

// 버튼에 변화를 줘야함 = StatefulWidget
class SectionSwitch extends StatefulWidget {
  final String _title;

  const SectionSwitch(
    this._title, {
    Key key,
  }) : super(key: key);

  @override
  _SectionSwitchState createState() => _SectionSwitchState();
}

class _SectionSwitchState extends State<SectionSwitch> {
  bool checked = false;

  @override
  Widget build(BuildContext context) {
    return ListTile(
      title: Text(
        widget._title,
        style: TextStyle(fontWeight: FontWeight.w400),
      ),
      trailing: CupertinoSwitch(
        value: checked,
        onChanged: (onValue) {
          setState(() {
            checked = onValue;
          });
        },
      ),
      dense: true,
      /* 작게 만듦. */
      contentPadding: EdgeInsets.symmetric(horizontal: common_gap),
    );
  }
}
Comments