Stream/Bloc/Repository/Firebase data flow Flutter

2,368

Ok, I I think I understood the logic behind it, but if you see that I didn't get it right please correct me as at this stage of getting into a new paradigm is very important not to carry any misconceptions.

  1. todos() is the Stream coming from Firebase and returns a List<Todo>.
  2. _mapLoadTodosToState() is the bloc method that attach a bloc listener to todos() and in the .listen(onData) callback, it sends to the bloc an TodosUpdated(todos) event containing the latest list.
  3. TodosUpdated(todos) gets mapped to _mapTodosUpdatedToState, which yields TodosLoaded(event.todos) , the new state that BlocProvider uses to build the UI.

Thank you and I hope this will help others struggling to master BloC pattern at a more complex level. Cheers

Share:
2,368
Vincenzo
Author by

Vincenzo

At age of 40 an accident forced me in bed for a few months and I realised that I wanted point my life in a different direction to really fulfil my purpose in life, help others and help making this planet a better place. I love to create stuff and I constantly have perhaps too many ideas. I'm now launching a bicycle related start-up. I got back into coding after 25 years away from it as it was a fundamental skill to get updated to get the project started and since then I'm working on the start-up's products. I first learned Swift as it seemed the easiest language I could learn at fast pace, but when Apple created SwiftUI framework ( Flutter copycat A.F.A.I.K ) I decided to move away from Swift the obvious choice has been learning Flutter ( the original ). So happy I made the transition and I'm not looking back.

Updated on December 08, 2022

Comments

  • Vincenzo
    Vincenzo over 1 year

    I'm starting with flutter as I want to port my swift app to Flutter, but I'm getting stuck understanding the pattern Bloc/Repository/Firebase as I'm following the tutorial https://bloclibrary.dev/#/flutterfirestoretodostutorial dough I use the real time database, not Firestore. My swift app is basically a map where you can add Alerts at your actual coordinates. The Alert get sent to Firebase and the firebase observer on the map updates the map showing the just added alert. The above tutorial should help me porting my app. I'm just not sure I do understand the logic behind the code. My concerns are 2:

    First. There is an Entity layer between the model object and the firebase object. It is explained that this will facilitate having different Data providers, but I don't really see it facilitating anything. In the Model class there is a toEntity() and a fromEntity() conversion method, and in the Entity class there is a fromSnapshot() and a toDocument() conversion method. I don't see what's the point here. Is it really necessary? What's wrong with doing the conversion directly in the Model class , having different methods for each Data provider?

    Second. Inside the TodoBloc I can't follow the logic. The first event that is sent to the bloc at AppStart is LoadTodos.

    BlocProvider<TodosBloc>(
              create: (context) {
                return TodosBloc(
                  todosRepository: FirebaseTodosRepository(),
                )..add(LoadTodos());
    

    In the mapEventToState() method of TodoBloc that event gets mapped to this Stream:

    Stream<TodosState> _mapLoadTodosToState() async* {
        _todosSubscription?.cancel();
        _todosSubscription = _todosRepository.todos().listen(
              (todos) => add(TodosUpdated(todos)),
            );
      }
    

    So far so good. As I understand this subscribes to the todos() Stream ()

    @override
      Stream<List<Todo>> todos() {
        return todoCollection.snapshots().map((snapshot) {
          return snapshot.documents
              .map((doc) => Todo.fromEntity(TodoEntity.fromSnapshot(doc)))
              .toList();
        });
      }
    

    and this should be the equivalent of the firebase observer in my swift app. It this part inside the listen closure I'm not sure to understand: (todos) => add(TodosUpdated(todos)) .

    This sends to itself (TodoBloc) a TodosUpdated event on which the bloc will map this Stream:

    Stream<TodosState> _mapTodosUpdatedToState(TodosUpdated event) async* {
        yield TodosLoaded(event.todos);
      }
    

    which is this:

    class TodosLoaded extends TodosState {
      final List<Todo> todos;
    
      const TodosLoaded([this.todos = const []]);
    
      @override
      List<Object> get props => [todos];
    
      @override
      String toString() => 'TodosLoaded { todos: $todos }';
    }
    

    Is this the actual list of Firebase objects? Does the todos() Stream return the entire node every time a new object is added in Firebase? In my swift app the observer returns only the .childAdded after the first download of the node. Should I use the firebase_database package that has a FirebaseList class(https://pub.dev/documentation/firebase_database/latest/ui_firebase_list/FirebaseList-class.html) that will just return a List on any change on the node as my observers do in my swift app? Sorry for this very long and messy question, but I'm quite lost here starting with bloc pattern. Thank you very much for your time and help.