Moor: Using watch() with StreamBuilders

Created on 4 Jul 2019  路  10Comments  路  Source: simolus3/moor

Great work on the library, I have only found it yesterday and it looks really promising!

I have a question regarding the watch() functionality and StreamBuilders. I think it is mostly a Dart/Flutter question, but I would like to hear your recommended approach.

I currently use the Provider package to provide the Database class to the whole app. I also use DAOs to separate the table's getters and function's to separate files. Currently I have a Rooms table and it has a Stream to watch all the rooms: Stream<List<Room>> get watchAll => select(rooms).watch();.

On the home screen I have a StreamBuilder, which uses the watchAll Stream. For testing I added buttons to add and clear the rooms. So far everything works fine.

Then I use Navigator.pushNamed() to navigate to a new screen, the home screen gets pushed to the back stack and the StreamBuilder is still listening to the Stream.
This new screen (Manage Rooms) also uses a StreamBuilder with the same Stream (with different widgets inside), but it only has ConnectionState.waiting. I can see that the build method in the StreamBuilder of the Home screen gets called again from the logs.

I/flutter (17486): Rooms Snapshot: null
I/flutter (17486): Rooms connection state: ConnectionState.waiting
I/flutter (17486): Home snapshot: [Room(id: 33, name: Room name), Room(id: 34, name: Room name)]

The StreamBuilder in the new screen never gets updated. (The watchAll Stream emits data only on the first listener I think)
I added an Add button to this screen as well. This way the Stream will off course emit new data and the new screen also gets updated along with the Home screen:

I/flutter (19891): Home snapshot: [Room(id: 33, name: Room name), Room(id: 34, name: Room name), Room(id: 35, name: Room name)]
I/flutter (19891): Rooms Snapshot: [Room(id: 33, name: Room name), Room(id: 34, name: Room name), Room(id: 35, name: Room name)]
I/flutter (19891): Rooms connection state: ConnectionState.active
  • I tried it with get() and FutureBuilder instead of watch() and StreamBuilder. It will of course work because no Streams are involved.
  • I also tried using pushReplacementNamed(), so the first screen gets disposed of. This way I won't see any logs from the Home screen, but the new screen still only displays loading. Only after I add a new entry with the button will it update.

What would you recommend if I wanted to use Streams on both screens?
I tried to write it as detailed as I can. If you would like to see a code example I could write it here.

Thank you!

bug

Most helpful comment

FYI, moor 1.6 has just been released and it includes this fix. If you were using the beta or develop branch, you can now switch back to the regular pub version :)

All 10 comments

This seems like a bug in moor. In your pubspec.lock, can you verify that you're using moor version 1.5.1 (the version of flutter_moor doesn't matter for this case)? If not, try flutter packages upgrade as I fixed a very similar bug recently.
If you're on 1.5.1, this is a bug I need to address, please let me know in that case.

pubspec.lock was still using 1.5.0, but after upgrading to 1.5.1 the behavior is still the same.

In normal cases moor does not have to be included in the dependencies as moor_flutter will always get the newest major version, right?

That's exactly right - moor_flutter has a dependency on an appropriate version of moor that will be included.

This error should now be fixed on develop. To help me verify this, can you add this to your pubspec.yaml?

dependency_overrides:
  moor:
    git:
      url: https://github.com/simolus3/moor.git
      ref: develop
      path: moor/

If you get the packages and restart your app, does it work?

Unfortunately it is still the same using develop.

pubspec.lock contents:
```yaml
moor:
dependency: "direct overridden"
description:
path: "moor/"
ref: develop
resolved-ref: "01db5e2afcaf206782307c4d307389c4b54d1428"
url: "https://github.com/simolus3/moor.git"
source: git
version: "1.5.1"
````

This should use the develop branch, right?
Right now that branch seems even with the master branch.

Forgot to push :man_facepalming: Sorry, please try again now.

It did not seem to fix my problem.

Current pubspec.lock:

moor:
  dependency: "direct overridden"
  description:
    path: "moor/"
    ref: develop
    resolved-ref: d98449407e1f170487fd0a1990d29d7b3ddeb93e
    url: "https://github.com/simolus3/moor.git"
  source: git
  version: "1.5.1+1"

Weird :/ I tried to reconstruct your issue in this gist, where the latest develop version did fix the problem. It should display the amount of rooms in both the RoomsList page, which is shown on app start, and the NewPage, which is pushed after a button press. On both pages, the FloatingActionButton will insert a new room into the database and update the counter.

Are you doing anything major different than that gist? Particularly, are you explicitly checking whether the snapshot is in ConnectionState.waiting? In my gist, the pushed page still has ConnectionState.waiting after receiving the first data snapshot.

It works now!

I am not sure what changed from my previous try. I did not check the ConnectionState before. I can not reproduce the error now. Maybe the build was somehow not using the latest moor.

Is ConnectionState.waiting the expected behavior then?
It seems ConnectionState.active is only used now when a new data is received in the Stream, if cached value is used ConnectionState.waiting is returned.

Thank you very much for the quick fix!

I _think_ this is because the StreamBuilder sets the ConnectionState to waiting in its initState method. When a query has cached data available (which it definitely has in this case), that is emitted to new subscribers right away. So the order will be

  1. stream builder subscribes
  2. receives event, sets connection state to active
  3. end of its init state method is called, sets connection state to waiting

I don't think anybody relies on the ConnectionState in their UI (it would also be waiting when using initialData), so this should be fine. If it becomes a problem for anybody we can always emit the data in another microtask, which will cause a minor delay but still work, so I'll leave it as it is for now.

Btw, I've just set up the beta branch for versions that are more stable than develop but not released on pub yet. I recommend using that over develop until the fix is released on pub (I'll let you know).

FYI, moor 1.6 has just been released and it includes this fix. If you were using the beta or develop branch, you can now switch back to the regular pub version :)

Was this page helpful?
0 / 5 - 0 ratings