본문 바로가기

Flutter

[Flutter] 채팅앱 만들기 #7

What to do?

State 관리

React에서 상태관리를 redux, zustand, recoil과 같은 상태관리 라이브러리를 사용 하듯이,

유사하게 Flutter 프로젝트에서도 Bloc 라이브러리를 적용해보자.


Reference

 


라이브러리 설치

 

pubspec.yaml 파일 수정

  • 상태관리 라이브러리를 사용하기 위한 라이브러리 설치
dependencies:
  ...
  bloc: ^7.0.0
  equatable: ^2.0.2

pubspec.yaml


Usage

 

메세지의 상태를 정의하는 예시

 

  • State 정의
    • MessageState라는 Base State를 정의
    • MessageSentSuccess라는 State 정의 - props는 message
abstract class MessageState extends Equatable {
  // constructor
  const MessageState();

  // factory
  factory MessageState.sent(Message message) => MessageSentSuccess(message);

  // props
  @override
  List<Object> get props => [];
}


class MessageSentSuccess extends MessageState {
  // constructor
  final Message message;
  const MessageSentSuccess(this.message);

  // override getter
  @override
  List<Object> get props => [message];
}

 

  • Event 정의
    • MessageSentEvent라는 이벤트 정의
    • props는 message
abstract class MessageEvent extends Equatable {
  const MessageEvent();

  factory MessageEvent.onMessageSent(Message message) => MessageSentEvent(message);

  @override
  List<Object> get props => [];
}

class MessageSentEvent extends MessageEvent {
  final Message message;

  const MessageSentEvent(this.message);

  @override
  List<Object> get props => [message];
}

 

  • Bloc 정의
    • Bloc<MessageEvent, MessageState> : Event와 State를 extend해서 Bloc 클래스를 정의
    • overriding : 아래의 class들을 override
      • mapEventToState : 분기처리를 통해서 이벤트와 상태를 매핑시켜줌
        • 이벤트가 MessageSentEvent인 경우, MessageSentSuccess를 반환
      • close
class MessageBloc extends Bloc<MessageEvent, MessageState> {
  final IMessageService _messageService;
  StreamSubscription _subscription;

  MessageBloc(this._messageService) : super(MessageState.initial());

  @override
  Stream<MessageState> mapEventToState(MessageEvent event) async* {
    
    ...
    
    /// send
    if (event is MessageSentEvent) {
      await _messageService.send(event.message);
      yield MessageState.sent(event.message);
    }
  }

  @override
  Future<void> close() {
    _subscription?.cancel();
    _messageService.dispose();
    return super.close();
  }
}

State 정의

  1. init
  2. sent
  3. received

 

  • Message

/state_management/message/message_state.dart

abstract class MessageState extends Equatable {
  const MessageState();

  factory MessageState.initial() => MessageInitial();

  factory MessageState.sent(Message message) => MessageSentSuccess(message);

  factory MessageState.received(Message message) =>
      MessageReceivedSuccess(message);

  @override
  List<Object> get props => [];
}

class MessageInitial extends MessageState {}

class MessageSentSuccess extends MessageState {
  final Message message;

  const MessageSentSuccess(this.message);

  @override
  List<Object> get props => [message];
}

class MessageReceivedSuccess extends MessageState {
  final Message message;

  const MessageReceivedSuccess(this.message);

  @override
  List<Object> get props => [message];
}

 

  • Receipt

/state_management/receipt/receipt_state.dart

abstract class ReceiptState extends Equatable {
  const ReceiptState();

  factory ReceiptState.initial() => ReceiptInitial();

  factory ReceiptState.sent(Receipt receipt) => ReceiptSentSuccess(receipt);

  factory ReceiptState.received(Receipt receipt) =>
      ReceiptReceivedSuccess(receipt);

  @override
  List<Object> get props => [];
}

class ReceiptInitial extends ReceiptState {}

class ReceiptSentSuccess extends ReceiptState {
  final Receipt receipt;

  const ReceiptSentSuccess(this.receipt);

