pinchbv / floor

The typesafe, reactive, and lightweight SQLite abstraction for your Flutter applications
https://pinchbv.github.io/floor/
Apache License 2.0
965 stars 191 forks source link

Possibility to add ephemeral/transient data on the stream #308

Closed cassioseffrin closed 4 years ago

cassioseffrin commented 4 years ago

Guys first of all, pardon for using the issues to ask a question, however I think it can be helpful for the community as well.

I have a list of objects (ListView) on the screen, so a I need to increment some records and save all the work at once. The records need to be added in the stream and updated on screen but just like a transient ephemeral data. When I press the save button all the ephemeral records will be saved in the database at once. This is a common situation in multiples kinds of scenarios of huge number apps. Here is my Stream: lstObjsStream = dao.findAllObjsAsStream();

With an ordinary StreamController I can do it by sinking the data (lstObjsStream.sink.add(someEphemeralRecords);

So my question is, floor offer any kind of solution for that, or even how can I do it. I can't figure out a way do sink a single record to the Stream.

Thks in advance.

vitusortner commented 4 years ago

Hi! I don't quite understand your question. Can you supply some more code snippets?

cassioseffrin commented 4 years ago

Hi @vitusortner

I'll try to be more clear. Imagine any list of stream that we get from the dao. lstStream = dao.findAllObjsAsStream();

How can I include new transient elements to this list lstStream. I need to include some elements just in memory (just to be showed on screen), not in database. The process of save them will be committed in a further step.

In short my question is simple. How can I insert transient elements in a list after that list was populated from dao.

vitusortner commented 4 years ago

One solution would be to create a StreamController and add both the DAO stream to it and the transient data. An example can be seen in the following.

final daoStream = dao.findAllAsStream();
final transientRecords = [Record(), Record(), Record()];

final streamController = StreamController<List<Record>>()
  ..addStream(daoStream)
  ..add(transientRecords);

final finalStream = streamController.stream;

Does this solve your problem?

cassioseffrin commented 4 years ago

Thks @vitusortner. You gave me a light, but I am still getting an error when I try to manipulate the stream: Unhandled Exception: Bad state: Cannot add event while adding a stream.

I am a quite new in dealing with streams (I came from react-redux). I will try to create a new streamController in the construtor instead using the native stream from floor, so may I will get more luck when the transformations on stream are made. Or may I need to dig in the floor to see how it do the transformations when a new record is inserted by the framework.

I am avoiding to create a BloC for these screen. I want to keep things simple and explore floor streams instead of reinventing the wheel. For me it's a bit harder in comparison with redux, but I am new.

May i need to check if the stream is not closed before add the record.

if (! streamController.isClosed)
     streamController.sink.add(transientRecord);

If you have an a clue why this error "Cannot add event while adding a stream" was been thrown I will appreciate.

cassioseffrin commented 4 years ago

@vitusortner I could achieve my goal. It's very difficult to make it. The dart streams seems to get a breaking changes in last update of flutter and rxdart.

I will describe how, so may you could mark this issue as a question and close it. But please if you could give you opinion I will appreciate.

First I have created some variables to hold the ephemeral/transient data. They are in state class scope.

  var List<CustomerBankAccount> lstBankAccountTransients =
      List<CustomerBankAccount>();
  var lstBankAccountStreamController = new StreamController.broadcast();

In initState method, I have recovered the records from database and update the controller with them.

    lstBankAccountStream =
        dbs.smartDb.customerDao.findCustomerBankAccountByCustomerIdAsStream(1);
    lstBankAccountStreamController =
        StreamController<List<CustomerBankAccount>>()
          ..addStream(lstBankAccountStream); 

And lastly in the insert method, when I got a user command to insert a new record I have done the follow steps:

  1. update the transient list;
  2. combine 2 lists;
  3. update the controller.
 lstBankAccountTransients.add(contaBancaria);

        lstBankAccountStream = dbs.smartDb.customerDao
            .findCustomerBankAccountByCustomerIdAsStream(1)
            .map((rows) => [...rows, ...lstBankAccountTransients]);

    lstBankAccountStreamController =
            StreamController<List<CustomerBankAccount>>()
              ..addStream(lstBankAccountStream);

And it's all done as I expected. Please let me know if you notice a bad practice in this approach, and by the way, the streams that I get from the DAO need to be closed on dispose or it's done by the floor when the screen is closed?