flutter - 什么时候创建新的 Bloc ?

标签 flutter bloc

我还在学习 Bloc 模式。我使用单个 block 创建了两个页面:ViewPage 和DetailsPage。 这是我的区 block :

getRecordEvent
deleteRecordEvent
LoadedState
LoadedErrorState
DeletedState
DeletedErrorState

View 页面只会构建一个带有 LoadedState 上的记录列表的小部件。当用户点击任何记录时,它将推送详细信息页面并显示带有删除按钮的详细记录。当用户按下删除按钮时,我会监听 DeletedState 并调用 getRecord 事件以使用更新的记录再次填充 View 页面。 它一切正常,但我的问题是当我在删除记录时遇到错误。当状态为 DeleteErrorState 时,我的 View 页面会变为空,因为我没有在那里调用 getRecord ,因为错误可能是互联网连接,并且会显示两个错误对话框。一个用于 DeletedErrorStateLoadedErrorState

我知道这是 bloc 的默认行为。我是否必须创建一个仅包含 deleteRecordEvent 的单独 block ?另外,如果我创建一个新页面来添加记录,这也会是一个单独的 block 吗?

更新:

这是 ViewPage 的示例。只有按下按钮后,DetailsPage 才会调用deleteRecordEvent。

ViewPage.dart

  void getRecord() {
        BlocProvider.of<RecordBloc>(context).add(
            getRecordEvent());
  }
  
  @override
  Widget build(BuildContext context) {
    return
      Scaffold(
      body: buildBody(),
      ),
    );
  }
  
  buildBody() {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: BlocConsumer<RecordBloc, RecordState>(
            listener: (context, state) {
              if (state is LoadedErrorState) {
                showDialog(
                    barrierDismissible: false,
                    context: context,
                    builder: (_) {
                      return (WillPopScope(
                          onWillPop: () async => false,
                          child: ErrorDialog(
                            failure: state.failure,
                          )));
                    });
              } else if (state is DeletedState) {
                Navigator.pop(context);
                getRecord();
                
              } else if (state is DeletedErrorState) {
                Navigator.pop(context);
                showDialog(
                    barrierDismissible: false,
                    context: context,
                    builder: (_) {
                      return (WillPopScope(
                          onWillPop: () async => false,
                          child: ErrorDialog(
                            failure: state.failure,
                          )));
                    });
              }
            },
            builder: (context, state) {
              if (state is LoadedState) {
                return Expanded(
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      state.records.length <= 0
                          ? noRecordWidget()
                          : Expanded(
                              child: ListView.builder(
                                  shrinkWrap: true,
                                  itemCount: state.records.length,
                                  itemBuilder: (context, index) {
                                    return Card(
                                        child: Padding(
                                      padding: EdgeInsets.symmetric(
                                          vertical: Sizes.s8),
                                      child: ListTile(
                                        title: Text(state.records[index].Name),
                                        subtitle: state.records[index].date,
                                        onTap: () {
                                           showDialog(
                                              barrierDismissible: false,
                                              context: context,
                                              builder: (_) {
                                                return BlocProvider<RecordBloc>.value(
                                                    value: BlocProvider.of<RecordBloc>(context),
                                                    child: WillPopScope(
                                                      onWillPop: () async => false,
                                                      child:
                                                          DetailsPage(record:state.records[index]),
                                                    ));
                                              });
                                        },
  
                                      ),
                                    ));
                                  }),
                            ),
                    ],
                  ),
                );
              }
              return (Container());
            },
          ),
      ),
    );
  }  

最佳答案

关于 Bloc

根据一般经验,每个 ui 需要一个 bloc。当然,情况并非总是如此,因为它取决于几个因素,其中最重要的是您在 ui 中处理多少事件。对于您的情况,如果有一个 ui 将项目列表保存到项目详细信息 ui 中,我将创建两个 blocs。一个仅处理加载项目(例如ItemsBloc),另一个将处理单个项目(SingleItemBloc)的操作。我现在可能只使用 delete 事件,但随着应用程序的增长,我将添加更多事件。这一切都有助于Separation of Concerns概念。

将其应用于您的情况,SingleItemBloc 将处理对单个项目的删除、修改、订阅等操作,而 ItemsBloc 将处理从不同存储库加载项目(本地/远程)。

由于我没有适用于您的bloc的代码,因此我无法提供任何修改。

针对您的案例的解决方案

每次发出新的状态时,您似乎都会丢失项目列表的最后一个版本。您应该保留从存储库获取的最后一个列表的本地副本。如果出现错误,您只需使用该列表即可;如果不是,只需将新列表保存为您的最后一个列表。

class MyBloc extends Bloc<Event, State> {
  .....
  List<Item> _lastAcquiredList = [];
  
  Stream<State> mapEventToState(Event event) async* {
      try {
        ....

        if(event is GetItemsEvent) {
          var newList = _getItemsFromRepository();
          yield LoadedState(newList);
          _lastAcquiredList = newList;
        }
       ....
      } catch(err) {
         yield ErrorState(items: _lastAcquiredItems);
      }
  }
}

关于flutter - 什么时候创建新的 Bloc ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67820324/

相关文章:

Dart,嵌套类,如何访问子类变量

unit-testing - 如何在集成测试中模拟 BLoC

flutter - block 更新后无法使用 block 观察者

Flutter - Bloc 仅在状态未扩展 Equatable 时发出状态

java - Flutter FCM后台调用平台特定代码

flutter - 使Zara像产品页面一样 flutter 朔迷离

Flutter 如何将网站链接到文本小部件?

android - 纹理小部件渲染 OpenGL 示例

flutter - 抽象类不能被实例化!在身份验证 block 中

flutter - 如何使用整洁的架构和 bloc 库 flutter 来实现 WebSocket?