  @override
  List<Object> get props => [receipt];
}

class ReceiptReceivedSuccess extends ReceiptState {
  final Receipt receipt;

  const ReceiptReceivedSuccess(this.receipt);

  @override
  List<Object> get props => [receipt];
}

 

  • TypingNotification

/state_management/typing/typing_state.dart

abstract class TypingNotificationState extends Equatable {
  const TypingNotificationState();

  factory TypingNotificationState.initial() => TypingNotificationInitial();

  factory TypingNotificationState.sent() => TypingNotificationSentSuccess();

  factory TypingNotificationState.received(TypingEvent typingEvent) =>
      TypingNotificationReceivedSuccess(typingEvent);

  @override
  List<Object> get props => [];
}

class TypingNotificationInitial extends TypingNotificationState {}

class TypingNotificationSentSuccess extends TypingNotificationState {}

class TypingNotificationReceivedSuccess extends TypingNotificationState {
  final TypingEvent typingEvent;

  const TypingNotificationReceivedSuccess(this.typingEvent);

  @override
  List<Object> get props => [typingEvent];
}

Event 정의

 

  • MessageEvent

/state_management/message/message_event.dart

abstract class MessageEvent extends Equatable {
  const MessageEvent();

  factory MessageEvent.onSubscribed(User user) => SubscribeEvent(user);

  factory MessageEvent.onMessageReceived(Message message) =>
      MessageReceivedEvent(message);

  factory MessageEvent.onMessageSent(Message message) =>
      MessageSentEvent(message);

  @override
  List<Object> get props => [];
}

class SubscribeEvent extends MessageEvent {
  final User user;

  const SubscribeEvent(this.user);

  @override
  List<Object> get props => [user];
}

class MessageReceivedEvent extends MessageEvent {
  final Message message;

  const MessageReceivedEvent(this.message);

  @override
  List<Object> get props => [message];
}

class MessageSentEvent extends MessageEvent {
  final Message message;

  const MessageSentEvent(this.message);

  @override
  List<Object> get props => [message];
}

 

  • ReceiptEvent

/state_management/receipt/receipt_event.dart

abstract class ReceiptEvent extends Equatable {
  const ReceiptEvent();

  factory ReceiptEvent.onSubscribed(User user) => SubscribeEvent(user);

  factory ReceiptEvent.onReceiptSent(Receipt receipt) => ReceiptSentEvent(receipt);

  @override
  List<Object> get props => [];
}

class SubscribeEvent extends ReceiptEvent {
  final User user;

  const SubscribeEvent(this.user);

  @override
  List<Object> get props => [user];
}

class ReceiptReceivedEvent extends ReceiptEvent {
  final Receipt receipt;

  const ReceiptReceivedEvent(this.receipt);

  @override
  List<Object> get props => [receipt];
}

class ReceiptSentEvent extends ReceiptEvent {
  final Receipt receipt;

  const ReceiptSentEvent(this.receipt);

  @override
  List<Object> get props => [receipt];
}

 

  • TypingNotification

/state_management/typing/typing_event.dart

abstract class TypingNotificationEvent extends Equatable {
  const TypingNotificationEvent();

  factory TypingNotificationEvent.onSubscribed(User user,
          {List<String> userWithChat}) =>
      SubscribeEvent(user, userWithChat: userWithChat);

  factory TypingNotificationEvent.onTypingNotificationSent(
          TypingEvent typing) =>
      TypingNotificationSentEvent(typing);

  @override
  List<Object> get props => [];
}

class SubscribeEvent extends TypingNotificationEvent {
  final User user;
  final List<String> userWithChat;

  const SubscribeEvent(this.user, {this.userWithChat});

  @override
  List<Object> get props => [user];
}

class NotSubscribeEvent extends TypingNotificationEvent {}

class TypingNotificationReceivedEvent extends TypingNotificationEvent {
  final TypingEvent typing;

  const TypingNotificationReceivedEvent(this.typing);

  @override
  List<Object> get props => [typing];
}

