I am trying to use the bloc pattern to manage data from an API and show them in my widget. I am able to fetch data from API and process it and show it, but I am using a bottom navigation bar and when I change tab and go to my previous tab, it returns this error:
Unhandled Exception: Bad state: Cannot add new events after calling close.
I know it is because I am closing the stream and then trying to add to it, but I do not know how to fix it because not disposing the publishsubject
will result in memory leak
.
here is my Ui code:
class CategoryPage extends StatefulWidget {
@override
_CategoryPageState createState() => _CategoryPageState();
}
class _CategoryPageState extends State<CategoryPage> {
@override
void initState() {
serviceBloc.getAllServices();
super.initState();
}
@override
void dispose() {
serviceBloc.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: serviceBloc.allServices,
builder: (context, AsyncSnapshot<ServiceModel> snapshot) {
if (snapshot.hasData) {
return _homeBody(context, snapshot);
}
if (snapshot.hasError) {
return Center(
child: Text('Failed to load data'),
);
}
return CircularProgressIndicator();
},
);
}
}
_homeBody(BuildContext context, AsyncSnapshot<ServiceModel> snapshot) {
return Stack(
Padding(
padding: EdgeInsets.only(top: screenAwareSize(400, context)),
child: _buildCategories(context, snapshot))
],
);
}
_buildCategories(BuildContext context, AsyncSnapshot<ServiceModel> snapshot) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 20),
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3, crossAxisSpacing: 3.0),
itemCount: snapshot.data.result.length,
itemBuilder: (BuildContext context, int index) {
return InkWell(
child: CategoryWidget(
title: snapshot.data.result[index].name,
icon: Icons.phone_iphone,
),
onTap: () {},
);
},
),
);
}
here is my bloc code:
class ServiceBloc extends MainBloc {
final _repo = new Repo();
final PublishSubject<ServiceModel> _serviceController =
new PublishSubject<ServiceModel>();
Observable<ServiceModel> get allServices => _serviceController.stream;
getAllServices() async {
appIsLoading();
ServiceModel movieItem = await _repo.getAllServices();
_serviceController.sink.add(movieItem);
appIsNotLoading();
}
void dispose() {
_serviceController.close();
}
}
ServiceBloc serviceBloc = new ServiceBloc();
I did not include the repo and API code because it is not in the subject of this error.
Use StreamController.isClosed
to check if the controller is closed or not, if not closed add data to it.
if (!_controller.isClosed)
_controller.sink.add(...); // safe to add data as _controller isn't closed yet
From Docs:
Whether the stream controller is closed for adding more events.
The controller becomes closed by calling the close method. New events cannot be added, by calling add or addError, to a closed controller.
If the controller is closed, the "done" event might not have been delivered yet, but it has been scheduled, and it is too late to add more events.