8

There are some scenarios where screens with their respective BLoCs are frequently created and closed. So I'm somewhat concerned about memory safety of the Streams instances created in this process, because it doesn't seem they are disposed somewhere or whether they are GC-ed. This clearly depends on the specific implementation of DART libraries and flutter. So if you know about their behavior, please let me know.

These are some scenarios I have encountered.

  1. Multi-tab browser-like application.
  2. Navigation through screens. (But it's not that harmful.)
  3. showDialog() senarios when there are BLoCs inside the dialog. This is a far more common senario. There could be a lot of dialog popping up frequently in an app.

I wonder if it is necessary to override dispose() function and explicitly close all streams in BLoCProvider. It seems existing tutorials didn't mention it.

First_Strike
  • 1,029
  • 1
  • 10
  • 27

1 Answers1

9

Streams will properly be cleaned as long as they aren't used anymore. The thing is, to simply removing the variable isn't enough to unsure it's unused. It could still run in background.

You need to call Sink.close() so that it stops the associated StreamController, to ensure resources can later be freed by the GC.

To do that, you have to use StatefulWidget.dispose method:

abstract class MyBloc {
  Sink foo;
  Sink bar;
}

class MyWiget extends StatefulWidget {
  @override
  _MyWigetState createState() => _MyWigetState();
}

class _MyWigetState extends State<MyWiget> {
  MyBloc bloc;

  @override
  void dispose() {
    bloc.bar.close();
    bloc.foo.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // ...
  }
}
Rémi Rousselet
  • 256,336
  • 79
  • 519
  • 432
  • Do you think it's always better to use access a bloc via an interface than using a provider which effectively uses an inherited widget? – dragonfly02 Nov 20 '18 at 11:10
  • @stt106 What do you mean? An interface doesn't replace the provider, nor does the provider replace the interface – Rémi Rousselet Nov 20 '18 at 11:13
  • will chat to you on slack. – dragonfly02 Nov 20 '18 at 11:14
  • Oh, thanks. So it seems we need to close the Streams in the dispose() function. But there are some other problems with this solution. InheritedWidget has their own lifecycle. I'm not so sure about this, but is it likely that either user or flutter engine triggers an unintended disposal of InheritedWidget? Because I've seen in some tutorials that BLoCs becomes shared and InheritedWidget are created and re-assigned everywhere. – First_Strike Nov 20 '18 at 11:57
  • 1
    You need both an Inheritedwidget and a StatefulWidget – Rémi Rousselet Nov 20 '18 at 12:05
  • @RémiRousselet when I dispose then it's gone I go to another screen back to previous screen but the bloc is empty now – stuckedunderflow Jan 11 '19 at 10:11
  • 1
    @rxlky You'll want to use `rxdart` and `BehaviorSubject`. – Rémi Rousselet Jan 11 '19 at 10:14
  • @RémiRousselet wow correct BehaviorSubject makes it appear again great, But there is a glitch when I swipe back to initial screen then log error says...."Bad state: Cannot add new events after calling close" however it is stll loaded. Any idea? – stuckedunderflow Jan 11 '19 at 10:21
  • You called `close()` on the controller – Rémi Rousselet Jan 11 '19 at 10:22
  • @RémiRousselet sorry I don't get it. I don't have controller. It is actually tabbar which has its own stateful. I also tried calling init and dispose bloc from parent page also the same thing happens. Any idea? – stuckedunderflow Jan 11 '19 at 10:37
  • Can you make it a question on its own? Comments isn't really the right place – Rémi Rousselet Jan 11 '19 at 10:39
  • @RémiRousselet it's ok I got the answer here https://stackoverflow.com/questions/52191451/bad-state-you-cannot-close-the-subject-while-items-are-being-added-from-addstre/52191587#comment95123235_52191587 I call await drain then everything ok – stuckedunderflow Jan 12 '19 at 14:40