Don't think! Just do it!

종합 IT 기술 정체성 카오스 블로그! 이... 이곳은 어디지?

Flutter/Flutter Study

[Flutter] - Firebase firestore

방피터 2022. 10. 12. 16:57

React native에서는 React Query가 있어서 디따 편하게 했는데;;; 찾아보니까 Flutter에는 대표적으로 사용되는 React Query처럼 대표적으로 많이 사용되는 패키지는 없는 듯해... 음 뭐 그렇다고 Firestore 테스트를 못할 건 아니니까! 프로젝트 구조적으로 개판이되서 글치.. 암튼 고고.. 달려!

우선 Firebase console 설정부터!

Cloud Firestore 데이터베이스 만들기

그리고 걍 프로덕션 모드에서 시작하기 선택(로그인하면 DB 접근 가능)하고 다음

걍 프로덕션 모드하자

그리고 DB 위치를 선택해야 하는데 asia-northeast3가 서울 리젼이니까 난 이거 선택했음. 그런데 실제로 배포할 때는 여러가지 고려할 게 많겠지 주요 사용자의 위치라던지, 리전마다 DB 가격도 틀리니깐! (서울 리젼이 조금 비쌌던 걸로 기억함.)

서울 리젼 선택

사용 설정을 누르면 이제 DB는 준비가 끝났어. 이제 앱에서 접근해볼거야. cloud_firestore 패키지 설치부터👇

%flutter pub add cloud_firestore

그리고 TextField와 ElevatedButton을 만들어서 버튼을 누르면 TextField에 입력된 Text를 firestore에 업로드 하도록 만들어 볼거야.👇👇

class ScreenA extends StatelessWidget {
...
  //TextField controller
  final inputController = TextEditingController();
  //버튼에 물릴 텍스트 업로드 함수!
  void fbstoreWrite() {
    //"user@email.com" 컬렉션에 {userText:유저가 입력한 텍스트} 문서 추가!
    FirebaseFirestore.instance
        .collection(FirebaseAuth.instance.currentUser!.email.toString())
        .add({'userText': inputController.text})
        .then((value) => print("document added"))	//잘 들어갔니?
        .catchError((error) => print("Fail to add doc ${error}"));	//에러가 있니?
  }
...
            //텍스트를 입력햇! 
            TextField(
              controller: inputController,
              decoration: InputDecoration(
                  border: OutlineInputBorder(), label: Text("텍스트를 입력하세요.")),
            ),
            //버튼을 눌러서 업로드 햇!
            ElevatedButton(
            	//onPressed 함수를 물려줬!
                onPressed: () => fbstoreWrite(), child: Text("Text Upload")),
...

그리고 테스트를 해봅시다. 아래와 같이 "핼로우 월드" 입력하고 Text Upload 버튼을 누르면!👇

Firestore 테스트 스크린

Firebase console에서 DB에 핼로우 월드 문서가 잘 생성된 걸 확인할 수 있어.👇

Firestore 콘솔화면

여기서 끝내지 말고 읽어오는 것도 해보자구. 일단 단순하게 읽어오는 것부터! 그럴려면 읽어온 데이터를 저장할 State가 필요할 것 같아. 그러니까 Text 박스 하나랑 읽어오는 버튼 하나로 구성된 단순한 StatefulWidget을 하나 만들거야.

먼저 유저 텍스트 업로드 함수에 데이터 정렬을 위한 timeStamp를 하나 추가해줬음.👇

...
  void fbstoreWrite() {
    FirebaseFirestore.instance
        .collection(FirebaseAuth.instance.currentUser!.email.toString())
        //timeStamp 추가!
        .add({'userText': inputController.text, 'createdAt': Timestamp.now()})
        .then((value) => print("document added"))
        .catchError((error) => print("Fail to add doc ${error}"));
  }
...

그 다음에 위에서 말한 StatefulWidget도 새로 만들어서 넣어줬어!👇

class FirestoreRead extends StatefulWidget {
  const FirestoreRead({super.key});

