Committed by
GitHub
Merge pull request #16 from jonataslaw/master
Update
Showing
43 changed files
with
2080 additions
and
1405 deletions
@@ -23,7 +23,7 @@ jobs: | @@ -23,7 +23,7 @@ jobs: | ||
23 | # https://github.com/marketplace/actions/flutter-action | 23 | # https://github.com/marketplace/actions/flutter-action |
24 | - uses: subosito/flutter-action@v1 | 24 | - uses: subosito/flutter-action@v1 |
25 | with: | 25 | with: |
26 | - flutter-version: "1.22.2" | 26 | + flutter-version: "1.22.3" |
27 | channel: "stable" | 27 | channel: "stable" |
28 | - run: flutter pub get | 28 | - run: flutter pub get |
29 | #- run: flutter analyze | 29 | #- run: flutter analyze |
1 | +## [3.16.0] | ||
2 | +- Documentation translated into Russian language. (@Renat Fakhrutdinov, @Doaxan and @BatttA) | ||
3 | +- Added error message callback for StateMixin (@eduardoflorence) | ||
4 | +- Fix incorrect Get.reference when pop route (@4mb1t) | ||
5 | +- Added Uppercase/Capital letter on GetUtils (@AleFachini) | ||
6 | +- Redraw the Streams api to use GetStream instead of StreamControllers. Why this change? GetStream is as light as a ValueNotifier, has very low latency and low consumption of RAM and CPU. There are cases where the performance gain exceeds 9000%, making Get unique when it comes to low latency and resource savings. We did this because new devices are being equipped with 120hz refresh rate, and to provide an even smoother reconstruction of the widgets, it was necessary to create a low latency solution from scratch. GetStream was then created, released in the previous version, and improved in that version. In the previous version, only a small part of GetX used this low-latency API so that it was possible to verify solidly if the api was mature enough to equip state management, which is the most used feature of this library. After two weeks of unremitting tests, we realized that in addition to being fast, the new api is reliable, and will even equip the Stable version of GetServer, which due to the low latency will have performance of low level languages close to C, C ++, Rust and GO. | ||
7 | + | ||
8 | +## [3.15.0] - Big update | ||
9 | +- **Improve Performance**: We made modifications to make GetBuilder even faster. We have improved the structure behind it so that listeners are notified faster. Perhaps in version 4.0 everything will be based on this new structure, but maintaining the power and compatibility with streams. If you want to know how much Getx is faster than pure streams or ChangeNotifier (even after the last update using LinkedList), you can create run the repository tests at: (https://github.com/jonataslaw/getx/blob/master/test/benchmarks/benckmark_test.dart) | ||
10 | +- **Added StateMixin** | ||
11 | +StateMixin allows you to change the state of the controller, and display a loading, an error message, or a widget you want with 0 boilerplate. This makes things like API / Rest communication or websocket absurdly simple, and it's a real revolution in how state management has behaved so far. | ||
12 | +You no longer need to have a ternary in your code, and you don't need a widget like FutureBuilder, StreamBuilder or even Obx / GetBuilder to encompass your Visibility. This will change with the way you manage the state of your controllers, decrease your boilerplate absurdly, and give you more security in your code. | ||
13 | +- **Added GetNotifier** | ||
14 | +GetNotifier is a super and powerful ValueNotifier, which in addition to having the life cycle of the controllers, is extremely fast, and can manage a single state, as a simplified immutable state management solution. | ||
15 | +In theory, the only difference between it and GetxController is the possibility of setting an initial value in the constructor's super (exactly as ValueNotifier does). If the initial value is null, use GetxController. If you need a starting value, GetNotifier can be more useful and have less boilerplate, but both serve the same purpose: to decouple your visualization layer from your presentation logic. | ||
16 | +- Other Fixes and improvements: | ||
17 | + - Fixed GetxController is closed twice when smartManagement.full is turn on | ||
18 | + - Fixed phone number validation | ||
19 | + - Fixed some inconsistencies in GetWidget and the life cycle of controllers | ||
20 | + - It made controller testing completely safe with navigation. | ||
21 | + - Improve docs (@eduardoflorence) | ||
22 | + - Improve security types on routes (@unacorbatanegra) | ||
23 | + - Improve code structure with less duplicate code: (@kranfix) | ||
24 | + - Fix named route erroring when route does not exist (@FiercestT) | ||
25 | + | ||
1 | ## [3.13.2] | 26 | ## [3.13.2] |
2 | - Reunification of the package. | 27 | - Reunification of the package. |
3 | During the 2 week period, we try to keep this package as a compilation of smaller packages. We were successful in separating, getx is well decoupled and it was only necessary to send the internal folders as packages to pub.dev, however, it became very complicated to contribute to the package. This is because it was necessary to clone the repository, replace all pubspec packages with local paths, and after modification, return the original paths to do the PR. With that, the frequency of updates, which was about 4 to 5 days, became almost 2 weeks, and this is not legal for a community as active as Getx, which uses this package precisely in addition to being modern and performance, be constantly improving. This led contributors to the conclusion that getx works best together. | 28 | During the 2 week period, we try to keep this package as a compilation of smaller packages. We were successful in separating, getx is well decoupled and it was only necessary to send the internal folders as packages to pub.dev, however, it became very complicated to contribute to the package. This is because it was necessary to clone the repository, replace all pubspec packages with local paths, and after modification, return the original paths to do the PR. With that, the frequency of updates, which was about 4 to 5 days, became almost 2 weeks, and this is not legal for a community as active as Getx, which uses this package precisely in addition to being modern and performance, be constantly improving. This led contributors to the conclusion that getx works best together. |
1 |  | 1 |  |
2 | 2 | ||
3 | -*Idiomas: Español (este archivo), [Lengua china](README.zh-cn.md), [Inglés](README.md), [Portugués de Brasil](README.pt-br.md), [Polaco](README.pl.md).* | 3 | +*Idiomas: Español (este archivo), [Lengua china](README.zh-cn.md), [Inglés](README.md), [Portugués de Brasil](README.pt-br.md), [Russo](README-ru.md), [Polaco](README.pl.md).* |
4 | 4 | ||
5 | [](https://pub.dev/packages/get) | 5 | [](https://pub.dev/packages/get) |
6 |  | 6 |  |
1 |  | 1 |  |
2 | 2 | ||
3 | -_Languages: English (this file), [Chinese](README.zh-cn.md), [Brazilian Portuguese](README.pt-br.md), [Spanish](README-es.md),[Polish](README.pl.md)._ | 3 | +_Languages: English (this file), [Chinese](README.zh-cn.md), [Brazilian Portuguese](README.pt-br.md), [Spanish](README-es.md), [Russian](README-ru.md), [Polish](README.pl.md)._ |
4 | 4 | ||
5 | [](https://pub.dev/packages/get) | 5 | [](https://pub.dev/packages/get) |
6 |  | 6 |  |
1 |  | 1 |  |
2 | 2 | ||
3 | -*Languages: [English](README.md), [Język chiński](README.zh-cn.md), [Brazilian Portuguese](README.pt-br.md), [Spanish](README-es.md), Polish (Jesteś tu).* | 3 | +*Languages: [English](README.md), [Język chiński](README.zh-cn.md), [Brazilian Portuguese](README.pt-br.md), [Spanish](README-es.md), [Russian](README-ru.md), Polish (Jesteś tu).* |
4 | 4 | ||
5 | [](https://pub.dev/packages/get) | 5 | [](https://pub.dev/packages/get) |
6 |  | 6 |  |
1 |  | 1 |  |
2 | 2 | ||
3 | -*Idiomas: [Inglês](README.md), [Língua chinesa](README.zh-cn.md), Português Brasileiro (este arquivo), [Espanhol](README-es.md), [Polaco](README.pl.md).* | 3 | +*Idiomas: [Inglês](README.md), [Chinês](README.zh-cn.md), Português Brasileiro (este arquivo), [Espanhol](README-es.md), [Russo](README-ru.md), [Polonês](README.pl.md).* |
4 | 4 | ||
5 | [](https://pub.dev/packages/get) | 5 | [](https://pub.dev/packages/get) |
6 |  | 6 |  |
1 |  | 1 |  |
2 | 2 | ||
3 | -_语言: 中文, [英文](README.md), [巴西葡萄牙语](README.pt-br.md), [西班牙语](README-es.md), [波兰语](README.pl.md)_ | 3 | +_语言: 中文, [英文](README.md), [巴西葡萄牙语](README.pt-br.md), [俄语](README-ru.md), [西班牙语](README-es.md), [波兰语](README.pl.md)_ |
4 | 4 | ||
5 | [](https://pub.dev/packages/get) | 5 | [](https://pub.dev/packages/get) |
6 |  | 6 |  |
@@ -49,6 +49,7 @@ linter: | @@ -49,6 +49,7 @@ linter: | ||
49 | prefer_equal_for_default_values: true | 49 | prefer_equal_for_default_values: true |
50 | avoid_init_to_null: true | 50 | avoid_init_to_null: true |
51 | unnecessary_getters_setters: true | 51 | unnecessary_getters_setters: true |
52 | + annotate_overrides: true | ||
52 | #- unnecessary_getters # prefer # Disabled pending fix: https://github.com/dart-lang/linter/issues/23 | 53 | #- unnecessary_getters # prefer # Disabled pending fix: https://github.com/dart-lang/linter/issues/23 |
53 | #- prefer_expression_function_bodies # consider | 54 | #- prefer_expression_function_bodies # consider |
54 | unnecessary_this: true | 55 | unnecessary_this: true |
@@ -3,43 +3,22 @@ import 'package:get/get.dart'; | @@ -3,43 +3,22 @@ import 'package:get/get.dart'; | ||
3 | import '../../domain/adapters/repository_adapter.dart'; | 3 | import '../../domain/adapters/repository_adapter.dart'; |
4 | import '../../domain/entity/cases_model.dart'; | 4 | import '../../domain/entity/cases_model.dart'; |
5 | 5 | ||
6 | -enum Status { loading, success, error } | ||
7 | - | ||
8 | -class HomeController extends GetxController { | 6 | +class HomeController extends GetxController with StateMixin<CasesModel> { |
9 | HomeController({this.homeRepository}); | 7 | HomeController({this.homeRepository}); |
10 | 8 | ||
11 | /// inject repo abstraction dependency | 9 | /// inject repo abstraction dependency |
12 | final IHomeRepository homeRepository; | 10 | final IHomeRepository homeRepository; |
13 | 11 | ||
14 | - /// create a reactive status from request with initial value = loading | ||
15 | - final status = Status.loading.obs; | ||
16 | - | ||
17 | - /// create a reactive CasesModel. CasesModel().obs has same result | ||
18 | - final cases = Rx<CasesModel>(); | ||
19 | - | ||
20 | /// When the controller is initialized, make the http request | 12 | /// When the controller is initialized, make the http request |
21 | @override | 13 | @override |
22 | void onInit() { | 14 | void onInit() { |
23 | super.onInit(); | 15 | super.onInit(); |
24 | - fetchDataFromApi(); | ||
25 | - } | ||
26 | - | ||
27 | - /// fetch cases from Api | ||
28 | - Future<void> fetchDataFromApi() async { | ||
29 | - /// When the repository returns the value, change the status to success, | ||
30 | - /// and fill in "cases" | ||
31 | - return homeRepository.getCases().then( | ||
32 | - (data) { | ||
33 | - cases(data); | ||
34 | - status(Status.success); | ||
35 | - }, | ||
36 | - | ||
37 | - /// In case of error, print the error and change the status | ||
38 | - /// to Status.error | ||
39 | - onError: (err) { | ||
40 | - print("$err"); | ||
41 | - return status(Status.error); | ||
42 | - }, | ||
43 | - ); | 16 | + // show loading on start, data on success |
17 | + // and error message on error with 0 boilerplate | ||
18 | + homeRepository.getCases().then((data) { | ||
19 | + change(data, status: RxStatus.success()); | ||
20 | + }, onError: (err) { | ||
21 | + change(null, status: RxStatus.error(err.toString())); | ||
22 | + }); | ||
44 | } | 23 | } |
45 | } | 24 | } |
@@ -29,9 +29,9 @@ class CountryView extends GetView<HomeController> { | @@ -29,9 +29,9 @@ class CountryView extends GetView<HomeController> { | ||
29 | ), | 29 | ), |
30 | body: Center( | 30 | body: Center( |
31 | child: ListView.builder( | 31 | child: ListView.builder( |
32 | - itemCount: controller.cases.value.countries.length, | 32 | + itemCount: controller.state.countries.length, |
33 | itemBuilder: (context, index) { | 33 | itemBuilder: (context, index) { |
34 | - final country = controller.cases.value.countries[index]; | 34 | + final country = controller.state.countries[index]; |
35 | return ListTile( | 35 | return ListTile( |
36 | onTap: () { | 36 | onTap: () { |
37 | Get.toNamed('/details', arguments: country); | 37 | Get.toNamed('/details', arguments: country); |
@@ -27,11 +27,8 @@ class HomeView extends GetView<HomeController> { | @@ -27,11 +27,8 @@ class HomeView extends GetView<HomeController> { | ||
27 | centerTitle: true, | 27 | centerTitle: true, |
28 | ), | 28 | ), |
29 | body: Center( | 29 | body: Center( |
30 | - child: Obx( | ||
31 | - () { | ||
32 | - final status = controller.status.value; | ||
33 | - if (status == Status.loading) return CircularProgressIndicator(); | ||
34 | - if (status == Status.error) return Text('Error on connection :('); | 30 | + child: controller.obx( |
31 | + (state) { | ||
35 | return Column( | 32 | return Column( |
36 | mainAxisAlignment: MainAxisAlignment.center, | 33 | mainAxisAlignment: MainAxisAlignment.center, |
37 | children: [ | 34 | children: [ |
@@ -45,7 +42,7 @@ class HomeView extends GetView<HomeController> { | @@ -45,7 +42,7 @@ class HomeView extends GetView<HomeController> { | ||
45 | ), | 42 | ), |
46 | ), | 43 | ), |
47 | Text( | 44 | Text( |
48 | - '${controller.cases.value.global.totalConfirmed}', | 45 | + '${state.global.totalConfirmed}', |
49 | style: TextStyle(fontSize: 45, fontWeight: FontWeight.bold), | 46 | style: TextStyle(fontSize: 45, fontWeight: FontWeight.bold), |
50 | ), | 47 | ), |
51 | SizedBox( | 48 | SizedBox( |
@@ -58,7 +55,7 @@ class HomeView extends GetView<HomeController> { | @@ -58,7 +55,7 @@ class HomeView extends GetView<HomeController> { | ||
58 | ), | 55 | ), |
59 | ), | 56 | ), |
60 | Text( | 57 | Text( |
61 | - '${controller.cases.value.global.totalDeaths}', | 58 | + '${state.global.totalDeaths}', |
62 | style: TextStyle(fontSize: 45, fontWeight: FontWeight.bold), | 59 | style: TextStyle(fontSize: 45, fontWeight: FontWeight.bold), |
63 | ), | 60 | ), |
64 | SizedBox( | 61 | SizedBox( |
@@ -59,18 +59,18 @@ void main() { | @@ -59,18 +59,18 @@ void main() { | ||
59 | expect(controller.initialized, true); | 59 | expect(controller.initialized, true); |
60 | 60 | ||
61 | /// check initial Status | 61 | /// check initial Status |
62 | - expect(controller.status.value, Status.loading); | 62 | + expect(controller.status.isLoading, true); |
63 | 63 | ||
64 | /// await time request | 64 | /// await time request |
65 | await Future.delayed(Duration(milliseconds: 100)); | 65 | await Future.delayed(Duration(milliseconds: 100)); |
66 | 66 | ||
67 | - if (controller.status.value == Status.error) { | ||
68 | - expect(controller.cases.value, null); | 67 | + if (controller.status.isError) { |
68 | + expect(controller.state, null); | ||
69 | } | 69 | } |
70 | 70 | ||
71 | - if (controller.status.value == Status.success) { | ||
72 | - expect(controller.cases.value.global.totalDeaths, 100); | ||
73 | - expect(controller.cases.value.global.totalConfirmed, 200); | 71 | + if (controller.status.isSuccess) { |
72 | + expect(controller.state.global.totalDeaths, 100); | ||
73 | + expect(controller.state.global.totalConfirmed, 200); | ||
74 | } | 74 | } |
75 | }); | 75 | }); |
76 | 76 |
1 | -import 'package:meta/meta.dart'; | ||
2 | import '../../get_core/get_core.dart'; | 1 | import '../../get_core/get_core.dart'; |
3 | 2 | ||
4 | /// Special callable class to keep the contract of a regular method, and avoid | 3 | /// Special callable class to keep the contract of a regular method, and avoid |
@@ -18,37 +17,34 @@ class _InternalFinalCallback<T> { | @@ -18,37 +17,34 @@ class _InternalFinalCallback<T> { | ||
18 | /// ```dart | 17 | /// ```dart |
19 | /// class SomeController with GetLifeCycle { | 18 | /// class SomeController with GetLifeCycle { |
20 | /// SomeController() { | 19 | /// SomeController() { |
21 | -/// initLifeCycle(); | 20 | +/// configureLifeCycle(); |
22 | /// } | 21 | /// } |
23 | /// } | 22 | /// } |
24 | /// ``` | 23 | /// ``` |
25 | -mixin GetLifeCycle { | ||
26 | - /// The `initLifeCycle` works as a constructor for the [GetLifeCycle] | ||
27 | - /// | ||
28 | - /// This method must be invoked in the constructor of the implementation | ||
29 | - void initLifeCycle() { | ||
30 | - onStart._callback = _onStart; | ||
31 | - onDelete._callback = _onDelete; | ||
32 | - } | ||
33 | - | 24 | +mixin GetLifeCycleBase { |
34 | /// Called at the exact moment the widget is allocated in memory. | 25 | /// Called at the exact moment the widget is allocated in memory. |
35 | /// It uses an internal "callable" type, to avoid any @overrides in subclases. | 26 | /// It uses an internal "callable" type, to avoid any @overrides in subclases. |
36 | /// This method should be internal and is required to define the | 27 | /// This method should be internal and is required to define the |
37 | /// lifetime cycle of the subclass. | 28 | /// lifetime cycle of the subclass. |
38 | final onStart = _InternalFinalCallback<void>(); | 29 | final onStart = _InternalFinalCallback<void>(); |
39 | 30 | ||
31 | + // /// The `configureLifeCycle` works as a constructor for the [GetLifeCycle] | ||
32 | + // /// | ||
33 | + // /// This method must be invoked in the constructor of the implementation | ||
34 | + // void configureLifeCycle() { | ||
35 | + // if (_initialized) return; | ||
36 | + // } | ||
37 | + | ||
40 | /// Internal callback that starts the cycle of this controller. | 38 | /// Internal callback that starts the cycle of this controller. |
41 | final onDelete = _InternalFinalCallback<void>(); | 39 | final onDelete = _InternalFinalCallback<void>(); |
42 | 40 | ||
43 | /// Called immediately after the widget is allocated in memory. | 41 | /// Called immediately after the widget is allocated in memory. |
44 | /// You might use this to initialize something for the controller. | 42 | /// You might use this to initialize something for the controller. |
45 | - @mustCallSuper | ||
46 | void onInit() {} | 43 | void onInit() {} |
47 | 44 | ||
48 | /// Called 1 frame after onInit(). It is the perfect place to enter | 45 | /// Called 1 frame after onInit(). It is the perfect place to enter |
49 | /// navigation events, like snackbar, dialogs, or a new route, or | 46 | /// navigation events, like snackbar, dialogs, or a new route, or |
50 | /// async request. | 47 | /// async request. |
51 | - @mustCallSuper | ||
52 | void onReady() {} | 48 | void onReady() {} |
53 | 49 | ||
54 | /// Called before [onDelete] method. [onClose] might be used to | 50 | /// Called before [onDelete] method. [onClose] might be used to |
@@ -57,7 +53,6 @@ mixin GetLifeCycle { | @@ -57,7 +53,6 @@ mixin GetLifeCycle { | ||
57 | /// Or dispose objects that can potentially create some memory leaks, | 53 | /// Or dispose objects that can potentially create some memory leaks, |
58 | /// like TextEditingControllers, AnimationControllers. | 54 | /// like TextEditingControllers, AnimationControllers. |
59 | /// Might be useful as well to persist some data on disk. | 55 | /// Might be useful as well to persist some data on disk. |
60 | - @mustCallSuper | ||
61 | void onClose() {} | 56 | void onClose() {} |
62 | 57 | ||
63 | bool _initialized = false; | 58 | bool _initialized = false; |
@@ -83,6 +78,26 @@ mixin GetLifeCycle { | @@ -83,6 +78,26 @@ mixin GetLifeCycle { | ||
83 | _isClosed = true; | 78 | _isClosed = true; |
84 | onClose(); | 79 | onClose(); |
85 | } | 80 | } |
81 | + | ||
82 | + void $configureLifeCycle() { | ||
83 | + _checkIfAlreadyConfigured(); | ||
84 | + onStart._callback = _onStart; | ||
85 | + onDelete._callback = _onDelete; | ||
86 | + } | ||
87 | + | ||
88 | + void _checkIfAlreadyConfigured() { | ||
89 | + if (_initialized) { | ||
90 | + throw """You can only call configureLifeCycle once. | ||
91 | +The proper place to insert it is in your class's constructor | ||
92 | +that inherits GetLifeCycle."""; | ||
93 | + } | ||
94 | + } | ||
95 | +} | ||
96 | + | ||
97 | +abstract class GetLifeCycle with GetLifeCycleBase { | ||
98 | + GetLifeCycle() { | ||
99 | + $configureLifeCycle(); | ||
100 | + } | ||
86 | } | 101 | } |
87 | 102 | ||
88 | /// Allow track difference between GetxServices and GetxControllers | 103 | /// Allow track difference between GetxServices and GetxControllers |
@@ -11,9 +11,6 @@ import 'root/parse_route.dart'; | @@ -11,9 +11,6 @@ import 'root/parse_route.dart'; | ||
11 | import 'root/root_controller.dart'; | 11 | import 'root/root_controller.dart'; |
12 | import 'routes/transitions_type.dart'; | 12 | import 'routes/transitions_type.dart'; |
13 | 13 | ||
14 | -//TODO: Split this class on "Snackbar" "Dialog" "bottomSheet" | ||
15 | -//and "navigation" extensions | ||
16 | - | ||
17 | extension ExtensionSnackbar on GetInterface { | 14 | extension ExtensionSnackbar on GetInterface { |
18 | void rawSnackbar({ | 15 | void rawSnackbar({ |
19 | String title, | 16 | String title, |
@@ -127,14 +127,15 @@ class GetObserver extends NavigatorObserver { | @@ -127,14 +127,15 @@ class GetObserver extends NavigatorObserver { | ||
127 | @override | 127 | @override |
128 | void didPop(Route route, Route previousRoute) { | 128 | void didPop(Route route, Route previousRoute) { |
129 | super.didPop(route, previousRoute); | 129 | super.didPop(route, previousRoute); |
130 | - final newRoute = _RouteData.ofRoute(route); | ||
131 | - | ||
132 | - if (newRoute.isSnackbar) { | ||
133 | - Get.log("CLOSE SNACKBAR ${newRoute.name}"); | ||
134 | - } else if (newRoute.isBottomSheet || newRoute.isDialog) { | ||
135 | - Get.log("CLOSE ${newRoute.name}"); | ||
136 | - } else if (newRoute.isGetPageRoute) { | ||
137 | - Get.log("CLOSE TO ROUTE ${newRoute.name}"); | 130 | + final currentRoute = _RouteData.ofRoute(route); |
131 | + final newRoute = _RouteData.ofRoute(previousRoute); | ||
132 | + | ||
133 | + if (currentRoute.isSnackbar) { | ||
134 | + Get.log("CLOSE SNACKBAR ${currentRoute.name}"); | ||
135 | + } else if (currentRoute.isBottomSheet || currentRoute.isDialog) { | ||
136 | + Get.log("CLOSE ${currentRoute.name}"); | ||
137 | + } else if (currentRoute.isGetPageRoute) { | ||
138 | + Get.log("CLOSE TO ROUTE ${currentRoute.name}"); | ||
138 | } | 139 | } |
139 | 140 | ||
140 | Get.reference = newRoute.name; | 141 | Get.reference = newRoute.name; |
1 | library get_rx; | 1 | library get_rx; |
2 | 2 | ||
3 | -export 'src/rx_core/rx_impl.dart'; | ||
4 | -export 'src/rx_core/rx_interface.dart'; | ||
5 | -export 'src/rx_iterables/rx_list.dart'; | ||
6 | -export 'src/rx_iterables/rx_map.dart'; | ||
7 | -export 'src/rx_iterables/rx_set.dart'; | 3 | +export 'src/rx_stream/rx_stream.dart'; |
4 | +export 'src/rx_types/rx_types.dart'; | ||
8 | export 'src/rx_workers/rx_workers.dart'; | 5 | export 'src/rx_workers/rx_workers.dart'; |
1 | -import 'dart:async'; | ||
2 | -import 'dart:collection'; | ||
3 | -import 'dart:math'; | ||
4 | -import 'package:flutter/foundation.dart'; | ||
5 | - | ||
6 | -import '../rx_core/rx_impl.dart'; | ||
7 | -import '../rx_core/rx_interface.dart'; | ||
8 | -import '../rx_typedefs/rx_typedefs.dart'; | ||
9 | - | ||
10 | -/// Create a list similar to `List<T>` | ||
11 | -class RxList<E> implements List<E>, RxInterface<List<E>> { | ||
12 | - RxList([List<E> initial]) { | ||
13 | - if (initial != null) _list = initial; | ||
14 | - } | ||
15 | - | ||
16 | - List<E> _list = <E>[]; | ||
17 | - | ||
18 | - @override | ||
19 | - Iterator<E> get iterator => value.iterator; | ||
20 | - | ||
21 | - @override | ||
22 | - bool get isEmpty => value.isEmpty; | ||
23 | - | ||
24 | - bool get canUpdate { | ||
25 | - return _subscriptions.length > 0; | ||
26 | - } | ||
27 | - | ||
28 | - @override | ||
29 | - bool get isNotEmpty => value.isNotEmpty; | ||
30 | - | ||
31 | - @override | ||
32 | - StreamController<List<E>> subject = StreamController.broadcast(); | ||
33 | - | ||
34 | - final _subscriptions = HashMap<Stream<List<E>>, StreamSubscription>(); | ||
35 | - | ||
36 | - void operator []=(int index, E val) { | ||
37 | - _list[index] = val; | ||
38 | - refresh(); | ||
39 | - } | ||
40 | - | ||
41 | - void refresh() { | ||
42 | - subject.add(_list); | ||
43 | - } | ||
44 | - | ||
45 | - /// Special override to push() element(s) in a reactive way | ||
46 | - /// inside the List, | ||
47 | - RxList<E> operator +(Iterable<E> val) { | ||
48 | - addAll(val); | ||
49 | - refresh(); | ||
50 | - return this; | ||
51 | - } | ||
52 | - | ||
53 | - E operator [](int index) { | ||
54 | - return value[index]; | ||
55 | - } | ||
56 | - | ||
57 | - void add(E item) { | ||
58 | - _list.add(item); | ||
59 | - refresh(); | ||
60 | - } | ||
61 | - | ||
62 | - @override | ||
63 | - void addAll(Iterable<E> item) { | ||
64 | - _list.addAll(item); | ||
65 | - refresh(); | ||
66 | - } | ||
67 | - | ||
68 | - /// Add [item] to [List<E>] only if [item] is not null. | ||
69 | - void addNonNull(E item) { | ||
70 | - if (item != null) add(item); | ||
71 | - } | ||
72 | - | ||
73 | - /// Add [Iterable<E>] to [List<E>] only if [Iterable<E>] is not null. | ||
74 | - void addAllNonNull(Iterable<E> item) { | ||
75 | - if (item != null) addAll(item); | ||
76 | - } | ||
77 | - | ||
78 | - /// Add [item] to [List<E>] only if [condition] is true. | ||
79 | - void addIf(dynamic condition, E item) { | ||
80 | - if (condition is Condition) condition = condition(); | ||
81 | - if (condition is bool && condition) add(item); | ||
82 | - } | ||
83 | - | ||
84 | - /// Adds [Iterable<E>] to [List<E>] only if [condition] is true. | ||
85 | - void addAllIf(dynamic condition, Iterable<E> items) { | ||
86 | - if (condition is Condition) condition = condition(); | ||
87 | - if (condition is bool && condition) addAll(items); | ||
88 | - } | ||
89 | - | ||
90 | - @override | ||
91 | - void insert(int index, E item) { | ||
92 | - _list.insert(index, item); | ||
93 | - refresh(); | ||
94 | - } | ||
95 | - | ||
96 | - @override | ||
97 | - void insertAll(int index, Iterable<E> iterable) { | ||
98 | - _list.insertAll(index, iterable); | ||
99 | - refresh(); | ||
100 | - } | ||
101 | - | ||
102 | - @override | ||
103 | - int get length => value.length; | ||
104 | - | ||
105 | - /// Removes an item from the list. | ||
106 | - /// | ||
107 | - /// This is O(N) in the number of items in the list. | ||
108 | - /// | ||
109 | - /// Returns whether the item was present in the list. | ||
110 | - @override | ||
111 | - bool remove(Object item) { | ||
112 | - final hasRemoved = _list.remove(item); | ||
113 | - if (hasRemoved) { | ||
114 | - refresh(); | ||
115 | - } | ||
116 | - return hasRemoved; | ||
117 | - } | ||
118 | - | ||
119 | - @override | ||
120 | - E removeAt(int index) { | ||
121 | - final item = _list.removeAt(index); | ||
122 | - refresh(); | ||
123 | - return item; | ||
124 | - } | ||
125 | - | ||
126 | - @override | ||
127 | - E removeLast() { | ||
128 | - final item = _list.removeLast(); | ||
129 | - refresh(); | ||
130 | - return item; | ||
131 | - } | ||
132 | - | ||
133 | - @override | ||
134 | - void removeRange(int start, int end) { | ||
135 | - _list.removeRange(start, end); | ||
136 | - refresh(); | ||
137 | - } | ||
138 | - | ||
139 | - @override | ||
140 | - void removeWhere(bool Function(E) test) { | ||
141 | - _list.removeWhere(test); | ||
142 | - refresh(); | ||
143 | - } | ||
144 | - | ||
145 | - @override | ||
146 | - void clear() { | ||
147 | - _list.clear(); | ||
148 | - refresh(); | ||
149 | - } | ||
150 | - | ||
151 | - @override | ||
152 | - void sort([int compare(E a, E b)]) { | ||
153 | - _list.sort(compare); | ||
154 | - refresh(); | ||
155 | - } | ||
156 | - | ||
157 | - @override | ||
158 | - void close() { | ||
159 | - _subscriptions.forEach((observable, subscription) { | ||
160 | - subscription.cancel(); | ||
161 | - }); | ||
162 | - _subscriptions.clear(); | ||
163 | - subject.close(); | ||
164 | - } | ||
165 | - | ||
166 | - /// Replaces all existing items of this list with [item] | ||
167 | - void assign(E item) { | ||
168 | - clear(); | ||
169 | - add(item); | ||
170 | - } | ||
171 | - | ||
172 | - void update(void fn(Iterable<E> value)) { | ||
173 | - fn(value); | ||
174 | - refresh(); | ||
175 | - } | ||
176 | - | ||
177 | - /// Replaces all existing items of this list with [items] | ||
178 | - void assignAll(Iterable<E> items) { | ||
179 | - clear(); | ||
180 | - addAll(items); | ||
181 | - } | ||
182 | - | ||
183 | - @protected | ||
184 | - List<E> get value { | ||
185 | - if (getObs != null) { | ||
186 | - getObs.addListener(subject.stream); | ||
187 | - } | ||
188 | - return _list; | ||
189 | - } | ||
190 | - | ||
191 | - String get string => value.toString(); | ||
192 | - | ||
193 | - void addListener(Stream<List<E>> rxGetX) { | ||
194 | - if (_subscriptions.containsKey(rxGetX)) { | ||
195 | - return; | ||
196 | - } | ||
197 | - _subscriptions[rxGetX] = rxGetX.listen(subject.add); | ||
198 | - } | ||
199 | - | ||
200 | - set value(List<E> val) { | ||
201 | - if (_list == val) return; | ||
202 | - _list = val; | ||
203 | - refresh(); | ||
204 | - } | ||
205 | - | ||
206 | - Stream<List<E>> get stream => subject.stream; | ||
207 | - | ||
208 | - StreamSubscription<List<E>> listen( | ||
209 | - void Function(List<E>) onData, { | ||
210 | - Function onError, | ||
211 | - void Function() onDone, | ||
212 | - bool cancelOnError, | ||
213 | - }) => | ||
214 | - stream.listen(onData, onError: onError, onDone: onDone); | ||
215 | - | ||
216 | - /// Binds an existing [Stream<List>] to this [RxList]. | ||
217 | - /// You can bind multiple sources to update the value. | ||
218 | - /// Closing the subscription will happen automatically when the observer | ||
219 | - /// Widget ([GetX] or [Obx]) gets unmounted from the Widget tree. | ||
220 | - void bindStream(Stream<List<E>> stream) { | ||
221 | - _subscriptions[stream] = stream.listen((va) => value = va); | ||
222 | - } | ||
223 | - | ||
224 | - @override | ||
225 | - E get first => value.first; | ||
226 | - | ||
227 | - @override | ||
228 | - E get last => value.last; | ||
229 | - | ||
230 | - @override | ||
231 | - bool any(bool Function(E) test) { | ||
232 | - return value.any(test); | ||
233 | - } | ||
234 | - | ||
235 | - @override | ||
236 | - Map<int, E> asMap() { | ||
237 | - return value.asMap(); | ||
238 | - } | ||
239 | - | ||
240 | - @override | ||
241 | - List<R> cast<R>() { | ||
242 | - return value.cast<R>(); | ||
243 | - } | ||
244 | - | ||
245 | - @override | ||
246 | - bool contains(Object element) { | ||
247 | - return value.contains(element); | ||
248 | - } | ||
249 | - | ||
250 | - @override | ||
251 | - E elementAt(int index) { | ||
252 | - return value.elementAt(index); | ||
253 | - } | ||
254 | - | ||
255 | - @override | ||
256 | - bool every(bool Function(E) test) { | ||
257 | - return value.every(test); | ||
258 | - } | ||
259 | - | ||
260 | - @override | ||
261 | - Iterable<T> expand<T>(Iterable<T> Function(E) f) { | ||
262 | - return value.expand(f); | ||
263 | - } | ||
264 | - | ||
265 | - @override | ||
266 | - void fillRange(int start, int end, [E fillValue]) { | ||
267 | - _list.fillRange(start, end, fillValue); | ||
268 | - refresh(); | ||
269 | - } | ||
270 | - | ||
271 | - @override | ||
272 | - E firstWhere(bool Function(E) test, {E Function() orElse}) { | ||
273 | - return value.firstWhere(test, orElse: orElse); | ||
274 | - } | ||
275 | - | ||
276 | - @override | ||
277 | - T fold<T>(T initialValue, T Function(T, E) combine) { | ||
278 | - return value.fold(initialValue, combine); | ||
279 | - } | ||
280 | - | ||
281 | - @override | ||
282 | - Iterable<E> followedBy(Iterable<E> other) { | ||
283 | - return value.followedBy(other); | ||
284 | - } | ||
285 | - | ||
286 | - @override | ||
287 | - void forEach(void Function(E) f) { | ||
288 | - value.forEach(f); | ||
289 | - } | ||
290 | - | ||
291 | - @override | ||
292 | - Iterable<E> getRange(int start, int end) { | ||
293 | - return value.getRange(start, end); | ||
294 | - } | ||
295 | - | ||
296 | - @override | ||
297 | - int indexOf(E element, [int start = 0]) { | ||
298 | - return value.indexOf(element, start); | ||
299 | - } | ||
300 | - | ||
301 | - @override | ||
302 | - int indexWhere(bool Function(E) test, [int start = 0]) { | ||
303 | - return value.indexWhere(test, start); | ||
304 | - } | ||
305 | - | ||
306 | - @override | ||
307 | - String join([String separator = ""]) { | ||
308 | - return value.join(separator); | ||
309 | - } | ||
310 | - | ||
311 | - @override | ||
312 | - int lastIndexOf(E element, [int start]) { | ||
313 | - return value.lastIndexOf(element, start); | ||
314 | - } | ||
315 | - | ||
316 | - @override | ||
317 | - int lastIndexWhere(bool Function(E) test, [int start]) { | ||
318 | - return value.lastIndexWhere(test, start); | ||
319 | - } | ||
320 | - | ||
321 | - @override | ||
322 | - E lastWhere(bool Function(E) test, {E Function() orElse}) { | ||
323 | - return value.lastWhere(test, orElse: orElse); | ||
324 | - } | ||
325 | - | ||
326 | - @override | ||
327 | - set length(int newLength) { | ||
328 | - _list.length = newLength; | ||
329 | - refresh(); | ||
330 | - } | ||
331 | - | ||
332 | - @override | ||
333 | - Iterable<T> map<T>(T Function(E) f) { | ||
334 | - return value.map(f); | ||
335 | - } | ||
336 | - | ||
337 | - @override | ||
338 | - E reduce(E Function(E, E) combine) { | ||
339 | - return value.reduce(combine); | ||
340 | - } | ||
341 | - | ||
342 | - @override | ||
343 | - void replaceRange(int start, int end, Iterable<E> replacement) { | ||
344 | - _list.replaceRange(start, end, replacement); | ||
345 | - refresh(); | ||
346 | - } | ||
347 | - | ||
348 | - @override | ||
349 | - void retainWhere(bool Function(E) test) { | ||
350 | - _list.retainWhere(test); | ||
351 | - refresh(); | ||
352 | - } | ||
353 | - | ||
354 | - @override | ||
355 | - Iterable<E> get reversed => value.reversed; | ||
356 | - | ||
357 | - @override | ||
358 | - void setAll(int index, Iterable<E> iterable) { | ||
359 | - _list.setAll(index, iterable); | ||
360 | - refresh(); | ||
361 | - } | ||
362 | - | ||
363 | - @override | ||
364 | - void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) { | ||
365 | - _list.setRange(start, end, iterable, skipCount); | ||
366 | - refresh(); | ||
367 | - } | ||
368 | - | ||
369 | - @override | ||
370 | - void shuffle([Random random]) { | ||
371 | - _list.shuffle(random); | ||
372 | - refresh(); | ||
373 | - } | ||
374 | - | ||
375 | - @override | ||
376 | - E get single => value.single; | ||
377 | - | ||
378 | - @override | ||
379 | - E singleWhere(bool Function(E) test, {E Function() orElse}) { | ||
380 | - return value.singleWhere(test, orElse: orElse); | ||
381 | - } | ||
382 | - | ||
383 | - @override | ||
384 | - Iterable<E> skip(int count) { | ||
385 | - return value.skip(count); | ||
386 | - } | ||
387 | - | ||
388 | - @override | ||
389 | - Iterable<E> skipWhile(bool Function(E) test) { | ||
390 | - return value.skipWhile(test); | ||
391 | - } | ||
392 | - | ||
393 | - @override | ||
394 | - List<E> sublist(int start, [int end]) { | ||
395 | - return value.sublist(start, end); | ||
396 | - } | ||
397 | - | ||
398 | - @override | ||
399 | - Iterable<E> take(int count) { | ||
400 | - return value.take(count); | ||
401 | - } | ||
402 | - | ||
403 | - @override | ||
404 | - Iterable<E> takeWhile(bool Function(E) test) { | ||
405 | - return value.takeWhile(test); | ||
406 | - } | ||
407 | - | ||
408 | - @override | ||
409 | - List<E> toList({bool growable = true}) { | ||
410 | - return value.toList(growable: growable); | ||
411 | - } | ||
412 | - | ||
413 | - @override | ||
414 | - Set<E> toSet() { | ||
415 | - return value.toSet(); | ||
416 | - } | ||
417 | - | ||
418 | - @override | ||
419 | - Iterable<E> where(bool Function(E) test) { | ||
420 | - return value.where(test); | ||
421 | - } | ||
422 | - | ||
423 | - @override | ||
424 | - Iterable<T> whereType<T>() { | ||
425 | - return value.whereType<T>(); | ||
426 | - } | ||
427 | - | ||
428 | - @override | ||
429 | - set first(E value) { | ||
430 | - _list.first = value; | ||
431 | - refresh(); | ||
432 | - } | ||
433 | - | ||
434 | - @override | ||
435 | - set last(E value) { | ||
436 | - _list.last = value; | ||
437 | - refresh(); | ||
438 | - } | ||
439 | -} | ||
440 | - | ||
441 | -extension ListExtension<E> on List<E> { | ||
442 | - RxList<E> get obs { | ||
443 | - if (this != null) { | ||
444 | - return RxList<E>(<E>[])..addAllNonNull(this); | ||
445 | - } else { | ||
446 | - return RxList<E>(null); | ||
447 | - } | ||
448 | - } | ||
449 | -} |
1 | -import 'dart:async'; | ||
2 | -import 'dart:collection'; | ||
3 | -import 'package:flutter/foundation.dart'; | ||
4 | - | ||
5 | -import '../rx_core/rx_impl.dart'; | ||
6 | -import '../rx_core/rx_interface.dart'; | ||
7 | -import '../rx_typedefs/rx_typedefs.dart'; | ||
8 | - | ||
9 | -class RxSet<E> implements Set<E>, RxInterface<Set<E>> { | ||
10 | - RxSet([Set<E> initial]) { | ||
11 | - if (initial != null) _set = initial; | ||
12 | - } | ||
13 | - | ||
14 | - Set<E> _set = <E>{}; | ||
15 | - | ||
16 | - @override | ||
17 | - Iterator<E> get iterator => value.iterator; | ||
18 | - | ||
19 | - @override | ||
20 | - bool get isEmpty => value.isEmpty; | ||
21 | - | ||
22 | - bool get canUpdate { | ||
23 | - return _subscriptions.length > 0; | ||
24 | - } | ||
25 | - | ||
26 | - @override | ||
27 | - bool get isNotEmpty => value.isNotEmpty; | ||
28 | - | ||
29 | - StreamController<Set<E>> subject = StreamController<Set<E>>.broadcast(); | ||
30 | - final _subscriptions = HashMap<Stream<Set<E>>, StreamSubscription>(); | ||
31 | - | ||
32 | - /// Adds [item] only if [condition] resolves to true. | ||
33 | - void addIf(dynamic condition, E item) { | ||
34 | - if (condition is Condition) condition = condition(); | ||
35 | - if (condition is bool && condition) add(item); | ||
36 | - } | ||
37 | - | ||
38 | - /// Adds all [items] only if [condition] resolves to true. | ||
39 | - void addAllIf(dynamic condition, Iterable<E> items) { | ||
40 | - if (condition is Condition) condition = condition(); | ||
41 | - if (condition is bool && condition) addAll(items); | ||
42 | - } | ||
43 | - | ||
44 | - void refresh() { | ||
45 | - subject.add(_set); | ||
46 | - } | ||
47 | - | ||
48 | - /// Special override to push() element(s) in a reactive way | ||
49 | - /// inside the List, | ||
50 | - RxSet<E> operator +(Set<E> val) { | ||
51 | - addAll(val); | ||
52 | - refresh(); | ||
53 | - return this; | ||
54 | - } | ||
55 | - | ||
56 | - @override | ||
57 | - bool add(E value) { | ||
58 | - final val = _set.add(value); | ||
59 | - refresh(); | ||
60 | - return val; | ||
61 | - } | ||
62 | - | ||
63 | - @override | ||
64 | - void addAll(Iterable<E> item) { | ||
65 | - _set.addAll(item); | ||
66 | - refresh(); | ||
67 | - } | ||
68 | - | ||
69 | - /// Adds only if [item] is not null. | ||
70 | - void addNonNull(E item) { | ||
71 | - if (item != null) add(item); | ||
72 | - } | ||
73 | - | ||
74 | - /// Adds only if [item] is not null. | ||
75 | - void addAllNonNull(Iterable<E> item) { | ||
76 | - if (item != null) addAll(item); | ||
77 | - } | ||
78 | - | ||
79 | - int get length => value.length; | ||
80 | - | ||
81 | - /// Removes an item from the list. | ||
82 | - /// | ||
83 | - /// This is O(N) in the number of items in the list. | ||
84 | - /// | ||
85 | - /// Returns whether the item was present in the list. | ||
86 | - bool remove(Object item) { | ||
87 | - var hasRemoved = _set.remove(item); | ||
88 | - if (hasRemoved) { | ||
89 | - refresh(); | ||
90 | - } | ||
91 | - return hasRemoved; | ||
92 | - } | ||
93 | - | ||
94 | - void removeWhere(bool Function(E) test) { | ||
95 | - _set.removeWhere(test); | ||
96 | - refresh(); | ||
97 | - } | ||
98 | - | ||
99 | - void clear() { | ||
100 | - _set.clear(); | ||
101 | - refresh(); | ||
102 | - } | ||
103 | - | ||
104 | - void close() { | ||
105 | - _subscriptions.forEach((observable, subscription) { | ||
106 | - subscription.cancel(); | ||
107 | - }); | ||
108 | - _subscriptions.clear(); | ||
109 | - subject.close(); | ||
110 | - } | ||
111 | - | ||
112 | - /// Replaces all existing items of this list with [item] | ||
113 | - void assign(E item) { | ||
114 | - clear(); | ||
115 | - add(item); | ||
116 | - } | ||
117 | - | ||
118 | - void update(void fn(Iterable<E> value)) { | ||
119 | - fn(value); | ||
120 | - refresh(); | ||
121 | - } | ||
122 | - | ||
123 | - /// Replaces all existing items of this list with [items] | ||
124 | - void assignAll(Iterable<E> items) { | ||
125 | - clear(); | ||
126 | - addAll(items); | ||
127 | - } | ||
128 | - | ||
129 | - @protected | ||
130 | - Set<E> get value { | ||
131 | - if (getObs != null) { | ||
132 | - getObs.addListener(subject.stream); | ||
133 | - } | ||
134 | - return _set; | ||
135 | - } | ||
136 | - | ||
137 | - String get string => value.toString(); | ||
138 | - | ||
139 | - void addListener(Stream<Set<E>> rxGetX) { | ||
140 | - if (_subscriptions.containsKey(rxGetX)) { | ||
141 | - return; | ||
142 | - } | ||
143 | - _subscriptions[rxGetX] = rxGetX.listen((data) { | ||
144 | - subject.add(data); | ||
145 | - }); | ||
146 | - } | ||
147 | - | ||
148 | - set value(Set<E> val) { | ||
149 | - if (_set == val) return; | ||
150 | - _set = val; | ||
151 | - refresh(); | ||
152 | - } | ||
153 | - | ||
154 | - Stream<Set<E>> get stream => subject.stream; | ||
155 | - | ||
156 | - StreamSubscription<Set<E>> listen(void Function(Set<E>) onData, | ||
157 | - {Function onError, void Function() onDone, bool cancelOnError}) => | ||
158 | - stream.listen(onData, onError: onError, onDone: onDone); | ||
159 | - | ||
160 | - /// Binds an existing [Stream<Set>] to this [RxSet]. | ||
161 | - /// You can bind multiple sources to update the value. | ||
162 | - /// Closing the subscription will happen automatically when the observer | ||
163 | - /// Widget ([GetX] or [Obx]) gets unmounted from the Widget tree. | ||
164 | - void bindStream(Stream<Set<E>> stream) { | ||
165 | - _subscriptions[stream] = stream.listen((va) => value = va); | ||
166 | - } | ||
167 | - | ||
168 | - @override | ||
169 | - E get first => value.first; | ||
170 | - | ||
171 | - @override | ||
172 | - E get last => value.last; | ||
173 | - | ||
174 | - @override | ||
175 | - bool any(bool Function(E) test) { | ||
176 | - return value.any(test); | ||
177 | - } | ||
178 | - | ||
179 | - @override | ||
180 | - Set<R> cast<R>() { | ||
181 | - return value.cast<R>(); | ||
182 | - } | ||
183 | - | ||
184 | - @override | ||
185 | - bool contains(Object element) { | ||
186 | - return value.contains(element); | ||
187 | - } | ||
188 | - | ||
189 | - @override | ||
190 | - E elementAt(int index) { | ||
191 | - return value.elementAt(index); | ||
192 | - } | ||
193 | - | ||
194 | - @override | ||
195 | - bool every(bool Function(E) test) { | ||
196 | - return value.every(test); | ||
197 | - } | ||
198 | - | ||
199 | - @override | ||
200 | - Iterable<T> expand<T>(Iterable<T> Function(E) f) { | ||
201 | - return value.expand(f); | ||
202 | - } | ||
203 | - | ||
204 | - @override | ||
205 | - E firstWhere(bool Function(E) test, {E Function() orElse}) { | ||
206 | - return value.firstWhere(test, orElse: orElse); | ||
207 | - } | ||
208 | - | ||
209 | - @override | ||
210 | - T fold<T>(T initialValue, T Function(T, E) combine) { | ||
211 | - return value.fold(initialValue, combine); | ||
212 | - } | ||
213 | - | ||
214 | - @override | ||
215 | - Iterable<E> followedBy(Iterable<E> other) { | ||
216 | - return value.followedBy(other); | ||
217 | - } | ||
218 | - | ||
219 | - @override | ||
220 | - void forEach(void Function(E) f) { | ||
221 | - value.forEach(f); | ||
222 | - } | ||
223 | - | ||
224 | - @override | ||
225 | - String join([String separator = ""]) { | ||
226 | - return value.join(separator); | ||
227 | - } | ||
228 | - | ||
229 | - @override | ||
230 | - E lastWhere(bool Function(E) test, {E Function() orElse}) { | ||
231 | - return value.lastWhere(test, orElse: orElse); | ||
232 | - } | ||
233 | - | ||
234 | - @override | ||
235 | - Iterable<T> map<T>(T Function(E) f) { | ||
236 | - return value.map(f); | ||
237 | - } | ||
238 | - | ||
239 | - @override | ||
240 | - E reduce(E Function(E, E) combine) { | ||
241 | - return value.reduce(combine); | ||
242 | - } | ||
243 | - | ||
244 | - @override | ||
245 | - E get single => value.single; | ||
246 | - | ||
247 | - @override | ||
248 | - E singleWhere(bool Function(E) test, {E Function() orElse}) { | ||
249 | - return value.singleWhere(test, orElse: orElse); | ||
250 | - } | ||
251 | - | ||
252 | - @override | ||
253 | - Iterable<E> skip(int count) { | ||
254 | - return value.skip(count); | ||
255 | - } | ||
256 | - | ||
257 | - @override | ||
258 | - Iterable<E> skipWhile(bool Function(E) test) { | ||
259 | - return value.skipWhile(test); | ||
260 | - } | ||
261 | - | ||
262 | - @override | ||
263 | - Iterable<E> take(int count) { | ||
264 | - return value.take(count); | ||
265 | - } | ||
266 | - | ||
267 | - @override | ||
268 | - Iterable<E> takeWhile(bool Function(E) test) { | ||
269 | - return value.takeWhile(test); | ||
270 | - } | ||
271 | - | ||
272 | - @override | ||
273 | - List<E> toList({bool growable = true}) { | ||
274 | - return value.toList(growable: growable); | ||
275 | - } | ||
276 | - | ||
277 | - @override | ||
278 | - Set<E> toSet() { | ||
279 | - return value.toSet(); | ||
280 | - } | ||
281 | - | ||
282 | - @override | ||
283 | - Iterable<E> where(bool Function(E) test) { | ||
284 | - return value.where(test); | ||
285 | - } | ||
286 | - | ||
287 | - @override | ||
288 | - Iterable<T> whereType<T>() { | ||
289 | - return value.whereType<T>(); | ||
290 | - } | ||
291 | - | ||
292 | - @override | ||
293 | - bool containsAll(Iterable<Object> other) { | ||
294 | - return value.containsAll(other); | ||
295 | - } | ||
296 | - | ||
297 | - @override | ||
298 | - Set<E> difference(Set<Object> other) { | ||
299 | - return value.difference(other); | ||
300 | - } | ||
301 | - | ||
302 | - @override | ||
303 | - Set<E> intersection(Set<Object> other) { | ||
304 | - return value.intersection(other); | ||
305 | - } | ||
306 | - | ||
307 | - @override | ||
308 | - E lookup(Object object) { | ||
309 | - return value.lookup(object); | ||
310 | - } | ||
311 | - | ||
312 | - @override | ||
313 | - void removeAll(Iterable<Object> elements) { | ||
314 | - _set.removeAll(elements); | ||
315 | - refresh(); | ||
316 | - } | ||
317 | - | ||
318 | - @override | ||
319 | - void retainAll(Iterable<Object> elements) { | ||
320 | - _set.retainAll(elements); | ||
321 | - refresh(); | ||
322 | - } | ||
323 | - | ||
324 | - @override | ||
325 | - void retainWhere(bool Function(E) E) { | ||
326 | - _set.retainWhere(E); | ||
327 | - refresh(); | ||
328 | - } | ||
329 | - | ||
330 | - @override | ||
331 | - Set<E> union(Set<E> other) { | ||
332 | - return value.union(other); | ||
333 | - } | ||
334 | -} | ||
335 | - | ||
336 | -extension SetExtension<E> on Set<E> { | ||
337 | - RxSet<E> get obs { | ||
338 | - if (this != null) { | ||
339 | - return RxSet<E>(<E>{})..addAllNonNull(this); | ||
340 | - } else { | ||
341 | - return RxSet<E>(null); | ||
342 | - } | ||
343 | - } | ||
344 | -} |
lib/get_rx/src/rx_stream/get_stream.dart
0 → 100644
1 | +part of rx_stream; | ||
2 | + | ||
3 | +/// [GetStream] is the lightest and most performative way of working | ||
4 | +/// with events at Dart. You sintaxe is like StreamController, but it works | ||
5 | +/// with simple callbacks. In this way, every event calls only one function. | ||
6 | +/// There is no buffering, to very low memory consumption. | ||
7 | +/// event [add] will add a object to stream. [addError] will add a error | ||
8 | +/// to stream. [listen] is a very light StreamSubscription interface. | ||
9 | +/// Is possible take the last value with [value] property. | ||
10 | +class GetStream<T> { | ||
11 | + LightListenable<T> listenable = LightListenable<T>(); | ||
12 | + | ||
13 | + T _value; | ||
14 | + | ||
15 | + T get value => _value; | ||
16 | + | ||
17 | + void add(T event) { | ||
18 | + _value = event; | ||
19 | + _checkIfDisposed(); | ||
20 | + listenable.notifyData(event); | ||
21 | + } | ||
22 | + | ||
23 | + void _checkIfDisposed([bool isClosed = false]) { | ||
24 | + if (listenable == null) { | ||
25 | + throw '''[LightStream] Error: | ||
26 | +You cannot ${isClosed ? "close" : "add events to"} a closed stream.'''; | ||
27 | + } | ||
28 | + } | ||
29 | + | ||
30 | + void addError(Object error, [StackTrace stackTrace]) { | ||
31 | + _checkIfDisposed(); | ||
32 | + listenable.notifyError(error, stackTrace); | ||
33 | + } | ||
34 | + | ||
35 | + void close() { | ||
36 | + _checkIfDisposed(true); | ||
37 | + listenable.notifyDone(); | ||
38 | + listenable.dispose(); | ||
39 | + listenable = null; | ||
40 | + _value = null; | ||
41 | + } | ||
42 | + | ||
43 | + int get length => listenable.length; | ||
44 | + | ||
45 | + bool get hasListeners => listenable.hasListeners; | ||
46 | + | ||
47 | + bool get isClosed => listenable == null; | ||
48 | + | ||
49 | + LightSubscription<T> listen(void Function(T event) onData, | ||
50 | + {Function onError, void Function() onDone, bool cancelOnError}) { | ||
51 | + final subs = LightSubscription<T>(listenable) | ||
52 | + ..onData(onData) | ||
53 | + ..onError(onError) | ||
54 | + ..onDone(onDone); | ||
55 | + listenable.addSubscription(subs); | ||
56 | + return subs; | ||
57 | + } | ||
58 | + | ||
59 | + Stream<T> get stream => GetStreamTransformation(listenable); | ||
60 | +} | ||
61 | + | ||
62 | +class LightListenable<T> { | ||
63 | + List<LightSubscription<T>> _onData = <LightSubscription<T>>[]; | ||
64 | + | ||
65 | + bool _isBusy = false; | ||
66 | + | ||
67 | + FutureOr<bool> removeSubscription(LightSubscription<T> subs) async { | ||
68 | + if (!_isBusy) { | ||
69 | + return _onData.remove(subs); | ||
70 | + } else { | ||
71 | + await Future.delayed(Duration.zero); | ||
72 | + return _onData.remove(subs); | ||
73 | + } | ||
74 | + } | ||
75 | + | ||
76 | + FutureOr<void> addSubscription(LightSubscription<T> subs) async { | ||
77 | + if (!_isBusy) { | ||
78 | + return _onData.add(subs); | ||
79 | + } else { | ||
80 | + await Future.delayed(Duration.zero); | ||
81 | + return _onData.add(subs); | ||
82 | + } | ||
83 | + } | ||
84 | + | ||
85 | + int get length => _onData?.length; | ||
86 | + | ||
87 | + bool get hasListeners => _onData.isNotEmpty; | ||
88 | + | ||
89 | + void notifyData(T data) { | ||
90 | + assert(!isDisposed, 'You cannot add data to a closed stream.'); | ||
91 | + _isBusy = true; | ||
92 | + for (final item in _onData) { | ||
93 | + if (item.isPaused) { | ||
94 | + break; | ||
95 | + } | ||
96 | + item._data?.call(data); | ||
97 | + } | ||
98 | + _isBusy = false; | ||
99 | + } | ||
100 | + | ||
101 | + void notifyError(Object error, [StackTrace stackTrace]) { | ||
102 | + assert(!isDisposed, 'You cannot add errors to a closed stream.'); | ||
103 | + _isBusy = true; | ||
104 | + for (final item in _onData) { | ||
105 | + if (item.isPaused) { | ||
106 | + break; | ||
107 | + } | ||
108 | + item._onError?.call(error, stackTrace); | ||
109 | + if (item.cancelOnError) { | ||
110 | + item.cancel?.call(); | ||
111 | + item._onDone?.call(); | ||
112 | + } | ||
113 | + } | ||
114 | + _isBusy = false; | ||
115 | + } | ||
116 | + | ||
117 | + void notifyDone() { | ||
118 | + assert(!isDisposed, 'You cannot close a closed stream.'); | ||
119 | + _isBusy = true; | ||
120 | + for (final item in _onData) { | ||
121 | + if (item.isPaused) { | ||
122 | + break; | ||
123 | + } | ||
124 | + item._onDone?.call(); | ||
125 | + } | ||
126 | + _isBusy = false; | ||
127 | + } | ||
128 | + | ||
129 | + void dispose() { | ||
130 | + _onData = null; | ||
131 | + _isBusy = null; | ||
132 | + } | ||
133 | + | ||
134 | + bool get isDisposed => _onData == null; | ||
135 | +} | ||
136 | + | ||
137 | +class LightSubscription<T> extends StreamSubscription<T> { | ||
138 | + final LightListenable<T> listener; | ||
139 | + | ||
140 | + LightSubscription(this.listener); | ||
141 | + | ||
142 | + bool cancelOnError = false; | ||
143 | + | ||
144 | + @override | ||
145 | + Future<void> cancel() { | ||
146 | + listener.removeSubscription(this); | ||
147 | + return Future.value(); | ||
148 | + } | ||
149 | + | ||
150 | + OnData<T> _data; | ||
151 | + | ||
152 | + Function _onError; | ||
153 | + | ||
154 | + Callback _onDone; | ||
155 | + | ||
156 | + bool _isPaused = false; | ||
157 | + | ||
158 | + @override | ||
159 | + void onData(OnData<T> handleData) => _data = handleData; | ||
160 | + | ||
161 | + @override | ||
162 | + void onError(Function handleError) => _onError = handleError; | ||
163 | + | ||
164 | + @override | ||
165 | + void onDone(Callback handleDone) => _onDone = handleDone; | ||
166 | + | ||
167 | + @override | ||
168 | + void pause([Future<void> resumeSignal]) => _isPaused = true; | ||
169 | + | ||
170 | + @override | ||
171 | + void resume() => _isPaused = false; | ||
172 | + | ||
173 | + @override | ||
174 | + bool get isPaused => _isPaused; | ||
175 | + | ||
176 | + @override | ||
177 | + Future<E> asFuture<E>([E futureValue]) => Future.value(futureValue); | ||
178 | +} | ||
179 | + | ||
180 | +class GetStreamTransformation<T> extends Stream<T> { | ||
181 | + final LightListenable<T> listenable; | ||
182 | + | ||
183 | + GetStreamTransformation(this.listenable); | ||
184 | + | ||
185 | + @override | ||
186 | + LightSubscription<T> listen(void Function(T event) onData, | ||
187 | + {Function onError, void Function() onDone, bool cancelOnError}) { | ||
188 | + final subs = LightSubscription<T>(listenable) | ||
189 | + ..onData(onData) | ||
190 | + ..onError(onError) | ||
191 | + ..onDone(onDone); | ||
192 | + listenable.addSubscription(subs); | ||
193 | + return subs; | ||
194 | + } | ||
195 | +} |
lib/get_rx/src/rx_stream/mini_stream.dart
0 → 100644
1 | +part of rx_stream; | ||
2 | + | ||
3 | +class Node<T> { | ||
4 | + T data; | ||
5 | + Node<T> next; | ||
6 | + Node({this.data, this.next}); | ||
7 | +} | ||
8 | + | ||
9 | +class MiniSubscription<T> { | ||
10 | + const MiniSubscription( | ||
11 | + this.data, this.onError, this.onDone, this.cancelOnError, this.listener); | ||
12 | + final OnData<T> data; | ||
13 | + final Function onError; | ||
14 | + final Callback onDone; | ||
15 | + final bool cancelOnError; | ||
16 | + | ||
17 | + Future<void> cancel() async => listener.removeListener(this); | ||
18 | + | ||
19 | + final FastList<T> listener; | ||
20 | +} | ||
21 | + | ||
22 | +class MiniStream<T> { | ||
23 | + FastList<T> listenable = FastList<T>(); | ||
24 | + | ||
25 | + T _value; | ||
26 | + | ||
27 | + T get value => _value; | ||
28 | + | ||
29 | + set value(T val) { | ||
30 | + add(val); | ||
31 | + } | ||
32 | + | ||
33 | + void add(T event) { | ||
34 | + assert(listenable != null); | ||
35 | + _value = event; | ||
36 | + listenable._notifyData(event); | ||
37 | + } | ||
38 | + | ||
39 | + void addError(Object error, [StackTrace stackTrace]) { | ||
40 | + assert(listenable != null); | ||
41 | + listenable._notifyError(error, stackTrace); | ||
42 | + } | ||
43 | + | ||
44 | + int get length => listenable.length; | ||
45 | + | ||
46 | + bool get hasListeners => listenable.isNotEmpty; | ||
47 | + | ||
48 | + bool get isClosed => listenable == null; | ||
49 | + | ||
50 | + MiniSubscription<T> listen(void Function(T event) onData, | ||
51 | + {Function onError, void Function() onDone, bool cancelOnError = false}) { | ||
52 | + final subs = MiniSubscription<T>( | ||
53 | + onData, | ||
54 | + onError, | ||
55 | + onDone, | ||
56 | + cancelOnError, | ||
57 | + listenable, | ||
58 | + ); | ||
59 | + listenable.addListener(subs); | ||
60 | + return subs; | ||
61 | + } | ||
62 | + | ||
63 | + void close() { | ||
64 | + if (listenable == null) { | ||
65 | + throw 'You can not close a closed Stream'; | ||
66 | + } | ||
67 | + listenable._notifyDone(); | ||
68 | + listenable = null; | ||
69 | + _value = null; | ||
70 | + } | ||
71 | +} | ||
72 | + | ||
73 | +class FastList<T> { | ||
74 | + Node<MiniSubscription<T>> _head; | ||
75 | + | ||
76 | + void _notifyData(T data) { | ||
77 | + var currentNode = _head; | ||
78 | + do { | ||
79 | + currentNode.data.data(data); | ||
80 | + currentNode = currentNode.next; | ||
81 | + } while (currentNode != null); | ||
82 | + } | ||
83 | + | ||
84 | + void _notifyDone() { | ||
85 | + var currentNode = _head; | ||
86 | + do { | ||
87 | + currentNode.data.onDone?.call(); | ||
88 | + currentNode = currentNode.next; | ||
89 | + } while (currentNode != null); | ||
90 | + } | ||
91 | + | ||
92 | + void _notifyError(Object error, [StackTrace stackTrace]) { | ||
93 | + var currentNode = _head; | ||
94 | + while (currentNode != null) { | ||
95 | + currentNode.data.onError?.call(error, stackTrace); | ||
96 | + currentNode = currentNode.next; | ||
97 | + } | ||
98 | + } | ||
99 | + | ||
100 | + /// Checks if this list is empty | ||
101 | + bool get isEmpty => _head == null; | ||
102 | + | ||
103 | + bool get isNotEmpty => !isEmpty; | ||
104 | + | ||
105 | + /// Returns the length of this list | ||
106 | + int get length { | ||
107 | + var length = 0; | ||
108 | + var currentNode = _head; | ||
109 | + | ||
110 | + while (currentNode != null) { | ||
111 | + currentNode = currentNode.next; | ||
112 | + length++; | ||
113 | + } | ||
114 | + return length; | ||
115 | + } | ||
116 | + | ||
117 | + /// Shows the element at position [position]. `null` for invalid positions. | ||
118 | + MiniSubscription<T> _elementAt(int position) { | ||
119 | + if (isEmpty || length < position || position < 0) return null; | ||
120 | + | ||
121 | + var node = _head; | ||
122 | + var current = 0; | ||
123 | + | ||
124 | + while (current != position) { | ||
125 | + node = node.next; | ||
126 | + current++; | ||
127 | + } | ||
128 | + return node.data; | ||
129 | + } | ||
130 | + | ||
131 | + /// Inserts [data] at the end of the list. | ||
132 | + void addListener(MiniSubscription<T> data) { | ||
133 | + var newNode = Node(data: data); | ||
134 | + | ||
135 | + if (isEmpty) { | ||
136 | + _head = newNode; | ||
137 | + } else { | ||
138 | + var currentNode = _head; | ||
139 | + while (currentNode.next != null) { | ||
140 | + currentNode = currentNode.next; | ||
141 | + } | ||
142 | + currentNode.next = newNode; | ||
143 | + } | ||
144 | + } | ||
145 | + | ||
146 | + bool contains(T element) { | ||
147 | + var length = this.length; | ||
148 | + for (var i = 0; i < length; i++) { | ||
149 | + if (_elementAt(i) == element) return true; | ||
150 | + if (length != this.length) { | ||
151 | + throw ConcurrentModificationError(this); | ||
152 | + } | ||
153 | + } | ||
154 | + return false; | ||
155 | + } | ||
156 | + | ||
157 | + void removeListener(MiniSubscription<T> element) { | ||
158 | + var length = this.length; | ||
159 | + for (var i = 0; i < length; i++) { | ||
160 | + if (_elementAt(i) == element) { | ||
161 | + _removeAt(i); | ||
162 | + break; | ||
163 | + } | ||
164 | + } | ||
165 | + } | ||
166 | + | ||
167 | + MiniSubscription<T> _removeAt(int position) { | ||
168 | + var index = 0; | ||
169 | + var currentNode = _head; | ||
170 | + Node<MiniSubscription<T>> previousNode; | ||
171 | + | ||
172 | + if (isEmpty || length < position || position < 0) { | ||
173 | + throw Exception('Invalid position'); | ||
174 | + } else if (position == 0) { | ||
175 | + _head = _head.next; | ||
176 | + } else { | ||
177 | + while (index != position) { | ||
178 | + previousNode = currentNode; | ||
179 | + currentNode = currentNode.next; | ||
180 | + index++; | ||
181 | + } | ||
182 | + | ||
183 | + if (previousNode == null) { | ||
184 | + _head = null; | ||
185 | + } else { | ||
186 | + previousNode.next = currentNode.next; | ||
187 | + } | ||
188 | + | ||
189 | + currentNode.next = null; | ||
190 | + } | ||
191 | + | ||
192 | + return currentNode.data; | ||
193 | + } | ||
194 | +} |
lib/get_rx/src/rx_stream/rx_stream.dart
0 → 100644
1 | -import 'dart:async'; | ||
2 | -import 'dart:collection'; | ||
3 | -import '../rx_core/rx_interface.dart'; | ||
4 | -part 'rx_num.dart'; | 1 | +part of rx_types; |
5 | 2 | ||
6 | /// global object that registers against `GetX` and `Obx`, and allows the | 3 | /// global object that registers against `GetX` and `Obx`, and allows the |
7 | /// reactivity | 4 | /// reactivity |
8 | /// of those `Widgets` and Rx values. | 5 | /// of those `Widgets` and Rx values. |
9 | RxInterface getObs; | 6 | RxInterface getObs; |
10 | 7 | ||
11 | -/// Base Rx class that manages all the stream logic for any Type. | ||
12 | -abstract class _RxImpl<T> implements RxInterface<T> { | ||
13 | - _RxImpl(T initial) { | ||
14 | - _value = initial; | ||
15 | - } | ||
16 | - StreamController<T> subject = StreamController<T>.broadcast(); | ||
17 | - final _subscriptions = HashMap<Stream<T>, StreamSubscription>(); | ||
18 | - | ||
19 | - T _value; | ||
20 | - | 8 | +mixin RxObjectMixin<T> { |
9 | + GetStream<T> subject = GetStream<T>(); | ||
10 | + final _subscriptions = <StreamSubscription>[]; | ||
21 | bool get canUpdate => _subscriptions.isNotEmpty; | 11 | bool get canUpdate => _subscriptions.isNotEmpty; |
22 | 12 | ||
23 | - /// Makes this Rx looks like a function so you can update a new | ||
24 | - /// value using [rx(someOtherValue)]. Practical to assign the Rx directly | ||
25 | - /// to some Widget that has a signature ::onChange( value ) | ||
26 | - /// | ||
27 | - /// Example: | ||
28 | - /// ``` | ||
29 | - /// final myText = 'GetX rocks!'.obs; | ||
30 | - /// | ||
31 | - /// // in your Constructor, just to check it works :P | ||
32 | - /// ever( myText, print ) ; | ||
33 | - /// | ||
34 | - /// // in your build(BuildContext) { | ||
35 | - /// TextField( | ||
36 | - /// onChanged: myText, | ||
37 | - /// ), | ||
38 | - ///``` | ||
39 | - T call([T v]) { | ||
40 | - if (v != null) { | ||
41 | - value = v; | ||
42 | - } | ||
43 | - return value; | ||
44 | - } | 13 | + T _value; |
45 | 14 | ||
46 | /// Makes a direct update of [value] adding it to the Stream | 15 | /// Makes a direct update of [value] adding it to the Stream |
47 | /// useful when you make use of Rx for custom Types to referesh your UI. | 16 | /// useful when you make use of Rx for custom Types to referesh your UI. |
@@ -65,31 +34,6 @@ abstract class _RxImpl<T> implements RxInterface<T> { | @@ -65,31 +34,6 @@ abstract class _RxImpl<T> implements RxInterface<T> { | ||
65 | subject.add(value); | 34 | subject.add(value); |
66 | } | 35 | } |
67 | 36 | ||
68 | - /// Uses a callback to update [value] internally, similar to [refresh], | ||
69 | - /// but provides the current value as the argument. | ||
70 | - /// Makes sense for custom Rx types (like Models). | ||
71 | - /// | ||
72 | - /// Sample: | ||
73 | - /// ``` | ||
74 | - /// class Person { | ||
75 | - /// String name, last; | ||
76 | - /// int age; | ||
77 | - /// Person({this.name, this.last, this.age}); | ||
78 | - /// @override | ||
79 | - /// String toString() => '$name $last, $age years old'; | ||
80 | - /// } | ||
81 | - /// | ||
82 | - /// final person = Person(name: 'John', last: 'Doe', age: 18).obs; | ||
83 | - /// person.update((person) { | ||
84 | - /// person.name = 'Roi'; | ||
85 | - /// }); | ||
86 | - /// print( person ); | ||
87 | - /// ``` | ||
88 | - void update(void fn(T val)) { | ||
89 | - fn(_value); | ||
90 | - subject.add(_value); | ||
91 | - } | ||
92 | - | ||
93 | /// updates the value to [null] and adds it to the Stream. | 37 | /// updates the value to [null] and adds it to the Stream. |
94 | /// Even with null-safety coming, is still an important feature to support, as | 38 | /// Even with null-safety coming, is still an important feature to support, as |
95 | /// [call()] doesn't accept [null] values. For instance, | 39 | /// [call()] doesn't accept [null] values. For instance, |
@@ -104,6 +48,53 @@ abstract class _RxImpl<T> implements RxInterface<T> { | @@ -104,6 +48,53 @@ abstract class _RxImpl<T> implements RxInterface<T> { | ||
104 | subject.add(_value = null); | 48 | subject.add(_value = null); |
105 | } | 49 | } |
106 | 50 | ||
51 | + /// Closes the subscriptions for this Rx, releasing the resources. | ||
52 | + void close() { | ||
53 | + for (final subscription in _subscriptions) { | ||
54 | + subscription?.cancel(); | ||
55 | + } | ||
56 | + _subscriptions.clear(); | ||
57 | + subject.close(); | ||
58 | + } | ||
59 | + | ||
60 | + /// Makes this Rx looks like a function so you can update a new | ||
61 | + /// value using [rx(someOtherValue)]. Practical to assign the Rx directly | ||
62 | + /// to some Widget that has a signature ::onChange( value ) | ||
63 | + /// | ||
64 | + /// Example: | ||
65 | + /// ``` | ||
66 | + /// final myText = 'GetX rocks!'.obs; | ||
67 | + /// | ||
68 | + /// // in your Constructor, just to check it works :P | ||
69 | + /// ever( myText, print ) ; | ||
70 | + /// | ||
71 | + /// // in your build(BuildContext) { | ||
72 | + /// TextField( | ||
73 | + /// onChanged: myText, | ||
74 | + /// ), | ||
75 | + ///``` | ||
76 | + T call([T v]) { | ||
77 | + if (v != null) { | ||
78 | + value = v; | ||
79 | + } | ||
80 | + return value; | ||
81 | + } | ||
82 | + | ||
83 | + /// This is an internal method. | ||
84 | + /// Subscribe to changes on the inner stream. | ||
85 | + void addListener(GetStream<T> rxGetx) { | ||
86 | + if (_subscriptions.contains(rxGetx.listen)) { | ||
87 | + return; | ||
88 | + } | ||
89 | + | ||
90 | + final subs = rxGetx.listen((data) { | ||
91 | + subject.add(data); | ||
92 | + }); | ||
93 | + _subscriptions.add(subs); | ||
94 | + } | ||
95 | + | ||
96 | + bool firstRebuild = true; | ||
97 | + | ||
107 | /// Same as `toString()` but using a getter. | 98 | /// Same as `toString()` but using a getter. |
108 | String get string => value.toString(); | 99 | String get string => value.toString(); |
109 | 100 | ||
@@ -128,26 +119,6 @@ abstract class _RxImpl<T> implements RxInterface<T> { | @@ -128,26 +119,6 @@ abstract class _RxImpl<T> implements RxInterface<T> { | ||
128 | // ignore: avoid_equals_and_hash_code_on_mutable_classes | 119 | // ignore: avoid_equals_and_hash_code_on_mutable_classes |
129 | int get hashCode => _value.hashCode; | 120 | int get hashCode => _value.hashCode; |
130 | 121 | ||
131 | - /// Closes the subscriptions for this Rx, releasing the resources. | ||
132 | - void close() { | ||
133 | - _subscriptions.forEach((observable, subscription) => subscription.cancel()); | ||
134 | - _subscriptions.clear(); | ||
135 | - subject.close(); | ||
136 | - } | ||
137 | - | ||
138 | - /// This is an internal method. | ||
139 | - /// Subscribe to changes on the inner stream. | ||
140 | - void addListener(Stream<T> rxGetx) { | ||
141 | - if (_subscriptions.containsKey(rxGetx)) { | ||
142 | - return; | ||
143 | - } | ||
144 | - _subscriptions[rxGetx] = rxGetx.listen((data) { | ||
145 | - subject.add(data); | ||
146 | - }); | ||
147 | - } | ||
148 | - | ||
149 | - bool firstRebuild = true; | ||
150 | - | ||
151 | /// Updates the [value] and adds it to the stream, updating the observer | 122 | /// Updates the [value] and adds it to the stream, updating the observer |
152 | /// Widget, only if it's different from the previous value. | 123 | /// Widget, only if it's different from the previous value. |
153 | set value(T val) { | 124 | set value(T val) { |
@@ -160,26 +131,63 @@ abstract class _RxImpl<T> implements RxInterface<T> { | @@ -160,26 +131,63 @@ abstract class _RxImpl<T> implements RxInterface<T> { | ||
160 | /// Returns the current [value] | 131 | /// Returns the current [value] |
161 | T get value { | 132 | T get value { |
162 | if (getObs != null) { | 133 | if (getObs != null) { |
163 | - getObs.addListener(subject.stream); | 134 | + getObs.addListener(subject); |
164 | } | 135 | } |
165 | return _value; | 136 | return _value; |
166 | } | 137 | } |
167 | 138 | ||
168 | - Stream<T> get stream => subject.stream; | 139 | + Stream<T> get stream => GetStreamTransformation<T>(subject.listenable); |
169 | 140 | ||
170 | - StreamSubscription<T> listen(void Function(T) onData, | ||
171 | - {Function onError, void Function() onDone, bool cancelOnError}) => | ||
172 | - stream.listen(onData, onError: onError, onDone: onDone); | 141 | + StreamSubscription<T> listen( |
142 | + void Function(T) onData, { | ||
143 | + Function onError, | ||
144 | + void Function() onDone, | ||
145 | + bool cancelOnError = false, | ||
146 | + }) => | ||
147 | + subject.listen(onData, | ||
148 | + onError: onError, onDone: onDone, cancelOnError: cancelOnError); | ||
173 | 149 | ||
174 | /// Binds an existing [Stream<T>] to this Rx<T> to keep the values in sync. | 150 | /// Binds an existing [Stream<T>] to this Rx<T> to keep the values in sync. |
175 | /// You can bind multiple sources to update the value. | 151 | /// You can bind multiple sources to update the value. |
176 | /// Closing the subscription will happen automatically when the observer | 152 | /// Closing the subscription will happen automatically when the observer |
177 | /// Widget ([GetX] or [Obx]) gets unmounted from the Widget tree. | 153 | /// Widget ([GetX] or [Obx]) gets unmounted from the Widget tree. |
178 | void bindStream(Stream<T> stream) { | 154 | void bindStream(Stream<T> stream) { |
179 | - _subscriptions[stream] = stream.listen((va) => value = va); | 155 | + _subscriptions.add(stream.listen((va) => value = va)); |
156 | + } | ||
157 | +} | ||
158 | + | ||
159 | +/// Base Rx class that manages all the stream logic for any Type. | ||
160 | +abstract class _RxImpl<T> with RxObjectMixin<T> implements RxInterface<T> { | ||
161 | + _RxImpl(T initial) { | ||
162 | + _value = initial; | ||
180 | } | 163 | } |
181 | 164 | ||
182 | Stream<R> map<R>(R mapper(T data)) => stream.map(mapper); | 165 | Stream<R> map<R>(R mapper(T data)) => stream.map(mapper); |
166 | + | ||
167 | + /// Uses a callback to update [value] internally, similar to [refresh], | ||
168 | + /// but provides the current value as the argument. | ||
169 | + /// Makes sense for custom Rx types (like Models). | ||
170 | + /// | ||
171 | + /// Sample: | ||
172 | + /// ``` | ||
173 | + /// class Person { | ||
174 | + /// String name, last; | ||
175 | + /// int age; | ||
176 | + /// Person({this.name, this.last, this.age}); | ||
177 | + /// @override | ||
178 | + /// String toString() => '$name $last, $age years old'; | ||
179 | + /// } | ||
180 | + /// | ||
181 | + /// final person = Person(name: 'John', last: 'Doe', age: 18).obs; | ||
182 | + /// person.update((person) { | ||
183 | + /// person.name = 'Roi'; | ||
184 | + /// }); | ||
185 | + /// print( person ); | ||
186 | + /// ``` | ||
187 | + void update(void fn(T val)) { | ||
188 | + fn(_value); | ||
189 | + subject.add(_value); | ||
190 | + } | ||
183 | } | 191 | } |
184 | 192 | ||
185 | /// Rx class for `bool` Type. | 193 | /// Rx class for `bool` Type. |
@@ -202,6 +210,7 @@ class RxBool extends _RxImpl<bool> { | @@ -202,6 +210,7 @@ class RxBool extends _RxImpl<bool> { | ||
202 | return this; | 210 | return this; |
203 | } | 211 | } |
204 | 212 | ||
213 | + @override | ||
205 | String toString() { | 214 | String toString() { |
206 | return value ? "true" : "false"; | 215 | return value ? "true" : "false"; |
207 | } | 216 | } |
1 | -import 'dart:async'; | ||
2 | -import '../rx_typedefs/rx_typedefs.dart'; | 1 | +part of rx_types; |
3 | 2 | ||
4 | /// This class is the foundation for all reactive (Rx) classes that makes Get | 3 | /// This class is the foundation for all reactive (Rx) classes that makes Get |
5 | /// so powerful. | 4 | /// so powerful. |
@@ -8,10 +7,10 @@ import '../rx_typedefs/rx_typedefs.dart'; | @@ -8,10 +7,10 @@ import '../rx_typedefs/rx_typedefs.dart'; | ||
8 | abstract class RxInterface<T> { | 7 | abstract class RxInterface<T> { |
9 | RxInterface([T initial]); | 8 | RxInterface([T initial]); |
10 | 9 | ||
11 | - StreamController<T> subject; | 10 | + GetStream<T> subject; |
12 | 11 | ||
13 | /// Adds a listener to stream | 12 | /// Adds a listener to stream |
14 | - void addListener(Stream<T> rxGetx); | 13 | + void addListener(GetStream<T> rxGetx); |
15 | 14 | ||
16 | bool get canUpdate; | 15 | bool get canUpdate; |
17 | 16 | ||
@@ -21,8 +20,10 @@ abstract class RxInterface<T> { | @@ -21,8 +20,10 @@ abstract class RxInterface<T> { | ||
21 | 20 | ||
22 | /// Closes the stream | 21 | /// Closes the stream |
23 | // FIXME: shouldn't we expose the returned future? | 22 | // FIXME: shouldn't we expose the returned future? |
24 | - void close() => subject?.close(); | 23 | + void close(); |
25 | 24 | ||
26 | /// Calls [callback] with current value, when the value changes. | 25 | /// Calls [callback] with current value, when the value changes. |
27 | - StreamSubscription<T> listen(ValueCallback<T> callback); | 26 | + StreamSubscription<T> listen(void Function(T event) onData, |
27 | + {Function onError, void Function() onDone, bool cancelOnError}); | ||
28 | } | 28 | } |
29 | + |
1 | -part of 'rx_impl.dart'; | 1 | +part of rx_types; |
2 | 2 | ||
3 | /// Base Rx class for all num Rx's. | 3 | /// Base Rx class for all num Rx's. |
4 | abstract class _BaseRxNum<T extends num> extends _RxImpl<T> { | 4 | abstract class _BaseRxNum<T extends num> extends _RxImpl<T> { |
@@ -299,23 +299,29 @@ class RxDouble extends _BaseRxNum<double> { | @@ -299,23 +299,29 @@ class RxDouble extends _BaseRxNum<double> { | ||
299 | } | 299 | } |
300 | 300 | ||
301 | /// Multiplication operator. | 301 | /// Multiplication operator. |
302 | + @override | ||
302 | double operator *(num other) => value * other; | 303 | double operator *(num other) => value * other; |
303 | 304 | ||
305 | + @override | ||
304 | double operator %(num other) => value % other; | 306 | double operator %(num other) => value % other; |
305 | 307 | ||
306 | /// Division operator. | 308 | /// Division operator. |
309 | + @override | ||
307 | double operator /(num other) => value / other; | 310 | double operator /(num other) => value / other; |
308 | 311 | ||
309 | /// Truncating division operator. | 312 | /// Truncating division operator. |
310 | /// | 313 | /// |
311 | /// The result of the truncating division `a ~/ b` is equivalent to | 314 | /// The result of the truncating division `a ~/ b` is equivalent to |
312 | /// `(a / b).truncate()`. | 315 | /// `(a / b).truncate()`. |
316 | + @override | ||
313 | int operator ~/(num other) => value ~/ other; | 317 | int operator ~/(num other) => value ~/ other; |
314 | 318 | ||
315 | /// Negate operator. */ | 319 | /// Negate operator. */ |
320 | + @override | ||
316 | double operator -() => -value; | 321 | double operator -() => -value; |
317 | 322 | ||
318 | /// Returns the absolute value of this [double]. | 323 | /// Returns the absolute value of this [double]. |
324 | + @override | ||
319 | double abs() => value.abs(); | 325 | double abs() => value.abs(); |
320 | 326 | ||
321 | /// Returns the sign of the double's numerical value. | 327 | /// Returns the sign of the double's numerical value. |
@@ -323,6 +329,7 @@ class RxDouble extends _BaseRxNum<double> { | @@ -323,6 +329,7 @@ class RxDouble extends _BaseRxNum<double> { | ||
323 | /// Returns -1.0 if the value is less than zero, | 329 | /// Returns -1.0 if the value is less than zero, |
324 | /// +1.0 if the value is greater than zero, | 330 | /// +1.0 if the value is greater than zero, |
325 | /// and the value itself if it is -0.0, 0.0 or NaN. | 331 | /// and the value itself if it is -0.0, 0.0 or NaN. |
332 | + @override | ||
326 | double get sign => value.sign; | 333 | double get sign => value.sign; |
327 | 334 | ||
328 | /// Returns the integer closest to `this`. | 335 | /// Returns the integer closest to `this`. |
@@ -331,22 +338,26 @@ class RxDouble extends _BaseRxNum<double> { | @@ -331,22 +338,26 @@ class RxDouble extends _BaseRxNum<double> { | ||
331 | /// `(3.5).round() == 4` and `(-3.5).round() == -4`. | 338 | /// `(3.5).round() == 4` and `(-3.5).round() == -4`. |
332 | /// | 339 | /// |
333 | /// If `this` is not finite (`NaN` or infinity), throws an [UnsupportedError]. | 340 | /// If `this` is not finite (`NaN` or infinity), throws an [UnsupportedError]. |
341 | + @override | ||
334 | int round() => value.round(); | 342 | int round() => value.round(); |
335 | 343 | ||
336 | /// Returns the greatest integer no greater than `this`. | 344 | /// Returns the greatest integer no greater than `this`. |
337 | /// | 345 | /// |
338 | /// If `this` is not finite (`NaN` or infinity), throws an [UnsupportedError]. | 346 | /// If `this` is not finite (`NaN` or infinity), throws an [UnsupportedError]. |
347 | + @override | ||
339 | int floor() => value.floor(); | 348 | int floor() => value.floor(); |
340 | 349 | ||
341 | /// Returns the least integer no smaller than `this`. | 350 | /// Returns the least integer no smaller than `this`. |
342 | /// | 351 | /// |
343 | /// If `this` is not finite (`NaN` or infinity), throws an [UnsupportedError]. | 352 | /// If `this` is not finite (`NaN` or infinity), throws an [UnsupportedError]. |
353 | + @override | ||
344 | int ceil() => value.ceil(); | 354 | int ceil() => value.ceil(); |
345 | 355 | ||
346 | /// Returns the integer obtained by discarding any fractional | 356 | /// Returns the integer obtained by discarding any fractional |
347 | /// digits from `this`. | 357 | /// digits from `this`. |
348 | /// | 358 | /// |
349 | /// If `this` is not finite (`NaN` or infinity), throws an [UnsupportedError]. | 359 | /// If `this` is not finite (`NaN` or infinity), throws an [UnsupportedError]. |
360 | + @override | ||
350 | int truncate() => value.truncate(); | 361 | int truncate() => value.truncate(); |
351 | 362 | ||
352 | /// Returns the integer double value closest to `this`. | 363 | /// Returns the integer double value closest to `this`. |
@@ -361,6 +372,7 @@ class RxDouble extends _BaseRxNum<double> { | @@ -361,6 +372,7 @@ class RxDouble extends _BaseRxNum<double> { | ||
361 | /// and `-0.0` is therefore considered closer to negative numbers than `0.0`. | 372 | /// and `-0.0` is therefore considered closer to negative numbers than `0.0`. |
362 | /// This means that for a value, `d` in the range `-0.5 < d < 0.0`, | 373 | /// This means that for a value, `d` in the range `-0.5 < d < 0.0`, |
363 | /// the result is `-0.0`. | 374 | /// the result is `-0.0`. |
375 | + @override | ||
364 | double roundToDouble() => value.roundToDouble(); | 376 | double roundToDouble() => value.roundToDouble(); |
365 | 377 | ||
366 | /// Returns the greatest integer double value no greater than `this`. | 378 | /// Returns the greatest integer double value no greater than `this`. |
@@ -370,6 +382,7 @@ class RxDouble extends _BaseRxNum<double> { | @@ -370,6 +382,7 @@ class RxDouble extends _BaseRxNum<double> { | ||
370 | /// | 382 | /// |
371 | /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. | 383 | /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. |
372 | /// A number `d` in the range `0.0 < d < 1.0` will return `0.0`. | 384 | /// A number `d` in the range `0.0 < d < 1.0` will return `0.0`. |
385 | + @override | ||
373 | double floorToDouble() => value.floorToDouble(); | 386 | double floorToDouble() => value.floorToDouble(); |
374 | 387 | ||
375 | /// Returns the least integer double value no smaller than `this`. | 388 | /// Returns the least integer double value no smaller than `this`. |
@@ -379,6 +392,7 @@ class RxDouble extends _BaseRxNum<double> { | @@ -379,6 +392,7 @@ class RxDouble extends _BaseRxNum<double> { | ||
379 | /// | 392 | /// |
380 | /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. | 393 | /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. |
381 | /// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`. | 394 | /// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`. |
395 | + @override | ||
382 | double ceilToDouble() => value.ceilToDouble(); | 396 | double ceilToDouble() => value.ceilToDouble(); |
383 | 397 | ||
384 | /// Returns the integer double value obtained by discarding any fractional | 398 | /// Returns the integer double value obtained by discarding any fractional |
@@ -390,6 +404,7 @@ class RxDouble extends _BaseRxNum<double> { | @@ -390,6 +404,7 @@ class RxDouble extends _BaseRxNum<double> { | ||
390 | /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. | 404 | /// For the purpose of rounding, `-0.0` is considered to be below `0.0`. |
391 | /// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`, and | 405 | /// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`, and |
392 | /// in the range `0.0 < d < 1.0` it will return 0.0. | 406 | /// in the range `0.0 < d < 1.0` it will return 0.0. |
407 | + @override | ||
393 | double truncateToDouble() => value.truncateToDouble(); | 408 | double truncateToDouble() => value.truncateToDouble(); |
394 | } | 409 | } |
395 | 410 | ||
@@ -578,40 +593,51 @@ class RxInt extends _BaseRxNum<int> { | @@ -578,40 +593,51 @@ class RxInt extends _BaseRxNum<int> { | ||
578 | /// | 593 | /// |
579 | /// The result of negating an integer always has the opposite sign, except | 594 | /// The result of negating an integer always has the opposite sign, except |
580 | /// for zero, which is its own negation. | 595 | /// for zero, which is its own negation. |
596 | + @override | ||
581 | int operator -() => -value; | 597 | int operator -() => -value; |
582 | 598 | ||
583 | /// Returns the absolute value of this integer. | 599 | /// Returns the absolute value of this integer. |
584 | /// | 600 | /// |
585 | /// For any integer `x`, the result is the same as `x < 0 ? -x : x`. | 601 | /// For any integer `x`, the result is the same as `x < 0 ? -x : x`. |
602 | + @override | ||
586 | int abs() => value.abs(); | 603 | int abs() => value.abs(); |
587 | 604 | ||
588 | /// Returns the sign of this integer. | 605 | /// Returns the sign of this integer. |
589 | /// | 606 | /// |
590 | /// Returns 0 for zero, -1 for values less than zero and | 607 | /// Returns 0 for zero, -1 for values less than zero and |
591 | /// +1 for values greater than zero. | 608 | /// +1 for values greater than zero. |
609 | + @override | ||
592 | int get sign => value.sign; | 610 | int get sign => value.sign; |
593 | 611 | ||
594 | /// Returns `this`. | 612 | /// Returns `this`. |
613 | + @override | ||
595 | int round() => value.round(); | 614 | int round() => value.round(); |
596 | 615 | ||
597 | /// Returns `this`. | 616 | /// Returns `this`. |
617 | + @override | ||
598 | int floor() => value.floor(); | 618 | int floor() => value.floor(); |
599 | 619 | ||
600 | /// Returns `this`. | 620 | /// Returns `this`. |
621 | + @override | ||
601 | int ceil() => value.ceil(); | 622 | int ceil() => value.ceil(); |
602 | 623 | ||
603 | /// Returns `this`. | 624 | /// Returns `this`. |
625 | + @override | ||
604 | int truncate() => value.truncate(); | 626 | int truncate() => value.truncate(); |
605 | 627 | ||
606 | /// Returns `this.toDouble()`. | 628 | /// Returns `this.toDouble()`. |
629 | + @override | ||
607 | double roundToDouble() => value.roundToDouble(); | 630 | double roundToDouble() => value.roundToDouble(); |
608 | 631 | ||
609 | /// Returns `this.toDouble()`. | 632 | /// Returns `this.toDouble()`. |
633 | + @override | ||
610 | double floorToDouble() => value.floorToDouble(); | 634 | double floorToDouble() => value.floorToDouble(); |
611 | 635 | ||
612 | /// Returns `this.toDouble()`. | 636 | /// Returns `this.toDouble()`. |
637 | + @override | ||
613 | double ceilToDouble() => value.ceilToDouble(); | 638 | double ceilToDouble() => value.ceilToDouble(); |
614 | 639 | ||
615 | /// Returns `this.toDouble()`. | 640 | /// Returns `this.toDouble()`. |
641 | + @override | ||
616 | double truncateToDouble() => value.truncateToDouble(); | 642 | double truncateToDouble() => value.truncateToDouble(); |
617 | } | 643 | } |
1 | +part of rx_types; | ||
2 | + | ||
3 | +/// Create a list similar to `List<T>` | ||
4 | +class RxList<E> extends ListMixin<E> | ||
5 | + with RxObjectMixin<List<E>> | ||
6 | + implements RxInterface<List<E>> { | ||
7 | + RxList([List<E> initial]) { | ||
8 | + _value = initial; | ||
9 | + } | ||
10 | + | ||
11 | + @override | ||
12 | + Iterator<E> get iterator => value.iterator; | ||
13 | + | ||
14 | + @override | ||
15 | + void operator []=(int index, E val) { | ||
16 | + _value[index] = val; | ||
17 | + refresh(); | ||
18 | + } | ||
19 | + | ||
20 | + /// Special override to push() element(s) in a reactive way | ||
21 | + /// inside the List, | ||
22 | + @override | ||
23 | + RxList<E> operator +(Iterable<E> val) { | ||
24 | + addAll(val); | ||
25 | + refresh(); | ||
26 | + return this; | ||
27 | + } | ||
28 | + | ||
29 | + @override | ||
30 | + E operator [](int index) { | ||
31 | + return value[index]; | ||
32 | + } | ||
33 | + | ||
34 | + @override | ||
35 | + void add(E item) { | ||
36 | + _value.add(item); | ||
37 | + refresh(); | ||
38 | + } | ||
39 | + | ||
40 | + @override | ||
41 | + void addAll(Iterable<E> item) { | ||
42 | + _value.addAll(item); | ||
43 | + refresh(); | ||
44 | + } | ||
45 | + | ||
46 | + /// Add [item] to [List<E>] only if [item] is not null. | ||
47 | + void addNonNull(E item) { | ||
48 | + if (item != null) add(item); | ||
49 | + } | ||
50 | + | ||
51 | + /// Add [Iterable<E>] to [List<E>] only if [Iterable<E>] is not null. | ||
52 | + void addAllNonNull(Iterable<E> item) { | ||
53 | + if (item != null) addAll(item); | ||
54 | + } | ||
55 | + | ||
56 | + /// Add [item] to [List<E>] only if [condition] is true. | ||
57 | + void addIf(dynamic condition, E item) { | ||
58 | + if (condition is Condition) condition = condition(); | ||
59 | + if (condition is bool && condition) add(item); | ||
60 | + } | ||
61 | + | ||
62 | + /// Adds [Iterable<E>] to [List<E>] only if [condition] is true. | ||
63 | + void addAllIf(dynamic condition, Iterable<E> items) { | ||
64 | + if (condition is Condition) condition = condition(); | ||
65 | + if (condition is bool && condition) addAll(items); | ||
66 | + } | ||
67 | + | ||
68 | + @override | ||
69 | + int get length => value.length; | ||
70 | + | ||
71 | + /// Replaces all existing items of this list with [item] | ||
72 | + void assign(E item) { | ||
73 | + clear(); | ||
74 | + add(item); | ||
75 | + } | ||
76 | + | ||
77 | + /// Replaces all existing items of this list with [items] | ||
78 | + void assignAll(Iterable<E> items) { | ||
79 | + clear(); | ||
80 | + addAll(items); | ||
81 | + } | ||
82 | + | ||
83 | + @override | ||
84 | + @protected | ||
85 | + List<E> get value { | ||
86 | + if (getObs != null) { | ||
87 | + getObs.addListener(subject); | ||
88 | + } | ||
89 | + return _value; | ||
90 | + } | ||
91 | + | ||
92 | + @override | ||
93 | + @protected | ||
94 | + @Deprecated('List.value is deprecated. use [yourList.assignAll(newList)]') | ||
95 | + set value(List<E> val) { | ||
96 | + if (_value == val) return; | ||
97 | + _value = val; | ||
98 | + refresh(); | ||
99 | + } | ||
100 | + | ||
101 | + @override | ||
102 | + set length(int newLength) { | ||
103 | + _value.length = newLength; | ||
104 | + refresh(); | ||
105 | + } | ||
106 | + | ||
107 | + @override | ||
108 | + void insertAll(int index, Iterable<E> iterable) { | ||
109 | + _value.insertAll(index, iterable); | ||
110 | + refresh(); | ||
111 | + } | ||
112 | + | ||
113 | + @override | ||
114 | + Iterable<E> get reversed => value.reversed; | ||
115 | + | ||
116 | + @override | ||
117 | + Iterable<E> where(bool Function(E) test) { | ||
118 | + return value.where(test); | ||
119 | + } | ||
120 | + | ||
121 | + @override | ||
122 | + Iterable<T> whereType<T>() { | ||
123 | + return value.whereType<T>(); | ||
124 | + } | ||
125 | + | ||
126 | + @override | ||
127 | + void sort([int compare(E a, E b)]) { | ||
128 | + _value.sort(compare); | ||
129 | + refresh(); | ||
130 | + } | ||
131 | +} | ||
132 | + | ||
133 | +// /// Create a list similar to `List<T>` | ||
134 | +// class RxList<E> implements List<E>, RxInterface<List<E>> { | ||
135 | +// RxList([List<E> initial]) { | ||
136 | +// if (initial != null) _value = initial; | ||
137 | +// } | ||
138 | + | ||
139 | +// List<E> _value = <E>[]; | ||
140 | + | ||
141 | +// @override | ||
142 | +// Iterator<E> get iterator => value.iterator; | ||
143 | + | ||
144 | +// @override | ||
145 | +// bool get isEmpty => value.isEmpty; | ||
146 | + | ||
147 | +// bool get canUpdate { | ||
148 | +// return _subscriptions.length > 0; | ||
149 | +// } | ||
150 | + | ||
151 | +// @override | ||
152 | +// bool get isNotEmpty => value.isNotEmpty; | ||
153 | + | ||
154 | +// @override | ||
155 | +// StreamController<List<E>> subject = StreamController.broadcast(); | ||
156 | + | ||
157 | +// final _subscriptions = HashMap<Stream<List<E>>, StreamSubscription>(); | ||
158 | + | ||
159 | +// void operator []=(int index, E val) { | ||
160 | +// _value[index] = val; | ||
161 | +// refresh(); | ||
162 | +// } | ||
163 | + | ||
164 | +// void refresh() { | ||
165 | +// subject.add(_value); | ||
166 | +// } | ||
167 | + | ||
168 | +// /// Special override to push() element(s) in a reactive way | ||
169 | +// /// inside the List, | ||
170 | +// RxList<E> operator +(Iterable<E> val) { | ||
171 | +// addAll(val); | ||
172 | +// refresh(); | ||
173 | +// return this; | ||
174 | +// } | ||
175 | + | ||
176 | +// E operator [](int index) { | ||
177 | +// return value[index]; | ||
178 | +// } | ||
179 | + | ||
180 | +// void add(E item) { | ||
181 | +// _value.add(item); | ||
182 | +// refresh(); | ||
183 | +// } | ||
184 | + | ||
185 | +// @override | ||
186 | +// void addAll(Iterable<E> item) { | ||
187 | +// _value.addAll(item); | ||
188 | +// refresh(); | ||
189 | +// } | ||
190 | + | ||
191 | +// /// Add [item] to [List<E>] only if [item] is not null. | ||
192 | +// void addNonNull(E item) { | ||
193 | +// if (item != null) add(item); | ||
194 | +// } | ||
195 | + | ||
196 | +// /// Add [Iterable<E>] to [List<E>] only if [Iterable<E>] is not null. | ||
197 | +// void addAllNonNull(Iterable<E> item) { | ||
198 | +// if (item != null) addAll(item); | ||
199 | +// } | ||
200 | + | ||
201 | +// /// Add [item] to [List<E>] only if [condition] is true. | ||
202 | +// void addIf(dynamic condition, E item) { | ||
203 | +// if (condition is Condition) condition = condition(); | ||
204 | +// if (condition is bool && condition) add(item); | ||
205 | +// } | ||
206 | + | ||
207 | +// /// Adds [Iterable<E>] to [List<E>] only if [condition] is true. | ||
208 | +// void addAllIf(dynamic condition, Iterable<E> items) { | ||
209 | +// if (condition is Condition) condition = condition(); | ||
210 | +// if (condition is bool && condition) addAll(items); | ||
211 | +// } | ||
212 | + | ||
213 | +// @override | ||
214 | +// void insert(int index, E item) { | ||
215 | +// _value.insert(index, item); | ||
216 | +// refresh(); | ||
217 | +// } | ||
218 | + | ||
219 | +// @override | ||
220 | +// void insertAll(int index, Iterable<E> iterable) { | ||
221 | +// _value.insertAll(index, iterable); | ||
222 | +// refresh(); | ||
223 | +// } | ||
224 | + | ||
225 | +// @override | ||
226 | +// int get length => value.length; | ||
227 | + | ||
228 | +// /// Removes an item from the list. | ||
229 | +// /// | ||
230 | +// /// This is O(N) in the number of items in the list. | ||
231 | +// /// | ||
232 | +// /// Returns whether the item was present in the list. | ||
233 | +// @override | ||
234 | +// bool remove(Object item) { | ||
235 | +// final hasRemoved = _value.remove(item); | ||
236 | +// if (hasRemoved) { | ||
237 | +// refresh(); | ||
238 | +// } | ||
239 | +// return hasRemoved; | ||
240 | +// } | ||
241 | + | ||
242 | +// @override | ||
243 | +// E removeAt(int index) { | ||
244 | +// final item = _value.removeAt(index); | ||
245 | +// refresh(); | ||
246 | +// return item; | ||
247 | +// } | ||
248 | + | ||
249 | +// @override | ||
250 | +// E removeLast() { | ||
251 | +// final item = _value.removeLast(); | ||
252 | +// refresh(); | ||
253 | +// return item; | ||
254 | +// } | ||
255 | + | ||
256 | +// @override | ||
257 | +// void removeRange(int start, int end) { | ||
258 | +// _value.removeRange(start, end); | ||
259 | +// refresh(); | ||
260 | +// } | ||
261 | + | ||
262 | +// @override | ||
263 | +// void removeWhere(bool Function(E) test) { | ||
264 | +// _value.removeWhere(test); | ||
265 | +// refresh(); | ||
266 | +// } | ||
267 | + | ||
268 | +// @override | ||
269 | +// void clear() { | ||
270 | +// _value.clear(); | ||
271 | +// refresh(); | ||
272 | +// } | ||
273 | + | ||
274 | +// @override | ||
275 | +// void sort([int compare(E a, E b)]) { | ||
276 | +// _value.sort(compare); | ||
277 | +// refresh(); | ||
278 | +// } | ||
279 | + | ||
280 | +// @override | ||
281 | +// void close() { | ||
282 | +// _subscriptions.forEach((observable, subscription) { | ||
283 | +// subscription.cancel(); | ||
284 | +// }); | ||
285 | +// _subscriptions.clear(); | ||
286 | +// subject.close(); | ||
287 | +// } | ||
288 | + | ||
289 | +// /// Replaces all existing items of this list with [item] | ||
290 | +// void assign(E item) { | ||
291 | +// clear(); | ||
292 | +// add(item); | ||
293 | +// } | ||
294 | + | ||
295 | +// void update(void fn(Iterable<E> value)) { | ||
296 | +// fn(value); | ||
297 | +// refresh(); | ||
298 | +// } | ||
299 | + | ||
300 | +// /// Replaces all existing items of this list with [items] | ||
301 | +// void assignAll(Iterable<E> items) { | ||
302 | +// clear(); | ||
303 | +// addAll(items); | ||
304 | +// } | ||
305 | + | ||
306 | +// @protected | ||
307 | +// List<E> get value { | ||
308 | +// if (getObs != null) { | ||
309 | +// getObs.addListener(subject.stream); | ||
310 | +// } | ||
311 | +// return _value; | ||
312 | +// } | ||
313 | + | ||
314 | +// String get string => value.toString(); | ||
315 | + | ||
316 | +// void addListener(Stream<List<E>> rxGetX) { | ||
317 | +// if (_subscriptions.containsKey(rxGetX)) { | ||
318 | +// return; | ||
319 | +// } | ||
320 | +// _subscriptions[rxGetX] = rxGetX.listen(subject.add); | ||
321 | +// } | ||
322 | + | ||
323 | +// set value(List<E> val) { | ||
324 | +// if (_value == val) return; | ||
325 | +// _value = val; | ||
326 | +// refresh(); | ||
327 | +// } | ||
328 | + | ||
329 | +// Stream<List<E>> get stream => subject.stream; | ||
330 | + | ||
331 | +// StreamSubscription<List<E>> listen( | ||
332 | +// void Function(List<E>) onData, { | ||
333 | +// Function onError, | ||
334 | +// void Function() onDone, | ||
335 | +// bool cancelOnError, | ||
336 | +// }) => | ||
337 | +// stream.listen(onData, onError: onError, onDone: onDone); | ||
338 | + | ||
339 | +// /// Binds an existing [Stream<List>] to this [RxList]. | ||
340 | +// /// You can bind multiple sources to update the value. | ||
341 | +// /// Closing the subscription will happen automatically when the observer | ||
342 | +// /// Widget ([GetX] or [Obx]) gets unmounted from the Widget tree. | ||
343 | +// void bindStream(Stream<List<E>> stream) { | ||
344 | +// _subscriptions[stream] = stream.listen((va) => value = va); | ||
345 | +// } | ||
346 | + | ||
347 | +// @override | ||
348 | +// E get first => value.first; | ||
349 | + | ||
350 | +// @override | ||
351 | +// E get last => value.last; | ||
352 | + | ||
353 | +// @override | ||
354 | +// bool any(bool Function(E) test) { | ||
355 | +// return value.any(test); | ||
356 | +// } | ||
357 | + | ||
358 | +// @override | ||
359 | +// Map<int, E> asMap() { | ||
360 | +// return value.asMap(); | ||
361 | +// } | ||
362 | + | ||
363 | +// @override | ||
364 | +// List<R> cast<R>() { | ||
365 | +// return value.cast<R>(); | ||
366 | +// } | ||
367 | + | ||
368 | +// @override | ||
369 | +// bool contains(Object element) { | ||
370 | +// return value.contains(element); | ||
371 | +// } | ||
372 | + | ||
373 | +// @override | ||
374 | +// E elementAt(int index) { | ||
375 | +// return value.elementAt(index); | ||
376 | +// } | ||
377 | + | ||
378 | +// @override | ||
379 | +// bool every(bool Function(E) test) { | ||
380 | +// return value.every(test); | ||
381 | +// } | ||
382 | + | ||
383 | +// @override | ||
384 | +// Iterable<T> expand<T>(Iterable<T> Function(E) f) { | ||
385 | +// return value.expand(f); | ||
386 | +// } | ||
387 | + | ||
388 | +// @override | ||
389 | +// void fillRange(int start, int end, [E fillValue]) { | ||
390 | +// _value.fillRange(start, end, fillValue); | ||
391 | +// refresh(); | ||
392 | +// } | ||
393 | + | ||
394 | +// @override | ||
395 | +// E firstWhere(bool Function(E) test, {E Function() orElse}) { | ||
396 | +// return value.firstWhere(test, orElse: orElse); | ||
397 | +// } | ||
398 | + | ||
399 | +// @override | ||
400 | +// T fold<T>(T initialValue, T Function(T, E) combine) { | ||
401 | +// return value.fold(initialValue, combine); | ||
402 | +// } | ||
403 | + | ||
404 | +// @override | ||
405 | +// Iterable<E> followedBy(Iterable<E> other) { | ||
406 | +// return value.followedBy(other); | ||
407 | +// } | ||
408 | + | ||
409 | +// @override | ||
410 | +// void forEach(void Function(E) f) { | ||
411 | +// value.forEach(f); | ||
412 | +// } | ||
413 | + | ||
414 | +// @override | ||
415 | +// Iterable<E> getRange(int start, int end) { | ||
416 | +// return value.getRange(start, end); | ||
417 | +// } | ||
418 | + | ||
419 | +// @override | ||
420 | +// int indexOf(E element, [int start = 0]) { | ||
421 | +// return value.indexOf(element, start); | ||
422 | +// } | ||
423 | + | ||
424 | +// @override | ||
425 | +// int indexWhere(bool Function(E) test, [int start = 0]) { | ||
426 | +// return value.indexWhere(test, start); | ||
427 | +// } | ||
428 | + | ||
429 | +// @override | ||
430 | +// String join([String separator = ""]) { | ||
431 | +// return value.join(separator); | ||
432 | +// } | ||
433 | + | ||
434 | +// @override | ||
435 | +// int lastIndexOf(E element, [int start]) { | ||
436 | +// return value.lastIndexOf(element, start); | ||
437 | +// } | ||
438 | + | ||
439 | +// @override | ||
440 | +// int lastIndexWhere(bool Function(E) test, [int start]) { | ||
441 | +// return value.lastIndexWhere(test, start); | ||
442 | +// } | ||
443 | + | ||
444 | +// @override | ||
445 | +// E lastWhere(bool Function(E) test, {E Function() orElse}) { | ||
446 | +// return value.lastWhere(test, orElse: orElse); | ||
447 | +// } | ||
448 | + | ||
449 | +// @override | ||
450 | +// set length(int newLength) { | ||
451 | +// _value.length = newLength; | ||
452 | +// refresh(); | ||
453 | +// } | ||
454 | + | ||
455 | +// @override | ||
456 | +// Iterable<T> map<T>(T Function(E) f) { | ||
457 | +// return value.map(f); | ||
458 | +// } | ||
459 | + | ||
460 | +// @override | ||
461 | +// E reduce(E Function(E, E) combine) { | ||
462 | +// return value.reduce(combine); | ||
463 | +// } | ||
464 | + | ||
465 | +// @override | ||
466 | +// void replaceRange(int start, int end, Iterable<E> replacement) { | ||
467 | +// _value.replaceRange(start, end, replacement); | ||
468 | +// refresh(); | ||
469 | +// } | ||
470 | + | ||
471 | +// @override | ||
472 | +// void retainWhere(bool Function(E) test) { | ||
473 | +// _value.retainWhere(test); | ||
474 | +// refresh(); | ||
475 | +// } | ||
476 | + | ||
477 | +// @override | ||
478 | +// Iterable<E> get reversed => value.reversed; | ||
479 | + | ||
480 | +// @override | ||
481 | +// void setAll(int index, Iterable<E> iterable) { | ||
482 | +// _value.setAll(index, iterable); | ||
483 | +// refresh(); | ||
484 | +// } | ||
485 | + | ||
486 | +// @override | ||
487 | +// void setRange(int start, int end, | ||
488 | +// Iterable<E> iterable, [int skipCount = 0],) { | ||
489 | +// _value.setRange(start, end, iterable, skipCount); | ||
490 | +// refresh(); | ||
491 | +// } | ||
492 | + | ||
493 | +// @override | ||
494 | +// void shuffle([Random random]) { | ||
495 | +// _value.shuffle(random); | ||
496 | +// refresh(); | ||
497 | +// } | ||
498 | + | ||
499 | +// @override | ||
500 | +// E get single => value.single; | ||
501 | + | ||
502 | +// @override | ||
503 | +// E singleWhere(bool Function(E) test, {E Function() orElse}) { | ||
504 | +// return value.singleWhere(test, orElse: orElse); | ||
505 | +// } | ||
506 | + | ||
507 | +// @override | ||
508 | +// Iterable<E> skip(int count) { | ||
509 | +// return value.skip(count); | ||
510 | +// } | ||
511 | + | ||
512 | +// @override | ||
513 | +// Iterable<E> skipWhile(bool Function(E) test) { | ||
514 | +// return value.skipWhile(test); | ||
515 | +// } | ||
516 | + | ||
517 | +// @override | ||
518 | +// List<E> sublist(int start, [int end]) { | ||
519 | +// return value.sublist(start, end); | ||
520 | +// } | ||
521 | + | ||
522 | +// @override | ||
523 | +// Iterable<E> take(int count) { | ||
524 | +// return value.take(count); | ||
525 | +// } | ||
526 | + | ||
527 | +// @override | ||
528 | +// Iterable<E> takeWhile(bool Function(E) test) { | ||
529 | +// return value.takeWhile(test); | ||
530 | +// } | ||
531 | + | ||
532 | +// @override | ||
533 | +// List<E> toList({bool growable = true}) { | ||
534 | +// return value.toList(growable: growable); | ||
535 | +// } | ||
536 | + | ||
537 | +// @override | ||
538 | +// Set<E> toSet() { | ||
539 | +// return value.toSet(); | ||
540 | +// } | ||
541 | + | ||
542 | +// @override | ||
543 | +// Iterable<E> where(bool Function(E) test) { | ||
544 | +// return value.where(test); | ||
545 | +// } | ||
546 | + | ||
547 | +// @override | ||
548 | +// Iterable<T> whereType<T>() { | ||
549 | +// return value.whereType<T>(); | ||
550 | +// } | ||
551 | + | ||
552 | +// @override | ||
553 | +// set first(E value) { | ||
554 | +// _value.first = value; | ||
555 | +// refresh(); | ||
556 | +// } | ||
557 | + | ||
558 | +// @override | ||
559 | +// set last(E value) { | ||
560 | +// _value.last = value; | ||
561 | +// refresh(); | ||
562 | +// } | ||
563 | +// } | ||
564 | + | ||
565 | +extension ListExtension<E> on List<E> { | ||
566 | + RxList<E> get obs { | ||
567 | + if (this != null) { | ||
568 | + return RxList<E>(<E>[])..addAllNonNull(this); | ||
569 | + } else { | ||
570 | + return RxList<E>(null); | ||
571 | + } | ||
572 | + } | ||
573 | +} |
1 | -import 'dart:async'; | ||
2 | -import 'dart:collection'; | ||
3 | -import 'package:flutter/foundation.dart'; | ||
4 | -import '../rx_core/rx_impl.dart'; | ||
5 | -import '../rx_core/rx_interface.dart'; | ||
6 | -import '../rx_typedefs/rx_typedefs.dart'; | 1 | +part of rx_types; |
7 | 2 | ||
8 | -class RxMap<K, V> implements RxInterface<Map<K, V>>, Map<K, V> { | 3 | +class RxMap<K, V> extends MapMixin<K, V> |
4 | + with RxObjectMixin<Map<K, V>> | ||
5 | + implements RxInterface<Map<K, V>> { | ||
9 | RxMap([Map<K, V> initial]) { | 6 | RxMap([Map<K, V> initial]) { |
10 | - if (initial != null) _value = initial; | ||
11 | - } | ||
12 | - | ||
13 | - @override | ||
14 | - StreamController<Map<K, V>> subject = StreamController<Map<K, V>>.broadcast(); | ||
15 | - final _subscriptions = HashMap<Stream<Map<K, V>>, StreamSubscription>(); | ||
16 | - | ||
17 | - Map<K, V> _value; | ||
18 | - | ||
19 | - @protected | ||
20 | - Map<K, V> get value { | ||
21 | - if (getObs != null) { | ||
22 | - getObs.addListener(subject.stream); | ||
23 | - } | ||
24 | - return _value; | ||
25 | - } | ||
26 | - | ||
27 | - void refresh() { | ||
28 | - subject.add(_value); | ||
29 | - } | ||
30 | - | ||
31 | - String get string => value.toString(); | ||
32 | - | ||
33 | - bool get canUpdate { | ||
34 | - return _subscriptions.length > 0; | ||
35 | - } | ||
36 | - | ||
37 | - @override | ||
38 | - void close() { | ||
39 | - _subscriptions.forEach((observable, subscription) { | ||
40 | - subscription.cancel(); | ||
41 | - }); | ||
42 | - _subscriptions.clear(); | ||
43 | - subject.close(); | ||
44 | - } | ||
45 | - | ||
46 | - @override | ||
47 | - void addListener(Stream<Map<K, V>> rxGetX) { | ||
48 | - if (_subscriptions.containsKey(rxGetX)) { | ||
49 | - return; | ||
50 | - } | ||
51 | - _subscriptions[rxGetX] = rxGetX.listen((data) { | ||
52 | - subject.add(data); | ||
53 | - }); | ||
54 | - } | ||
55 | - | ||
56 | - set value(Map<K, V> val) { | ||
57 | - if (_value == val) return; | ||
58 | - _value = val; | ||
59 | - refresh(); | ||
60 | - } | ||
61 | - | ||
62 | - Stream<Map<K, V>> get stream => subject.stream; | ||
63 | - | ||
64 | - StreamSubscription<Map<K, V>> listen(void Function(Map<K, V>) onData, | ||
65 | - {Function onError, void Function() onDone, bool cancelOnError}) => | ||
66 | - stream.listen(onData, onError: onError, onDone: onDone); | ||
67 | - | ||
68 | - /// Binds an existing [Stream<Map>] to this [RxMap]. | ||
69 | - /// You can bind multiple sources to update the value. | ||
70 | - /// Closing the subscription will happen automatically when the observer | ||
71 | - /// Widget ([GetX] or [Obx]) gets unmounted from the Widget tree. | ||
72 | - void bindStream(Stream<Map<K, V>> stream) { | ||
73 | - _subscriptions[stream] = stream.listen((va) => value = va); | ||
74 | - } | ||
75 | - | ||
76 | - void add(K key, V value) { | ||
77 | - _value[key] = value; | ||
78 | - refresh(); | ||
79 | - } | ||
80 | - | ||
81 | - void addIf(dynamic condition, K key, V value) { | ||
82 | - if (condition is Condition) condition = condition(); | ||
83 | - if (condition is bool && condition) { | ||
84 | - _value[key] = value; | ||
85 | - refresh(); | ||
86 | - } | ||
87 | - } | ||
88 | - | ||
89 | - void addAllIf(dynamic condition, Map<K, V> values) { | ||
90 | - if (condition is Condition) condition = condition(); | ||
91 | - if (condition is bool && condition) addAll(values); | 7 | + _value = initial; |
92 | } | 8 | } |
93 | 9 | ||
94 | @override | 10 | @override |
@@ -103,64 +19,15 @@ class RxMap<K, V> implements RxInterface<Map<K, V>>, Map<K, V> { | @@ -103,64 +19,15 @@ class RxMap<K, V> implements RxInterface<Map<K, V>>, Map<K, V> { | ||
103 | } | 19 | } |
104 | 20 | ||
105 | @override | 21 | @override |
106 | - void addAll(Map<K, V> other) { | ||
107 | - _value.addAll(other); | ||
108 | - refresh(); | ||
109 | - } | ||
110 | - | ||
111 | - @override | ||
112 | - void addEntries(Iterable<MapEntry<K, V>> entries) { | ||
113 | - _value.addEntries(entries); | ||
114 | - refresh(); | ||
115 | - } | ||
116 | - | ||
117 | - @override | ||
118 | void clear() { | 22 | void clear() { |
119 | _value.clear(); | 23 | _value.clear(); |
120 | refresh(); | 24 | refresh(); |
121 | } | 25 | } |
122 | 26 | ||
123 | @override | 27 | @override |
124 | - Map<K2, V2> cast<K2, V2>() => value.cast<K2, V2>(); | ||
125 | - | ||
126 | - @override | ||
127 | - bool containsKey(Object key) => value.containsKey(key); | ||
128 | - | ||
129 | - @override | ||
130 | - bool containsValue(Object value) => _value.containsValue(value); | ||
131 | - | ||
132 | - @override | ||
133 | - Iterable<MapEntry<K, V>> get entries => value.entries; | ||
134 | - | ||
135 | - @override | ||
136 | - void forEach(void Function(K, V) f) { | ||
137 | - value.forEach(f); | ||
138 | - } | ||
139 | - | ||
140 | - @override | ||
141 | - bool get isEmpty => value.isEmpty; | ||
142 | - | ||
143 | - @override | ||
144 | - bool get isNotEmpty => value.isNotEmpty; | ||
145 | - | ||
146 | - @override | ||
147 | Iterable<K> get keys => value.keys; | 28 | Iterable<K> get keys => value.keys; |
148 | 29 | ||
149 | @override | 30 | @override |
150 | - int get length => value.length; | ||
151 | - | ||
152 | - @override | ||
153 | - Map<K2, V2> map<K2, V2>(MapEntry<K2, V2> Function(K, V) transform) => | ||
154 | - value.map(transform); | ||
155 | - | ||
156 | - @override | ||
157 | - V putIfAbsent(K key, V Function() ifAbsent) { | ||
158 | - final val = _value.putIfAbsent(key, ifAbsent); | ||
159 | - refresh(); | ||
160 | - return val; | ||
161 | - } | ||
162 | - | ||
163 | - @override | ||
164 | V remove(Object key) { | 31 | V remove(Object key) { |
165 | final val = _value.remove(key); | 32 | final val = _value.remove(key); |
166 | refresh(); | 33 | refresh(); |
@@ -168,37 +35,51 @@ class RxMap<K, V> implements RxInterface<Map<K, V>>, Map<K, V> { | @@ -168,37 +35,51 @@ class RxMap<K, V> implements RxInterface<Map<K, V>>, Map<K, V> { | ||
168 | } | 35 | } |
169 | 36 | ||
170 | @override | 37 | @override |
171 | - void removeWhere(bool Function(K, V) test) { | ||
172 | - _value.removeWhere(test); | ||
173 | - refresh(); | 38 | + @protected |
39 | + Map<K, V> get value { | ||
40 | + if (getObs != null) { | ||
41 | + getObs.addListener(subject); | ||
42 | + } | ||
43 | + return _value; | ||
174 | } | 44 | } |
175 | 45 | ||
176 | - @override | ||
177 | - Iterable<V> get values => value.values; | ||
178 | - | ||
179 | - @override | ||
180 | - String toString() => _value.toString(); | 46 | + void assign(K key, V val) { |
47 | + _value.clear(); | ||
48 | + _value[key] = val; | ||
49 | + refresh(); | ||
50 | + } | ||
181 | 51 | ||
182 | - @override | ||
183 | - V update(K key, V Function(V) update, {V Function() ifAbsent}) { | ||
184 | - final val = _value.update(key, update, ifAbsent: ifAbsent); | 52 | + void assignAll(Map<K, V> val) { |
53 | + if (_value == val) return; | ||
54 | + _value = val; | ||
185 | refresh(); | 55 | refresh(); |
186 | - return val; | ||
187 | } | 56 | } |
188 | 57 | ||
189 | @override | 58 | @override |
190 | - void updateAll(V Function(K, V) update) { | ||
191 | - _value.updateAll(update); | 59 | + @protected |
60 | + @Deprecated('Map.value is deprecated. use [yourMap.assignAll(newMap)]') | ||
61 | + set value(Map<K, V> val) { | ||
62 | + if (_value == val) return; | ||
63 | + _value = val; | ||
192 | refresh(); | 64 | refresh(); |
193 | } | 65 | } |
66 | + | ||
67 | + void addIf(dynamic condition, K key, V value) { | ||
68 | + if (condition is Condition) condition = condition(); | ||
69 | + if (condition is bool && condition) { | ||
70 | + _value[key] = value; | ||
71 | + refresh(); | ||
72 | + } | ||
73 | + } | ||
74 | + | ||
75 | + void addAllIf(dynamic condition, Map<K, V> values) { | ||
76 | + if (condition is Condition) condition = condition(); | ||
77 | + if (condition is bool && condition) addAll(values); | ||
78 | + } | ||
194 | } | 79 | } |
195 | 80 | ||
196 | extension MapExtension<K, V> on Map<K, V> { | 81 | extension MapExtension<K, V> on Map<K, V> { |
197 | RxMap<K, V> get obs { | 82 | RxMap<K, V> get obs { |
198 | - if (this != null) { | ||
199 | - return RxMap<K, V>(<K, V>{})..addAll(this); | ||
200 | - } else { | ||
201 | - return RxMap<K, V>(null); | ||
202 | - } | 83 | + return RxMap<K, V>(this); |
203 | } | 84 | } |
204 | } | 85 | } |
1 | +part of rx_types; | ||
2 | + | ||
3 | +class RxSet<E> extends SetMixin<E> | ||
4 | + with RxObjectMixin<Set<E>> | ||
5 | + implements RxInterface<Set<E>> { | ||
6 | + RxSet([Set<E> initial]) { | ||
7 | + if (initial != null) _value = initial; | ||
8 | + } | ||
9 | + | ||
10 | + /// Adds [item] only if [condition] resolves to true. | ||
11 | + void addIf(dynamic condition, E item) { | ||
12 | + if (condition is Condition) condition = condition(); | ||
13 | + if (condition is bool && condition) add(item); | ||
14 | + } | ||
15 | + | ||
16 | + /// Adds all [items] only if [condition] resolves to true. | ||
17 | + void addAllIf(dynamic condition, Iterable<E> items) { | ||
18 | + if (condition is Condition) condition = condition(); | ||
19 | + if (condition is bool && condition) addAll(items); | ||
20 | + } | ||
21 | + | ||
22 | + /// Special override to push() element(s) in a reactive way | ||
23 | + /// inside the List, | ||
24 | + RxSet<E> operator +(Set<E> val) { | ||
25 | + addAll(val); | ||
26 | + refresh(); | ||
27 | + return this; | ||
28 | + } | ||
29 | + | ||
30 | + /// Adds only if [item] is not null. | ||
31 | + void addNonNull(E item) { | ||
32 | + if (item != null) add(item); | ||
33 | + } | ||
34 | + | ||
35 | + /// Adds only if [item] is not null. | ||
36 | + void addAllNonNull(Iterable<E> item) { | ||
37 | + if (item != null) addAll(item); | ||
38 | + } | ||
39 | + | ||
40 | + /// Replaces all existing items of this list with [item] | ||
41 | + void assign(E item) { | ||
42 | + clear(); | ||
43 | + add(item); | ||
44 | + } | ||
45 | + | ||
46 | + void update(void fn(Iterable<E> value)) { | ||
47 | + fn(value); | ||
48 | + refresh(); | ||
49 | + } | ||
50 | + | ||
51 | + /// Replaces all existing items of this list with [items] | ||
52 | + void assignAll(Iterable<E> items) { | ||
53 | + clear(); | ||
54 | + addAll(items); | ||
55 | + } | ||
56 | + | ||
57 | + @override | ||
58 | + @protected | ||
59 | + Set<E> get value { | ||
60 | + if (getObs != null) { | ||
61 | + getObs.addListener(subject); | ||
62 | + } | ||
63 | + return _value; | ||
64 | + } | ||
65 | + | ||
66 | + @override | ||
67 | + @protected | ||
68 | + set value(Set<E> val) { | ||
69 | + if (_value == val) return; | ||
70 | + _value = val; | ||
71 | + refresh(); | ||
72 | + } | ||
73 | + | ||
74 | + | ||
75 | + | ||
76 | + @override | ||
77 | + bool add(E value) { | ||
78 | + final val = _value.add(value); | ||
79 | + refresh(); | ||
80 | + return val; | ||
81 | + } | ||
82 | + | ||
83 | + @override | ||
84 | + bool contains(Object element) { | ||
85 | + return value.contains(element); | ||
86 | + } | ||
87 | + | ||
88 | + @override | ||
89 | + Iterator<E> get iterator => value.iterator; | ||
90 | + | ||
91 | + @override | ||
92 | + int get length => value.length; | ||
93 | + | ||
94 | + @override | ||
95 | + E lookup(Object object) { | ||
96 | + return value.lookup(object); | ||
97 | + } | ||
98 | + | ||
99 | + @override | ||
100 | + bool remove(Object item) { | ||
101 | + var hasRemoved = _value.remove(item); | ||
102 | + if (hasRemoved) { | ||
103 | + refresh(); | ||
104 | + } | ||
105 | + return hasRemoved; | ||
106 | + } | ||
107 | + | ||
108 | + @override | ||
109 | + Set<E> toSet() { | ||
110 | + return value.toSet(); | ||
111 | + } | ||
112 | + | ||
113 | + @override | ||
114 | + void addAll(Iterable<E> item) { | ||
115 | + _value.addAll(item); | ||
116 | + refresh(); | ||
117 | + } | ||
118 | + | ||
119 | + @override | ||
120 | + void clear() { | ||
121 | + _value.clear(); | ||
122 | + refresh(); | ||
123 | + } | ||
124 | + | ||
125 | + @override | ||
126 | + void removeAll(Iterable<Object> elements) { | ||
127 | + _value.removeAll(elements); | ||
128 | + refresh(); | ||
129 | + } | ||
130 | + | ||
131 | + @override | ||
132 | + void retainAll(Iterable<Object> elements) { | ||
133 | + _value.retainAll(elements); | ||
134 | + refresh(); | ||
135 | + } | ||
136 | + | ||
137 | + @override | ||
138 | + void retainWhere(bool Function(E) E) { | ||
139 | + _value.retainWhere(E); | ||
140 | + refresh(); | ||
141 | + } | ||
142 | +} | ||
143 | + | ||
144 | +// class RxSet<E> implements Set<E>, RxInterface<Set<E>> { | ||
145 | +// RxSet([Set<E> initial]) { | ||
146 | +// if (initial != null) _value = initial; | ||
147 | +// } | ||
148 | + | ||
149 | +// Set<E> _value = <E>{}; | ||
150 | + | ||
151 | +// @override | ||
152 | +// Iterator<E> get iterator => value.iterator; | ||
153 | + | ||
154 | +// @override | ||
155 | +// bool get isEmpty => value.isEmpty; | ||
156 | + | ||
157 | +// bool get canUpdate { | ||
158 | +// return _subscriptions.length > 0; | ||
159 | +// } | ||
160 | + | ||
161 | +// @override | ||
162 | +// bool get isNotEmpty => value.isNotEmpty; | ||
163 | + | ||
164 | +// StreamController<Set<E>> subject = StreamController<Set<E>>.broadcast(); | ||
165 | +// final _subscriptions = HashMap<Stream<Set<E>>, StreamSubscription>(); | ||
166 | + | ||
167 | +// /// Adds [item] only if [condition] resolves to true. | ||
168 | +// void addIf(dynamic condition, E item) { | ||
169 | +// if (condition is Condition) condition = condition(); | ||
170 | +// if (condition is bool && condition) add(item); | ||
171 | +// } | ||
172 | + | ||
173 | +// /// Adds all [items] only if [condition] resolves to true. | ||
174 | +// void addAllIf(dynamic condition, Iterable<E> items) { | ||
175 | +// if (condition is Condition) condition = condition(); | ||
176 | +// if (condition is bool && condition) addAll(items); | ||
177 | +// } | ||
178 | + | ||
179 | +// void refresh() { | ||
180 | +// subject.add(_value); | ||
181 | +// } | ||
182 | + | ||
183 | +// /// Special override to push() element(s) in a reactive way | ||
184 | +// /// inside the List, | ||
185 | +// RxSet<E> operator +(Set<E> val) { | ||
186 | +// addAll(val); | ||
187 | +// refresh(); | ||
188 | +// return this; | ||
189 | +// } | ||
190 | + | ||
191 | +// @override | ||
192 | +// bool add(E value) { | ||
193 | +// final val = _value.add(value); | ||
194 | +// refresh(); | ||
195 | +// return val; | ||
196 | +// } | ||
197 | + | ||
198 | +// @override | ||
199 | +// void addAll(Iterable<E> item) { | ||
200 | +// _value.addAll(item); | ||
201 | +// refresh(); | ||
202 | +// } | ||
203 | + | ||
204 | +// /// Adds only if [item] is not null. | ||
205 | +// void addNonNull(E item) { | ||
206 | +// if (item != null) add(item); | ||
207 | +// } | ||
208 | + | ||
209 | +// /// Adds only if [item] is not null. | ||
210 | +// void addAllNonNull(Iterable<E> item) { | ||
211 | +// if (item != null) addAll(item); | ||
212 | +// } | ||
213 | + | ||
214 | +// int get length => value.length; | ||
215 | + | ||
216 | +// /// Removes an item from the list. | ||
217 | +// /// | ||
218 | +// /// This is O(N) in the number of items in the list. | ||
219 | +// /// | ||
220 | +// /// Returns whether the item was present in the list. | ||
221 | +// bool remove(Object item) { | ||
222 | +// var hasRemoved = _value.remove(item); | ||
223 | +// if (hasRemoved) { | ||
224 | +// refresh(); | ||
225 | +// } | ||
226 | +// return hasRemoved; | ||
227 | +// } | ||
228 | + | ||
229 | +// void removeWhere(bool Function(E) test) { | ||
230 | +// _value.removeWhere(test); | ||
231 | +// refresh(); | ||
232 | +// } | ||
233 | + | ||
234 | +// void clear() { | ||
235 | +// _value.clear(); | ||
236 | +// refresh(); | ||
237 | +// } | ||
238 | + | ||
239 | +// void close() { | ||
240 | +// _subscriptions.forEach((observable, subscription) { | ||
241 | +// subscription.cancel(); | ||
242 | +// }); | ||
243 | +// _subscriptions.clear(); | ||
244 | +// subject.close(); | ||
245 | +// } | ||
246 | + | ||
247 | +// /// Replaces all existing items of this list with [item] | ||
248 | +// void assign(E item) { | ||
249 | +// clear(); | ||
250 | +// add(item); | ||
251 | +// } | ||
252 | + | ||
253 | +// void update(void fn(Iterable<E> value)) { | ||
254 | +// fn(value); | ||
255 | +// refresh(); | ||
256 | +// } | ||
257 | + | ||
258 | +// /// Replaces all existing items of this list with [items] | ||
259 | +// void assignAll(Iterable<E> items) { | ||
260 | +// clear(); | ||
261 | +// addAll(items); | ||
262 | +// } | ||
263 | + | ||
264 | +// @protected | ||
265 | +// Set<E> get value { | ||
266 | +// if (getObs != null) { | ||
267 | +// getObs.addListener(subject.stream); | ||
268 | +// } | ||
269 | +// return _value; | ||
270 | +// } | ||
271 | + | ||
272 | +// String get string => value.toString(); | ||
273 | + | ||
274 | +// void addListener(Stream<Set<E>> rxGetX) { | ||
275 | +// if (_subscriptions.containsKey(rxGetX)) { | ||
276 | +// return; | ||
277 | +// } | ||
278 | +// _subscriptions[rxGetX] = rxGetX.listen((data) { | ||
279 | +// subject.add(data); | ||
280 | +// }); | ||
281 | +// } | ||
282 | + | ||
283 | +// set value(Set<E> val) { | ||
284 | +// if (_value == val) return; | ||
285 | +// _value = val; | ||
286 | +// refresh(); | ||
287 | +// } | ||
288 | + | ||
289 | +// Stream<Set<E>> get stream => subject.stream; | ||
290 | + | ||
291 | +// StreamSubscription<Set<E>> listen(void Function(Set<E>) onData, | ||
292 | +// {Function onError, void Function() onDone, bool cancelOnError}) => | ||
293 | +// stream.listen(onData, onError: onError, onDone: onDone); | ||
294 | + | ||
295 | +// /// Binds an existing [Stream<Set>] to this [RxSet]. | ||
296 | +// /// You can bind multiple sources to update the value. | ||
297 | +// /// Closing the subscription will happen automatically when the observer | ||
298 | +// /// Widget ([GetX] or [Obx]) gets unmounted from the Widget tree. | ||
299 | +// void bindStream(Stream<Set<E>> stream) { | ||
300 | +// _subscriptions[stream] = stream.listen((va) => value = va); | ||
301 | +// } | ||
302 | + | ||
303 | +// @override | ||
304 | +// E get first => value.first; | ||
305 | + | ||
306 | +// @override | ||
307 | +// E get last => value.last; | ||
308 | + | ||
309 | +// @override | ||
310 | +// bool any(bool Function(E) test) { | ||
311 | +// return value.any(test); | ||
312 | +// } | ||
313 | + | ||
314 | +// @override | ||
315 | +// Set<R> cast<R>() { | ||
316 | +// return value.cast<R>(); | ||
317 | +// } | ||
318 | + | ||
319 | +// @override | ||
320 | +// bool contains(Object element) { | ||
321 | +// return value.contains(element); | ||
322 | +// } | ||
323 | + | ||
324 | +// @override | ||
325 | +// E elementAt(int index) { | ||
326 | +// return value.elementAt(index); | ||
327 | +// } | ||
328 | + | ||
329 | +// @override | ||
330 | +// bool every(bool Function(E) test) { | ||
331 | +// return value.every(test); | ||
332 | +// } | ||
333 | + | ||
334 | +// @override | ||
335 | +// Iterable<T> expand<T>(Iterable<T> Function(E) f) { | ||
336 | +// return value.expand(f); | ||
337 | +// } | ||
338 | + | ||
339 | +// @override | ||
340 | +// E firstWhere(bool Function(E) test, {E Function() orElse}) { | ||
341 | +// return value.firstWhere(test, orElse: orElse); | ||
342 | +// } | ||
343 | + | ||
344 | +// @override | ||
345 | +// T fold<T>(T initialValue, T Function(T, E) combine) { | ||
346 | +// return value.fold(initialValue, combine); | ||
347 | +// } | ||
348 | + | ||
349 | +// @override | ||
350 | +// Iterable<E> followedBy(Iterable<E> other) { | ||
351 | +// return value.followedBy(other); | ||
352 | +// } | ||
353 | + | ||
354 | +// @override | ||
355 | +// void forEach(void Function(E) f) { | ||
356 | +// value.forEach(f); | ||
357 | +// } | ||
358 | + | ||
359 | +// @override | ||
360 | +// String join([String separator = ""]) { | ||
361 | +// return value.join(separator); | ||
362 | +// } | ||
363 | + | ||
364 | +// @override | ||
365 | +// E lastWhere(bool Function(E) test, {E Function() orElse}) { | ||
366 | +// return value.lastWhere(test, orElse: orElse); | ||
367 | +// } | ||
368 | + | ||
369 | +// @override | ||
370 | +// Iterable<T> map<T>(T Function(E) f) { | ||
371 | +// return value.map(f); | ||
372 | +// } | ||
373 | + | ||
374 | +// @override | ||
375 | +// E reduce(E Function(E, E) combine) { | ||
376 | +// return value.reduce(combine); | ||
377 | +// } | ||
378 | + | ||
379 | +// @override | ||
380 | +// E get single => value.single; | ||
381 | + | ||
382 | +// @override | ||
383 | +// E singleWhere(bool Function(E) test, {E Function() orElse}) { | ||
384 | +// return value.singleWhere(test, orElse: orElse); | ||
385 | +// } | ||
386 | + | ||
387 | +// @override | ||
388 | +// Iterable<E> skip(int count) { | ||
389 | +// return value.skip(count); | ||
390 | +// } | ||
391 | + | ||
392 | +// @override | ||
393 | +// Iterable<E> skipWhile(bool Function(E) test) { | ||
394 | +// return value.skipWhile(test); | ||
395 | +// } | ||
396 | + | ||
397 | +// @override | ||
398 | +// Iterable<E> take(int count) { | ||
399 | +// return value.take(count); | ||
400 | +// } | ||
401 | + | ||
402 | +// @override | ||
403 | +// Iterable<E> takeWhile(bool Function(E) test) { | ||
404 | +// return value.takeWhile(test); | ||
405 | +// } | ||
406 | + | ||
407 | +// @override | ||
408 | +// List<E> toList({bool growable = true}) { | ||
409 | +// return value.toList(growable: growable); | ||
410 | +// } | ||
411 | + | ||
412 | +// @override | ||
413 | +// Set<E> toSet() { | ||
414 | +// return value.toSet(); | ||
415 | +// } | ||
416 | + | ||
417 | +// @override | ||
418 | +// Iterable<E> where(bool Function(E) test) { | ||
419 | +// return value.where(test); | ||
420 | +// } | ||
421 | + | ||
422 | +// @override | ||
423 | +// Iterable<T> whereType<T>() { | ||
424 | +// return value.whereType<T>(); | ||
425 | +// } | ||
426 | + | ||
427 | +// @override | ||
428 | +// bool containsAll(Iterable<Object> other) { | ||
429 | +// return value.containsAll(other); | ||
430 | +// } | ||
431 | + | ||
432 | +// @override | ||
433 | +// Set<E> difference(Set<Object> other) { | ||
434 | +// return value.difference(other); | ||
435 | +// } | ||
436 | + | ||
437 | +// @override | ||
438 | +// Set<E> intersection(Set<Object> other) { | ||
439 | +// return value.intersection(other); | ||
440 | +// } | ||
441 | + | ||
442 | +// @override | ||
443 | +// E lookup(Object object) { | ||
444 | +// return value.lookup(object); | ||
445 | +// } | ||
446 | + | ||
447 | +// @override | ||
448 | +// void removeAll(Iterable<Object> elements) { | ||
449 | +// _value.removeAll(elements); | ||
450 | +// refresh(); | ||
451 | +// } | ||
452 | + | ||
453 | +// @override | ||
454 | +// void retainAll(Iterable<Object> elements) { | ||
455 | +// _value.retainAll(elements); | ||
456 | +// refresh(); | ||
457 | +// } | ||
458 | + | ||
459 | +// @override | ||
460 | +// void retainWhere(bool Function(E) E) { | ||
461 | +// _value.retainWhere(E); | ||
462 | +// refresh(); | ||
463 | +// } | ||
464 | + | ||
465 | +// @override | ||
466 | +// Set<E> union(Set<E> other) { | ||
467 | +// return value.union(other); | ||
468 | +// } | ||
469 | +// } | ||
470 | + | ||
471 | +extension SetExtension<E> on Set<E> { | ||
472 | + RxSet<E> get obs { | ||
473 | + if (this != null) { | ||
474 | + return RxSet<E>(<E>{})..addAllNonNull(this); | ||
475 | + } else { | ||
476 | + return RxSet<E>(null); | ||
477 | + } | ||
478 | + } | ||
479 | +} |
lib/get_rx/src/rx_types/rx_types.dart
0 → 100644
1 | +library rx_types; | ||
2 | + | ||
3 | +import 'dart:async'; | ||
4 | +import 'dart:collection'; | ||
5 | + | ||
6 | +import 'package:flutter/foundation.dart'; | ||
7 | +import '../rx_stream/rx_stream.dart'; | ||
8 | +import '../rx_typedefs/rx_typedefs.dart'; | ||
9 | + | ||
10 | +part 'rx_core/rx_impl.dart'; | ||
11 | +part 'rx_core/rx_interface.dart'; | ||
12 | +part 'rx_core/rx_num.dart'; | ||
13 | + | ||
14 | +part 'rx_iterables/rx_list.dart'; | ||
15 | +part 'rx_iterables/rx_set.dart'; | ||
16 | +part 'rx_iterables/rx_map.dart'; |
1 | import 'dart:async'; | 1 | import 'dart:async'; |
2 | + | ||
2 | import '../../../get_core/get_core.dart'; | 3 | import '../../../get_core/get_core.dart'; |
3 | -import '../rx_core/rx_interface.dart'; | 4 | +import '../rx_types/rx_types.dart'; |
4 | import 'utils/debouncer.dart'; | 5 | import 'utils/debouncer.dart'; |
5 | 6 | ||
6 | bool _conditional(dynamic condition) { | 7 | bool _conditional(dynamic condition) { |
@@ -10,6 +11,8 @@ bool _conditional(dynamic condition) { | @@ -10,6 +11,8 @@ bool _conditional(dynamic condition) { | ||
10 | return true; | 11 | return true; |
11 | } | 12 | } |
12 | 13 | ||
14 | +typedef WorkerCallback<T> = Function(T callback); | ||
15 | + | ||
13 | /// | 16 | /// |
14 | /// Called every time [listener] changes. As long as the [condition] | 17 | /// Called every time [listener] changes. As long as the [condition] |
15 | /// returns true. | 18 | /// returns true. |
@@ -40,9 +43,9 @@ bool _conditional(dynamic condition) { | @@ -40,9 +43,9 @@ bool _conditional(dynamic condition) { | ||
40 | /// void increment() => count + 1; | 43 | /// void increment() => count + 1; |
41 | /// } | 44 | /// } |
42 | /// ``` | 45 | /// ``` |
43 | -Worker ever<T>(RxInterface<T> listener, Function(T) callback, | 46 | +Worker ever<T>(RxInterface<T> listener, WorkerCallback<T> callback, |
44 | {dynamic condition = true}) { | 47 | {dynamic condition = true}) { |
45 | - StreamSubscription sub = listener.subject.stream.listen((event) { | 48 | + StreamSubscription sub = listener.subject.listen((event) { |
46 | if (_conditional(condition)) callback(event); | 49 | if (_conditional(condition)) callback(event); |
47 | }); | 50 | }); |
48 | return Worker(sub.cancel, '[ever]'); | 51 | return Worker(sub.cancel, '[ever]'); |
@@ -52,11 +55,11 @@ Worker ever<T>(RxInterface<T> listener, Function(T) callback, | @@ -52,11 +55,11 @@ Worker ever<T>(RxInterface<T> listener, Function(T) callback, | ||
52 | /// for the [callback] is common to all [listeners], | 55 | /// for the [callback] is common to all [listeners], |
53 | /// and the [callback] is executed to each one of them. The [Worker] is | 56 | /// and the [callback] is executed to each one of them. The [Worker] is |
54 | /// common to all, so [worker.dispose()] will cancel all streams. | 57 | /// common to all, so [worker.dispose()] will cancel all streams. |
55 | -Worker everAll(List<RxInterface> listeners, Function(dynamic) callback, | 58 | +Worker everAll(List<RxInterface> listeners, WorkerCallback callback, |
56 | {dynamic condition = true}) { | 59 | {dynamic condition = true}) { |
57 | final evers = <StreamSubscription>[]; | 60 | final evers = <StreamSubscription>[]; |
58 | for (var i in listeners) { | 61 | for (var i in listeners) { |
59 | - final sub = i.subject.stream.listen((event) { | 62 | + final sub = i.subject.listen((event) { |
60 | if (_conditional(condition)) callback(event); | 63 | if (_conditional(condition)) callback(event); |
61 | }); | 64 | }); |
62 | evers.add(sub); | 65 | evers.add(sub); |
@@ -93,11 +96,11 @@ Worker everAll(List<RxInterface> listeners, Function(dynamic) callback, | @@ -93,11 +96,11 @@ Worker everAll(List<RxInterface> listeners, Function(dynamic) callback, | ||
93 | /// void increment() => count + 1; | 96 | /// void increment() => count + 1; |
94 | /// } | 97 | /// } |
95 | ///``` | 98 | ///``` |
96 | -Worker once<T>(RxInterface<T> listener, Function(T) callback, | 99 | +Worker once<T>(RxInterface<T> listener, WorkerCallback<T> callback, |
97 | {dynamic condition}) { | 100 | {dynamic condition}) { |
98 | Worker ref; | 101 | Worker ref; |
99 | StreamSubscription sub; | 102 | StreamSubscription sub; |
100 | - sub = listener.subject.stream.listen((event) { | 103 | + sub = listener.subject.listen((event) { |
101 | if (!_conditional(condition)) return; | 104 | if (!_conditional(condition)) return; |
102 | ref._disposed = true; | 105 | ref._disposed = true; |
103 | ref._log('called'); | 106 | ref._log('called'); |
@@ -125,11 +128,11 @@ Worker once<T>(RxInterface<T> listener, Function(T) callback, | @@ -125,11 +128,11 @@ Worker once<T>(RxInterface<T> listener, Function(T) callback, | ||
125 | /// condition: () => count < 20, | 128 | /// condition: () => count < 20, |
126 | /// ); | 129 | /// ); |
127 | /// ``` | 130 | /// ``` |
128 | -Worker interval<T>(RxInterface<T> listener, Function(T) callback, | 131 | +Worker interval<T>(RxInterface<T> listener, WorkerCallback<T> callback, |
129 | {Duration time = const Duration(seconds: 1), dynamic condition = true}) { | 132 | {Duration time = const Duration(seconds: 1), dynamic condition = true}) { |
130 | var debounceActive = false; | 133 | var debounceActive = false; |
131 | time ??= const Duration(seconds: 1); | 134 | time ??= const Duration(seconds: 1); |
132 | - StreamSubscription sub = listener.subject.stream.listen((event) async { | 135 | + StreamSubscription sub = listener.subject.listen((event) async { |
133 | if (debounceActive || !_conditional(condition)) return; | 136 | if (debounceActive || !_conditional(condition)) return; |
134 | debounceActive = true; | 137 | debounceActive = true; |
135 | await Future.delayed(time); | 138 | await Future.delayed(time); |
@@ -158,11 +161,11 @@ Worker interval<T>(RxInterface<T> listener, Function(T) callback, | @@ -158,11 +161,11 @@ Worker interval<T>(RxInterface<T> listener, Function(T) callback, | ||
158 | /// ); | 161 | /// ); |
159 | /// } | 162 | /// } |
160 | /// ``` | 163 | /// ``` |
161 | -Worker debounce<T>(RxInterface<T> listener, Function(T) callback, | 164 | +Worker debounce<T>(RxInterface<T> listener, WorkerCallback<T> callback, |
162 | {Duration time}) { | 165 | {Duration time}) { |
163 | final _debouncer = | 166 | final _debouncer = |
164 | Debouncer(delay: time ?? const Duration(milliseconds: 800)); | 167 | Debouncer(delay: time ?? const Duration(milliseconds: 800)); |
165 | - StreamSubscription sub = listener.subject.stream.listen((event) { | 168 | + StreamSubscription sub = listener.subject.listen((event) { |
166 | _debouncer(() { | 169 | _debouncer(() { |
167 | callback(event); | 170 | callback(event); |
168 | }); | 171 | }); |
@@ -10,11 +10,7 @@ import '../../../get_instance/src/lifecycle.dart'; | @@ -10,11 +10,7 @@ import '../../../get_instance/src/lifecycle.dart'; | ||
10 | /// it is Get.reset(). | 10 | /// it is Get.reset(). |
11 | abstract class GetxService extends DisposableInterface with GetxServiceMixin {} | 11 | abstract class GetxService extends DisposableInterface with GetxServiceMixin {} |
12 | 12 | ||
13 | -abstract class DisposableInterface with GetLifeCycle { | ||
14 | - DisposableInterface() { | ||
15 | - initLifeCycle(); | ||
16 | - } | ||
17 | - | 13 | +abstract class DisposableInterface extends GetLifeCycle { |
18 | /// Called immediately after the widget is allocated in memory. | 14 | /// Called immediately after the widget is allocated in memory. |
19 | /// You might use this to initialize something for the controller. | 15 | /// You might use this to initialize something for the controller. |
20 | @override | 16 | @override |
@@ -29,7 +25,9 @@ abstract class DisposableInterface with GetLifeCycle { | @@ -29,7 +25,9 @@ abstract class DisposableInterface with GetLifeCycle { | ||
29 | /// async request. | 25 | /// async request. |
30 | @override | 26 | @override |
31 | @mustCallSuper | 27 | @mustCallSuper |
32 | - void onReady() {} | 28 | + void onReady() { |
29 | + super.onReady(); | ||
30 | + } | ||
33 | 31 | ||
34 | /// Called before [onDelete] method. [onClose] might be used to | 32 | /// Called before [onDelete] method. [onClose] might be used to |
35 | /// dispose resources used by the controller. Like closing events, | 33 | /// dispose resources used by the controller. Like closing events, |
@@ -38,5 +36,7 @@ abstract class DisposableInterface with GetLifeCycle { | @@ -38,5 +36,7 @@ abstract class DisposableInterface with GetLifeCycle { | ||
38 | /// like TextEditingControllers, AnimationControllers. | 36 | /// like TextEditingControllers, AnimationControllers. |
39 | /// Might be useful as well to persist some data on disk. | 37 | /// Might be useful as well to persist some data on disk. |
40 | @override | 38 | @override |
41 | - void onClose() {} | 39 | + void onClose() { |
40 | + super.onClose(); | ||
41 | + } | ||
42 | } | 42 | } |
@@ -4,7 +4,7 @@ import 'package:flutter/widgets.dart'; | @@ -4,7 +4,7 @@ import 'package:flutter/widgets.dart'; | ||
4 | 4 | ||
5 | import '../../../get_core/get_core.dart'; | 5 | import '../../../get_core/get_core.dart'; |
6 | import '../../../get_instance/src/get_instance.dart'; | 6 | import '../../../get_instance/src/get_instance.dart'; |
7 | -import '../../../get_rx/get_rx.dart'; | 7 | +import '../../../get_rx/src/rx_types/rx_types.dart'; |
8 | import '../../get_state_manager.dart'; | 8 | import '../../get_state_manager.dart'; |
9 | 9 | ||
10 | typedef GetXControllerBuilder<T extends DisposableInterface> = Widget Function( | 10 | typedef GetXControllerBuilder<T extends DisposableInterface> = Widget Function( |
@@ -38,10 +38,11 @@ class GetX<T extends DisposableInterface> extends StatefulWidget { | @@ -38,10 +38,11 @@ class GetX<T extends DisposableInterface> extends StatefulWidget { | ||
38 | // this.streamController | 38 | // this.streamController |
39 | }); | 39 | }); |
40 | 40 | ||
41 | - GetImplXState<T> createState() => GetImplXState<T>(); | 41 | + @override |
42 | + GetXState<T> createState() => GetXState<T>(); | ||
42 | } | 43 | } |
43 | 44 | ||
44 | -class GetImplXState<T extends DisposableInterface> extends State<GetX<T>> { | 45 | +class GetXState<T extends DisposableInterface> extends State<GetX<T>> { |
45 | RxInterface _observer; | 46 | RxInterface _observer; |
46 | T controller; | 47 | T controller; |
47 | bool isCreator = false; | 48 | bool isCreator = false; |
@@ -76,7 +77,7 @@ class GetImplXState<T extends DisposableInterface> extends State<GetX<T>> { | @@ -76,7 +77,7 @@ class GetImplXState<T extends DisposableInterface> extends State<GetX<T>> { | ||
76 | if (widget.global && Get.smartManagement == SmartManagement.onlyBuilder) { | 77 | if (widget.global && Get.smartManagement == SmartManagement.onlyBuilder) { |
77 | controller?.onStart(); | 78 | controller?.onStart(); |
78 | } | 79 | } |
79 | - subs = _observer.subject.stream.listen((data) => setState(() {})); | 80 | + subs = _observer.subject.listen((data) => setState(() {})); |
80 | super.initState(); | 81 | super.initState(); |
81 | } | 82 | } |
82 | 83 |
@@ -5,29 +5,101 @@ import '../../../instance_manager.dart'; | @@ -5,29 +5,101 @@ import '../../../instance_manager.dart'; | ||
5 | import '../../get_state_manager.dart'; | 5 | import '../../get_state_manager.dart'; |
6 | import '../simple/list_notifier.dart'; | 6 | import '../simple/list_notifier.dart'; |
7 | 7 | ||
8 | -class Value<T> extends ListNotifier implements ValueListenable<T> { | ||
9 | - Value(this._value); | 8 | +mixin StateMixin<T> on ListNotifier { |
9 | + T _value; | ||
10 | + RxStatus _status; | ||
11 | + | ||
12 | + bool _isNullOrEmpty(dynamic val) { | ||
13 | + if (val == null) return true; | ||
14 | + var result = false; | ||
15 | + if (val is Iterable) { | ||
16 | + result = val.isEmpty; | ||
17 | + } else if (val is String) { | ||
18 | + result = val.isEmpty; | ||
19 | + } else if (val is Map) { | ||
20 | + result = val.isEmpty; | ||
21 | + } | ||
22 | + return result; | ||
23 | + } | ||
10 | 24 | ||
25 | + void _fillEmptyStatus() { | ||
26 | + _status = _isNullOrEmpty(_value) ? RxStatus.loading() : RxStatus.success(); | ||
27 | + } | ||
28 | + | ||
29 | + RxStatus get status { | ||
30 | + notifyChildrens(); | ||
31 | + return _status ??= _status = RxStatus.loading(); | ||
32 | + } | ||
33 | + | ||
34 | + T get state => value; | ||
35 | + | ||
36 | + @protected | ||
11 | T get value { | 37 | T get value { |
12 | notifyChildrens(); | 38 | notifyChildrens(); |
13 | return _value; | 39 | return _value; |
14 | } | 40 | } |
15 | 41 | ||
16 | - @override | ||
17 | - String toString() => value.toString(); | 42 | + @protected |
43 | + set value(T newValue) { | ||
44 | + if (_value == newValue) return; | ||
45 | + _value = newValue; | ||
46 | + refresh(); | ||
47 | + } | ||
18 | 48 | ||
19 | - T _value; | 49 | + @protected |
50 | + void change(T newState, {RxStatus status}) { | ||
51 | + var _canUpdate = false; | ||
52 | + if (status != null) { | ||
53 | + _status = status; | ||
54 | + _canUpdate = true; | ||
55 | + } | ||
56 | + if (newState != _value) { | ||
57 | + _value = newState; | ||
58 | + _canUpdate = true; | ||
59 | + } | ||
60 | + if (_canUpdate) { | ||
61 | + refresh(); | ||
62 | + } | ||
63 | + } | ||
64 | +} | ||
65 | + | ||
66 | +class Value<T> extends ListNotifier | ||
67 | + with StateMixin<T> | ||
68 | + implements ValueListenable<T> { | ||
69 | + Value(T val) { | ||
70 | + _value = val; | ||
71 | + _fillEmptyStatus(); | ||
72 | + } | ||
73 | + | ||
74 | + @override | ||
75 | + T get value { | ||
76 | + notifyChildrens(); | ||
77 | + return _value; | ||
78 | + } | ||
20 | 79 | ||
80 | + @override | ||
21 | set value(T newValue) { | 81 | set value(T newValue) { |
22 | if (_value == newValue) return; | 82 | if (_value == newValue) return; |
23 | _value = newValue; | 83 | _value = newValue; |
24 | - updater(); | 84 | + refresh(); |
85 | + } | ||
86 | + | ||
87 | + T call([T v]) { | ||
88 | + if (v != null) { | ||
89 | + value = v; | ||
90 | + } | ||
91 | + return value; | ||
25 | } | 92 | } |
26 | 93 | ||
27 | void update(void fn(T value)) { | 94 | void update(void fn(T value)) { |
28 | fn(value); | 95 | fn(value); |
29 | - updater(); | 96 | + refresh(); |
30 | } | 97 | } |
98 | + | ||
99 | + @override | ||
100 | + String toString() => value.toString(); | ||
101 | + | ||
102 | + dynamic toJson() => (value as dynamic)?.toJson(); | ||
31 | } | 103 | } |
32 | 104 | ||
33 | extension ReactiveT<T> on T { | 105 | extension ReactiveT<T> on T { |
@@ -36,10 +108,9 @@ extension ReactiveT<T> on T { | @@ -36,10 +108,9 @@ extension ReactiveT<T> on T { | ||
36 | 108 | ||
37 | typedef Condition = bool Function(); | 109 | typedef Condition = bool Function(); |
38 | 110 | ||
39 | -abstract class GetNotifier<T> extends Value<T> with GetLifeCycle { | 111 | +abstract class GetNotifier<T> extends Value<T> with GetLifeCycleBase { |
40 | GetNotifier(T initial) : super(initial) { | 112 | GetNotifier(T initial) : super(initial) { |
41 | - initLifeCycle(); | ||
42 | - _fillEmptyStatus(); | 113 | + $configureLifeCycle(); |
43 | } | 114 | } |
44 | 115 | ||
45 | @override | 116 | @override |
@@ -48,62 +119,27 @@ abstract class GetNotifier<T> extends Value<T> with GetLifeCycle { | @@ -48,62 +119,27 @@ abstract class GetNotifier<T> extends Value<T> with GetLifeCycle { | ||
48 | super.onInit(); | 119 | super.onInit(); |
49 | SchedulerBinding.instance?.addPostFrameCallback((_) => onReady()); | 120 | SchedulerBinding.instance?.addPostFrameCallback((_) => onReady()); |
50 | } | 121 | } |
122 | +} | ||
51 | 123 | ||
52 | - RxStatus _status; | ||
53 | - | ||
54 | - bool get isNullOrEmpty { | ||
55 | - if (_value == null) return true; | ||
56 | - dynamic val = _value; | ||
57 | - var result = false; | ||
58 | - if (val is Iterable) { | ||
59 | - result = val.isEmpty; | ||
60 | - } else if (val is String) { | ||
61 | - result = val.isEmpty; | ||
62 | - } else if (val is Map) { | ||
63 | - result = val.isEmpty; | ||
64 | - } | ||
65 | - return result; | ||
66 | - } | ||
67 | - | ||
68 | - void _fillEmptyStatus() { | ||
69 | - _status = isNullOrEmpty ? RxStatus.loading() : RxStatus.success(); | ||
70 | - } | ||
71 | - | ||
72 | - RxStatus get status { | ||
73 | - notifyChildrens(); | ||
74 | - return _status; | ||
75 | - } | ||
76 | - | ||
77 | - Widget call(NotifierBuilder<T> widget, {Widget onError, Widget onLoading}) { | 124 | +extension StateExt<T> on StateMixin<T> { |
125 | + Widget obx( | ||
126 | + NotifierBuilder<T> widget, { | ||
127 | + Widget Function(String error) onError, | ||
128 | + Widget onLoading, | ||
129 | + }) { | ||
78 | assert(widget != null); | 130 | assert(widget != null); |
79 | return SimpleBuilder(builder: (_) { | 131 | return SimpleBuilder(builder: (_) { |
80 | if (status.isLoading) { | 132 | if (status.isLoading) { |
81 | - return onLoading ?? CircularProgressIndicator(); | 133 | + return onLoading ?? Center(child: CircularProgressIndicator()); |
82 | } else if (status.isError) { | 134 | } else if (status.isError) { |
83 | - return onError ?? Text('A error occured: ${status.errorMessage}'); | 135 | + return onError != null |
136 | + ? onError(status.errorMessage) | ||
137 | + : Center(child: Text('A error occured: ${status.errorMessage}')); | ||
84 | } else { | 138 | } else { |
85 | return widget(value); | 139 | return widget(value); |
86 | } | 140 | } |
87 | }); | 141 | }); |
88 | } | 142 | } |
89 | - | ||
90 | - @protected | ||
91 | - void change(T newState, {RxStatus status}) { | ||
92 | - var _canUpdate = false; | ||
93 | - if (status != null) { | ||
94 | - _status = status; | ||
95 | - _canUpdate = true; | ||
96 | - } | ||
97 | - if (newState != _value) { | ||
98 | - _value = newState; | ||
99 | - _canUpdate = true; | ||
100 | - } | ||
101 | - if (_canUpdate) { | ||
102 | - updater(); | ||
103 | - } | ||
104 | - } | ||
105 | - | ||
106 | - dynamic toJson() => (value as dynamic)?.toJson(); | ||
107 | } | 143 | } |
108 | 144 | ||
109 | class RxStatus { | 145 | class RxStatus { |
1 | import 'dart:async'; | 1 | import 'dart:async'; |
2 | import 'package:flutter/widgets.dart'; | 2 | import 'package:flutter/widgets.dart'; |
3 | -import '../../../get_rx/get_rx.dart'; | 3 | +import '../../../get_rx/src/rx_types/rx_types.dart'; |
4 | 4 | ||
5 | typedef WidgetCallback = Widget Function(); | 5 | typedef WidgetCallback = Widget Function(); |
6 | 6 | ||
@@ -12,6 +12,7 @@ typedef WidgetCallback = Widget Function(); | @@ -12,6 +12,7 @@ typedef WidgetCallback = Widget Function(); | ||
12 | abstract class ObxWidget extends StatefulWidget { | 12 | abstract class ObxWidget extends StatefulWidget { |
13 | const ObxWidget({Key key}) : super(key: key); | 13 | const ObxWidget({Key key}) : super(key: key); |
14 | 14 | ||
15 | + @override | ||
15 | _ObxState createState() => _ObxState(); | 16 | _ObxState createState() => _ObxState(); |
16 | 17 | ||
17 | @protected | 18 | @protected |
@@ -28,7 +29,7 @@ class _ObxState extends State<ObxWidget> { | @@ -28,7 +29,7 @@ class _ObxState extends State<ObxWidget> { | ||
28 | 29 | ||
29 | @override | 30 | @override |
30 | void initState() { | 31 | void initState() { |
31 | - subs = _observer.subject.stream.listen((data) => setState(() {})); | 32 | + subs = _observer.subject.listen((data) => setState(() {})); |
32 | super.initState(); | 33 | super.initState(); |
33 | } | 34 | } |
34 | 35 |
@@ -21,5 +21,6 @@ import '../../get_state_manager.dart'; | @@ -21,5 +21,6 @@ import '../../get_state_manager.dart'; | ||
21 | /// ``` | 21 | /// ``` |
22 | mixin SingleGetTickerProviderMixin on DisposableInterface | 22 | mixin SingleGetTickerProviderMixin on DisposableInterface |
23 | implements TickerProvider { | 23 | implements TickerProvider { |
24 | + @override | ||
24 | Ticker createTicker(TickerCallback onTick) => Ticker(onTick); | 25 | Ticker createTicker(TickerCallback onTick) => Ticker(onTick); |
25 | } | 26 | } |
1 | -import 'dart:collection'; | ||
2 | import 'package:flutter/material.dart'; | 1 | import 'package:flutter/material.dart'; |
3 | import '../../../get_core/get_core.dart'; | 2 | import '../../../get_core/get_core.dart'; |
4 | import '../../../get_instance/src/get_instance.dart'; | 3 | import '../../../get_instance/src/get_instance.dart'; |
5 | import '../../get_state_manager.dart'; | 4 | import '../../get_state_manager.dart'; |
6 | - | ||
7 | -// Changed to VoidCallback. | ||
8 | -//typedef Disposer = void Function(); | ||
9 | - | ||
10 | -// replacing StateSetter, return if the Widget is mounted for extra validation. | ||
11 | -// if it brings overhead the extra call, | ||
12 | -typedef GetStateUpdate = void Function(); | ||
13 | -//typedef GetStateUpdate = void Function(VoidCallback fn); | 5 | +import 'list_notifier.dart'; |
14 | 6 | ||
15 | /// Complies with [GetStateUpdater] | 7 | /// Complies with [GetStateUpdater] |
16 | /// | 8 | /// |
@@ -31,14 +23,8 @@ mixin GetStateUpdaterMixin<T extends StatefulWidget> on State<T> { | @@ -31,14 +23,8 @@ mixin GetStateUpdaterMixin<T extends StatefulWidget> on State<T> { | ||
31 | } | 23 | } |
32 | } | 24 | } |
33 | 25 | ||
34 | -class GetxController extends DisposableInterface { | ||
35 | - final _updaters = <GetStateUpdate>[]; | ||
36 | - | ||
37 | -// final _updatersIds = HashMap<String, StateSetter>(); //<old> | ||
38 | - final _updatersIds = HashMap<String, GetStateUpdate>(); | ||
39 | - | ||
40 | - final _updatersGroupIds = HashMap<String, List<GetStateUpdate>>(); | ||
41 | - | 26 | +// ignore: prefer_mixin |
27 | +class GetxController extends DisposableInterface with ListNotifier { | ||
42 | /// Rebuilds [GetBuilder] each time you call [update()]; | 28 | /// Rebuilds [GetBuilder] each time you call [update()]; |
43 | /// Can take a List of [ids], that will only update the matching | 29 | /// Can take a List of [ids], that will only update the matching |
44 | /// `GetBuilder( id: )`, | 30 | /// `GetBuilder( id: )`, |
@@ -49,73 +35,13 @@ class GetxController extends DisposableInterface { | @@ -49,73 +35,13 @@ class GetxController extends DisposableInterface { | ||
49 | return; | 35 | return; |
50 | } | 36 | } |
51 | if (ids == null) { | 37 | if (ids == null) { |
52 | -// _updaters?.forEach((rs) => rs(() {})); //<old> | ||
53 | - for (final updater in _updaters) { | ||
54 | - updater(); | ||
55 | - } | 38 | + refresh(); |
56 | } else { | 39 | } else { |
57 | - // @jonny, remove this commented code if it's not more optimized. | ||
58 | -// for (final id in ids) { | ||
59 | -// if (_updatersIds[id] != null) _updatersIds[id](); | ||
60 | -// if (_updatersGroupIds[id] != null) | ||
61 | -// for (final rs in _updatersGroupIds[id]) rs(); | ||
62 | -// } | ||
63 | - | ||
64 | for (final id in ids) { | 40 | for (final id in ids) { |
65 | - _updatersIds[id]?.call(); | ||
66 | - // ignore: avoid_function_literals_in_foreach_calls | ||
67 | - _updatersGroupIds[id]?.forEach((rs) => rs()); | 41 | + refreshGroup(id); |
68 | } | 42 | } |
69 | } | 43 | } |
70 | } | 44 | } |
71 | - | ||
72 | -// VoidCallback addListener(StateSetter listener) {//<old> | ||
73 | - VoidCallback addListener(GetStateUpdate listener) { | ||
74 | - _updaters.add(listener); | ||
75 | - return () => _updaters.remove(listener); | ||
76 | - } | ||
77 | - | ||
78 | -// VoidCallback addListenerId(String key, StateSetter listener) {//<old> | ||
79 | - VoidCallback addListenerId(String key, GetStateUpdate listener) { | ||
80 | -// _printCurrentIds(); | ||
81 | - if (_updatersIds.containsKey(key)) { | ||
82 | - _updatersGroupIds[key] ??= <GetStateUpdate>[]; | ||
83 | - _updatersGroupIds[key].add(listener); | ||
84 | - return () { | ||
85 | - _updatersGroupIds[key].remove(listener); | ||
86 | - }; | ||
87 | - } else { | ||
88 | - _updatersIds[key] = listener; | ||
89 | - return () => _updatersIds.remove(key); | ||
90 | - } | ||
91 | - } | ||
92 | - | ||
93 | - /// To dispose an [id] from future updates(), this ids are registered | ||
94 | - /// by [GetBuilder()] or similar, so is a way to unlink the state change with | ||
95 | - /// the Widget from the Controller. | ||
96 | - void disposeId(String id) { | ||
97 | - _updatersIds.remove(id); | ||
98 | - _updatersGroupIds.remove(id); | ||
99 | - } | ||
100 | - | ||
101 | - /// Remove this after checking the new implementation makes sense. | ||
102 | - /// Uncomment this if you wanna control the removal of ids.. | ||
103 | - /// bool _debugging = false; | ||
104 | - /// Future<void> _printCurrentIds() async { | ||
105 | - /// if (_debugging) return; | ||
106 | - /// _debugging = true; | ||
107 | - /// print('about to debug...'); | ||
108 | - /// await Future.delayed(Duration(milliseconds: 10)); | ||
109 | - /// int totalGroups = 0; | ||
110 | - /// _updatersGroupIds.forEach((key, value) { | ||
111 | - /// totalGroups += value.length; | ||
112 | - /// }); | ||
113 | - /// int totalIds = _updatersIds.length; | ||
114 | - /// print( | ||
115 | - /// 'Total: ${totalIds + totalGroups},'+ | ||
116 | - /// 'in groups:$totalGroups, solo ids:$totalIds',); | ||
117 | - /// _debugging = false; | ||
118 | - /// } | ||
119 | } | 45 | } |
120 | 46 | ||
121 | typedef GetControllerBuilder<T extends DisposableInterface> = Widget Function( | 47 | typedef GetControllerBuilder<T extends DisposableInterface> = Widget Function( |
@@ -187,10 +113,6 @@ class _GetBuilderState<T extends GetxController> extends State<GetBuilder<T>> | @@ -187,10 +113,6 @@ class _GetBuilderState<T extends GetxController> extends State<GetBuilder<T>> | ||
187 | controller?.onStart(); | 113 | controller?.onStart(); |
188 | } | 114 | } |
189 | 115 | ||
190 | - // if (widget.global && Get.smartManagement == | ||
191 | - //SmartManagement.onlyBuilder) { | ||
192 | - // controller?.onStart(); | ||
193 | - // } | ||
194 | _subscribeToController(); | 116 | _subscribeToController(); |
195 | } | 117 | } |
196 | 118 | ||
@@ -200,20 +122,10 @@ class _GetBuilderState<T extends GetxController> extends State<GetBuilder<T>> | @@ -200,20 +122,10 @@ class _GetBuilderState<T extends GetxController> extends State<GetBuilder<T>> | ||
200 | void _subscribeToController() { | 122 | void _subscribeToController() { |
201 | remove?.call(); | 123 | remove?.call(); |
202 | remove = (widget.id == null) | 124 | remove = (widget.id == null) |
203 | -// ? controller?.addListener(setState) //<old> | ||
204 | -// : controller?.addListenerId(widget.id, setState); //<old> | ||
205 | ? controller?.addListener(getUpdate) | 125 | ? controller?.addListener(getUpdate) |
206 | : controller?.addListenerId(widget.id, getUpdate); | 126 | : controller?.addListenerId(widget.id, getUpdate); |
207 | } | 127 | } |
208 | 128 | ||
209 | - /// Sample for [GetStateUpdate] when you don't wanna | ||
210 | - /// use [GetStateHelper mixin]. | ||
211 | - /// bool _getUpdater() { | ||
212 | - /// final _mounted = mounted; | ||
213 | - /// if (_mounted) setState(() {}); | ||
214 | - /// return _mounted; | ||
215 | - /// } | ||
216 | - | ||
217 | @override | 129 | @override |
218 | void dispose() { | 130 | void dispose() { |
219 | super.dispose(); | 131 | super.dispose(); |
@@ -249,26 +161,6 @@ class _GetBuilderState<T extends GetxController> extends State<GetBuilder<T>> | @@ -249,26 +161,6 @@ class _GetBuilderState<T extends GetxController> extends State<GetBuilder<T>> | ||
249 | Widget build(BuildContext context) => widget.builder(controller); | 161 | Widget build(BuildContext context) => widget.builder(controller); |
250 | } | 162 | } |
251 | 163 | ||
252 | -/// This is a experimental feature. | ||
253 | -/// Meant to be used with SimpleBuilder, it auto-registers the variable | ||
254 | -/// like Rx() does with Obx(). | ||
255 | -// class Value<T> extends GetxController { | ||
256 | -// Value([this._value]); | ||
257 | - | ||
258 | -// T _value; | ||
259 | - | ||
260 | -// T get value { | ||
261 | -// TaskManager.instance.notify(_updaters); | ||
262 | -// return _value; | ||
263 | -// } | ||
264 | - | ||
265 | -// set value(T newValue) { | ||
266 | -// if (_value == newValue) return; | ||
267 | -// _value = newValue; | ||
268 | -// update(); | ||
269 | -// } | ||
270 | -// } | ||
271 | - | ||
272 | /// It's Experimental class, the Api can be change | 164 | /// It's Experimental class, the Api can be change |
273 | abstract class GetState<T> extends GetxController { | 165 | abstract class GetState<T> extends GetxController { |
274 | GetState(T initialValue) { | 166 | GetState(T initialValue) { |
1 | +import 'dart:collection'; | ||
1 | import 'package:flutter/foundation.dart'; | 2 | import 'package:flutter/foundation.dart'; |
3 | +import 'package:flutter/widgets.dart'; | ||
2 | 4 | ||
3 | -import 'simple_builder.dart'; | 5 | +// This callback remove the listener on addListener function |
6 | +typedef Disposer = void Function(); | ||
7 | + | ||
8 | +// replacing StateSetter, return if the Widget is mounted for extra validation. | ||
9 | +// if it brings overhead the extra call, | ||
10 | +typedef GetStateUpdate = void Function(); | ||
4 | 11 | ||
5 | class ListNotifier implements Listenable { | 12 | class ListNotifier implements Listenable { |
6 | - List<VoidCallback> _listeners = <VoidCallback>[]; | 13 | + List<GetStateUpdate> _updaters = <GetStateUpdate>[]; |
14 | + | ||
15 | + HashMap<String, List<GetStateUpdate>> _updatersGroupIds = | ||
16 | + HashMap<String, List<GetStateUpdate>>(); | ||
7 | 17 | ||
8 | @protected | 18 | @protected |
9 | - void updater() { | 19 | + void refresh() { |
10 | assert(_debugAssertNotDisposed()); | 20 | assert(_debugAssertNotDisposed()); |
11 | - for (var element in _listeners) { | 21 | + for (var element in _updaters) { |
12 | element(); | 22 | element(); |
13 | } | 23 | } |
14 | } | 24 | } |
15 | 25 | ||
26 | + @protected | ||
27 | + void refreshGroup(String id) { | ||
28 | + assert(_debugAssertNotDisposed()); | ||
29 | + if (_updatersGroupIds.containsKey(id)) { | ||
30 | + for (var item in _updatersGroupIds[id]) { | ||
31 | + item(); | ||
32 | + } | ||
33 | + } | ||
34 | + } | ||
35 | + | ||
16 | bool _debugAssertNotDisposed() { | 36 | bool _debugAssertNotDisposed() { |
17 | assert(() { | 37 | assert(() { |
18 | - if (_listeners == null) { | 38 | + if (_updaters == null) { |
19 | throw FlutterError('''A $runtimeType was used after being disposed.\n | 39 | throw FlutterError('''A $runtimeType was used after being disposed.\n |
20 | 'Once you have called dispose() on a $runtimeType, it can no longer be used.'''); | 40 | 'Once you have called dispose() on a $runtimeType, it can no longer be used.'''); |
21 | } | 41 | } |
@@ -26,29 +46,87 @@ class ListNotifier implements Listenable { | @@ -26,29 +46,87 @@ class ListNotifier implements Listenable { | ||
26 | 46 | ||
27 | @protected | 47 | @protected |
28 | void notifyChildrens() { | 48 | void notifyChildrens() { |
29 | - TaskManager.instance.notify(_listeners); | 49 | + TaskManager.instance.notify(_updaters); |
30 | } | 50 | } |
31 | 51 | ||
32 | bool get hasListeners { | 52 | bool get hasListeners { |
33 | assert(_debugAssertNotDisposed()); | 53 | assert(_debugAssertNotDisposed()); |
34 | - return _listeners.isNotEmpty; | 54 | + return _updaters.isNotEmpty; |
35 | } | 55 | } |
36 | 56 | ||
37 | @override | 57 | @override |
38 | - void addListener(VoidCallback listener) { | 58 | + void removeListener(VoidCallback listener) { |
39 | assert(_debugAssertNotDisposed()); | 59 | assert(_debugAssertNotDisposed()); |
40 | - _listeners.add(listener); | 60 | + _updaters.remove(listener); |
41 | } | 61 | } |
42 | 62 | ||
43 | - @override | ||
44 | - void removeListener(VoidCallback listener) { | 63 | + void removeListenerId(String id, VoidCallback listener) { |
45 | assert(_debugAssertNotDisposed()); | 64 | assert(_debugAssertNotDisposed()); |
46 | - _listeners.remove(listener); | 65 | + if (_updatersGroupIds.containsKey(id)) { |
66 | + _updatersGroupIds[id].remove(listener); | ||
67 | + } | ||
68 | + _updaters.remove(listener); | ||
47 | } | 69 | } |
48 | 70 | ||
49 | @mustCallSuper | 71 | @mustCallSuper |
50 | void dispose() { | 72 | void dispose() { |
51 | assert(_debugAssertNotDisposed()); | 73 | assert(_debugAssertNotDisposed()); |
52 | - _listeners = null; | 74 | + _updaters = null; |
75 | + _updatersGroupIds = null; | ||
76 | + } | ||
77 | + | ||
78 | + @override | ||
79 | + Disposer addListener(GetStateUpdate listener) { | ||
80 | + assert(_debugAssertNotDisposed()); | ||
81 | + _updaters.add(listener); | ||
82 | + return () => _updaters.remove(listener); | ||
83 | + } | ||
84 | + | ||
85 | + Disposer addListenerId(String key, GetStateUpdate listener) { | ||
86 | + _updatersGroupIds[key] ??= <GetStateUpdate>[]; | ||
87 | + _updatersGroupIds[key].add(listener); | ||
88 | + return () => _updatersGroupIds[key].remove(listener); | ||
89 | + } | ||
90 | + | ||
91 | + /// To dispose an [id] from future updates(), this ids are registered | ||
92 | + /// by [GetBuilder()] or similar, so is a way to unlink the state change with | ||
93 | + /// the Widget from the Controller. | ||
94 | + void disposeId(String id) { | ||
95 | + _updatersGroupIds.remove(id); | ||
96 | + } | ||
97 | +} | ||
98 | + | ||
99 | +class TaskManager { | ||
100 | + TaskManager._(); | ||
101 | + | ||
102 | + static TaskManager _instance; | ||
103 | + | ||
104 | + static TaskManager get instance => _instance ??= TaskManager._(); | ||
105 | + | ||
106 | + GetStateUpdate _setter; | ||
107 | + | ||
108 | + List<VoidCallback> _remove; | ||
109 | + | ||
110 | + void notify(List<GetStateUpdate> _updaters) { | ||
111 | + if (_setter != null) { | ||
112 | + if (!_updaters.contains(_setter)) { | ||
113 | + _updaters.add(_setter); | ||
114 | + _remove.add(() => _updaters.remove(_setter)); | ||
115 | + } | ||
116 | + } | ||
117 | + } | ||
118 | + | ||
119 | + Widget exchange( | ||
120 | + List<VoidCallback> disposers, | ||
121 | + GetStateUpdate setState, | ||
122 | + Widget Function(BuildContext) builder, | ||
123 | + BuildContext context, | ||
124 | + ) { | ||
125 | + _remove = disposers; | ||
126 | + _setter = setState; | ||
127 | + final result = builder(context); | ||
128 | + _remove = null; | ||
129 | + _setter = null; | ||
130 | + return result; | ||
53 | } | 131 | } |
54 | } | 132 | } |
1 | import 'dart:async'; | 1 | import 'dart:async'; |
2 | import 'package:flutter/widgets.dart'; | 2 | import 'package:flutter/widgets.dart'; |
3 | import 'get_state.dart'; | 3 | import 'get_state.dart'; |
4 | +import 'list_notifier.dart'; | ||
4 | 5 | ||
5 | typedef ValueBuilderUpdateCallback<T> = void Function(T snapshot); | 6 | typedef ValueBuilderUpdateCallback<T> = void Function(T snapshot); |
6 | typedef ValueBuilderBuilder<T> = Widget Function( | 7 | typedef ValueBuilderBuilder<T> = Widget Function( |
@@ -87,7 +88,7 @@ class SimpleBuilder extends StatefulWidget { | @@ -87,7 +88,7 @@ class SimpleBuilder extends StatefulWidget { | ||
87 | 88 | ||
88 | class _SimpleBuilderState extends State<SimpleBuilder> | 89 | class _SimpleBuilderState extends State<SimpleBuilder> |
89 | with GetStateUpdaterMixin { | 90 | with GetStateUpdaterMixin { |
90 | - final disposers = <VoidCallback>[]; | 91 | + final disposers = <Disposer>[]; |
91 | 92 | ||
92 | @override | 93 | @override |
93 | void dispose() { | 94 | void dispose() { |
@@ -107,38 +108,3 @@ class _SimpleBuilderState extends State<SimpleBuilder> | @@ -107,38 +108,3 @@ class _SimpleBuilderState extends State<SimpleBuilder> | ||
107 | ); | 108 | ); |
108 | } | 109 | } |
109 | } | 110 | } |
110 | - | ||
111 | -class TaskManager { | ||
112 | - TaskManager._(); | ||
113 | - | ||
114 | - static TaskManager _instance; | ||
115 | - | ||
116 | - static TaskManager get instance => _instance ??= TaskManager._(); | ||
117 | - | ||
118 | - GetStateUpdate _setter; | ||
119 | - | ||
120 | - List<VoidCallback> _remove; | ||
121 | - | ||
122 | - void notify(List<GetStateUpdate> _updaters) { | ||
123 | - if (_setter != null) { | ||
124 | - if (!_updaters.contains(_setter)) { | ||
125 | - _updaters.add(_setter); | ||
126 | - _remove.add(() => _updaters.remove(_setter)); | ||
127 | - } | ||
128 | - } | ||
129 | - } | ||
130 | - | ||
131 | - Widget exchange( | ||
132 | - List<VoidCallback> disposers, | ||
133 | - GetStateUpdate setState, | ||
134 | - Widget Function(BuildContext) builder, | ||
135 | - BuildContext context, | ||
136 | - ) { | ||
137 | - _remove = disposers; | ||
138 | - _setter = setState; | ||
139 | - final result = builder(context); | ||
140 | - _remove = null; | ||
141 | - _setter = null; | ||
142 | - return result; | ||
143 | - } | ||
144 | -} |
@@ -94,6 +94,9 @@ class GetUtils { | @@ -94,6 +94,9 @@ class GetUtils { | ||
94 | 94 | ||
95 | /// Checks if string consist only Alphabet. (No Whitespace) | 95 | /// Checks if string consist only Alphabet. (No Whitespace) |
96 | static bool isAlphabetOnly(String s) => hasMatch(s, r'^[a-zA-Z]+$'); | 96 | static bool isAlphabetOnly(String s) => hasMatch(s, r'^[a-zA-Z]+$'); |
97 | + | ||
98 | + /// Checks if string contains at least one Capital Letter | ||
99 | + static bool hasCapitalletter(String s) => hasMatch(s, r'[A-Z]'); | ||
97 | 100 | ||
98 | /// Checks if string is boolean. | 101 | /// Checks if string is boolean. |
99 | static bool isBool(String value) { | 102 | static bool isBool(String value) { |
1 | name: get | 1 | name: get |
2 | description: Open screens/snackbars/dialogs/bottomSheets without context, manage states and inject dependencies easily with GetX. | 2 | description: Open screens/snackbars/dialogs/bottomSheets without context, manage states and inject dependencies easily with GetX. |
3 | -version: 3.13.2 | 3 | +version: 3.15.0 |
4 | homepage: https://github.com/jonataslaw/getx | 4 | homepage: https://github.com/jonataslaw/getx |
5 | 5 | ||
6 | environment: | 6 | environment: |
@@ -9,7 +9,6 @@ environment: | @@ -9,7 +9,6 @@ environment: | ||
9 | dependencies: | 9 | dependencies: |
10 | flutter: | 10 | flutter: |
11 | sdk: flutter | 11 | sdk: flutter |
12 | - meta: 1.3.0-nullsafety.3 | ||
13 | 12 | ||
14 | dev_dependencies: | 13 | dev_dependencies: |
15 | flutter_test: | 14 | flutter_test: |
1 | import 'dart:async'; | 1 | import 'dart:async'; |
2 | - | ||
3 | import 'package:flutter/foundation.dart'; | 2 | import 'package:flutter/foundation.dart'; |
3 | +import 'package:flutter_test/flutter_test.dart'; | ||
4 | import 'package:get/state_manager.dart'; | 4 | import 'package:get/state_manager.dart'; |
5 | 5 | ||
6 | -int times = 3; | ||
7 | -int get last => times - 1; | 6 | +int times = 30; |
8 | 7 | ||
9 | -Future<String> valueNotifier() { | ||
10 | - final c = Completer<String>(); | 8 | +Future<int> valueNotifier() { |
9 | + final c = Completer<int>(); | ||
11 | final value = ValueNotifier<int>(0); | 10 | final value = ValueNotifier<int>(0); |
12 | final timer = Stopwatch(); | 11 | final timer = Stopwatch(); |
13 | timer.start(); | 12 | timer.start(); |
14 | 13 | ||
15 | value.addListener(() { | 14 | value.addListener(() { |
16 | - if (last == value.value) { | 15 | + if (times == value.value) { |
17 | timer.stop(); | 16 | timer.stop(); |
18 | - c.complete("""${value.value} item value notifier | ||
19 | -objs time: ${timer.elapsedMicroseconds}ms"""); | 17 | + print( |
18 | + """${value.value} listeners notified | [VALUE_NOTIFIER] time: ${timer.elapsedMicroseconds}ms"""); | ||
19 | + c.complete(timer.elapsedMicroseconds); | ||
20 | } | 20 | } |
21 | }); | 21 | }); |
22 | 22 | ||
23 | - for (var i = 0; i < times; i++) { | 23 | + for (var i = 0; i < times + 1; i++) { |
24 | value.value = i; | 24 | value.value = i; |
25 | } | 25 | } |
26 | 26 | ||
27 | return c.future; | 27 | return c.future; |
28 | } | 28 | } |
29 | 29 | ||
30 | -Future<String> getValue() { | ||
31 | - final c = Completer<String>(); | 30 | +Future<int> getValue() { |
31 | + final c = Completer<int>(); | ||
32 | final value = Value<int>(0); | 32 | final value = Value<int>(0); |
33 | final timer = Stopwatch(); | 33 | final timer = Stopwatch(); |
34 | timer.start(); | 34 | timer.start(); |
35 | 35 | ||
36 | value.addListener(() { | 36 | value.addListener(() { |
37 | - if (last == value.value) { | 37 | + if (times == value.value) { |
38 | timer.stop(); | 38 | timer.stop(); |
39 | - c.complete("""${value.value} item get value objs | ||
40 | - time: ${timer.elapsedMicroseconds}ms"""); | 39 | + print( |
40 | + """${value.value} listeners notified | [GETX_VALUE] time: ${timer.elapsedMicroseconds}ms"""); | ||
41 | + c.complete(timer.elapsedMicroseconds); | ||
41 | } | 42 | } |
42 | }); | 43 | }); |
43 | 44 | ||
44 | - for (var i = 0; i < times; i++) { | 45 | + for (var i = 0; i < times + 1; i++) { |
45 | value.value = i; | 46 | value.value = i; |
46 | } | 47 | } |
47 | 48 | ||
48 | return c.future; | 49 | return c.future; |
49 | } | 50 | } |
50 | 51 | ||
51 | -Future<String> getStream() { | ||
52 | - final c = Completer<String>(); | 52 | +Future<int> stream() { |
53 | + final c = Completer<int>(); | ||
53 | 54 | ||
54 | final value = StreamController<int>(); | 55 | final value = StreamController<int>(); |
55 | final timer = Stopwatch(); | 56 | final timer = Stopwatch(); |
56 | timer.start(); | 57 | timer.start(); |
57 | 58 | ||
58 | value.stream.listen((v) { | 59 | value.stream.listen((v) { |
59 | - if (last == v) { | 60 | + if (times == v) { |
61 | + timer.stop(); | ||
62 | + print( | ||
63 | + """$v listeners notified | [STREAM] time: ${timer.elapsedMicroseconds}ms"""); | ||
64 | + c.complete(timer.elapsedMicroseconds); | ||
65 | + } | ||
66 | + }); | ||
67 | + | ||
68 | + for (var i = 0; i < times + 1; i++) { | ||
69 | + value.add(i); | ||
70 | + } | ||
71 | + | ||
72 | + return c.future; | ||
73 | +} | ||
74 | + | ||
75 | +Future<int> getStream() { | ||
76 | + final c = Completer<int>(); | ||
77 | + | ||
78 | + final value = GetStream<int>(); | ||
79 | + final timer = Stopwatch(); | ||
80 | + timer.start(); | ||
81 | + | ||
82 | + value.listen((v) { | ||
83 | + if (times == v) { | ||
84 | + timer.stop(); | ||
85 | + print( | ||
86 | + """$v listeners notified | [GET_STREAM] time: ${timer.elapsedMicroseconds}ms"""); | ||
87 | + c.complete(timer.elapsedMicroseconds); | ||
88 | + } | ||
89 | + }); | ||
90 | + | ||
91 | + for (var i = 0; i < times + 1; i++) { | ||
92 | + value.add(i); | ||
93 | + } | ||
94 | + | ||
95 | + return c.future; | ||
96 | +} | ||
97 | + | ||
98 | +Future<int> miniStream() { | ||
99 | + final c = Completer<int>(); | ||
100 | + | ||
101 | + final value = MiniStream<int>(); | ||
102 | + final timer = Stopwatch(); | ||
103 | + timer.start(); | ||
104 | + | ||
105 | + value.listen((v) { | ||
106 | + if (times == v) { | ||
60 | timer.stop(); | 107 | timer.stop(); |
61 | - c.complete("$v item stream objs time: ${timer.elapsedMicroseconds}ms"); | 108 | + print( |
109 | + """$v listeners notified | [MINI_STREAM] time: ${timer.elapsedMicroseconds}ms"""); | ||
110 | + c.complete(timer.elapsedMicroseconds); | ||
62 | } | 111 | } |
63 | }); | 112 | }); |
64 | 113 | ||
65 | - for (var i = 0; i < times; i++) { | 114 | + for (var i = 0; i < times + 1; i++) { |
66 | value.add(i); | 115 | value.add(i); |
67 | } | 116 | } |
68 | 117 | ||
69 | return c.future; | 118 | return c.future; |
70 | } | 119 | } |
71 | 120 | ||
72 | -void main() async { | ||
73 | - print(await getValue()); | ||
74 | - print(await valueNotifier()); | ||
75 | - print(await getStream()); | ||
76 | - times = 30000; | ||
77 | - print(await getValue()); | ||
78 | - print(await valueNotifier()); | ||
79 | - print(await getStream()); | 121 | +void main() { |
122 | + test('percentage test', () { | ||
123 | + print('============================================'); | ||
124 | + print('PERCENTAGE TEST'); | ||
125 | + | ||
126 | + final referenceValue = 200; | ||
127 | + final requestedValue = 100; | ||
128 | + | ||
129 | + print(''' | ||
130 | +referenceValue is ${calculePercentage(referenceValue, requestedValue)}% more than requestedValue'''); | ||
131 | + expect(calculePercentage(referenceValue, requestedValue), 100); | ||
132 | + }); | ||
133 | + test('run benchmarks from ValueNotifier', () async { | ||
134 | + times = 30; | ||
135 | + print('============================================'); | ||
136 | + print('VALUE_NOTIFIER X GETX_VALUE TEST'); | ||
137 | + print('-----------'); | ||
138 | + await getValue(); | ||
139 | + await valueNotifier(); | ||
140 | + print('-----------'); | ||
141 | + | ||
142 | + times = 30000; | ||
143 | + final getx = await getValue(); | ||
144 | + final dart = await valueNotifier(); | ||
145 | + print('-----------'); | ||
146 | + | ||
147 | + print('ValueNotifier delay $dart ms to made $times requests'); | ||
148 | + print('GetValue delay $getx ms to made $times requests'); | ||
149 | + print('-----------'); | ||
150 | + print(''' | ||
151 | +GetValue is ${calculePercentage(dart, getx).round()}% more fast than Default ValueNotifier with $times listeners'''); | ||
152 | + }); | ||
153 | + | ||
154 | + test('run benchmarks from Streams', () async { | ||
155 | + times = 30; | ||
156 | + print('============================================'); | ||
157 | + print('DART STREAM X GET_STREAM X GET_MINI_STREAM TEST'); | ||
158 | + print('-----------'); | ||
159 | + var getx = await getStream(); | ||
160 | + var mini = await miniStream(); | ||
161 | + var dart = await stream(); | ||
162 | + print('-----------'); | ||
163 | + print(''' | ||
164 | +GetStream is ${calculePercentage(dart, mini).round()}% more fast than Default Stream with $times listeners'''); | ||
165 | + print('-----------'); | ||
166 | + times = 30000; | ||
167 | + dart = await stream(); | ||
168 | + getx = await getStream(); | ||
169 | + mini = await miniStream(); | ||
170 | + print('-----------'); | ||
171 | + print('dart_stream delay $dart ms to made $times requests'); | ||
172 | + print('getx_stream delay $getx ms to made $times requests'); | ||
173 | + print('getx_mini_stream delay $mini ms to made $times requests'); | ||
174 | + print('-----------'); | ||
175 | + print(''' | ||
176 | +GetStream is ${calculePercentage(dart, getx).round()}% more fast than Default Stream with $times listeners'''); | ||
177 | + }); | ||
80 | } | 178 | } |
81 | 179 | ||
82 | -typedef VoidCallback = void Function(); | 180 | +int calculePercentage(int dart, int getx) { |
181 | + return (dart / getx * 100).round() - 100; | ||
182 | +} |
@@ -9,11 +9,7 @@ class Mock { | @@ -9,11 +9,7 @@ class Mock { | ||
9 | } | 9 | } |
10 | } | 10 | } |
11 | 11 | ||
12 | -class DisposableController with GetLifeCycle { | ||
13 | - DisposableController() { | ||
14 | - initLifeCycle(); | ||
15 | - } | ||
16 | -} | 12 | +class DisposableController extends GetLifeCycle {} |
17 | 13 | ||
18 | // ignore: one_member_abstracts | 14 | // ignore: one_member_abstracts |
19 | abstract class Service { | 15 | abstract class Service { |
-
Please register or login to post a comment