[Flutter] 실시간 채팅 기능

메모하는 개발자·2021년 12월 6일
2
post-custom-banner

socket_io사용

socket_io를 사용하여 특정 이벤트 발생시 서버에서 연결하고 있는 클라이언트들에게 데이터를 전달하도록 했다.
https://pub.dev/packages/socket_io_client/install

socket_io 연결

_socket = _io.io(
        'ex) http://localhost:3000',
        <String, dynamic>{
          'transports': ['websocket'],
          'forceNew': true,
        });
  • transports 옵션을 websocket 으로 하면 폴링이 아닌 순수 웹소켓 모드로 연결을 업그레이드 시킨다.
  • forceNew 옵션을 통해 connection 재사용 여부를 정한다.

socket을 채팅리스트 위젯에서 사용

상세내용은 주석 참고

class LiveChatList extends StatefulWidget {
  final _io.Socket socket;

  const LiveChatList({
    Key? key,
    required this.socket,
  }) : super(key: key);

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

class _LiveChatListState extends State<LiveChatList> {
  //새로운 채팅 올때마다 추가하기위한 StreamController
  final StreamController<LiveChatObject> _streamController =
      StreamController<LiveChatObject>();
      
  //메세지 리스트 (노출할 채팅목록이 있는 리스트)
  final List<LiveChatObject> _messageList = [];
  
  //채팅리스트 scrollController
  final ScrollController _scrollController = ScrollController();

  @override
  void initState() {
    super.initState();

    //이전 채팅 목록 가져오기
    WidgetsBinding.instance!.addPostFrameCallback((_) {
      widget.socket.emit('chats');
      widget.socket.on('CHATS', (data) {
        Iterable list = data['CA'];
        
        //Json정보를 받아올 채팅 구조(LiveChatObject)의 리스트형태로 변환
        var beforeChats =
            list.map((i) => LiveChatObject.fromJson(i)).toList();

        setState(() {
          //받아온 이전 채팅목록을 messageList에 추가
          beforeChats.forEach((chatData) {
              _messageList.add(chatData);
            
          });
        });
      });
    });
    
    //새로운 채팅 올때마다 messageList에 추가
    widget.socket.on('MSSG', (data) {
      _streamController.sink.add(LiveChatObject.fromJson(data));
      setState(() {
          _messageList.add(LiveChatObject.fromJson(data));
        
      });
    });
  }

  @override
  void dispose() {
    super.dispose();

    _scrollController.dispose();
    _streamController.close();
  }

  @override
  Widget build(BuildContext context) {
    // build마다 채팅 스크롤 맨 밑으로
    if (_messageList.isNotEmpty ) {
      SchedulerBinding.instance!.addPostFrameCallback((_) {
        _scrollController.animateTo(
          _scrollController.position.maxScrollExtent,
          duration: Duration(milliseconds: 50),
          curve: Curves.easeOut,
        );
      });
    }

    return
    	Container(
          margin: EdgeInsets.fromLTRB(0, 0, 0, 7),
          height: 170,
          padding: EdgeInsets.fromLTRB(0, 5, 5, 5),
          child: ListView.builder(
            shrinkWrap: true,
            padding: EdgeInsets.zero,
            controller: _scrollController,
            itemCount: _messageList.length,
            itemBuilder: (BuildContext context, int index) {
              return _messageList[index].PMO != null
                  ShoppyLiveChatText(
                      nickname:  _messageList[index].NI,
                      message: _messageList[index].MG,
                      empFlag:  false,
                      isPMO: false);
            		},
          		),
       		 ),
         )
    );
  }
}

post-custom-banner

0개의 댓글