【问题标题】:flutter: Unhandled Exception: Bad state: Cannot add new events after calling close颤振:未处理的异常:错误状态:调用关闭后无法添加新事件
【发布时间】:2019-08-27 10:05:59
【问题描述】:

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

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

我知道这是因为我正在关闭流然后尝试添加到它,但我不知道如何修复它,因为不处理 publishsubject 将导致 memory leak。 这是我的用户界面代码:

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: () {},
        );
      },
    ),
  );
}

这是我的集团代码:

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();

我没有包含 repo 和 API 代码,因为它不在此错误的主题中。

【问题讨论】:

    标签: dart flutter bloc


    【解决方案1】:

    使用StreamController.isClosed检查控制器是否关闭,如果没有关闭则添加数据。

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

    来自文档:

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

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

    如果控制器关闭,“done”事件可能还没有传递,但是已经安排好了,再添加更多事件已经来不及了。

    【讨论】:

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

    如果错误实际上是由您发布的代码引起的,我只需添加一个检查以确保在调用 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();
    

    【讨论】:

      【解决方案3】:

      我遇到了同样的错误,并注意到如果您检查 isClosed,屏幕不会更新。在您的代码中,您必须从 Bloc 文件中删除最后一行:

      ServiceBloc serviceBloc = new ServiceBloc();
      

      并将这一行放在CategoryPage 中initState() 之前。通过这种方式,您的小部件正在创建和处置该集团。以前,widget 只处理 bloc,但在重新创建小部件时永远不会重新创建它。

      【讨论】:

        【解决方案4】:

        除了提供的解决方案之外,我认为您还应该通过以下方式排空 ServiceBloc 中使用的流 allServices

        @override
        void dispose() {
              ...
              allServices?.drain(); 
        }
        

        【讨论】:

          【解决方案5】:

          @cwhisperer 是绝对正确的。像下面这样在小部件中初始化和处理你的块。

          final ServiceBloc serviceBloc = new ServiceBloc();
          
            @override
            void initState() {
              serviceBloc.getAllServices();
              super.initState();
            }
          
            @override
            void dispose() {
              serviceBloc.dispose();
              super.dispose();
            }
          

          并从您的class ServiceBloc 中删除ServiceBloc serviceBloc = new ServiceBloc();

          【讨论】:

            【解决方案6】:
            ServiceBloc serviceBloc = new ServiceBloc();
            

            // 删除这段代码 // 不要在同一个页面中初始化类,这会导致错误的状态。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-10-29
              • 2020-12-05
              • 2022-01-05
              • 2022-11-27
              • 2019-11-27
              • 2020-03-15
              相关资源
              最近更新 更多