{"id":21372252,"url":"https://github.com/rxlabz/redarx","last_synced_at":"2025-10-12T02:04:16.330Z","repository":{"id":56837995,"uuid":"75393052","full_name":"rxlabz/redarx","owner":"rxlabz","description":"Experimental Dart State Management inspired by Redux, ngrx \u0026 Parsley ","archived":false,"fork":false,"pushed_at":"2017-09-06T13:29:32.000Z","size":382,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-08-09T11:15:54.103Z","etag":null,"topics":["dart","flux-architecture","reactive"],"latest_commit_sha":null,"homepage":"https://pub.dartlang.org/packages/redarx","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rxlabz.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":"2016-12-02T12:17:10.000Z","updated_at":"2020-08-01T23:46:23.000Z","dependencies_parsed_at":"2022-09-13T08:40:33.595Z","dependency_job_id":null,"html_url":"https://github.com/rxlabz/redarx","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rxlabz%2Fredarx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rxlabz%2Fredarx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rxlabz%2Fredarx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rxlabz%2Fredarx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rxlabz","download_url":"https://codeload.github.com/rxlabz/redarx/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225863467,"owners_count":17536115,"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":["dart","flux-architecture","reactive"],"created_at":"2024-11-22T08:18:44.047Z","updated_at":"2025-10-12T02:04:11.298Z","avatar_url":"https://github.com/rxlabz.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Unstart ( ex Redarx )\n\n(Experimental) Unidirectional data-flow State management for Dart \nhumbly inspired by [ngrx](https://github.com/ngrx) \u003c= [Redux](http://redux.js.org) \u003c= [Elm](http://elm-lang.org/), [André Stalz work](https://github.com/staltz) \u0026 [Parsley](http://www.spicefactory.org/parsley/).\n\n![redarx-principles](docs/graphs/redarx_graph.jpg)\n\n## Examples\n\n- [Redarx with Flutter example](https://github.com/rxlabz/redarx_flutter_example) (uses [built_value])\n- [Redarx with AngularDart example](https://github.com/rxlabz/redarx-angular-example)\n- [Redarx with Vanilla Dart example](https://github.com/rxlabz/redarx-todo)\n\n\n## Principles\n\n### Requests to Commands Mapping via CommanderConfig\n\nGoal : decouple request from action. View only dispatch dumb requests, the logic to execute the request is delegated to a command.\n\n```dart\nfinal requestMap =\n    \u003cRequestType, CommandBuilder\u003cTodoModel\u003e\u003e{\n  RequestType.ADD_TODO: AddTodoCommand.constructor(),\n  RequestType.UPDATE_TODO: UpdateTodoCommand.constructor(),\n  RequestType.CLEAR_ARCHIVES: ClearArchivesCommand.constructor(),\n  RequestType.COMPLETE_ALL: CompleteAllCommand.constructor(),\n  RequestType.LOAD_ALL: AsyncLoadAllCommand.constructor(DATA_PATH),\n  RequestType.TOGGLE_SHOW_COMPLETED: ToggleShowArchivesCommand.constructor()\n};\n```\n\n### Initialization\n\n```dart\nfinal config = new CommanderConfig\u003cRequestType\u003e(requestMap);\nfinal store = new Store\u003cTodoModel\u003e(() =\u003e new TodoModel.empty());\nfinal dispatcher = new Dispatcher();\n\nfinal cmder = new Commander(config, store, dispatcher.onRequest);\n```\n\n*Flutter*\n```dart\nfinal requestMap = \u003cRequestType, CommandBuilder\u003e{\n  RequestType.LOAD_ALL: AsyncLoadAllCommand.constructor(DATA_PATH),\n  RequestType.ADD_TODO: AddTodoCommand.constructor(),\n  RequestType.UPDATE_TODO: UpdateTodoCommand.constructor(),\n  RequestType.CLEAR_ARCHIVES: ClearArchivesCommand.constructor(),\n  RequestType.COMPLETE_ALL: CompleteAllCommand.constructor(),\n  RequestType.TOGGLE_SHOW_COMPLETED: ToggleShowArchivesCommand.constructor()\n};\n\nvoid main() {\n  runApp(new StoreProvider(requestMap: requestMap, child:new TodoApp()));\n}\n```\n\n*Vanilla Dart*\n```dart\nvar app = new AppComponent(querySelector('#app') )\n..model$ = store.data$\n..dispatch = dispatcher.dispatch\n..render();\n```\n\n### Dispatching requests\n\nRequestType are defined via an enum\n\n```dart\nenum RequestType {\n  ADD_TODO,\n  UPDATE_TODO,\n  ARCHIVE,\n  CLEAR_ARCHIVES,\n  TOGGLE_SHOW_COMPLETED\n}\n\n// you can enforce the typing by creating an optionnal request class\nclass TodoRequest\u003cT extends Todo\u003e extends Request\u003cRequestType, T\u003e {\n  TodoRequest.loadAll() : super(RequestType.LOAD_ALL);\n  TodoRequest.clearArchives() : super(RequestType.CLEAR_ARCHIVES);\n  TodoRequest.completeAll() : super(RequestType.COMPLETE_ALL);\n  TodoRequest.toggleStatusFilter() : super(RequestType.TOGGLE_SHOW_COMPLETED);\n  TodoRequest.add(T todo) : super(RequestType.ADD_TODO, withData: todo);\n  TodoRequest.cancel(T todo) : super(RequestType.CANCEL_TODO, withData: todo);\n  TodoRequest.update(T todo) : super(RequestType.UPDATE_TODO, withData: todo);\n}\n\n```\n\nRequests are defined by a type and an optional payload.\n\n```dart\ndispatch( new TodoRequest.add(new Todo(fldTodo.value)));\n```\n\nor\n```dart\n\n```\n\n### Commands\n\nRequests are mapped to commands by Commander and passed to the store.update()\n\n```dart\nexec(Request a) {\n    store.update(config[a.type](a.payload));\n  }\n```\n\nThe commands define a public exec method which receive the currentState and return the new one.\n\n```dart\nclass AddTodoCommand extends Command\u003cTodoModel\u003e {\n  Todo todo;\n\n  AddTodoCommand(this.todo);\n\n  @override\n  TodoModel exec(TodoModel model) =\u003e model..items.add(todo);\n\n  static CommandBuilder constructor() {\n    return (Todo todo) =\u003e new AddTodoCommand(todo);\n  }\n}\n```\n\n### Async Commands\n\nAsync commands allows async evaluation of the new state \n\n### Store\n\nBasically a `(stream\u003cCommand\u003cModel\u003e\u003e) =\u003e stream\u003cModel\u003e` transformer\n\nReceives new commands, and executes those with the current state/Model\n\nUse a `CommandStreamReducer\u003cS extends Command, T extends AbstractModel\u003e` to stream reduced states\n\nThe store manage a stream of immutable states instances. \n\n### Reversible Store\n\nThe reversible store keep a history list of all executed commands and allow cancelling.\n\nit provide an access to currentState by reducing all the commands history. \n\n### State listening\n\nThe store exposes a stream of immutable states\n\n```dart\nStream\u003cTodoModel\u003e _model$;\n\nset model$(Stream\u003cTodoModel\u003e value) {\n_model$ = value;\n    \nmodelSub = _model$.listen((TodoModel model) {\n      list.todos = model.todos;\n      footer.numCompleted = model.numCompleted;\n      footer.numRemaining = model.numRemaining;\n      footer.showCompleted = model.showCompleted;\n    });\n  }\n```\n\n### Experimental multi-channel dispatcher (0.6.0)\n \nAsync commands are maybe not the best way to connect a Firebase data-source.\n \nThe [redarx_ng_firebase](https://github.com/rxlabz/redarx_ng_firebase) example shows a way to dispatch firebase queries via a new dispatcher method : query.\n \nQueries are dispatched to a Firebase service, which update the base.\nThe service handles firebase.database child and values events and dispatch update request via the dispatch() method. \n\n## Event$ » Request$ » Command$ » state$ \n\nThe Application State is managed in a Store\u003cT extends AbstractModel\u003e.\n\nState is updated by commands, and the store keep a list of executed commands.\n\nState is evaluated by commands updates,\n\nIn reversible-store, cancellation is allowed by simply remove the last command from \"history\".\n\nA Commander listen to a stream of Requests dispatched by a Dispatcher injected in the application components | controllers | PM | VM\n\nEach Request is defined by an RequestType enum, and can contains data.\n\nRequests are \"converted\" to commands by the Commander, based on the CommanderConfig.map definition  \n\n- the dispatcher.dispatch function is injected in view || controller || PresentationModel || ViewModel  \n- Request are categorized by types, types are defined in RequestType enum\n- the dispatcher stream Requests\n- the dispatcher requestStream is injected in Commander, the commander listen to it,\ntransforms Request to Command and transfer to the store.apply( command ) method\n\n- each Request is tied to a command via a CommanderConfig which is injected in Commander\n\n```dart\n// instanciate commands form requests \nconfig[request.type](request.payload);\n```\n\n- Commander need a CommanderConfig containing a Map\u003cRequestType,CommandBuilder\u003e\n- the store then execute commandHistory and push the new model value to a model stream\n\n\n## TODO \n\n- ~~fix the generic/command ( \u003cT extends Model\u003e mess)~~\n- ~~implements a Scan stream transformer » to allow only run the last commands \u0026 emit the last reduced state~~\n- ~~async commands~~\n- ~~test Angular integration~~\n- ~~test with Firebase~~\n- ~~typed Request ? BookRequest, UserRequest ...? =\u003e TodoRequest cf. flutter example~~\n- use values types [cf built_value](https://github.com/google/built_value.dart)\n- multiple stores ?\n- time travel / history UI\n\n- tests\n- external config file ? \n- ...\n\n## Doubts\n\n- use a EnumClass implementation rather than dart enum type\n- dispatcher : use a streamController.add rather than dispatch method ?\n- multiple store ? dispatcher ? commander ?\n- each component could set an Request stream and the commander could maybe listen to it\n\n## Goals\n\n- study Dart : streams, generics, annotations, asynchrony...\n- study Redux \u0026 ngrx, play with reducers \u0026 Request/Commands mapping...\n- and more studies, more experiments, more play...\n- define a solid architecture for my coming projects\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frxlabz%2Fredarx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frxlabz%2Fredarx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frxlabz%2Fredarx/lists"}