颤动:未处理的异常:错误状态:调用关闭后无法添加新事件

gee*_*ano 4 dart flutter bloc

我正在尝试使用bloc模式来管理来自API的数据,并将其显示在我的小部件中。我能够从API提取数据并对其进行处理并显示,但我使用的是底部导航栏,当我更改选项卡并转到上一个选项卡时,它将返回此错误:

未处理的异常:错误状态:调用close后无法添加新事件。

我知道这是因为我正在关闭流,然后尝试对其进行添加,但是我不知道如何解决它,因为不处理publishsubject将导致memory leak。这是我的UI代码:

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: () {},
        );
      },
    ),
  );
}
Run Code Online (Sandbox Code Playgroud)

这是我的集团代码:

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();
Run Code Online (Sandbox Code Playgroud)

我没有包含回购和API代码,因为它不在此错误的主题之内。

Cop*_*oad 27

使用StreamController.isClosed检查控制器是否闭合,如果不封闭的添加数据。

if (!_controller.isClosed) 
  _controller.sink.add(...); // safe to add data as _controller isn't closed yet
Run Code Online (Sandbox Code Playgroud)

从文档:

是否关闭流控制器以添加更多事件。

控制器通过调用 close 方法关闭。不能通过调用 add 或 addError 将新事件添加到关闭的控制器。

如果控制器关闭,则“完成”事件可能尚未交付,但已被调度,添加更多事件为时已晚。

  • 框架不应该自己处理这个问题而不是抛出错误吗? (3认同)
  • 这个解决方案在不创建任何变量的情况下要好得多。这个例子对我帮助很大。 (2认同)

Gün*_*uer 6

如果错误实际上是由您发布的代码引起的,那么我将添加一个检查以确保在dispose()调用之后不添加任何新事件。

class ServiceBloc extends MainBloc {
  final _repo = new Repo();
  final PublishSubject<ServiceModel> _serviceController =
      new PublishSubject<ServiceModel>();
  Observable<ServiceModel> get allServices => _serviceController.stream;
  getAllServices() async {
    // do nothing if already disposed
    if(_isDisposed) {
      return;
    }
    appIsLoading();
    ServiceModel movieItem = await _repo.getAllServices();
    _serviceController.sink.add(movieItem);
    appIsNotLoading();
  }

  bool _isDisposed = false;
  void dispose() {
    _serviceController.close();
    _isDisposed = true;
  }
}

ServiceBloc serviceBloc = new ServiceBloc();
Run Code Online (Sandbox Code Playgroud)


BLB*_*BLB 6

检查块/肘是否由isClosed变量封闭。将此 if 条件包装到那些抛出异常的状态。

示例代码

class LandingCubit extends Cubit<LandingState> {
  LandingCubit(this.repository) : super(LandingInitial());
  final CoreRepository repository;

  // Fetches image urls that needs to shown in landing page
  void getLandingImages() async {
    emit(LandingImagesLoading());
    try {
      List<File> landingImages = await repository.landingImages();
      if (!isClosed) {
        emit(LandingImagesSuccess(landingImages));
      }
    } catch (e) {
      if (!isClosed) {
        emit(LandingImagesFetchError(e.toString()));
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)