September 8, 2021

How to solve snapshot ConnectionState.waiting problem

By lj

The problem here is ‘ConnectionState.waiting’. We will see why it is problem and how to solve it. Go through the post if you have this problem in your flutter app. But first, Let’s see basis of it and where actually it is usefull.

I am using BloC in my current application. Inside BloCBuilder, I am also using StreamBuilder. It is good way or not, I will checkout it and post. I didn’t find best practice for it, so currently I am using it in my app.

BLoC

BLoC stands for Business Logic Controller, which is mainly use for state management with MVVM or MVC or other. I can say it is very useful in straight scenario, but little bit difficult for complex scenarios. If your screen have straight states, like loading, api call, result (success or fail), use this because it manages UI refreshes and gives you state based on your process in BLoC. For further information you cal also visit its official site –  https://bloclibrary.dev/#/. I also have one post on How to implement BLoC in flutter application here – https://thejadav.in/2021/08/how-to-implement-bloc-architecture-in-flutter-app/

Stream Builder

The StreamBuilder can exposed stream and can return different widget based on data in Stream. Stream is like pipeline, you can continually add data in stream and other side (builder) will listen your data and update UI. To put value in stream, we need to create StreamController. Below is example of StreamController :

StreamController<Object> mystream =
StreamController<Object>.broadcast();

Object is any data type or model class. I have encountered problem with StreamController like ‘stream already used’, so I prefer to use broadcast in general. Now we need to listen stream in widget, so we can use StreamBuilder

StreamBuilder<int>(
builder: (context, snapshot) {
return Image.asset(
snapshot.data == 0
? 'images/person_pin_selected.png'
: 'images/person_pin_black.png',
height: 32,
width: 32,
);
},
stream: mystream.stream,
),

StreamBuilder needs builder and stream. ‘mystream’ is StreamController which is directly not use full so we need to use ‘mystream.stream’. You can also specify get method for stream easily.

Stream<Object> get getMyStream => mystream.stream;

These are basic things to make it working, but it will still not work. Use stream, only if you need continuous update from code to UI. like updating number every second, or updating list dynamically by some conditions or may be with filter.

Notify UI with Stream

So, we can notify stream builder by adding data in stream controller.

mystream.add(X);

This will notify builder, data is changed. we now can update our UI based on snapshot in builder.

AsyncSnapshot<dynamic>

This is snapshot datatype. It has different state like none, waiting, active, done. Mostly I have used waiting and active state. If snapshot is in waiting state we can show progressbar, If it is active state we must get at-least one notify from code. ‘done’ – when asynchronous computation is terminated.

Why snapshot stuck in waiting state

Let tell about my problem, I am using StreamBuilder inside BlocBuilder. I have one refresh button, which refresh screen data. On click of button it triggers one event, which goes to bloc, It changes bloc state to loading. Once data loaded, bloc state is changed to completed. here ‘loading’ and ‘completed’ is my custom state from BLoC, not for stream.

In this process, if user have refreshed already I am returning BLoC state from ‘loading’ to ‘completed’ direclty, so, StreamBuilder is confused when got two continues state update – or it is waiting for interaction. This is problem.

Solution

Actually there is problem of UI rebuilding, so I thought creating interaction can solve my problem (see notify section). But, it is not working. UI is not build at that time. So, we need to wait for UI to re-build (I have added some delay).

Here is my solution:

yield BlocState.loadCompleted();
await Future.delayed(Duration(milliseconds: 300));
mystream.add(X);

Conclusion

StreamBuilder need interaction to come in active state, that means we need to must add some data in stream by add method (as in notify section). If we think assigning stream to strambuilder will automatically give us data in UI, then we are wrong. Once our UI is ready, we need to notify builder.

Thanks for reading,

All suggestions and comments are accepted.