Renat Fakhrutdinov
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 ![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png) 1 ![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
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 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get) 5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
6 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg) 6 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
1 ![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png) 1 ![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
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 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get) 5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
6 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg) 6 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
1 ![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png) 1 ![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
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 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get) 5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
6 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg) 6 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
1 ![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png) 1 ![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
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 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get) 5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
6 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg) 6 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
1 ![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png) 1 ![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
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 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get) 5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
6 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg) 6 ![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
@@ -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 -}  
  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 +}
  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 +}
  1 +library rx_stream;
  2 +
  3 +import 'dart:async';
  4 +import '../rx_typedefs/rx_typedefs.dart';
  5 +
  6 +part 'get_stream.dart';
  7 +part 'mini_stream.dart';
1 typedef Condition = bool Function(); 1 typedef Condition = bool Function();
2 -typedef ValueCallback<T> = Function(T v); 2 +typedef OnData<T> = void Function(T data);
  3 +typedef Callback = void Function();
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 +}
  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) {
@@ -146,5 +146,3 @@ @@ -146,5 +146,3 @@
146 // return widget.builder(controller.state); 146 // return widget.builder(controller.state);
147 // } 147 // }
148 //} 148 //}
149 -  
150 -  
  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 {