{"id":19648955,"url":"https://github.com/frideosapps/frideos_core","last_synced_at":"2026-05-10T23:43:40.150Z","repository":{"id":56830853,"uuid":"175996100","full_name":"frideosapps/frideos_core","owner":"frideosapps","description":"A library for streams, BLoC pattern, tunnel pattern, timing and animations. Core of the frideos package.","archived":false,"fork":false,"pushed_at":"2019-12-25T13:54:40.000Z","size":32,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-09T23:11:56.913Z","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":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/frideosapps.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-16T16:26:41.000Z","updated_at":"2019-12-25T13:54:42.000Z","dependencies_parsed_at":"2022-09-09T18:01:24.995Z","dependency_job_id":null,"html_url":"https://github.com/frideosapps/frideos_core","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/frideosapps%2Ffrideos_core","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frideosapps%2Ffrideos_core/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frideosapps%2Ffrideos_core/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frideosapps%2Ffrideos_core/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/frideosapps","download_url":"https://codeload.github.com/frideosapps/frideos_core/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240958889,"owners_count":19884908,"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-11-11T14:51:03.160Z","updated_at":"2026-05-10T23:43:40.092Z","avatar_url":"https://github.com/frideosapps.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# frideos_core [![pub package](https://img.shields.io/pub/v/frideos_core.svg)](https://pub.dartlang.org/packages/frideos_core)\n\nA library for streams, BLoC pattern, tunnel pattern, timing and animations. Core of the following packages:\n- [frideos](https://pub.dartlang.org/packages/frideos)\n- [frideos_light](https://pub.dartlang.org/packages/frideos_light)\n- [frideos_kvprx](https://pub.dartlang.org/packages/frideos_kvprx) \n\n#### [Classes for streams and BLoC pattern](#streams-and-bloc-pattern):\n\n- StreamedValue\n- StreamedTransformed\n- StreamedList\n- StreamedMap\n- MemoryValue\n- HistoryObject\n- StreamedSender\n\n#### [Classes for tunnel pattern](#tunnel-pattern)\n- StreamedSender\n- ListSender \n- MapSender\n\n#### [Classes for animations and timing](#animations-and-timing)\n\n- TimerObject\n- AnimatedObject\n- StagedObject\n- StagedWidget\n\n\n### Dependencies\n\n- [RxDart](https://pub.dartlang.org/packages/rxdart)\n\n\n## Streams and BLoC pattern\n\nUtility classes to work with streams and BLoC pattern.\n\nThis example (you can find it in the [frideos_examples](https://github.com/frideosapps/frideos_examples) repository) shows how to use some classes of this library, and a comparison code without it. It is just a page with two textfields to add a key/value pair to a map. The map is then used to drive a `ListView.builder` showing all the pairs.\n\n#### Common code\n\n```dart\nclass Validators {\n  final validateText =\n      StreamTransformer\u003cString, String\u003e.fromHandlers(handleData: (str, sink) {\n    if (str.isNotEmpty) {\n      sink.add(str);\n    } else {\n      sink.addError('The text must not be empty.');\n    }\n  });\n\n  final validateKey =\n      StreamTransformer\u003cString, int\u003e.fromHandlers(handleData: (key, sink) {\n    final k = int.tryParse(key);\n    if (k != null) {\n      sink.add(k);\n    } else {\n      sink.addError('The key must be an integer.');\n    }\n  });\n}\n```\n\n1. ##### BLoC without this library\n\n```dart\nclass StreamedMapCleanBloc extends BlocBase with Validators {\n  StreamedMapCleanBloc() {\n    print('-------StreamedMapClean BLOC--------');\n  }\n\n  final _map = BehaviorSubject\u003cMap\u003cint, String\u003e\u003e();\n  Stream\u003cMap\u003cint, String\u003e\u003e get outMap =\u003e _map.stream;\n  Function(Map\u003cint, String\u003e map) get inMap =\u003e _map.sink.add;\n  final map = Map\u003cint, String\u003e();\n\n  final _text = BehaviorSubject\u003cString\u003e();\n  Stream\u003cString\u003e get outText =\u003e _text.stream;\n  Stream\u003cString\u003e get outTextTransformed =\u003e _text.stream.transform(validateText);\n  Function(String text) get inText =\u003e _text.sink.add;\n\n  final _key = BehaviorSubject\u003cString\u003e();\n  Stream\u003cString\u003e get outKey =\u003e _key.stream;\n  Stream\u003cint\u003e get outKeyTransformed =\u003e _key.stream.transform(validateKey);\n  Function(String) get inKey =\u003e _key.sink.add;\n\n  Observable\u003cbool\u003e get isFilled =\u003e Observable.combineLatest2(\n      outTextTransformed, outKeyTransformed, (a, b) =\u003e true);\n\n  // Add to the streamed map the key/value pair put by the user\n  void addText() {\n    final key = int.parse(_key.value);\n    final value = _text.value;\n    final streamMap = _map.value;\n\n    if (streamMap != null) {\n      map.addAll(streamMap);\n    }\n\n    map[key] = value;\n    inMap(map);\n  }\n\n  @override\n  void dispose() {\n    print('-------StreamedMapClean BLOC DISPOSE--------');\n\n    _map.close();\n    _text.close();\n    _key.close();\n  }\n}\n```\n\n2. ##### With this library:\n\n```dart\nclass StreamedMapBloc extends BlocBase with Validators {\n  StreamedMapBloc() {\n    print('-------StreamedMap BLOC--------');\n\n    // Set the validation transformers for the textfields\n    streamedText.setTransformer(validateText);\n    streamedKey.setTransformer(validateKey);\n\n    // Activate the debug console messages on disposing\n    streamedMap.debugMode();\n    streamedText.debugMode();\n    streamedKey.debugMode();\n  }\n\n  final streamedMap = StreamedMap\u003cint, String\u003e(initialData: {});\n  final streamedText = StreamedTransformed\u003cString, String\u003e();\n  final streamedKey = StreamedTransformed\u003cString, int\u003e();\n\n  Observable\u003cbool\u003e get isFilled =\u003e Observable.combineLatest2(\n      streamedText.outTransformed, streamedKey.outTransformed, (a, b) =\u003e true);\n\n  // Add to the streamed map the key/value pair put by the user\n  void addText() {\n    final key = int.parse(streamedKey.value);\n    final value = streamedText.value;\n\n    streamedMap.addKey(key, value);\n\n    // Or, as an alternative:\n    //streamedMap.value[key] = value;\n    //streamedMap.refresh();\n  }\n\n  @override\n  void dispose() {\n    print('-------StreamedMap BLOC DISPOSE--------');\n    streamedMap.dispose();\n    streamedText.dispose();\n    streamedKey.dispose();\n  }\n}\n```\n\nAs you can see the code is more clean, easier to read and to mantain.\n\n### StreamedValue\n\nIt's the simplest class that implements the `StreamedObject` interface.\n\nEvery time a new value is set, this is compared to the oldest one and if it is different, it is sent to stream. Used in tandem with `ValueBuilder` it automatically triggers the rebuild of the widgets returned by its builder.\n\nSo for example, instead of:\n\n```dart\ncounter += 1;\nstream.sink.add(counter);\n```\n\nIt becomes just:\n\n```dart\ncounter.value += 1;\n```\n\nIt can be used even with `StreamedWidget` and `StreamBuilder` by using its stream getter `outStream`.\n\nN.B. when the type is not a basic type (e.g int, double, String etc.) and the value of a property of the object is changed, it is necessary to call the `refresh` method to update the stream.\n\n#### Usage\n\n```dart\n// In the BLoC\nfinal count = StreamedValue\u003cint\u003e(initialData: 0);\n\nincrementCounter() {\n  count.value += 2.0;\n}\n\n// View\nValueBuilder\u003cint\u003e(\n  streamed: bloc.count, // no need of the outStream getter with ValueBuilder\n  builder: (context, snapshot) =\u003e\n    Text('Value: ${snapshot.data}'),\n  noDataChild: Text('NO DATA'),\n),\nRaisedButton(\n    color: buttonColor,\n    child: Text('+'),\n    onPressed: () {\n      bloc.incrementCounter();\n    },\n),\n\n// As an alternative:\n//\n// StreamedWidget\u003cint\u003e(    \n//    stream: bloc.count.outStream,\n//    builder: (context, snapshot) =\u003e Text('Value: ${snapshot.data}'),\n//    noDataChild: Text('NO DATA'),\n//),\n```\n\nOn update the `timesUpdated` increases showing how many times the value has been updated.\n\nN.B. For collections use `StreamedList` and `StreamedMap` instead.\n\n### StreamedTransformed\n\nA particular class the implement the `StreamedObject` interface, to use when there is the need of a `StreamTransformer` (e.g. stream transformation, validation of input\nfields, etc.).\n\n#### Usage\n\nFrom the StreamedMap example:\n\n```dart\n// In the BLoC class\nfinal streamedKey = StreamedTransformed\u003cString, int\u003e();\n\n\n\n// In the constructor of the BLoC class\nstreamedKey.setTransformer(validateKey);\n\n\n\n// Validation (e.g. in the BLoC or in a mixin class)\nfinal validateKey =\n      StreamTransformer\u003cString, int\u003e.fromHandlers(handleData: (key, sink) {\n    var k = int.tryParse(key);\n    if (k != null) {\n      sink.add(k);\n    } else {\n      sink.addError('The key must be an integer.');\n    }\n  });\n\n\n// In the view:\nStreamBuilder\u003cint\u003e(\n            stream: bloc.streamedKey.outTransformed,\n            builder: (context, snapshot) {\n              return Column(\n                children: \u003cWidget\u003e[\n                  Padding(\n                    padding: const EdgeInsets.symmetric(\n                      vertical: 12.0,\n                      horizontal: 20.0,\n                    ),\n                    child: TextField(\n                      style: TextStyle(\n                        fontSize: 18.0,\n                        color: Colors.black,\n                      ),\n                      decoration: InputDecoration(\n                        labelText: 'Key:',\n                        hintText: 'Insert an integer...',\n                        errorText: snapshot.error,\n                      ),\n                      // To avoid the user could insert text use the TextInputType.number\n                      // Here is commented to show the error msg.\n                      //keyboardType: TextInputType.number,\n                      onChanged: bloc.streamedKey.inStream,\n                    ),\n                  ),\n                ],\n              );\n            }),\n```\n\n### StreamedList\n\nThis class has been created to work with lists. It works like `StreamedValue`.\n\nTo modify the list (e.g. adding items) and update the stream automatically\nuse these methods:\n\n- `AddAll`\n- `addElement`\n- `clear`\n- `removeAt`\n- `removeElement`\n- `replace`\n- `replaceAt`\n\nFor other direct actions on the list, to update the stream call\nthe `refresh` method instead.\n\n#### Usage\n\ne.g. adding an item:\n\n```dart\n streamedList.addElement(item);\n```\n\nit is the same as:\n\n```dart\n  streamedList.value.add(item);\n  streamedList.refresh();\n```\n\nFrom the StreamedList example:\n\n```dart\n  final streamedList = StreamedList\u003cString\u003e();\n\n\n  // Add to the streamed list the string from the textfield\n  addText() {\n    streamedList.addElement(streamedText.value);\n\n    // Or, as an alternative:\n    // streamedList.value.add(streamedText.value);\n    // streamedList.refresh(); // To refresh the stream with the new value\n  }\n```\n\n### StreamedMap\n\nThis class has been created to work with maps, it works like `StreamedList`.\n\nTo modify the list (e.g. adding items) and update the stream automatically\nuse these methods:\n\n- `addKey`\n- `removeKey`\n- `clear`\n\nFor other direct actions on the map, to update the stream call\nthe `refresh` method instead.\n\n#### Usage\n\ne.g. adding a key/value pair:\n\n```dart\n  streamedMap.addKey(1, 'first');\n```\n\nit is the same as:\n\n```dart\n   streamedMap.value[1] = 'first';\n   streamedList.refresh();\n```\n\nFrom the streamed map example:\n\n```dart\n  final streamedMap = StreamedMap\u003cint, String\u003e();\n\n\n  // Add to the streamed map the key/value pair put by the user\n  addText() {\n    var key = int.parse(streamedKey.value);\n    var value = streamedText.value;\n\n    streamedMap.addKey(key, value);\n\n    // Or, as an alternative:\n    //streamedMap.value[key] = value;\n    //streamedMap.refresh();\n  }\n```\n\n### MemoryValue\n\nThe `MemoryValue` has a property to preserve the previous value. The setter checks for the new value, if it is different from the one already stored, this one is given `oldValue` before storing and streaming the new one.\n\n#### Usage\n\n```dart\nfinal countMemory = MemoryValue\u003cint\u003e();\n\ncountMemory.value // current value\ncouneMemory.oldValue // previous value\n```\n\n## HistoryObject\n\nExtends the `MemoryValue` class, adding a `StreamedList`. Useful when it is need to store a value in a list.\n\n```dart\nfinal countHistory = HistoryObject\u003cint\u003e();\n\nincrementCounterHistory() {\n  countHistory.value++;\n}\n\nsaveToHistory() {\n  countHistory.saveValue();\n}\n```\n\n## Tunnel pattern\n\nEasy pattern to send data from one BLoC to another one.\n\n### StreamedSender\n\nUsed to make a one-way tunnel beetween two blocs (from blocA to a StremedValue on blocB).\n\n#### Usage\n\n1. #### Define an object that implements the `StreamedObject` interface in the blocB (e.g. a `StreamedValue`):\n\n```dart\nfinal receiverStr = StreamedValue\u003cString\u003e();\n```\n\n2. #### Define a `StreamedSender` in the blocA:\n\n```dart\nfinal tunnelSenderStr = StreamedSender\u003cString\u003e();\n```\n\n3. #### Set the receiver in the sender on the class the holds the instances of the blocs:\n\n```dart\nblocA.tunnelSenderStr.setReceiver(blocB.receiverStr);\n```\n\n4. #### To send data from blocA to blocB then:\n\n```dart\ntunnelSenderStr.send(\"Text from blocA to blocB\");\n```\n\n## ListSender and MapSender\n\nLike the StreamedSender, but used with collections.\n\n#### Usage\n\n1. #### Define a `StreamedList` or `StreamedMap` object in the blocB\n\n```dart\nfinal receiverList = StreamedList\u003cint\u003e();\nfinal receiverMap = StreamedMap\u003cint, String\u003e();\n```\n\n2. #### Define a `ListSender`/`MapSender` in the blocA\n\n```dart\nfinal tunnelList = ListSender\u003cint\u003e();\nfinal tunnelMap = MapSender\u003cint, String\u003e();\n```\n\n3. #### Set the receiver in the sender on the class the holds the instances of the blocs\n\n```dart\nblocA.tunnelList.setReceiver(blocB.receiverList);\nblocA.tunnelMap.setReceiver(blocB.receiverMap);\n```\n\n4. #### To send data from blocA to blocB then:\n\n```dart\ntunnelList.send(list);\ntunnelMap.send(map);\n```\n\n## Animations and timing\n\n### TimerObject\n\nAn object that embeds a timer and a stopwatch.\n\n#### Usage\n\n```dart\nfinal timerObject = TimerObject();\n\nstartTimer() {\n  timerObject.startTimer();\n}\n\nstopTimer() {\n  timerObject.stopTimer();\n}\n\ngetLapTime() {\n  timerObject.getLapTime();\n}\n\nincrementCounter(Timer t) {\n  counter.value += 2.0;\n}\n\nstartPeriodic() {\n   var interval = Duration(milliseconds: 1000);\n   timerObject.startPeriodic(interval, incrementCounter);\n}\n\n```\n\n### AnimatedObject\n\nThis class is used to update a value over a period of time. Useful to handle animations using the BLoC pattern.\n\nFrom the AnimatedObject example of the [frideos_examples](https://github.com/frideosapps/frideos_examples):\n\n![AnimatedObject](https://i.imgur.com/10nfh0R.gif)\n\n#### Usage\n\n- #### In the BLoC:\n\n```dart\n// Initial value 0.5, updating interval 20 milliseconds\n  final scaleAnimation =\n      AnimatedObject\u003cdouble\u003e(initialValue: 0.5, interval: 20);\n\n\n  final rotationAnimation =\n      AnimatedObject\u003cdouble\u003e(initialValue: 0.5, interval: 20);\n\n  start() {\n    scaleAnimation.start(updateScale);\n    rotationAnimation.start(updateRotation);\n  }\n\n  updateScale(Timer t) {\n    scaleAnimation.value += 0.03;\n\n    if (scaleAnimation.value \u003e 8.0) {\n      scaleAnimation.reset();\n    }\n  }\n\n  updateRotation(Timer t) {\n    rotationAnimation.value += 0.1;\n  }\n\n\n  stop() {\n    scaleAnimation.stop();\n    rotationAnimation.stop();\n  }\n\n  reset() {\n    scaleAnimation.reset();\n    rotationAnimation.reset();\n  }\n```\n\n- #### In the view:\n\n```dart\n      Container(\n          color: Colors.blueGrey[100],\n          child: Column(\n            children: \u003cWidget\u003e[\n              Container(height: 20.0,),\n               ValueBuilder\u003cAnimatedStatus\u003e(\n                streamed: bloc.scaleAnimation.status,\n                builder: (context, snapshot) {\n                  return Row(\n                    mainAxisAlignment: MainAxisAlignment.center,\n                    children: \u003cWidget\u003e[\n                      snapshot.data == AnimatedStatus.active\n                          ? RaisedButton(\n                              color: Colors.lightBlueAccent,\n                              child: Text('Reset'),\n                              onPressed: () {\n                                bloc.reset();\n                              })\n                          : Container(),\n                      snapshot.data == AnimatedStatus.stop\n                          ? RaisedButton(\n                              color: Colors.lightBlueAccent,\n                              child: Text('Start'),\n                              onPressed: () {\n                                bloc.start();\n                              })\n                          : Container(),\n                      snapshot.data == AnimatedStatus.active\n                          ? RaisedButton(\n                              color: Colors.lightBlueAccent,\n                              child: Text('Stop'),\n                              onPressed: () {\n                                bloc.stop();\n                              })\n                          : Container(),\n                    ],\n                  );\n                },\n              ),\n              Expanded(\n                child: ValueBuilder\u003cdouble\u003e(\n                    streamed: bloc.scaleAnimation,\n                    builder: (context, snapshot) {\n                      return Transform.scale(\n                          scale: snapshot.data,\n                          // No need for StreamBuilder here, the widget\n                          // is already updating\n                          child: Transform.rotate(\n                              angle: bloc.rotationAnimation.value,\n                              // Same here\n                              //\n                              child: Transform(\n                                  transform: Matrix4.rotationY(\n                                      bloc.rotationAnimation.value),\n                                  child: FlutterLogo())));\n                    }),\n              )\n            ],\n          ),\n        ),\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrideosapps%2Ffrideos_core","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffrideosapps%2Ffrideos_core","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrideosapps%2Ffrideos_core/lists"}