Bloc: Question: How do one yield a state from a future - Showing push messages from Firebase messaging

Created on 10 Jan 2020  路  1Comment  路  Source: felangel/bloc

How would one yield a state from within a future? I'm trying to implement firebase messaging using your library, but i'm stuck with this.

This is my code:

import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/cupertino.dart';
import 'package:sasos/models/push_message.dart';
import 'package:sasos/models/settings.dart';
import 'package:sasos/models/user.dart';
import 'package:sasos/stores/file/push_message_file_repository.dart';
import 'package:sasos/stores/http/sos_http_repository.dart';
import 'package:sasos/stores/user_repository.dart';
import './bloc.dart';

class MessagesBloc extends Bloc<MessagesEvent, MessagesState> {
  final PushMessageFileRepository messageFileRepository;
  final SOSHTTPRepository sosRepository;
  final UserRepository userRepository;

  MessagesBloc({@required this.messageFileRepository, @required this.sosRepository, @required this.userRepository});

  final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();

  @override
  MessagesState get initialState => InitialMessagesState();

  @override
  Stream<MessagesState> mapEventToState(MessagesEvent event) async* {
    if (event is StartFirebaseMessaging) {
      yield* _mapToInitialiseFirebaseEvent();
    } else if (event is LoadAllMessagesEvent) {
      yield* _mapToLoadAllMessagesEvent();
    } else if (event is MessageReceivedEvent) {
      yield* _mapToMessageReceivedEvent(event);
    }
  }

  Stream<MessagesState> _yieldMessage(Map<String, dynamic> message) async* {
    yield NewMessageState(message: message);
  }

  Stream<MessagesState> _mapToInitialiseFirebaseEvent() async* {
    _firebaseMessaging.configure(
      onMessage: (Map<String, dynamic> message) async {
        print("onMessage: $message");
        _yieldMessage(message);
      },
      onLaunch: (Map<String, dynamic> message) async {
        print("onLaunch: $message");
        _yieldMessage(message);
      },
      onResume: (Map<String, dynamic> message) async {
        print("onResume: $message");
        _yieldMessage(message);
      },
    );

    _firebaseMessaging.requestNotificationPermissions(const IosNotificationSettings(sound: true, badge: true, alert: true, provisional: true));
    _firebaseMessaging.onIosSettingsRegistered.listen((IosNotificationSettings settings) {
      print("Settings registered: $settings");
    });
    _firebaseMessaging.getToken().then((String token) async {
      assert(token != null);
      print("Push Messaging token: $token");
      Settings settings = await userRepository.loadSettingsFromStore();
      sosRepository.updateUser(User(username: settings.userName, imeinumber: token));
    });
  }

  Stream<MessagesState> _mapToLoadAllMessagesEvent() async* {
    yield MessagesLoadingState();
    List<PushMessage> messages = await messageFileRepository.load();
    if (messages.length > 0) {
      yield MessagesLoadedState(messages: messages);
    } else {
      yield NoMessagesState();
    }
  }

  Stream<MessagesState> _mapToMessageReceivedEvent(MessageReceivedEvent event) async* {
    yield NewMessageState();
  }
}
import 'package:equatable/equatable.dart';
import 'package:sasos/models/push_message.dart';

abstract class MessagesEvent extends Equatable {
  const MessagesEvent();

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

class StartFirebaseMessaging extends MessagesEvent {}

class LoadAllMessagesEvent extends MessagesEvent {}

class MessageReceivedEvent extends MessagesEvent {
  final PushMessage message;

  MessageReceivedEvent({this.message});

  @override
  List<Object> get props => [message];
}
import 'package:equatable/equatable.dart';
import 'package:sasos/models/push_message.dart';

abstract class MessagesState extends Equatable {
  const MessagesState();

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

class InitialMessagesState extends MessagesState {}

class NoMessagesState extends MessagesState {}

class MessagesLoadingState extends MessagesState {}

class MessagesLoadedState extends MessagesState {
  final List<PushMessage> messages;

  MessagesLoadedState({this.messages});

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

class NewMessageState extends MessagesState {
  final Map<String, dynamic> message;

  NewMessageState({this.message});

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

question

Most helpful comment

Hi @Theunodb 馃憢
Thanks for opening an issue!

You can't yield from within the async closure. Instead, I'd recommend adding a new event in response to each of the callbacks like:

    _firebaseMessaging.configure(
      onMessage: (Map<String, dynamic> message) async {
        print("onMessage: $message");
        add(MessageReceived(message));
      },
      onLaunch: (Map<String, dynamic> message) async {
        print("onLaunch: $message");
        add(Launched(message));
      },
      onResume: (Map<String, dynamic> message) async {
        print("onResume: $message");
        add(Resumed(message));
      },
    );

Hope that helps 馃憤

>All comments

Hi @Theunodb 馃憢
Thanks for opening an issue!

You can't yield from within the async closure. Instead, I'd recommend adding a new event in response to each of the callbacks like:

    _firebaseMessaging.configure(
      onMessage: (Map<String, dynamic> message) async {
        print("onMessage: $message");
        add(MessageReceived(message));
      },
      onLaunch: (Map<String, dynamic> message) async {
        print("onLaunch: $message");
        add(Launched(message));
      },
      onResume: (Map<String, dynamic> message) async {
        print("onResume: $message");
        add(Resumed(message));
      },
    );

Hope that helps 馃憤

Was this page helpful?
0 / 5 - 0 ratings