  @override
  State<FirestoreRead> createState() => _FirestoreReadState();
}

class _FirestoreReadState extends State<FirestoreRead> {
  Map<String, dynamic> FirebaseLastDocument = {'userText': ""};
  void readFbLastDoc() {
    FirebaseFirestore.instance
        .collection(FirebaseAuth.instance.currentUser!.email.toString())
        .orderBy('createdAt')//생성된 시간으로 정렬해서
        .limitToLast(1)//뒤에서 1개 읽어옴.
        .get()
        .then((value) {
      setState(() {//setState!
        FirebaseLastDocument = value.docs[0].data();
      });
      print("data: ${FirebaseLastDocument['userText']}");
    }).catchError((onError) => print("error ${onError}"));
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        child: Column(
      children: [
        Text(FirebaseLastDocument['userText']),//userText 출력!
        ElevatedButton(
        	//버튼 누르면 읽어왓!
            onPressed: () => readFbLastDoc(), child: Text("Read Last FB Doc"))
      ],
    ));
  }
}

그리고 나서 테스트해보면 잘 동작하는 걸 확인할 수 있다구!

잘 동작한다!

여기까지는 잘 동작했는데 읽어오기 버튼 눌러서 읽어오는 거 말고 실시간으로 읽어올 수 있는지 확인해 볼래.

class FirestoreRead extends StatefulWidget {
  const FirestoreRead({super.key});

  @override
  State<FirestoreRead> createState() => _FirestoreReadState();
}

class _FirestoreReadState extends State<FirestoreRead> {
  //user email 컬렉션을 스트림으로 선언!!
  final Stream<QuerySnapshot> userTextStream = FirebaseFirestore.instance
      .collection(FirebaseAuth.instance.currentUser!.email.toString())
      .snapshots();

  @override
  Widget build(BuildContext context) {
    return StreamBuilder(
      stream: userTextStream,//스트림 연결!
      builder: (context, snapshot) {
        if (!snapshot.hasData) {// 데이터 없으면!!
          return Text("There is no data!");
        }
        if (snapshot.hasError) {// 에러 있으면!!
          return Text("Failed to read the snapshot");
        }
        return Expanded(
          child: ListView(//리스트뷰 써보자! 왜냐면 데이터가 많을 거니까!
            shrinkWrap: true,//이거 없으면 hasSize에서 에러발생!!
            //snapshot을 map으로 돌려버림!
            children: snapshot.data!.docs.map((DocumentSnapshot document) {
              Map<String, dynamic> data =
                  document.data() as Map<String, dynamic>;
              return ListTile(title: Text(data['userText']));//Listtile 생성!
            }).toList(),//map을 list로 만들어서 반환!
          ),
        );
      },
    );
  }
}

테스트를 해보면 실시간으로 추가된 데이터를 읽어온다~ 음~ 매우 흡족하구만!! 그런데 데이터를 순서대로 안읽어오네?

실시간 데이터 읽어오기

Snapshot에다가 orderby()를 추가해주면 될까? ㅇㅇ 됨.

final Stream<QuerySnapshot> userTextStream = FirebaseFirestore.instance
      .collection(FirebaseAuth.instance.currentUser!.email.toString())
      .orderBy('createdAt')// 이렇게 추가 해버렷!
      .snapshots();

시간순으로 잘 정렬되어 실시간으로 읽어온다.(f 어디갔어?)

이 정도면 flutter 위에서 기본은 다 해본거 같아. 이제 심화학습? 노노! 바로 프로젝트 돌입이지! ㅋㅋㅋ 나중에 문제가 많이 될거라고? ㅋㅋㅋㅋㅋ 응~ 그 문제 해결하는 것도 배워야지 ㅋㅋㅋ 자 이제 달려보자구! ㅋㅋㅋ

반응형