{"id":15485977,"url":"https://github.com/codediodeio/flowmap","last_synced_at":"2025-04-22T17:21:20.727Z","repository":{"id":56828935,"uuid":"174848948","full_name":"codediodeio/flowmap","owner":"codediodeio","description":"Stream and slice data","archived":false,"fork":false,"pushed_at":"2019-03-10T17:10:13.000Z","size":68,"stargazers_count":13,"open_issues_count":1,"forks_count":2,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-10-19T07:03:40.294Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/codediodeio.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":"2019-03-10T16:20:48.000Z","updated_at":"2023-02-01T21:48:13.000Z","dependencies_parsed_at":"2022-08-26T13:50:51.124Z","dependency_job_id":null,"html_url":"https://github.com/codediodeio/flowmap","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/codediodeio%2Fflowmap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codediodeio%2Fflowmap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codediodeio%2Fflowmap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codediodeio%2Fflowmap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/codediodeio","download_url":"https://codeload.github.com/codediodeio/flowmap/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230022927,"owners_count":18161080,"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":[],"created_at":"2024-10-02T06:05:22.368Z","updated_at":"2024-12-16T20:31:22.656Z","avatar_url":"https://github.com/codediodeio.png","language":"Dart","readme":"# FlowMap\n\nA state management utility for Flutter that makes it easy to stream, slice, and paint widgets with key/value pairs. \n\n## Why?\n\n\nThe `Map` - when combined with RxDart - makes it possible to dynamically stream and share data in Flutter without boilerplate/configuration, leading to faster prototyping of complex state relationships when compared to redux, inherited widgets, bloc, etc. \n\n- Event-driven one-way data flow. \n- Stream data globally or scoped to your widgets.\n- Repaint widgets on specific value changes or actions/events. \n- Time-travel debugging with included devtools widget.\n- No boilerplate and does not take over your architecture.\n- Focused on simplicity and flexibility. \n\n\nDrawbacks. Values in the map are dynamic and the Actions are created dynamically at runtime, so it's much more implicit than most state management tools. \n\n\n## Quick Start\n\n1. Create a `FlowMap`.  2. Put some widgets its builder function 3. Change values on the map. \n\n```dart\n// Initialize data globally or scoped to a widget\nFlowMap counter = FlowMap(seed: { 'count': 0 });\n\n// somewhere in your widget tree...\n// works just like a StreamBuilder, but gives you the current value in the map\n    counter.builder(builder: (Build context, Map state) {\n        int currentCount = state['count'];\n        return FlatButton(\n            onPressed: () =\u003e counter.update('count', currentCount + 1), \n            child: Text('Count $currentCount')\n        );\n    })\n```\n\nIt has a special widget that allows you to view and \"time-travel\" across state changes. \n\n```dart\n// you'll want this in a scrollable view\nListView(\n    children: [counter.devtools()]\n)\n```\n\n## The FlowMap\n\nThe `FlowMap` is a key/value store similar to a Dart `Map`, but is treated like an event-driven Stream (similar to Redux under the hood). This means you can listen to data changes and/or events that get dispatched to map. \n\n### Create\n\nPass an optional seed `Map` containing default values or start empty.\n\nKeys must be strings. Values can be anything (but I would recommend using types that can be serialized). \n\n```dart\nFlowMap map = FlowMap(seed: { 'count': 0, 'mantra': 'everything is a stream' });\n```\n\n### Read\n\nYou can access the full value as a Stream/Observable or plain Map. \n\n```dart\nmap.stream; // Observable\u003cMap\u003e\n\nmap.value; // Map\n```\n\nOr you can listen to individual values by specifying a key. The stream will only emit on distinct changes to the value at this location. \n\n```dart\nObservable\u003cdynamic\u003e count$ = map.streamItem('count'); // Observable\u003cdynamic\u003e\n\nint count = map.getItem('count'); // dynamic\n```\n\n\n### Updates\n\nYou can mutate the state using the familiar API methods below. \n\n```dart\nmap.update('count', count + 1); // Sets data at this key\n\nmap.reset({ Map next }); // Resets to initial seed value, or sets a new Map\n\nmap.remove('widget'); // Removes the key/value pair\n\n// provides a context to do anything to the state\nmap.action(name: 'ADD_NAME', data: 'Jeff', mutation: (Map state, dynamic data) {\n    state['name'] = data;\n    state['hasName'] = true;\n    return state;\n})\n```\n\nWhen you call one of these methods you're actually dispatching an `Action`. For example, the first method above results in an action named `UPDATE-count`. Why does this matter? Actions allow us to keep track of every event that happens to the map and the diff. Tip: Use the devtools widget to inspect every action visually.  \n\n\nActions are synchronous, but you can also mutate the state with the value resolved from a future. This will dispatch two actions (1) START and (2a) SUCCESS and perform an update with the resolved value, or (2b) ERROR.  \n\n```dart\nvar future = Future.delayed(Duration(milliseconds: 100)).then((v) =\u003e 23)\nmap.updateAsync('count', future);\n// ASYNC-START\n// wait 100ms\n// ASYNC-SUCCESS and updates the state\n```\n\nIf you want a more explicit API, you can initialize a map in your own class and use custom action names. \n\n```dart\nclass Counter {\n\n    final countKey = 'count'; \n    final map FlowMap = FlowMap(seed: { countKey: 0 })\n\n    int get count {\n        return map.getItem(countKey);\n    }\n\n    // dispatchs INCREMENT-count\n    void increment() {\n        map.update(countKey, count + 1, name: 'INCREMENT');\n    }\n\n    // dispatchs DECREMENT-count\n    void decrement() {\n        map.update(countKey, count - 1, name: 'DECREMENT');\n    }\n}\n```\n\n\n### Actions\n\nActions are synchronous events that provide a context for modifying the state of the FlowMap. Keep in mind, the FlowMap update methods above create actions for you automatically, so you may never need to mess with them directly. \n\nActions must be synchronous and should only be concerned with changing values on the state. The `name` gives the action meaning, the optional `data` allows you to pass in data from external sources, and the optional `mutation` is a function that provides a context to create the next state (same as a reducer in redux). The callback provide a the current state, the optional data payload, and requires you to return the next state. \n\nNote: Actions are not required to mutate the state - they can be used simply dispatch events. \n\n```dart\nAction increment = Action(name: 'INCREMENT', data: null, mutation: (Map state, dynamic data) {\n    state['count']++;\n    return state;\n})\n\n// On some button tap\nmap.dispatch(increment);\n```\n\nYou can listen to the entire stream of actions, or specific actions, to create \"reactions\" for running side-effects. \n\n\n```dart\nmap.actions.listen((a) =\u003e print(action.name));\n\nmap.actionName('INCREMENT-count').listen(() =\u003e someSideEffect() );\n```\n\n\n### Build\n\nThe combination of an action stream with the current state is very powerful. You can rebuild widgets by passing the streams to a `StreamBuilder` and the builder will always have access to the current value on the FlowMap. \n\n\n```dart\nStreamBuilder(\n    stream: map.stream,\n    builder: (context, snap) =\u003e Text(value: '${snap.data.count}' ) \n),\n```\n\nThis package includes two widgets that wrap StreamBuilder to listen to value changes and actions. \n\n```dart\nFlowMapBuilder(\n    map: map,\n    keyName: 'counter',\n    builder: (context, state) {\n        print('${state}')\n        return Text('$state'); \n    } \n),\nActionBuilder(\n    map: map,\n    actionName: 'UPDATE-counter',\n    builder: (context, action) {\n        print('${action.name}')\n        return Text(map.getItem('count'),toString()); \n    } \n),\n```\n\nOr you can call the builder on the map directly for even more sugar. \n\n```dart\nmap.builder(builder: (context, state) {\n    var currentCount = map.getItem('count');\n    return FlatButton(\n            onPressed: () =\u003e map.update('count', currentCount + 1), \n            child: Text('Count $currentCount')\n    );\n}),\n```\n\n\n\n## DevTools\n\nThere is a special devtools widget for viewing and time-traveling through state changes. \n\n```dart\nFlowMapDevTools(map: yourFlowMap);\n// or\nyourFlowMap.devtools();\n\n// It works nicely in a Drawer with a ListView\n\nreturn Scaffold(\n      drawer: Drawer(child: ListView(shrinkWrap: true, children: [\n          yourFlowMap.devtools()\n        ])),\n)\n```\n\n\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodediodeio%2Fflowmap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcodediodeio%2Fflowmap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodediodeio%2Fflowmap/lists"}