Flutter realtime chatting app

강정우·2023년 8월 20일
0

Flutter&Dart

목록 보기
63/87
post-thumbnail

UI 작성


class _NewMessageState extends State<NewMessage> {
  final _messageController = TextEditingController();

  
  void dispose() {
    _messageController.dispose();
    super.dispose();
  }

  void _submitMessage() async {
    final enteredMessage = _messageController.text;
	...
  }

  
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(
        left: 15,
        right: 1,
        bottom: 14,
      ),
      child: Row(
        children: [
          Expanded(
            child: TextField(
              controller: _messageController,
              textCapitalization: TextCapitalization.sentences,
              autocorrect: true,
              enableSuggestions: true,
              decoration: const InputDecoration(labelText: 'Send a message...'),
            ),
          ),
          IconButton(
            onPressed: _submitMessage,
            icon: const Icon(Icons.send),
            color: Theme.of(context).colorScheme.primary,
          ),
        ],
      ),
    );
  }
}

FocusScope

  • unfoucous()
  void _submitMessage() async {
    final enteredMessage = _messageController.text;
    if (enteredMessage.trim().isEmpty) {
      return;
    }
    FocusScope.of(context).unfocus();
    final user = FirebaseAuth.instance.currentUser!;
    final userData = await FirebaseFirestore.instance
        .collection('users')
        .doc(user.uid)
        .get();

    FirebaseFirestore.instance.collection('chat').add({
      'text': enteredMessage,
      'createAt': Timestamp.now(),
      'userId': user.uid,
      'username': userData.data()!['username'],
      'userImage': userData.data()!['image_url'],
    });
    _messageController.clear();
  }
  • 당연 위 메서드는 일반 메서드이기에 async 메서드 안에서 await 이전에 실행해줘야한다. 멈출 수 있기 때문

핵심 기능

  • 사실 flutter firebase sdk를 사용하여 매우 짧고 간편하게 구현할 수 있다. 그것이 아니라면 websocket 과 같은 기능을 구현해야할 것이다.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';

class ChatMessages extends StatelessWidget {
  const ChatMessages({super.key});

  
  Widget build(BuildContext context) {
    return StreamBuilder(
      stream: FirebaseFirestore.instance
          .collection('chat')
          .orderBy('createdAt', descending: false)
          .snapshots(),
      builder: (ctx, chatSnapshots) {
        if (chatSnapshots.connectionState == ConnectionState.waiting) {
          return const Center(
            child: CircularProgressIndicator(),
          );
        }
        if (!chatSnapshots.hasData || chatSnapshots.data!.docs.isEmpty) {
          return const Center(
            child: Text('No messages found.'),
          );
        }
        if (chatSnapshots.hasError) {
          return const Center(
            child: Text('Something went wrong...'),
          );
        }
        final loadedMessages = chatSnapshots.data!.docs;
        return ListView.builder(
          itemCount: loadedMessages.length,
          itemBuilder: (ctx, index) => Text(
            loadedMessages[index].data()['text'],
          ),
        );
      },
    );
  }
}

스타일링

ListView

reverse

  • 이 속성값을 위치를 밑으로 바꿔준다. 뿐만아니라 순서도 역순으로 바꿔준다.
return ListView.builder(
  padding: const EdgeInsets.only(
  bottom: 40,
  left: 13,
  right: 13,
  ),
  reverse: true,
    itemCount: loadedMessages.length,
      itemBuilder: (ctx, index) {
        final chatMessage = loadedMessages[index].data();
        final nextChatMessage = index + 1 < loadedMessages.length
          ? loadedMessages[index + 1].data()
        : null;
        final currentMessageUserId = chatMessage['userId'];
        final nextMessageUserId =
          nextChatMessage != null ? nextChatMessage['userId'] : null;
        final nextUserIsSame = nextMessageUserId == currentMessageUserId;
        if (nextUserIsSame) {
          return MessageBubble.next(
            message: chatMessage['text'],
            isMe: authenticatedUser.uid == currentMessageUserId,
              );
        } else {
          return MessageBubble.first(
            userImage: chatMessage['userImage'],
            username: chatMessage['username'],
              message: chatMessage['text'],
                isMe: authenticatedUser.uid == currentMessageUserId,
          );
        }
      },
);

2개의 생성자 함수

class MessageBubble extends StatelessWidget {
  const MessageBubble.first({
    super.key,
    required this.userImage,
    required this.username,
    required this.message,
    required this.isMe,
  }) : isFirstInSequence = true;

  const MessageBubble.next({
    super.key,
    required this.message,
    required this.isMe,
  })  : isFirstInSequence = false,
        userImage = null,
        username = null;
  • 이때 2개의 생성자 함수로 isMe 속성값에 bool 속성을 주어 mainAxisAlignment 속성에 .end, .start 속성으로 메시지 효과를 부여하였다.

에러

flutter ':app:mapdebugsourcesetpaths'

  • 만약 위와같은 에러의 원인은 com.android.tools.build:gradle to version 7.3.0 으로 업데이트 되면서 생겼다고 한다. 그래서 com.google.gms:google-services to version 4.3.14 으로 설정해주면 된다고 한다.

  • 혹시 본인이 com.android.tools.build:gradle 버전이 7.3.0이 아니라고 한다면 다음 링크를 들어가면 각 버전별로 com.google.gms:google-services 를 설정할 수 있도록 알려준다.
    해결법 참조 스택오버플로 형님들

profile
智(지)! 德(덕)! 體(체)!

0개의 댓글