class TypingNotificationSentEvent extends TypingNotificationEvent {
  final TypingEvent typing;

  const TypingNotificationSentEvent(this.typing);

  @override
  List<Object> get props => [typing];
}

Bloc

 

  • Message

/state_management/message/message_block.dart

class MessageBloc extends Bloc<MessageEvent, MessageState> {
  final IMessageService _messageService;
  StreamSubscription _subscription;

  MessageBloc(this._messageService) : super(MessageState.initial());

  @override
  Stream<MessageState> mapEventToState(MessageEvent event) async* {
    /// subscribe
    if (event is SubscribeEvent) {
      await _subscription?.cancel();
      _subscription = _messageService.messages(user: event.user).listen((msg) {
        add(MessageReceivedEvent(msg));
      });
    }

    /// receive
    if (event is MessageReceivedEvent) {
      yield MessageState.received(event.message);
    }

    /// send
    if (event is MessageSentEvent) {
      await _messageService.send(event.message);
      yield MessageState.sent(event.message);
    }
  }

  @override
  Future<void> close() {
    _subscription?.cancel();
    _messageService.dispose();
    return super.close();
  }
}

 

  • Receipt

/state_management/receipt/receipt_bloc.dart

class ReceiptBloc extends Bloc<ReceiptEvent, ReceiptState> {
  final IReceiptService _receiptService;
  StreamSubscription _subscription;

  ReceiptBloc(this._receiptService) : super(ReceiptState.initial());

  @override
  Stream<ReceiptState> mapEventToState(ReceiptEvent event) async* {
    /// subscribe
    if (event is SubscribeEvent) {
      await _subscription?.cancel();
      _subscription = _receiptService
          .receipts(event.user)
          .listen((receipt) => add(ReceiptReceivedEvent(receipt)));
    }

    /// received
    if (event is ReceiptReceivedEvent) {
      yield ReceiptState.received(event.receipt);
    }

    /// send
    if (event is ReceiptSentEvent) {
      await _receiptService.send(event.receipt);
      yield ReceiptState.sent(event.receipt);
    }
  }

  @override
  Future<void> close() {
    _subscription?.cancel();
    _receiptService.dispose();
    return super.close();
  }
}

 

  • TypingEvent

/state_management/typing/typing_bloc.dart

class TypingBloc
    extends Bloc<TypingNotificationEvent, TypingNotificationState> {
  final ITypingNotificationService _typingNotificationService;
  StreamSubscription _subscription;

  TypingBloc(this._typingNotificationService)
      : super(TypingNotificationState.initial());

  @override
  Stream<TypingNotificationState> mapEventToState(
      TypingNotificationEvent event) async* {
    /// Subscribe but not user
    if (event is SubscribeEvent && event.userWithChat == null) {
      add(NotSubscribeEvent());
      return;
    }

    /// Subscribe
    if (event is SubscribeEvent) {
      await _subscription?.cancel();
      _subscription = _typingNotificationService
          .subscribe(event.user, event.userWithChat)
          .listen((typing) => add(TypingNotificationReceivedEvent(typing)));
    }

    /// Received
    if (event is TypingNotificationReceivedEvent) {
      yield TypingNotificationState.received(event.typing);
    }

    /// Send
    if (event is TypingNotificationSentEvent) {
      await _typingNotificationService.send(event: event.typing);
      yield TypingNotificationState.sent();
    }

    /// Not Subscribe
    if (event is NotSubscribeEvent) {
      yield TypingNotificationState.initial();
    }
  }

  @override
  Future<void> close() {
    _subscription?.cancel();
    _typingNotificationService.dispose();
    return super.close();
  }
}

'Flutter' 카테고리의 다른 글

[Flutter] 채팅앱 만들기 #9  (0) 2023.03.08
[Flutter] 채팅앱 만들기 #8  (0) 2023.03.07
[Flutter] 채팅앱 만들기 #6  (0) 2023.03.05
[Flutter] 채팅앱 만들기 #5  (0) 2023.03.04
[Flutter] 채팅앱 만들기 #4  (0) 2023.03.01