Stream/Bloc/Repository/Firebase 数据流 Flutter

Posted

技术标签:

【中文标题】Stream/Bloc/Repository/Firebase 数据流 Flutter【英文标题】:Stream/Bloc/Repository/Firebase data flow Flutter 【发布时间】:2020-06-07 09:29:30 【问题描述】:

我从 Flutter 开始,因为我想将我的 swift 应用程序移植到 Flutter,但是我在学习 Bloc/Repository/Firebase 模式时遇到了困难,因为我正在学习https://bloclibrary.dev/#/flutterfirestoretodostutorialdough 我实时使用的教程数据库,而不是 Firestore。 我的 swift 应用程序基本上是一张地图,您可以在其中在实际坐标处添加警报。警报被发送到 Firebase,地图上的 firebase 观察者更新地图,显示刚刚添加的警报。 上面的教程应该可以帮助我移植我的应用程序。我只是不确定我是否理解代码背后的逻辑。 我的担忧是2:

首先。在模型对象和 firebase 对象之间有一个Entity 层。据解释,这将有助于拥有不同的数据提供者,但我并没有真正看到它有什么促进作用。 Model 类中有toEntity()fromEntity() 转换方法,Entity 类中有fromSnapshot()toDocument() 转换方法。我不明白这里有什么意义。真的有必要吗?直接在 Model 类中进行转换有什么问题,每个数据提供者都有不同的方法?

第二。在TodoBloc 内部,我无法遵循逻辑。 在AppStart 发送到集团的第一个事件是LoadTodos

BlocProvider<TodosBloc>(
          create: (context) 
            return TodosBloc(
              todosRepository: FirebaseTodosRepository(),
            )..add(LoadTodos());

TodoBlocmapEventToState() 方法中,该事件被映射到此流:

Stream<TodosState> _mapLoadTodosToState() async* 
    _todosSubscription?.cancel();
    _todosSubscription = _todosRepository.todos().listen(
          (todos) => add(TodosUpdated(todos)),
        );
  

到目前为止一切顺利。据我了解,这订阅了todos() Stream ()

@override
  Stream<List<Todo>> todos() 
    return todoCollection.snapshots().map((snapshot) 
      return snapshot.documents
          .map((doc) => Todo.fromEntity(TodoEntity.fromSnapshot(doc)))
          .toList();
    );
  

这应该相当于我的 swift 应用程序中的 firebase 观察者。 listen 闭包内的这部分我不确定:(todos) =&gt; add(TodosUpdated(todos))

这会向自身 (TodoBloc) 发送一个 TodosUpdated 事件,该 bloc 将在该事件上映射此 Stream:

Stream<TodosState> _mapTodosUpdatedToState(TodosUpdated event) async* 
    yield TodosLoaded(event.todos);
  

这是:

class TodosLoaded extends TodosState 
  final List<Todo> todos;

  const TodosLoaded([this.todos = const []]);

  @override
  List<Object> get props => [todos];

  @override
  String toString() => 'TodosLoaded  todos: $todos ';

这是 Firebase 对象的实际列表吗?每次在 Firebase 中添加新对象时,todos() Stream 是否会返回整个节点? 在我的 swift 应用程序中,观察者在第一次下载节点后仅返回 .childAdded。 我是否应该使用具有FirebaseList 类(https://pub.dev/documentation/firebase_database/latest/ui_firebase_list/FirebaseList-class.html) 的firebase_database 包,它只会在节点上的任何更改上返回一个列表,就像我的观察者在我的swift 应用程序中所做的那样? 很抱歉这个冗长而混乱的问题,但我从 bloc 模式开始就迷失了。 非常感谢您的时间和帮助。

【问题讨论】:

【参考方案1】:

好的,我想我理解它背后的逻辑,但如果您发现我没有理解正确,请纠正我,因为在进入新范式的这个阶段,不要产生任何误解,这一点非常重要。

    todos() 是来自 Firebase 的流并返回 List&lt;Todo&gt;_mapLoadTodosToState() 是将 bloc 侦听器附加到 todos() 的 bloc 方法,在 .listen(onData) 回调中,它向 bloc 发送包含最新列表的 TodosUpdated(todos) 事件。 TodosUpdated(todos) 被映射到 _mapTodosUpdatedToState,从而产生 TodosLoaded(event.todos) ,BlocProvider 用于构建 UI 的新状态。

谢谢你,我希望这能帮助其他人在更复杂的层面上努力掌握 BloC 模式。 干杯

【讨论】:

也需要一些时间来理解这种模式,但我得出了相同的结论。唯一我还不太了解的是如何在我的流加载数据时显示 LoadingIndicator。例如,如果我的 todos 上有一个过滤器,并且我使用 .where() 方法将查询更改为 firestore,或者互联网连接速度可能很慢,该怎么办? 一旦你习惯了它就会变得轻而易举.. 需要编写相当多的代码.. 但是有一个新的更简单的包 Cubit 。 .(还没有尝试过)它是一个更简单的 bloc 版本,可以集成到 bloc 实现中.. 非常感谢。我对 Riverpod 做了同样的事情,但不确定这是否是正确的方法。如果您熟悉 Riverpod,那么我在使用这种方法以获得更好的性能方面还有一些问题。如果你觉得舒服,我可以问这些问题吗? 嗨,我还没有研究过 Riverpod,但让我们试试吧,无论如何我可能会有所帮助 从流中获取数据后,我将其转换为包含更改通知列表的对象(比如说objA)。因此,每当 Firestore 流获得新数据时,它将被转换为 objA,添加到状态(在我的情况下为 StateNotifier),并将发送到我们正在使用它的小部件。现在,如果对象相同或状态列表中的大多数项目相同,我只想更新已修改更改通知器中的更改,否则将重建所有对象。 (我知道如果它们相同,颤振会尽量不重建对象,但我们不能 100% 确定。)

以上是关于Stream/Bloc/Repository/Firebase 数据流 Flutter的主要内容,如果未能解决你的问题,请参考以下文章