Flutter

[Firebase+provider]Chp3. Chat App 채팅 페이지 만들기

모리선생 2023. 5. 12. 07:01
728x90

목표

provider과 Firebase를 기반으로 일회용 chat이 가능한 Application을 만들어보고 활용법을 알아본다.


해당 내용은 도그풋님의 #2 플러터 + 파이어베이스 채팅앱 만들기를 기초로 study를 진행하였으며, 일부 부분과 다르게 UI 및 기능적인 측면에서 추가를 하였습니다. 또한 Firebase와 Firecloud 등이 업데이트 됨에 따라 firebase 코드 관련 부분등은 수정된 부분이 있으니 참고하여 주시기 바랍니다.

 

또한 해당내용에 있어서 공부를 진행하는 부분이 있으므로 코드상에 문의가 있거나, 수정사항이 발견이 되면 알려주시면 감사하겠습니다.


해당 코드의 github

https://github.com/riris01/flutter_firebase_chat_provider

 

원본 코드의 github

https://github.com/wownsdl13/flutter_firebase_chatting_example


저번 시간 까지는 본격적인 채팅방에 들어가기전 UI를 구성해보았다. 이제 채팅방으로 넘어가서 작업을 진행해보자.

 

1.@chatting_page.dart

chatting_page.dart는 screens/chatting_page/chatting_page.dart내에 생성을 해준다.이번에도 동일하게 statefulWidget으로 ChattingPage를 생성을 해준다. 그다음 사용할 변수와 인스턴스들은 다음과 같다

  • TextEditingController _controller
  • StreamSubscription _streamSubscription

StreamSubscription은 Firebase Firestore의 스트림을 구독하게 해주는 역할을 한다.

 

그다음 Provider를 사용하여 데이터베이스에서 가져온 데이터를 Chattingmodel로 파싱할 수 있도록 설정을 한다. getSnanpshot() 함수의 경우에는 Firestore 스트림을 가져오고 ChattingProvider 클래스에서 변경 사항을 구독하고, 해당 변경 사항을 받으면 ChattingModel로 파싱을 수행하는 역할을 한다.

 

이렇게 설명한 내용들을 가지고 채팅페이지 초기화 작업과 관련된 코드를 작성하면

(...)

class _ChattingPageState extends State<ChattingPage> {
  late TextEditingController _controller;
  late StreamSubscription _streamSubscription;

  bool firstLoad = true;
  @override
  void initState() {
    _controller = TextEditingController();
    final p = Provider.of<ChattingProvider>(context, listen: false);
    _streamSubscription = p.getSnapshot().listen((event) {
      if (firstLoad) {
        firstLoad = false;
        return;
      }
      p.addOne(ChattingModel.fromJson(event.docs[0].data()
          as Map<String, dynamic>)); //Map<String, dynmamic>은 형변환을 하기 위한 역할을 함
    });
    Future.microtask(() {
      p.load();
    });
    super.initState();
  }
  
  (...)

여기서 addOne 함수는 ChattingProvider에 새로운 채팅을 추가하는 함수이고 load()함수는 데이터를 가져오기 위한 Firestore의 쿼리를 실행하는 역할을 한다. Future.microtask()는 현재 코드의 실행이 완료된 이후 다음 이벤트 루프 까지 다시 재 시작하지 않도로 비동기 작업을 예약하는데 사용이 되었다.

 

StatefulWidget의 initializing과 관련된 작업을 했으니 이제 소멸시 stream 취소하는 역할의 코드도 생성하자.

@override
  void dispose() {
    _streamSubscription.cancel();
    super.dispose();
  }

만약 제거를 하지 않는 다면 클래스 인스턴스가 메모리에서 지워질때 스트림이 종료하지 않아 불필요한 메모리 누수가 발생한다.

 

자, 그럼 이제 실제로 어떤 채팅 화면이 보여질지 화면 구성을 해보자.

 

일단 가장먼저 Scaffold내에 appbar를 생성하였다. 그래서 가장 먼저 만든것은 GestureDector를 사용하여 뒤로 나갈 수 있는 버튼을 생성하는 것이다. Navigator.of(context).pop()을 사용하면 뒤로 나갈 수 있다. 즉, Navigator.pop()을 사용하면 현재 페이지를 스택에서 제거할 수 있는 것이다. (혹시 궁금하신 분은 이 블로그를 참고해보자: https://seosh817.tistory.com/211)

(...)

@override
  Widget build(BuildContext context) {
    final p = Provider.of<ChattingProvider>(context);
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.blue[900],
        leading: GestureDetector(
            onTap: () {
              Navigator.of(context).pop();
            },
            child: Icon(Icons.arrow_back_ios_rounded)),
      ),
      
(...)

이제 그다음은 body를 만들어 보자. 일단 Column 으로 감싼뒤에 ListView로 각각의 입력 말풍선들이 나타날 수 있도록 만들어야 한다. chattingList에서 받아오는 데이터들을 기반으로 내용들이 나타나며, 여기서 chatting에 사용되는 말풍선들은 ChattingItem들에서 불러 온다. 여기서 .map()이란 것을 볼 수 있을 텐데, 다른언어에서는 객체로 불리지만 Dart에서는 map이라는 자료 형으로 불리며, 키와 값으로 구성이 되어있다 (왠만한 입문을 떼고 온 사람들이라면 아마 알 내용일꺼다).

 

아 그리고 여기서 Divider라고 하는 함수가 보일텐데 이는 입력된 말풍선간의 간격을 주기 위해서 사용이 되었다. 그럼 이 내용을 토대로 코드를 작성하면 다음과 같다.

(...)

body: Column(
        children: [
          Expanded(
            child: ListView(
              reverse: true,
              children: p.chattingList
                  .map((e) => ChattingItem(
                        chattingModel: e,
                        key: ValueKey(e), // 해당부분 수정
                      ))
                  .toList(),
            ),
          ),
          Divider(
            thickness: 1.5,
            height: 1.5,
            color: Colors.grey[300],
          ),

(...)

 이제 채팅을 하기 위해서 가장 중요한 입력창을 구성해본다. 일단 입력창은 Container()로 만들되 그 안에 TextField()를 넣는 형태 이다. 거기에 작성한 '내용'을 보낼 수 있는 버튼을 생성하면 된다. 여기서는 보내는 버튼을 새롭게 만들어서 onSubmitted로 처리했다.

 

아 그리고 카카오톡에서 편하게 쓰는기능이 'Enter'를 쳐서 보내는 기능인데 여기서는 keyboardType: TextInputType.text과 Onsubmitted: (text){_controller.clear(); p.send(text);를 입력하여 구현을 하였다. 이제 이렇게 하면은 채팅 창 내에서 텍스트를 입력하고 보낼 수 있는 기능과 UI까지 구현을 하였다.

 

기능을 추가해야할 당시에는 어떤 기능을 넣는것이 사람들이 편하게 느낄까라는 걸 생각하면 답이 나오는 듯 하다. 너무 화려한 기능 혹은 개발자만 알 수 있는 숨겨진 기능들은 유저들이 그 의도를 알아차리지 못하고 사용하지 않을 가능성이 크다.

 

여튼, 이렇게 하면 다음과 같이 만들어진다.

자, 그럼 아까전에 말하였던,

chatting_item.dart

ChattingProvider.dart

ChattingModel.dart

들을 마저 작성을 하여서 기능이 제대로 구현될 수 있도록하자.

728x90