I'm trying to implement a clean architecture with no dependency of the framework in the business' logic layers.
The following example is a Screen with only a Text
. I make an API Rest call in the repository and add the response to a BehaviorSubject
that is listened through a StreamBuilder
that will update the Text
. Since is an StatefulWidget
I'm using the dispose
method to close the BehaviorSubject
's StreamController
.
The example is simplified, no error/loading state handling, no dependency injection, base classes, dispose interfaces etc.
class Bloc {
final UserReposiotry _userReposiotry;
final BehaviorSubject<int> _activeUsersCount = BehaviorSubject.seeded(0);
Bloc(this._userReposiotry) {
_getActiveUsersCount();
}
void _getActiveUsersCount() async {
final response = await _userReposiotry.getActiveUsersCount();
_activeUsersCount.add(response.data);
}
ValueStream<int> get activeUsersCount => _activeUsersCount.stream;
void dispose() async {
await _activeUsersCount.drain(0);
_activeUsersCount.close();
}
}
class StatefulScreen extends StatefulWidget {
final Bloc bloc;
const StatefulScreen({Key? key, required this.bloc}) : super(key: key);
@override
State<StatefulScreen> createState() => _StatefulScreenState();
}
class _StatefulScreenState extends State<StatefulScreen> {
@override
Widget build(BuildContext context) {
final stream = widget.bloc.activeUsersCount;
return StreamBuilder<int>(
stream: stream,
initialData: stream.value,
builder: (context, snapshot) {
return Text(snapshot.data.toString());
}
);
}
@override
void dispose() {
widget.bloc.dispose();
super.dispose();
}
}
I have the following doubts regarding this approach.
StreamBuilder
cancels thestream
subscription automatically, but it doesn't close theStreamController
. I know that you should close it if you are reading a file, but in this case, if I don't manually close it, once theStatefulScreen
is no longer in the navigation stack, could it be destroyed, or it would be a memory leak?- I've seen a lot of people using
StatelessWidget
instead ofStatefulWidget
usingStream
andStreamBuilder
approach, if it is really needed to close theBehaviorSubject
it is a problem since we don't have thedispose
method, I found about theWillPopScope
but it won't fire in all navigation cases and also and more important would it be more performant an approach likeWillPopScope
, or having anStatefulWidget
wrapper (BlocProvider
) inside anStatelessWidget
just to do the dispose, than using anStatefulWidget
directly, and if so could you point to an example of that implementation? - I'm currently choosing
StatefulWidget
for widgets that have animations o controllers (map, text input, pageview...) or streams that I need to close, the restStatelessWidget
, is this correct or am I missing something? - About the
drain
method, I'm using it because I've encountered an error navigating back while an API rest call was on progress, I found a member of the RxDart team saying it isn't really necessary to calldrain
so I'm confused about this too..., the error:
You cannot close the subject while items are being added from addStream
Thanks for your time.