{"id":15067704,"url":"https://github.com/purplenoodlesoop/stream-bloc","last_synced_at":"2025-04-10T16:12:37.540Z","repository":{"id":37959370,"uuid":"437755422","full_name":"purplenoodlesoop/stream-bloc","owner":"purplenoodlesoop","description":"💫 Modern implementation of the original BLoC that uses asynchronous generators to describe relationships between events and states","archived":false,"fork":false,"pushed_at":"2022-11-12T21:51:36.000Z","size":74,"stargazers_count":25,"open_issues_count":1,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-24T14:02:16.859Z","etag":null,"topics":["bloc","flutter","reactive-programming","unidirectional-data-flow"],"latest_commit_sha":null,"homepage":"","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/purplenoodlesoop.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-12-13T06:06:21.000Z","updated_at":"2025-03-04T11:19:49.000Z","dependencies_parsed_at":"2023-01-21T14:01:38.144Z","dependency_job_id":null,"html_url":"https://github.com/purplenoodlesoop/stream-bloc","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/purplenoodlesoop%2Fstream-bloc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/purplenoodlesoop%2Fstream-bloc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/purplenoodlesoop%2Fstream-bloc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/purplenoodlesoop%2Fstream-bloc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/purplenoodlesoop","download_url":"https://codeload.github.com/purplenoodlesoop/stream-bloc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248251172,"owners_count":21072686,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bloc","flutter","reactive-programming","unidirectional-data-flow"],"created_at":"2024-09-25T01:26:14.123Z","updated_at":"2025-04-10T16:12:37.522Z","avatar_url":"https://github.com/purplenoodlesoop.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# stream_bloc\n\n[![Pub](https://img.shields.io/pub/v/stream_bloc.svg)](https://pub.dev/packages/stream_bloc)\n[![GitHub Stars](https://img.shields.io/github/stars/purplenoodlesoop/stream-bloc.svg)](https://github.com/purplenoodlesoop/stream-bloc)\n[![License: MIT](https://img.shields.io/badge/License-MIT-brightgreen.svg)](https://en.wikipedia.org/wiki/MIT_License)\n[![Linter](https://img.shields.io/badge/style-custom-brightgreen)](https://github.com/purplenoodlesoop/stream-bloc/blob/master/analysis_options.yaml)\n[![Code size](https://img.shields.io/github/languages/code-size/purplenoodlesoop/stream-bloc)](https://github.com/purplenoodlesoop/stream-bloc)\n\n---\n\nModern implementation of the Original BLoC\n\n## About\n\nThis package contains a modern (`bloc` package version 8.0.0+) implementation of the Original, Stream/generator-based BLoC with several modifications and convenience extras.\n\n## Motivation\n\nAfter the 7.2.0 version update, the bloc has changed. Generators and the signature method `mapEventToState` were replaced with method-based pattern-matching using the `on` method and its argument handler. This approach is defiantly usable and solves a particular bug of the Dart language itself, but it lacks a few things.\n\n1) Power of streams. They can be transformed natively using a vast choice of transformers, but with the new bloc, streams are hidden under the hood and are not \"first-class citizens\".\n2) Power of generators. They allow to asynchronously return multiple values, including other streams. The new version emulates their behavior using higher-order functions.\n3) New `on` approach makes `freezed` basically useless. It is possible to register just a single handler, but it negates the whole point of the `on` handlers.\n\nThis package brings back the Original bloc with all the benefits, whilst maintaining 100% compatibility with `bloc` and `flutter_bloc` packages. The `StreamBloc`s can be used with all `flutter_bloc` widgets; they implement the same interfaces.\n\n## Overview\n\nIf you are familiar with the bloc before the 8.0.0/7.2.0 you are familiar with `StreamBloc` – the central class of this package. Documentation for previous bloc's versions can be used for this package besides a few modifications that are listed in the next section.\n\n`StreamBloc` uses a central event-processing method called `mapEventToStates` to convert a single Event to a Stream of States that are emitted asynchronously. Official `Bloc` can be directly translated to `StreamBloc` as described below.\n\n**It is highly advised to use freezed package. The following example is a demonstration and should not be considered a \"Best practice\" for StreamBloc**\n\n```dart\n\n\n\nabstract class CounterEvent {} // Shared counter event type\n\nclass Increment implements CounterEvent {} // Increment counter event\n\nclass Decrement implements CounterEvent {} // Decrement counter event\n\nclass OnCounterBloc extends Bloc\u003cCounterEvent, int\u003e { // Official Bloc – `on`s\n  OnCounterBloc() : super(0) {\n    on\u003cIncrement\u003e((event, emit) =\u003e emit(state + 1));\n    on\u003cDecrement\u003e((event, emit) =\u003e emit(state - 1));\n  }\n}\n\nclass StreamCounterBloc extends StreamBloc\u003cCounterEvent, int\u003e { // StreamBloc – `mapEventToStates`\n  StreamCounterBloc() : super(0);\n\n  @override\n  Stream\u003cint\u003e mapEventToStates(CounterEvent event) async* {\n    if (event is Increment) {\n      yield state + 1;\n    } else if (event is Decrement) {\n      yield state - 1;\n    }\n  }\n}\n\n```\n\n## Modifications\n\nThere are five main differences from the Original bloc.\n\n1) `mapEventToState` is renamed to `mapEventToStates`. The method returns an asynchronous sequence of states – not a single state.\n\n2) `StreamBloc`'s type parameters/generics are constrained to subclasses of an `Object?`.\n\n3) Bloc can emit identical states consequentially. The output stream of the `StreamBloc` is not distinct because of two main reasons\n    - `flutter_bloc`'s `BlocListener`/`BlocConsumer` may be interested in any new emitted state, even if the state had not changed\n    - `stream.map(...)`/`stream.where(...)` (essentially `BlocBuilder` and/or `BlocSelector`) applied to `stream.distinct()` removes the guarantee of uniques event in the stream, making the `distinct` redundant; it should be applied last, not first.\n\n4) Bloc Observer can be injected both through zone injection and static variable with the specified priority.\n\n5) Bloc Transformers is extended with an additional method that transforms source events and is applied before the events-transitions transformer.\n\n## Extras\n\nThe package also offers a single convenience mixin `BlocLifecycleMixin` which makes Bloc-to-Bloc communications easier. It offers two methods: `listenToStream` and `listenToStreamable`. Below is an example of its usage.\n\n```dart\n\nclass IncrementEvent {\n  final int amount;\n\n  const IncrementEvent(this.amount);\n}\n\nclass IncrementBloc extends StreamBloc\u003cIncrementEvent, int\u003e\n    with BlocLifecycleMixin {\n  IncrementBloc() : super(0) {\n    reactToStream\u003cint\u003e(\n      Stream.periodic(const Duration(seconds: 1), (i) =\u003e i),\n      (passed) =\u003e IncrementEvent(passed),\n    );\n\n    listenToStream\u003cint\u003e(\n      stream,\n      print,\n    );\n  }\n\n  @override\n  Stream\u003cint\u003e mapEventToStates(IncrementEvent event) =\u003e Stream.value(\n        state + event.amount,\n      );\n}\n\n```\n\nAll methods return an instance of `StreamSubscription` which can be canceled by hand if desired, but it is optional – it will be canceled any way on the closing of a Bloc that mixes in `BlocLifecycleMixin`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpurplenoodlesoop%2Fstream-bloc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpurplenoodlesoop%2Fstream-bloc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpurplenoodlesoop%2Fstream-bloc/lists"}