Showing
16 changed files
with
513 additions
and
453 deletions
@@ -28,7 +28,7 @@ class Home extends ObxStatelessWidget { | @@ -28,7 +28,7 @@ class Home extends ObxStatelessWidget { | ||
28 | child: Column( | 28 | child: Column( |
29 | mainAxisAlignment: MainAxisAlignment.center, | 29 | mainAxisAlignment: MainAxisAlignment.center, |
30 | children: [ | 30 | children: [ |
31 | - SimpleBuilder(builder: (context) { | 31 | + Observer(builder: (context) { |
32 | print('builder'); | 32 | print('builder'); |
33 | return Text( | 33 | return Text( |
34 | '${controller.count.value}', | 34 | '${controller.count.value}', |
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 | - void Function()? onListen; | ||
12 | - void Function()? onPause; | ||
13 | - void Function()? onResume; | ||
14 | - FutureOr<void> Function()? onCancel; | ||
15 | - | ||
16 | - GetStream({this.onListen, this.onPause, this.onResume, this.onCancel}); | ||
17 | - | ||
18 | - factory GetStream.fromValue(T value, | ||
19 | - {Function()? onListen, | ||
20 | - Function()? onPause, | ||
21 | - Function()? onResume, | ||
22 | - FutureOr<void> Function()? onCancel}) { | ||
23 | - final valuedStream = GetStream<T>( | ||
24 | - onListen: onListen, | ||
25 | - onPause: onPause, | ||
26 | - onResume: onResume, | ||
27 | - onCancel: onCancel) | ||
28 | - .._value = value; | ||
29 | - | ||
30 | - return valuedStream; | ||
31 | - } | ||
32 | - | ||
33 | - List<LightSubscription<T>>? _onData = <LightSubscription<T>>[]; | ||
34 | - | ||
35 | - bool? _isBusy = false; | ||
36 | - | ||
37 | - FutureOr<bool?> removeSubscription(LightSubscription<T> subs) async { | ||
38 | - if (!_isBusy!) { | ||
39 | - return _onData!.remove(subs); | ||
40 | - } else { | ||
41 | - await Future.delayed(Duration.zero); | ||
42 | - return _onData?.remove(subs); | ||
43 | - } | ||
44 | - } | ||
45 | - | ||
46 | - FutureOr<void> addSubscription(LightSubscription<T> subs) async { | ||
47 | - if (!_isBusy!) { | ||
48 | - return _onData!.add(subs); | ||
49 | - } else { | ||
50 | - await Future.delayed(Duration.zero); | ||
51 | - return _onData!.add(subs); | ||
52 | - } | ||
53 | - } | ||
54 | - | ||
55 | - int? get length => _onData?.length; | ||
56 | - | ||
57 | - bool get hasListeners => _onData!.isNotEmpty; | ||
58 | - | ||
59 | - void _notifyData(T data) { | ||
60 | - _isBusy = true; | ||
61 | - for (final item in _onData!) { | ||
62 | - if (!item.isPaused) { | ||
63 | - item._data?.call(data); | ||
64 | - } | ||
65 | - } | ||
66 | - _isBusy = false; | ||
67 | - } | ||
68 | - | ||
69 | - void _notifyError(Object error, [StackTrace? stackTrace]) { | ||
70 | - assert(!isClosed, 'You cannot add errors to a closed stream.'); | ||
71 | - _isBusy = true; | ||
72 | - var itemsToRemove = <LightSubscription<T>>[]; | ||
73 | - for (final item in _onData!) { | ||
74 | - if (!item.isPaused) { | ||
75 | - if (stackTrace != null) { | ||
76 | - item._onError?.call(error, stackTrace); | ||
77 | - } else { | ||
78 | - item._onError?.call(error); | ||
79 | - } | ||
80 | - | ||
81 | - if (item.cancelOnError ?? false) { | ||
82 | - //item.cancel?.call(); | ||
83 | - itemsToRemove.add(item); | ||
84 | - item.pause(); | ||
85 | - item._onDone?.call(); | ||
86 | - } | ||
87 | - } | ||
88 | - } | ||
89 | - for (final item in itemsToRemove) { | ||
90 | - _onData!.remove(item); | ||
91 | - } | ||
92 | - _isBusy = false; | ||
93 | - } | ||
94 | - | ||
95 | - void _notifyDone() { | ||
96 | - assert(!isClosed, 'You cannot close a closed stream.'); | ||
97 | - _isBusy = true; | ||
98 | - for (final item in _onData!) { | ||
99 | - if (!item.isPaused) { | ||
100 | - item._onDone?.call(); | ||
101 | - } | ||
102 | - } | ||
103 | - _isBusy = false; | ||
104 | - } | ||
105 | - | ||
106 | - late T _value; | ||
107 | - | ||
108 | - T get value { | ||
109 | - // RxInterface.proxy?.addListener(this); | ||
110 | - return _value; | ||
111 | - } | ||
112 | - | ||
113 | - void add(T event) { | ||
114 | - assert(!isClosed, 'You cannot add event to closed Stream'); | ||
115 | - _value = event; | ||
116 | - _notifyData(event); | ||
117 | - } | ||
118 | - | ||
119 | - bool get isClosed => _onData == null; | ||
120 | - | ||
121 | - void addError(Object error, [StackTrace? stackTrace]) { | ||
122 | - assert(!isClosed, 'You cannot add error to closed Stream'); | ||
123 | - _notifyError(error, stackTrace); | ||
124 | - } | ||
125 | - | ||
126 | - void close() { | ||
127 | - assert(!isClosed, 'You cannot close a closed Stream'); | ||
128 | - _notifyDone(); | ||
129 | - _onData = null; | ||
130 | - _isBusy = null; | ||
131 | - // _value = null; | ||
132 | - } | ||
133 | - | ||
134 | - LightSubscription<T> listen(void Function(T event) onData, | ||
135 | - {Function? onError, void Function()? onDone, bool? cancelOnError}) { | ||
136 | - final subs = LightSubscription<T>( | ||
137 | - removeSubscription, | ||
138 | - onPause: onPause, | ||
139 | - onResume: onResume, | ||
140 | - onCancel: onCancel, | ||
141 | - ) | ||
142 | - ..onData(onData) | ||
143 | - ..onError(onError) | ||
144 | - ..onDone(onDone) | ||
145 | - ..cancelOnError = cancelOnError; | ||
146 | - addSubscription(subs); | ||
147 | - onListen?.call(); | ||
148 | - return subs; | ||
149 | - } | ||
150 | - | ||
151 | - Stream<T> get stream => | ||
152 | - GetStreamTransformation(addSubscription, removeSubscription); | ||
153 | -} | ||
154 | - | ||
155 | -class LightSubscription<T> extends StreamSubscription<T> { | ||
156 | - final RemoveSubscription<T> _removeSubscription; | ||
157 | - LightSubscription(this._removeSubscription, | ||
158 | - {this.onPause, this.onResume, this.onCancel}); | ||
159 | - final void Function()? onPause; | ||
160 | - final void Function()? onResume; | ||
161 | - final FutureOr<void> Function()? onCancel; | ||
162 | - | ||
163 | - bool? cancelOnError = false; | ||
164 | - | ||
165 | - @override | ||
166 | - Future<void> cancel() { | ||
167 | - _removeSubscription(this); | ||
168 | - onCancel?.call(); | ||
169 | - return Future.value(); | ||
170 | - } | ||
171 | - | ||
172 | - OnData<T>? _data; | ||
173 | - | ||
174 | - Function? _onError; | ||
175 | - | ||
176 | - Callback? _onDone; | ||
177 | - | ||
178 | - bool _isPaused = false; | ||
179 | - | ||
180 | - @override | ||
181 | - void onData(OnData<T>? handleData) => _data = handleData; | ||
182 | - | ||
183 | - @override | ||
184 | - void onError(Function? handleError) => _onError = handleError; | ||
185 | - | ||
186 | - @override | ||
187 | - void onDone(Callback? handleDone) => _onDone = handleDone; | ||
188 | - | ||
189 | - @override | ||
190 | - void pause([Future<void>? resumeSignal]) { | ||
191 | - _isPaused = true; | ||
192 | - onPause?.call(); | ||
193 | - } | ||
194 | - | ||
195 | - @override | ||
196 | - void resume() { | ||
197 | - _isPaused = false; | ||
198 | - onResume?.call(); | ||
199 | - } | ||
200 | - | ||
201 | - @override | ||
202 | - bool get isPaused => _isPaused; | ||
203 | - | ||
204 | - @override | ||
205 | - Future<E> asFuture<E>([E? futureValue]) => Future.value(futureValue); | ||
206 | -} | ||
207 | - | ||
208 | -class GetStreamTransformation<T> extends Stream<T> { | ||
209 | - final AddSubscription<T> _addSubscription; | ||
210 | - final RemoveSubscription<T> _removeSubscription; | ||
211 | - GetStreamTransformation(this._addSubscription, this._removeSubscription); | ||
212 | - | ||
213 | - @override | ||
214 | - LightSubscription<T> listen(void Function(T event)? onData, | ||
215 | - {Function? onError, void Function()? onDone, bool? cancelOnError}) { | ||
216 | - final subs = LightSubscription<T>(_removeSubscription) | ||
217 | - ..onData(onData) | ||
218 | - ..onError(onError) | ||
219 | - ..onDone(onDone); | ||
220 | - _addSubscription(subs); | ||
221 | - return subs; | ||
222 | - } | ||
223 | -} | ||
224 | - | ||
225 | -typedef RemoveSubscription<T> = FutureOr<bool?> Function( | ||
226 | - LightSubscription<T> subs); | ||
227 | - | ||
228 | -typedef AddSubscription<T> = FutureOr<void> Function(LightSubscription<T> subs); | 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 | +// void Function()? onListen; | ||
12 | +// void Function()? onPause; | ||
13 | +// void Function()? onResume; | ||
14 | +// FutureOr<void> Function()? onCancel; | ||
15 | + | ||
16 | +// GetStream({this.onListen, this.onPause, this.onResume, this.onCancel}); | ||
17 | + | ||
18 | +// factory GetStream.fromValue(T value, | ||
19 | +// {Function()? onListen, | ||
20 | +// Function()? onPause, | ||
21 | +// Function()? onResume, | ||
22 | +// FutureOr<void> Function()? onCancel}) { | ||
23 | +// final valuedStream = GetStream<T>( | ||
24 | +// onListen: onListen, | ||
25 | +// onPause: onPause, | ||
26 | +// onResume: onResume, | ||
27 | +// onCancel: onCancel) | ||
28 | +// .._value = value; | ||
29 | + | ||
30 | +// return valuedStream; | ||
31 | +// } | ||
32 | + | ||
33 | +// List<LightSubscription<T>>? _onData = <LightSubscription<T>>[]; | ||
34 | + | ||
35 | +// bool? _isBusy = false; | ||
36 | + | ||
37 | +// FutureOr<bool?> removeSubscription(LightSubscription<T> subs) async { | ||
38 | +// if (!_isBusy!) { | ||
39 | +// return _onData!.remove(subs); | ||
40 | +// } else { | ||
41 | +// await Future.delayed(Duration.zero); | ||
42 | +// return _onData?.remove(subs); | ||
43 | +// } | ||
44 | +// } | ||
45 | + | ||
46 | +// FutureOr<void> addSubscription(LightSubscription<T> subs) async { | ||
47 | +// if (!_isBusy!) { | ||
48 | +// return _onData!.add(subs); | ||
49 | +// } else { | ||
50 | +// await Future.delayed(Duration.zero); | ||
51 | +// return _onData!.add(subs); | ||
52 | +// } | ||
53 | +// } | ||
54 | + | ||
55 | +// int? get length => _onData?.length; | ||
56 | + | ||
57 | +// bool get hasListeners => _onData!.isNotEmpty; | ||
58 | + | ||
59 | +// void _notifyData(T data) { | ||
60 | +// _isBusy = true; | ||
61 | +// for (final item in _onData!) { | ||
62 | +// if (!item.isPaused) { | ||
63 | +// item._data?.call(data); | ||
64 | +// } | ||
65 | +// } | ||
66 | +// _isBusy = false; | ||
67 | +// } | ||
68 | + | ||
69 | +// void _notifyError(Object error, [StackTrace? stackTrace]) { | ||
70 | +// assert(!isClosed, 'You cannot add errors to a closed stream.'); | ||
71 | +// _isBusy = true; | ||
72 | +// var itemsToRemove = <LightSubscription<T>>[]; | ||
73 | +// for (final item in _onData!) { | ||
74 | +// if (!item.isPaused) { | ||
75 | +// if (stackTrace != null) { | ||
76 | +// item._onError?.call(error, stackTrace); | ||
77 | +// } else { | ||
78 | +// item._onError?.call(error); | ||
79 | +// } | ||
80 | + | ||
81 | +// if (item.cancelOnError ?? false) { | ||
82 | +// //item.cancel?.call(); | ||
83 | +// itemsToRemove.add(item); | ||
84 | +// item.pause(); | ||
85 | +// item._onDone?.call(); | ||
86 | +// } | ||
87 | +// } | ||
88 | +// } | ||
89 | +// for (final item in itemsToRemove) { | ||
90 | +// _onData!.remove(item); | ||
91 | +// } | ||
92 | +// _isBusy = false; | ||
93 | +// } | ||
94 | + | ||
95 | +// void _notifyDone() { | ||
96 | +// assert(!isClosed, 'You cannot close a closed stream.'); | ||
97 | +// _isBusy = true; | ||
98 | +// for (final item in _onData!) { | ||
99 | +// if (!item.isPaused) { | ||
100 | +// item._onDone?.call(); | ||
101 | +// } | ||
102 | +// } | ||
103 | +// _isBusy = false; | ||
104 | +// } | ||
105 | + | ||
106 | +// late T _value; | ||
107 | + | ||
108 | +// T get value { | ||
109 | +// // RxInterface.proxy?.addListener(this); | ||
110 | +// return _value; | ||
111 | +// } | ||
112 | + | ||
113 | +// void add(T event) { | ||
114 | +// assert(!isClosed, 'You cannot add event to closed Stream'); | ||
115 | +// _value = event; | ||
116 | +// _notifyData(event); | ||
117 | +// } | ||
118 | + | ||
119 | +// bool get isClosed => _onData == null; | ||
120 | + | ||
121 | +// void addError(Object error, [StackTrace? stackTrace]) { | ||
122 | +// assert(!isClosed, 'You cannot add error to closed Stream'); | ||
123 | +// _notifyError(error, stackTrace); | ||
124 | +// } | ||
125 | + | ||
126 | +// void close() { | ||
127 | +// assert(!isClosed, 'You cannot close a closed Stream'); | ||
128 | +// _notifyDone(); | ||
129 | +// _onData = null; | ||
130 | +// _isBusy = null; | ||
131 | +// // _value = null; | ||
132 | +// } | ||
133 | + | ||
134 | +// LightSubscription<T> listen(void Function(T event) onData, | ||
135 | +// {Function? onError, void Function()? onDone, bool? cancelOnError}) { | ||
136 | +// final subs = LightSubscription<T>( | ||
137 | +// removeSubscription, | ||
138 | +// onPause: onPause, | ||
139 | +// onResume: onResume, | ||
140 | +// onCancel: onCancel, | ||
141 | +// ) | ||
142 | +// ..onData(onData) | ||
143 | +// ..onError(onError) | ||
144 | +// ..onDone(onDone) | ||
145 | +// ..cancelOnError = cancelOnError; | ||
146 | +// addSubscription(subs); | ||
147 | +// onListen?.call(); | ||
148 | +// return subs; | ||
149 | +// } | ||
150 | + | ||
151 | +// Stream<T> get stream => | ||
152 | +// GetStreamTransformation(addSubscription, removeSubscription); | ||
153 | +// } | ||
154 | + | ||
155 | +// class LightSubscription<T> extends StreamSubscription<T> { | ||
156 | +// final RemoveSubscription<T> _removeSubscription; | ||
157 | +// LightSubscription(this._removeSubscription, | ||
158 | +// {this.onPause, this.onResume, this.onCancel}); | ||
159 | +// final void Function()? onPause; | ||
160 | +// final void Function()? onResume; | ||
161 | +// final FutureOr<void> Function()? onCancel; | ||
162 | + | ||
163 | +// bool? cancelOnError = false; | ||
164 | + | ||
165 | +// @override | ||
166 | +// Future<void> cancel() { | ||
167 | +// _removeSubscription(this); | ||
168 | +// onCancel?.call(); | ||
169 | +// return Future.value(); | ||
170 | +// } | ||
171 | + | ||
172 | +// OnData<T>? _data; | ||
173 | + | ||
174 | +// Function? _onError; | ||
175 | + | ||
176 | +// Callback? _onDone; | ||
177 | + | ||
178 | +// bool _isPaused = false; | ||
179 | + | ||
180 | +// @override | ||
181 | +// void onData(OnData<T>? handleData) => _data = handleData; | ||
182 | + | ||
183 | +// @override | ||
184 | +// void onError(Function? handleError) => _onError = handleError; | ||
185 | + | ||
186 | +// @override | ||
187 | +// void onDone(Callback? handleDone) => _onDone = handleDone; | ||
188 | + | ||
189 | +// @override | ||
190 | +// void pause([Future<void>? resumeSignal]) { | ||
191 | +// _isPaused = true; | ||
192 | +// onPause?.call(); | ||
193 | +// } | ||
194 | + | ||
195 | +// @override | ||
196 | +// void resume() { | ||
197 | +// _isPaused = false; | ||
198 | +// onResume?.call(); | ||
199 | +// } | ||
200 | + | ||
201 | +// @override | ||
202 | +// bool get isPaused => _isPaused; | ||
203 | + | ||
204 | +// @override | ||
205 | +// Future<E> asFuture<E>([E? futureValue]) => Future.value(futureValue); | ||
206 | +// } | ||
207 | + | ||
208 | +// class GetStreamTransformation<T> extends Stream<T> { | ||
209 | +// final AddSubscription<T> _addSubscription; | ||
210 | +// final RemoveSubscription<T> _removeSubscription; | ||
211 | +// GetStreamTransformation(this._addSubscription, this._removeSubscription); | ||
212 | + | ||
213 | +// @override | ||
214 | +// LightSubscription<T> listen(void Function(T event)? onData, | ||
215 | +// {Function? onError, void Function()? onDone, bool? cancelOnError}) { | ||
216 | +// final subs = LightSubscription<T>(_removeSubscription) | ||
217 | +// ..onData(onData) | ||
218 | +// ..onError(onError) | ||
219 | +// ..onDone(onDone); | ||
220 | +// _addSubscription(subs); | ||
221 | +// return subs; | ||
222 | +// } | ||
223 | +// } | ||
224 | + | ||
225 | +// typedef RemoveSubscription<T> = FutureOr<bool?> Function( | ||
226 | +// LightSubscription<T> subs); | ||
227 | + | ||
228 | +// typedef AddSubscription<T> = | ||
229 | +//FutureOr<void> Function(LightSubscription<T> subs); |
@@ -3,7 +3,6 @@ library rx_stream; | @@ -3,7 +3,6 @@ library rx_stream; | ||
3 | import 'dart:async'; | 3 | import 'dart:async'; |
4 | 4 | ||
5 | import '../rx_typedefs/rx_typedefs.dart'; | 5 | import '../rx_typedefs/rx_typedefs.dart'; |
6 | -import '../rx_types/rx_types.dart'; | ||
7 | 6 | ||
8 | -part 'get_stream.dart'; | 7 | +//part 'get_stream.dart'; |
9 | part 'mini_stream.dart'; | 8 | part 'mini_stream.dart'; |
@@ -4,7 +4,7 @@ part of rx_types; | @@ -4,7 +4,7 @@ part of rx_types; | ||
4 | /// reactivity | 4 | /// reactivity |
5 | /// of those `Widgets` and Rx values. | 5 | /// of those `Widgets` and Rx values. |
6 | 6 | ||
7 | -mixin RxObjectMixin<T> on NotifyManager<T> { | 7 | +mixin RxObjectMixin<T> on GetListenable<T> { |
8 | //late T _value; | 8 | //late T _value; |
9 | 9 | ||
10 | /// Makes a direct update of [value] adding it to the Stream | 10 | /// Makes a direct update of [value] adding it to the Stream |
@@ -25,9 +25,9 @@ mixin RxObjectMixin<T> on NotifyManager<T> { | @@ -25,9 +25,9 @@ mixin RxObjectMixin<T> on NotifyManager<T> { | ||
25 | /// person.refresh(); | 25 | /// person.refresh(); |
26 | /// print( person ); | 26 | /// print( person ); |
27 | /// ``` | 27 | /// ``` |
28 | - void refresh() { | ||
29 | - subject.add(value); | ||
30 | - } | 28 | + // void refresh() { |
29 | + // subject.add(value); | ||
30 | + // } | ||
31 | 31 | ||
32 | /// updates the value to `null` and adds it to the Stream. | 32 | /// updates the value to `null` and adds it to the Stream. |
33 | /// Even with null-safety coming, is still an important feature to support, as | 33 | /// Even with null-safety coming, is still an important feature to support, as |
@@ -59,6 +59,7 @@ mixin RxObjectMixin<T> on NotifyManager<T> { | @@ -59,6 +59,7 @@ mixin RxObjectMixin<T> on NotifyManager<T> { | ||
59 | /// onChanged: myText, | 59 | /// onChanged: myText, |
60 | /// ), | 60 | /// ), |
61 | ///``` | 61 | ///``` |
62 | + @override | ||
62 | T call([T? v]) { | 63 | T call([T? v]) { |
63 | if (v != null) { | 64 | if (v != null) { |
64 | value = v; | 65 | value = v; |
@@ -95,25 +96,18 @@ mixin RxObjectMixin<T> on NotifyManager<T> { | @@ -95,25 +96,18 @@ mixin RxObjectMixin<T> on NotifyManager<T> { | ||
95 | 96 | ||
96 | /// Updates the [value] and adds it to the stream, updating the observer | 97 | /// Updates the [value] and adds it to the stream, updating the observer |
97 | /// Widget, only if it's different from the previous value. | 98 | /// Widget, only if it's different from the previous value. |
99 | + @override | ||
98 | set value(T val) { | 100 | set value(T val) { |
99 | - if (subject.isClosed) return; | 101 | + if (isDisposed) return; |
100 | sentToStream = false; | 102 | sentToStream = false; |
101 | if (value == val && !firstRebuild) return; | 103 | if (value == val && !firstRebuild) return; |
102 | firstRebuild = false; | 104 | firstRebuild = false; |
103 | // _value = val; | 105 | // _value = val; |
104 | sentToStream = true; | 106 | sentToStream = true; |
105 | - subject.add(val); | ||
106 | - } | ||
107 | - | ||
108 | - /// Returns the current [value] | ||
109 | - T get value { | ||
110 | - return subject.value; | ||
111 | - //RxInterface.proxy?.addListener(subject); | ||
112 | - // return _value; | 107 | + //TODO: Check this |
108 | + super.value = val; | ||
113 | } | 109 | } |
114 | 110 | ||
115 | - Stream<T> get stream => subject.stream; | ||
116 | - | ||
117 | /// Returns a [StreamSubscription] similar to [listen], but with the | 111 | /// Returns a [StreamSubscription] similar to [listen], but with the |
118 | /// added benefit that it primes the stream with the current [value], rather | 112 | /// added benefit that it primes the stream with the current [value], rather |
119 | /// than waiting for the next [value]. This should not be called in [onInit] | 113 | /// than waiting for the next [value]. This should not be called in [onInit] |
@@ -127,6 +121,7 @@ mixin RxObjectMixin<T> on NotifyManager<T> { | @@ -127,6 +121,7 @@ mixin RxObjectMixin<T> on NotifyManager<T> { | ||
127 | cancelOnError: cancelOnError, | 121 | cancelOnError: cancelOnError, |
128 | ); | 122 | ); |
129 | 123 | ||
124 | + //TODO: Change to refresh???? | ||
130 | subject.add(value); | 125 | subject.add(value); |
131 | 126 | ||
132 | return subscription; | 127 | return subscription; |
@@ -137,64 +132,64 @@ mixin RxObjectMixin<T> on NotifyManager<T> { | @@ -137,64 +132,64 @@ mixin RxObjectMixin<T> on NotifyManager<T> { | ||
137 | /// Closing the subscription will happen automatically when the observer | 132 | /// Closing the subscription will happen automatically when the observer |
138 | /// Widget (`GetX` or `Obx`) gets unmounted from the Widget tree. | 133 | /// Widget (`GetX` or `Obx`) gets unmounted from the Widget tree. |
139 | void bindStream(Stream<T> stream) { | 134 | void bindStream(Stream<T> stream) { |
140 | - final listSubscriptions = | ||
141 | - _subscriptions[subject] ??= <StreamSubscription>[]; | ||
142 | - listSubscriptions.add(stream.listen((va) => value = va)); | ||
143 | - } | ||
144 | -} | ||
145 | - | ||
146 | -class RxNotifier<T> = RxInterface<T> with NotifyManager<T>; | ||
147 | - | ||
148 | -mixin NotifyManager<T> { | ||
149 | - GetStream<T> subject = GetStream<T>(); | ||
150 | - final _subscriptions = <GetStream, List<StreamSubscription>>{}; | ||
151 | - | ||
152 | - bool get canUpdate => _subscriptions.isNotEmpty; | 135 | + // final listSubscriptions = |
136 | + // _subscriptions[subject] ??= <StreamSubscription>[]; | ||
153 | 137 | ||
154 | - /// This is an internal method. | ||
155 | - /// Subscribe to changes on the inner stream. | ||
156 | - void addListener(GetStream<T> rxGetx) { | ||
157 | - if (!_subscriptions.containsKey(rxGetx)) { | ||
158 | - final subs = rxGetx.listen((data) { | ||
159 | - if (!subject.isClosed) subject.add(data); | ||
160 | - }); | ||
161 | - final listSubscriptions = | ||
162 | - _subscriptions[rxGetx] ??= <StreamSubscription>[]; | ||
163 | - listSubscriptions.add(subs); | ||
164 | - } | ||
165 | - } | ||
166 | - | ||
167 | - StreamSubscription<T> listen( | ||
168 | - void Function(T) onData, { | ||
169 | - Function? onError, | ||
170 | - void Function()? onDone, | ||
171 | - bool? cancelOnError, | ||
172 | - }) => | ||
173 | - subject.listen( | ||
174 | - onData, | ||
175 | - onError: onError, | ||
176 | - onDone: onDone, | ||
177 | - cancelOnError: cancelOnError ?? false, | ||
178 | - ); | ||
179 | - | ||
180 | - /// Closes the subscriptions for this Rx, releasing the resources. | ||
181 | - void close() { | ||
182 | - _subscriptions.forEach((getStream, _subscriptions) { | ||
183 | - for (final subscription in _subscriptions) { | ||
184 | - subscription.cancel(); | ||
185 | - } | ||
186 | - }); | ||
187 | - | ||
188 | - _subscriptions.clear(); | ||
189 | - subject.close(); | 138 | + final sub = stream.listen((va) => value = va); |
139 | + reportAdd(sub.cancel); | ||
190 | } | 140 | } |
191 | } | 141 | } |
192 | 142 | ||
143 | +//class RxNotifier<T> = RxInterface<T> with NotifyManager<T>; | ||
144 | + | ||
145 | +// mixin NotifyManager<T> { | ||
146 | +// GetStream<T> subject = GetStream<T>(); | ||
147 | +// final _subscriptions = <GetStream, List<StreamSubscription>>{}; | ||
148 | + | ||
149 | +// bool get canUpdate => _subscriptions.isNotEmpty; | ||
150 | + | ||
151 | +// /// This is an internal method. | ||
152 | +// /// Subscribe to changes on the inner stream. | ||
153 | +// void addListener(GetStream<T> rxGetx) { | ||
154 | +// if (!_subscriptions.containsKey(rxGetx)) { | ||
155 | +// final subs = rxGetx.listen((data) { | ||
156 | +// if (!subject.isClosed) subject.add(data); | ||
157 | +// }); | ||
158 | +// final listSubscriptions = | ||
159 | +// _subscriptions[rxGetx] ??= <StreamSubscription>[]; | ||
160 | +// listSubscriptions.add(subs); | ||
161 | +// } | ||
162 | +// } | ||
163 | + | ||
164 | +// StreamSubscription<T> listen( | ||
165 | +// void Function(T) onData, { | ||
166 | +// Function? onError, | ||
167 | +// void Function()? onDone, | ||
168 | +// bool? cancelOnError, | ||
169 | +// }) => | ||
170 | +// subject.listen( | ||
171 | +// onData, | ||
172 | +// onError: onError, | ||
173 | +// onDone: onDone, | ||
174 | +// cancelOnError: cancelOnError ?? false, | ||
175 | +// ); | ||
176 | + | ||
177 | +// /// Closes the subscriptions for this Rx, releasing the resources. | ||
178 | +// void close() { | ||
179 | +// _subscriptions.forEach((getStream, _subscriptions) { | ||
180 | +// for (final subscription in _subscriptions) { | ||
181 | +// subscription.cancel(); | ||
182 | +// } | ||
183 | +// }); | ||
184 | + | ||
185 | +// _subscriptions.clear(); | ||
186 | +// subject.close(); | ||
187 | +// } | ||
188 | +// } | ||
189 | + | ||
193 | /// Base Rx class that manages all the stream logic for any Type. | 190 | /// Base Rx class that manages all the stream logic for any Type. |
194 | -abstract class _RxImpl<T> extends RxNotifier<T> with RxObjectMixin<T> { | ||
195 | - _RxImpl(T initial) { | ||
196 | - subject = GetStream.fromValue(initial); | ||
197 | - } | 191 | +abstract class _RxImpl<T> extends GetListenable<T> with RxObjectMixin<T> { |
192 | + _RxImpl(T initial) : super(initial); | ||
198 | 193 | ||
199 | void addError(Object error, [StackTrace? stackTrace]) { | 194 | void addError(Object error, [StackTrace? stackTrace]) { |
200 | subject.addError(error, stackTrace); | 195 | subject.addError(error, stackTrace); |
@@ -5,12 +5,10 @@ part of rx_types; | @@ -5,12 +5,10 @@ part of rx_types; | ||
5 | /// This interface is the contract that _RxImpl]<T> uses in all it's | 5 | /// This interface is the contract that _RxImpl]<T> uses in all it's |
6 | /// subclass. | 6 | /// subclass. |
7 | abstract class RxInterface<T> { | 7 | abstract class RxInterface<T> { |
8 | - static RxInterface? proxy; | ||
9 | - | ||
10 | - bool get canUpdate; | 8 | + //bool get canUpdate; |
11 | 9 | ||
12 | /// Adds a listener to stream | 10 | /// Adds a listener to stream |
13 | - void addListener(GetStream<T> rxGetx); | 11 | + void addListener(VoidCallback listener); |
14 | 12 | ||
15 | /// Close the Rx Variable | 13 | /// Close the Rx Variable |
16 | void close(); | 14 | void close(); |
@@ -20,13 +18,24 @@ abstract class RxInterface<T> { | @@ -20,13 +18,24 @@ abstract class RxInterface<T> { | ||
20 | {Function? onError, void Function()? onDone, bool? cancelOnError}); | 18 | {Function? onError, void Function()? onDone, bool? cancelOnError}); |
21 | 19 | ||
22 | /// Avoids an unsafe usage of the `proxy` | 20 | /// Avoids an unsafe usage of the `proxy` |
23 | - static T notifyChildren<T>(RxNotifier observer, ValueGetter<T> builder) { | ||
24 | - final _observer = RxInterface.proxy; | ||
25 | - RxInterface.proxy = observer; | ||
26 | - final result = builder(); | ||
27 | - if (!observer.canUpdate) { | ||
28 | - RxInterface.proxy = _observer; | ||
29 | - throw """ | 21 | + // static T notifyChildren<T>(RxNotifier observer, ValueGetter<T> builder) { |
22 | + // final _observer = RxInterface.proxy; | ||
23 | + // RxInterface.proxy = observer; | ||
24 | + // final result = builder(); | ||
25 | + // if (!observer.canUpdate) { | ||
26 | + // RxInterface.proxy = _observer; | ||
27 | + // throw ObxError(); | ||
28 | + // } | ||
29 | + // RxInterface.proxy = _observer; | ||
30 | + // return result; | ||
31 | + // } | ||
32 | +} | ||
33 | + | ||
34 | +class ObxError { | ||
35 | + const ObxError(); | ||
36 | + @override | ||
37 | + String toString() { | ||
38 | + return """ | ||
30 | [Get] the improper use of a GetX has been detected. | 39 | [Get] the improper use of a GetX has been detected. |
31 | You should only use GetX or Obx for the specific widget that will be updated. | 40 | You should only use GetX or Obx for the specific widget that will be updated. |
32 | If you are seeing this error, you probably did not insert any observable variables into GetX/Obx | 41 | If you are seeing this error, you probably did not insert any observable variables into GetX/Obx |
@@ -35,7 +44,4 @@ abstract class RxInterface<T> { | @@ -35,7 +44,4 @@ abstract class RxInterface<T> { | ||
35 | If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX. | 44 | If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX. |
36 | """; | 45 | """; |
37 | } | 46 | } |
38 | - RxInterface.proxy = _observer; | ||
39 | - return result; | ||
40 | - } | ||
41 | } | 47 | } |
1 | part of rx_types; | 1 | part of rx_types; |
2 | 2 | ||
3 | /// Create a list similar to `List<T>` | 3 | /// Create a list similar to `List<T>` |
4 | -class RxList<E> extends ListMixin<E> | ||
5 | - with NotifyManager<List<E>>, RxObjectMixin<List<E>> | ||
6 | - implements RxInterface<List<E>> { | ||
7 | - RxList([List<E> initial = const []]) { | ||
8 | - subject = GetStream.fromValue(List.from(initial)); | ||
9 | - } | 4 | +class RxList<E> extends GetListenable<List<E>> |
5 | + with ListMixin<E>, RxObjectMixin<List<E>> { | ||
6 | + RxList([List<E> initial = const []]) : super(initial); | ||
10 | 7 | ||
11 | factory RxList.filled(int length, E fill, {bool growable = false}) { | 8 | factory RxList.filled(int length, E fill, {bool growable = false}) { |
12 | return RxList(List.filled(length, fill, growable: growable)); | 9 | return RxList(List.filled(length, fill, growable: growable)); |
@@ -87,12 +84,12 @@ class RxList<E> extends ListMixin<E> | @@ -87,12 +84,12 @@ class RxList<E> extends ListMixin<E> | ||
87 | @override | 84 | @override |
88 | int get length => value.length; | 85 | int get length => value.length; |
89 | 86 | ||
90 | - @override | ||
91 | - @protected | ||
92 | - List<E> get value { | ||
93 | - RxInterface.proxy?.addListener(subject); | ||
94 | - return subject.value; | ||
95 | - } | 87 | + // @override |
88 | + // @protected | ||
89 | + // List<E> get value { | ||
90 | + // RxInterface.proxy?.addListener(subject); | ||
91 | + // return subject.value; | ||
92 | + // } | ||
96 | 93 | ||
97 | @override | 94 | @override |
98 | set length(int newLength) { | 95 | set length(int newLength) { |
1 | part of rx_types; | 1 | part of rx_types; |
2 | 2 | ||
3 | -class RxMap<K, V> extends MapMixin<K, V> | ||
4 | - with NotifyManager<Map<K, V>>, RxObjectMixin<Map<K, V>> | ||
5 | - implements RxInterface<Map<K, V>> { | ||
6 | - RxMap([Map<K, V> initial = const {}]) { | ||
7 | - subject = GetStream.fromValue(Map.from(initial)); | ||
8 | - } | 3 | +class RxMap<K, V> extends GetListenable<Map<K, V>> |
4 | + with MapMixin<K, V>, RxObjectMixin<Map<K, V>> { | ||
5 | + RxMap([Map<K, V> initial = const {}]) : super(initial); | ||
9 | 6 | ||
10 | factory RxMap.from(Map<K, V> other) { | 7 | factory RxMap.from(Map<K, V> other) { |
11 | return RxMap(Map.from(other)); | 8 | return RxMap(Map.from(other)); |
@@ -53,13 +50,13 @@ class RxMap<K, V> extends MapMixin<K, V> | @@ -53,13 +50,13 @@ class RxMap<K, V> extends MapMixin<K, V> | ||
53 | return val; | 50 | return val; |
54 | } | 51 | } |
55 | 52 | ||
56 | - @override | ||
57 | - @protected | ||
58 | - Map<K, V> get value { | ||
59 | - return subject.value; | ||
60 | - // RxInterface.proxy?.addListener(subject); | ||
61 | - // return _value; | ||
62 | - } | 53 | + // @override |
54 | + // @protected | ||
55 | + // Map<K, V> get value { | ||
56 | + // return subject.value; | ||
57 | + // // RxInterface.proxy?.addListener(subject); | ||
58 | + // // return _value; | ||
59 | + // } | ||
63 | } | 60 | } |
64 | 61 | ||
65 | extension MapExtension<K, V> on Map<K, V> { | 62 | extension MapExtension<K, V> on Map<K, V> { |
1 | part of rx_types; | 1 | part of rx_types; |
2 | 2 | ||
3 | -class RxSet<E> extends SetMixin<E> | ||
4 | - with NotifyManager<Set<E>>, RxObjectMixin<Set<E>> | ||
5 | - implements RxInterface<Set<E>> { | ||
6 | - RxSet([Set<E> initial = const {}]) { | ||
7 | - subject = GetStream.fromValue(Set.from(initial)); | ||
8 | - } | 3 | +class RxSet<E> extends GetListenable<Set<E>> |
4 | + with SetMixin<E>, RxObjectMixin<Set<E>> { | ||
5 | + RxSet([Set<E> initial = const {}]) : super(initial); | ||
9 | 6 | ||
10 | /// Special override to push() element(s) in a reactive way | 7 | /// Special override to push() element(s) in a reactive way |
11 | /// inside the List, | 8 | /// inside the List, |
@@ -20,13 +17,13 @@ class RxSet<E> extends SetMixin<E> | @@ -20,13 +17,13 @@ class RxSet<E> extends SetMixin<E> | ||
20 | refresh(); | 17 | refresh(); |
21 | } | 18 | } |
22 | 19 | ||
23 | - @override | ||
24 | - @protected | ||
25 | - Set<E> get value { | ||
26 | - return subject.value; | ||
27 | - // RxInterface.proxy?.addListener(subject); | ||
28 | - // return _value; | ||
29 | - } | 20 | + // @override |
21 | + // @protected | ||
22 | + // Set<E> get value { | ||
23 | + // return subject.value; | ||
24 | + // // RxInterface.proxy?.addListener(subject); | ||
25 | + // // return _value; | ||
26 | + // } | ||
30 | 27 | ||
31 | @override | 28 | @override |
32 | @protected | 29 | @protected |
@@ -4,6 +4,7 @@ import 'dart:async'; | @@ -4,6 +4,7 @@ import 'dart:async'; | ||
4 | import 'dart:collection'; | 4 | import 'dart:collection'; |
5 | 5 | ||
6 | import 'package:flutter/foundation.dart'; | 6 | import 'package:flutter/foundation.dart'; |
7 | +import 'package:get/get_state_manager/src/rx_flutter/rx_notifier.dart'; | ||
7 | import 'package:get/get_state_manager/src/simple/list_notifier.dart'; | 8 | import 'package:get/get_state_manager/src/simple/list_notifier.dart'; |
8 | 9 | ||
9 | import '../rx_stream/rx_stream.dart'; | 10 | import '../rx_stream/rx_stream.dart'; |
1 | import 'dart:async'; | 1 | import 'dart:async'; |
2 | 2 | ||
3 | import '../../../get_core/get_core.dart'; | 3 | import '../../../get_core/get_core.dart'; |
4 | +import '../../../get_state_manager/src/rx_flutter/rx_notifier.dart'; | ||
4 | import '../rx_types/rx_types.dart'; | 5 | import '../rx_types/rx_types.dart'; |
5 | import 'utils/debouncer.dart'; | 6 | import 'utils/debouncer.dart'; |
6 | 7 | ||
@@ -57,7 +58,7 @@ class Workers { | @@ -57,7 +58,7 @@ class Workers { | ||
57 | /// } | 58 | /// } |
58 | /// ``` | 59 | /// ``` |
59 | Worker ever<T>( | 60 | Worker ever<T>( |
60 | - RxInterface<T> listener, | 61 | + GetListenable<T> listener, |
61 | WorkerCallback<T> callback, { | 62 | WorkerCallback<T> callback, { |
62 | dynamic condition = true, | 63 | dynamic condition = true, |
63 | Function? onError, | 64 | Function? onError, |
@@ -132,7 +133,7 @@ Worker everAll( | @@ -132,7 +133,7 @@ Worker everAll( | ||
132 | /// } | 133 | /// } |
133 | ///``` | 134 | ///``` |
134 | Worker once<T>( | 135 | Worker once<T>( |
135 | - RxInterface<T> listener, | 136 | + GetListenable<T> listener, |
136 | WorkerCallback<T> callback, { | 137 | WorkerCallback<T> callback, { |
137 | dynamic condition = true, | 138 | dynamic condition = true, |
138 | Function? onError, | 139 | Function? onError, |
@@ -175,7 +176,7 @@ Worker once<T>( | @@ -175,7 +176,7 @@ Worker once<T>( | ||
175 | /// ); | 176 | /// ); |
176 | /// ``` | 177 | /// ``` |
177 | Worker interval<T>( | 178 | Worker interval<T>( |
178 | - RxInterface<T> listener, | 179 | + GetListenable<T> listener, |
179 | WorkerCallback<T> callback, { | 180 | WorkerCallback<T> callback, { |
180 | Duration time = const Duration(seconds: 1), | 181 | Duration time = const Duration(seconds: 1), |
181 | dynamic condition = true, | 182 | dynamic condition = true, |
@@ -219,7 +220,7 @@ Worker interval<T>( | @@ -219,7 +220,7 @@ Worker interval<T>( | ||
219 | /// } | 220 | /// } |
220 | /// ``` | 221 | /// ``` |
221 | Worker debounce<T>( | 222 | Worker debounce<T>( |
222 | - RxInterface<T> listener, | 223 | + GetListenable<T> listener, |
223 | WorkerCallback<T> callback, { | 224 | WorkerCallback<T> callback, { |
224 | Duration? time, | 225 | Duration? time, |
225 | Function? onError, | 226 | Function? onError, |
1 | -import 'dart:async'; | ||
2 | - | ||
3 | import 'package:flutter/foundation.dart'; | 1 | import 'package:flutter/foundation.dart'; |
4 | import 'package:flutter/widgets.dart'; | 2 | import 'package:flutter/widgets.dart'; |
5 | 3 | ||
6 | import '../../../get_core/get_core.dart'; | 4 | import '../../../get_core/get_core.dart'; |
7 | import '../../../get_instance/src/get_instance.dart'; | 5 | import '../../../get_instance/src/get_instance.dart'; |
8 | import '../../../get_instance/src/lifecycle.dart'; | 6 | import '../../../get_instance/src/lifecycle.dart'; |
9 | -import '../../../get_rx/src/rx_types/rx_types.dart'; | 7 | +import '../simple/list_notifier.dart'; |
8 | +import '../simple/simple_builder.dart'; | ||
10 | 9 | ||
11 | typedef GetXControllerBuilder<T extends GetLifeCycleMixin> = Widget Function( | 10 | typedef GetXControllerBuilder<T extends GetLifeCycleMixin> = Widget Function( |
12 | T controller); | 11 | T controller); |
13 | 12 | ||
13 | +class StatefulObserverComponent = StatefulElement with ObserverComponent; | ||
14 | + | ||
14 | class GetX<T extends GetLifeCycleMixin> extends StatefulWidget { | 15 | class GetX<T extends GetLifeCycleMixin> extends StatefulWidget { |
15 | final GetXControllerBuilder<T> builder; | 16 | final GetXControllerBuilder<T> builder; |
16 | final bool global; | 17 | final bool global; |
@@ -39,6 +40,9 @@ class GetX<T extends GetLifeCycleMixin> extends StatefulWidget { | @@ -39,6 +40,9 @@ class GetX<T extends GetLifeCycleMixin> extends StatefulWidget { | ||
39 | }); | 40 | }); |
40 | 41 | ||
41 | @override | 42 | @override |
43 | + StatefulElement createElement() => StatefulElement(this); | ||
44 | + | ||
45 | + @override | ||
42 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { | 46 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
43 | super.debugFillProperties(properties); | 47 | super.debugFillProperties(properties); |
44 | properties | 48 | properties |
@@ -55,10 +59,8 @@ class GetX<T extends GetLifeCycleMixin> extends StatefulWidget { | @@ -55,10 +59,8 @@ class GetX<T extends GetLifeCycleMixin> extends StatefulWidget { | ||
55 | } | 59 | } |
56 | 60 | ||
57 | class GetXState<T extends GetLifeCycleMixin> extends State<GetX<T>> { | 61 | class GetXState<T extends GetLifeCycleMixin> extends State<GetX<T>> { |
58 | - final _observer = RxNotifier(); | ||
59 | T? controller; | 62 | T? controller; |
60 | bool? _isCreator = false; | 63 | bool? _isCreator = false; |
61 | - late StreamSubscription _subs; | ||
62 | 64 | ||
63 | @override | 65 | @override |
64 | void initState() { | 66 | void initState() { |
@@ -83,7 +85,7 @@ class GetXState<T extends GetLifeCycleMixin> extends State<GetX<T>> { | @@ -83,7 +85,7 @@ class GetXState<T extends GetLifeCycleMixin> extends State<GetX<T>> { | ||
83 | if (widget.global && Get.smartManagement == SmartManagement.onlyBuilder) { | 85 | if (widget.global && Get.smartManagement == SmartManagement.onlyBuilder) { |
84 | controller?.onStart(); | 86 | controller?.onStart(); |
85 | } | 87 | } |
86 | - _subs = _observer.listen((data) => setState(() {}), cancelOnError: false); | 88 | + |
87 | super.initState(); | 89 | super.initState(); |
88 | } | 90 | } |
89 | 91 | ||
@@ -109,22 +111,29 @@ class GetXState<T extends GetLifeCycleMixin> extends State<GetX<T>> { | @@ -109,22 +111,29 @@ class GetXState<T extends GetLifeCycleMixin> extends State<GetX<T>> { | ||
109 | GetInstance().delete<T>(tag: widget.tag); | 111 | GetInstance().delete<T>(tag: widget.tag); |
110 | } | 112 | } |
111 | } | 113 | } |
112 | - _subs.cancel(); | ||
113 | - _observer.close(); | 114 | + |
115 | + for (final disposer in disposers) { | ||
116 | + disposer(); | ||
117 | + } | ||
118 | + | ||
114 | controller = null; | 119 | controller = null; |
115 | _isCreator = null; | 120 | _isCreator = null; |
116 | super.dispose(); | 121 | super.dispose(); |
117 | } | 122 | } |
118 | 123 | ||
124 | + void _update() { | ||
125 | + setState(() {}); | ||
126 | + } | ||
127 | + | ||
128 | + final disposers = <Disposer>[]; | ||
129 | + | ||
130 | + @override | ||
131 | + Widget build(BuildContext context) => TaskManager.instance | ||
132 | + .exchange(disposers, _update, () => widget.builder(controller!)); | ||
133 | + | ||
119 | @override | 134 | @override |
120 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { | 135 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
121 | super.debugFillProperties(properties); | 136 | super.debugFillProperties(properties); |
122 | properties.add(DiagnosticsProperty<T>('controller', controller)); | 137 | properties.add(DiagnosticsProperty<T>('controller', controller)); |
123 | } | 138 | } |
124 | - | ||
125 | - @override | ||
126 | - Widget build(BuildContext context) => RxInterface.notifyChildren( | ||
127 | - _observer, | ||
128 | - () => widget.builder(controller!), | ||
129 | - ); | ||
130 | } | 139 | } |
1 | +import 'dart:async'; | ||
2 | + | ||
1 | import 'package:flutter/foundation.dart'; | 3 | import 'package:flutter/foundation.dart'; |
2 | import 'package:flutter/material.dart'; | 4 | import 'package:flutter/material.dart'; |
3 | 5 | ||
@@ -5,10 +7,7 @@ import '../../../instance_manager.dart'; | @@ -5,10 +7,7 @@ import '../../../instance_manager.dart'; | ||
5 | import '../../get_state_manager.dart'; | 7 | import '../../get_state_manager.dart'; |
6 | import '../simple/list_notifier.dart'; | 8 | import '../simple/list_notifier.dart'; |
7 | 9 | ||
8 | -mixin StateMixin<T> on ListNotifier { | ||
9 | - late T _value; | ||
10 | - RxStatus? _status; | ||
11 | - | 10 | +extension _NullOrEmpty on Object { |
12 | bool _isNullOrEmpty(dynamic val) { | 11 | bool _isNullOrEmpty(dynamic val) { |
13 | if (val == null) return true; | 12 | if (val == null) return true; |
14 | var result = false; | 13 | var result = false; |
@@ -21,6 +20,11 @@ mixin StateMixin<T> on ListNotifier { | @@ -21,6 +20,11 @@ mixin StateMixin<T> on ListNotifier { | ||
21 | } | 20 | } |
22 | return result; | 21 | return result; |
23 | } | 22 | } |
23 | +} | ||
24 | + | ||
25 | +mixin StateMixin<T> on ListNotifier { | ||
26 | + late T _value; | ||
27 | + RxStatus? _status; | ||
24 | 28 | ||
25 | void _fillEmptyStatus() { | 29 | void _fillEmptyStatus() { |
26 | _status = _isNullOrEmpty(_value) ? RxStatus.loading() : RxStatus.success(); | 30 | _status = _isNullOrEmpty(_value) ? RxStatus.loading() : RxStatus.success(); |
@@ -72,6 +76,77 @@ mixin StateMixin<T> on ListNotifier { | @@ -72,6 +76,77 @@ mixin StateMixin<T> on ListNotifier { | ||
72 | } | 76 | } |
73 | } | 77 | } |
74 | 78 | ||
79 | +class GetListenable<T> extends ListNotifierSingle | ||
80 | + implements ValueListenable<T> { | ||
81 | + GetListenable(T val) : _value = val; | ||
82 | + | ||
83 | + StreamController<T>? _controller; | ||
84 | + | ||
85 | + StreamController<T> get subject { | ||
86 | + if (_controller == null) { | ||
87 | + _controller = StreamController<T>.broadcast(); | ||
88 | + addListener(_streamListener); | ||
89 | + } | ||
90 | + return _controller!; | ||
91 | + } | ||
92 | + | ||
93 | + void _streamListener() { | ||
94 | + _controller?.add(_value); | ||
95 | + } | ||
96 | + | ||
97 | + @mustCallSuper | ||
98 | + void close() { | ||
99 | + removeListener(_streamListener); | ||
100 | + _controller?.close(); | ||
101 | + dispose(); | ||
102 | + } | ||
103 | + | ||
104 | + Stream<T> get stream { | ||
105 | + return subject.stream; | ||
106 | + } | ||
107 | + | ||
108 | + T _value; | ||
109 | + | ||
110 | + @override | ||
111 | + T get value { | ||
112 | + reportRead(); | ||
113 | + return _value; | ||
114 | + } | ||
115 | + | ||
116 | + void _notify() { | ||
117 | + refresh(); | ||
118 | + } | ||
119 | + | ||
120 | + set value(T newValue) { | ||
121 | + if (_value == newValue) return; | ||
122 | + _value = newValue; | ||
123 | + _notify(); | ||
124 | + } | ||
125 | + | ||
126 | + T? call([T? v]) { | ||
127 | + if (v != null) { | ||
128 | + value = v; | ||
129 | + } | ||
130 | + return value; | ||
131 | + } | ||
132 | + | ||
133 | + StreamSubscription<T> listen( | ||
134 | + void Function(T)? onData, { | ||
135 | + Function? onError, | ||
136 | + void Function()? onDone, | ||
137 | + bool? cancelOnError, | ||
138 | + }) => | ||
139 | + stream.listen( | ||
140 | + onData, | ||
141 | + onError: onError, | ||
142 | + onDone: onDone, | ||
143 | + cancelOnError: cancelOnError ?? false, | ||
144 | + ); | ||
145 | + | ||
146 | + @override | ||
147 | + String toString() => value.toString(); | ||
148 | +} | ||
149 | + | ||
75 | class Value<T> extends ListNotifier | 150 | class Value<T> extends ListNotifier |
76 | with StateMixin<T> | 151 | with StateMixin<T> |
77 | implements ValueListenable<T?> { | 152 | implements ValueListenable<T?> { |
@@ -115,8 +190,6 @@ extension ReactiveT<T> on T { | @@ -115,8 +190,6 @@ extension ReactiveT<T> on T { | ||
115 | Value<T> get reactive => Value<T>(this); | 190 | Value<T> get reactive => Value<T>(this); |
116 | } | 191 | } |
117 | 192 | ||
118 | -typedef Condition = bool Function(); | ||
119 | - | ||
120 | abstract class GetNotifier<T> extends Value<T> with GetLifeCycleMixin { | 193 | abstract class GetNotifier<T> extends Value<T> with GetLifeCycleMixin { |
121 | GetNotifier(T initial) : super(initial); | 194 | GetNotifier(T initial) : super(initial); |
122 | } | 195 | } |
@@ -128,7 +201,7 @@ extension StateExt<T> on StateMixin<T> { | @@ -128,7 +201,7 @@ extension StateExt<T> on StateMixin<T> { | ||
128 | Widget? onLoading, | 201 | Widget? onLoading, |
129 | Widget? onEmpty, | 202 | Widget? onEmpty, |
130 | }) { | 203 | }) { |
131 | - return SimpleBuilder(builder: (_) { | 204 | + return Observer(builder: (_) { |
132 | if (status.isLoading) { | 205 | if (status.isLoading) { |
133 | return onLoading ?? const Center(child: CircularProgressIndicator()); | 206 | return onLoading ?? const Center(child: CircularProgressIndicator()); |
134 | } else if (status.isError) { | 207 | } else if (status.isError) { |
1 | -import 'dart:async'; | ||
2 | - | ||
3 | -import 'package:flutter/foundation.dart'; | ||
4 | import 'package:flutter/widgets.dart'; | 1 | import 'package:flutter/widgets.dart'; |
5 | 2 | ||
6 | import '../../../get_rx/src/rx_types/rx_types.dart'; | 3 | import '../../../get_rx/src/rx_types/rx_types.dart'; |
4 | +import '../simple/simple_builder.dart'; | ||
7 | 5 | ||
8 | typedef WidgetCallback = Widget Function(); | 6 | typedef WidgetCallback = Widget Function(); |
9 | 7 | ||
@@ -12,48 +10,8 @@ typedef WidgetCallback = Widget Function(); | @@ -12,48 +10,8 @@ typedef WidgetCallback = Widget Function(); | ||
12 | /// See also: | 10 | /// See also: |
13 | /// - [Obx] | 11 | /// - [Obx] |
14 | /// - [ObxValue] | 12 | /// - [ObxValue] |
15 | -abstract class ObxWidget extends StatefulWidget { | 13 | +abstract class ObxWidget extends ObxStatelessWidget { |
16 | const ObxWidget({Key? key}) : super(key: key); | 14 | const ObxWidget({Key? key}) : super(key: key); |
17 | - | ||
18 | - @override | ||
19 | - void debugFillProperties(DiagnosticPropertiesBuilder properties) { | ||
20 | - super.debugFillProperties(properties); | ||
21 | - properties..add(ObjectFlagProperty<Function>.has('builder', build)); | ||
22 | - } | ||
23 | - | ||
24 | - @override | ||
25 | - _ObxState createState() => _ObxState(); | ||
26 | - | ||
27 | - @protected | ||
28 | - Widget build(); | ||
29 | -} | ||
30 | - | ||
31 | -class _ObxState extends State<ObxWidget> { | ||
32 | - final _observer = RxNotifier(); | ||
33 | - late StreamSubscription subs; | ||
34 | - | ||
35 | - @override | ||
36 | - void initState() { | ||
37 | - super.initState(); | ||
38 | - subs = _observer.subject.stream.listen(_updateTree, cancelOnError: false); | ||
39 | - } | ||
40 | - | ||
41 | - void _updateTree(_) { | ||
42 | - if (mounted) { | ||
43 | - setState(() {}); | ||
44 | - } | ||
45 | - } | ||
46 | - | ||
47 | - @override | ||
48 | - void dispose() { | ||
49 | - subs.cancel(); | ||
50 | - _observer.close(); | ||
51 | - super.dispose(); | ||
52 | - } | ||
53 | - | ||
54 | - @override | ||
55 | - Widget build(BuildContext context) => | ||
56 | - RxInterface.notifyChildren(_observer, widget.build); | ||
57 | } | 15 | } |
58 | 16 | ||
59 | /// The simplest reactive widget in GetX. | 17 | /// The simplest reactive widget in GetX. |
@@ -69,7 +27,9 @@ class Obx extends ObxWidget { | @@ -69,7 +27,9 @@ class Obx extends ObxWidget { | ||
69 | const Obx(this.builder); | 27 | const Obx(this.builder); |
70 | 28 | ||
71 | @override | 29 | @override |
72 | - Widget build() => builder(); | 30 | + Widget build(BuildContext context) { |
31 | + return builder(); | ||
32 | + } | ||
73 | } | 33 | } |
74 | 34 | ||
75 | /// Similar to Obx, but manages a local state. | 35 | /// Similar to Obx, but manages a local state. |
@@ -90,5 +50,5 @@ class ObxValue<T extends RxInterface> extends ObxWidget { | @@ -90,5 +50,5 @@ class ObxValue<T extends RxInterface> extends ObxWidget { | ||
90 | const ObxValue(this.builder, this.data, {Key? key}) : super(key: key); | 50 | const ObxValue(this.builder, this.data, {Key? key}) : super(key: key); |
91 | 51 | ||
92 | @override | 52 | @override |
93 | - Widget build() => builder(data); | 53 | + Widget build(BuildContext context) => builder(data); |
94 | } | 54 | } |
@@ -47,15 +47,22 @@ mixin ListNotifierSingleMixin on Listenable { | @@ -47,15 +47,22 @@ mixin ListNotifierSingleMixin on Listenable { | ||
47 | TaskManager.instance.notify(this); | 47 | TaskManager.instance.notify(this); |
48 | } | 48 | } |
49 | 49 | ||
50 | + @protected | ||
51 | + void reportAdd(VoidCallback disposer) { | ||
52 | + TaskManager.instance.reportAdd(disposer); | ||
53 | + } | ||
54 | + | ||
50 | void _notifyUpdate() { | 55 | void _notifyUpdate() { |
51 | for (var element in _updaters!) { | 56 | for (var element in _updaters!) { |
52 | element!(); | 57 | element!(); |
53 | } | 58 | } |
54 | } | 59 | } |
55 | 60 | ||
61 | + bool get isDisposed => _updaters == null; | ||
62 | + | ||
56 | bool _debugAssertNotDisposed() { | 63 | bool _debugAssertNotDisposed() { |
57 | assert(() { | 64 | assert(() { |
58 | - if (_updaters == null) { | 65 | + if (isDisposed) { |
59 | throw FlutterError('''A $runtimeType was used after being disposed.\n | 66 | throw FlutterError('''A $runtimeType was used after being disposed.\n |
60 | 'Once you have called dispose() on a $runtimeType, it can no longer be used.'''); | 67 | 'Once you have called dispose() on a $runtimeType, it can no longer be used.'''); |
61 | } | 68 | } |
@@ -151,18 +158,16 @@ class TaskManager { | @@ -151,18 +158,16 @@ class TaskManager { | ||
151 | GetStateUpdate? _setter; | 158 | GetStateUpdate? _setter; |
152 | List<VoidCallback>? _remove; | 159 | List<VoidCallback>? _remove; |
153 | 160 | ||
154 | - final listNotifier = ListNotifierGroup(); | ||
155 | - | ||
156 | - // void addElement(Object id, GetStateUpdate listener) { | ||
157 | - // _remove?.add(listNotifier.addListenerId(id, listener)); | ||
158 | - // } | 161 | + void reportAdd(VoidCallback listener) { |
162 | + _remove?.add(listener); | ||
163 | + } | ||
159 | 164 | ||
160 | void notify(ListNotifierSingleMixin _updaters) { | 165 | void notify(ListNotifierSingleMixin _updaters) { |
161 | final listener = _setter; | 166 | final listener = _setter; |
162 | if (listener != null) { | 167 | if (listener != null) { |
163 | if (!_updaters.containsListener(listener)) { | 168 | if (!_updaters.containsListener(listener)) { |
164 | _updaters.addListener(listener); | 169 | _updaters.addListener(listener); |
165 | - _remove?.add(() => _updaters.removeListener(listener)); | 170 | + reportAdd(() => _updaters.removeListener(listener)); |
166 | } | 171 | } |
167 | } | 172 | } |
168 | } | 173 | } |
@@ -172,8 +177,26 @@ class TaskManager { | @@ -172,8 +177,26 @@ class TaskManager { | ||
172 | _remove = disposers; | 177 | _remove = disposers; |
173 | _setter = setState; | 178 | _setter = setState; |
174 | final result = builder(); | 179 | final result = builder(); |
180 | + if (disposers.isEmpty) { | ||
181 | + throw ObxError(); | ||
182 | + } | ||
175 | _remove = null; | 183 | _remove = null; |
176 | _setter = null; | 184 | _setter = null; |
177 | return result; | 185 | return result; |
178 | } | 186 | } |
179 | } | 187 | } |
188 | + | ||
189 | +class ObxError { | ||
190 | + const ObxError(); | ||
191 | + @override | ||
192 | + String toString() { | ||
193 | + return """ | ||
194 | + [Get] the improper use of a GetX has been detected. | ||
195 | + You should only use GetX or Obx for the specific widget that will be updated. | ||
196 | + If you are seeing this error, you probably did not insert any observable variables into GetX/Obx | ||
197 | + or insert them outside the scope that GetX considers suitable for an update | ||
198 | + (example: GetX => HeavyWidget => variableObservable). | ||
199 | + If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX. | ||
200 | + """; | ||
201 | + } | ||
202 | +} |
@@ -78,10 +78,10 @@ class _ValueBuilderState<T> extends State<ValueBuilder<T?>> { | @@ -78,10 +78,10 @@ class _ValueBuilderState<T> extends State<ValueBuilder<T?>> { | ||
78 | class ObxElement = StatelessElement with ObserverComponent; | 78 | class ObxElement = StatelessElement with ObserverComponent; |
79 | 79 | ||
80 | // It's a experimental feature | 80 | // It's a experimental feature |
81 | -class SimpleBuilder extends ObxStatelessWidget { | 81 | +class Observer extends ObxStatelessWidget { |
82 | final WidgetBuilder builder; | 82 | final WidgetBuilder builder; |
83 | 83 | ||
84 | - const SimpleBuilder({Key? key, required this.builder}) : super(key: key); | 84 | + const Observer({Key? key, required this.builder}) : super(key: key); |
85 | 85 | ||
86 | @override | 86 | @override |
87 | Widget build(BuildContext context) => builder(context); | 87 | Widget build(BuildContext context) => builder(context); |
1 | import 'dart:async'; | 1 | import 'dart:async'; |
2 | + | ||
2 | import 'package:flutter/foundation.dart'; | 3 | import 'package:flutter/foundation.dart'; |
3 | import 'package:flutter_test/flutter_test.dart'; | 4 | import 'package:flutter_test/flutter_test.dart'; |
4 | import 'package:get/state_manager.dart'; | 5 | import 'package:get/state_manager.dart'; |
@@ -73,28 +74,28 @@ Future<int> stream() { | @@ -73,28 +74,28 @@ Future<int> stream() { | ||
73 | return c.future; | 74 | return c.future; |
74 | } | 75 | } |
75 | 76 | ||
76 | -Future<int> getStream() { | ||
77 | - final c = Completer<int>(); | 77 | +// Future<int> getStream() { |
78 | +// final c = Completer<int>(); | ||
78 | 79 | ||
79 | - final value = GetStream<int>(); | ||
80 | - final timer = Stopwatch(); | ||
81 | - timer.start(); | 80 | +// final value = GetStream<int>(); |
81 | +// final timer = Stopwatch(); | ||
82 | +// timer.start(); | ||
82 | 83 | ||
83 | - value.listen((v) { | ||
84 | - if (times == v) { | ||
85 | - timer.stop(); | ||
86 | - print( | ||
87 | - """$v listeners notified | [GET_STREAM] time: ${timer.elapsedMicroseconds}ms"""); | ||
88 | - c.complete(timer.elapsedMicroseconds); | ||
89 | - } | ||
90 | - }); | 84 | +// value.listen((v) { |
85 | +// if (times == v) { | ||
86 | +// timer.stop(); | ||
87 | +// print( | ||
88 | +// """$v listeners notified | [GET_STREAM] time: ${timer.elapsedMicroseconds}ms"""); | ||
89 | +// c.complete(timer.elapsedMicroseconds); | ||
90 | +// } | ||
91 | +// }); | ||
91 | 92 | ||
92 | - for (var i = 0; i < times + 1; i++) { | ||
93 | - value.add(i); | ||
94 | - } | 93 | +// for (var i = 0; i < times + 1; i++) { |
94 | +// value.add(i); | ||
95 | +// } | ||
95 | 96 | ||
96 | - return c.future; | ||
97 | -} | 97 | +// return c.future; |
98 | +// } | ||
98 | 99 | ||
99 | Future<int> miniStream() { | 100 | Future<int> miniStream() { |
100 | final c = Completer<int>(); | 101 | final c = Completer<int>(); |
@@ -157,7 +158,7 @@ GetValue is ${calculePercentage(dart, getx).round()}% faster than Default ValueN | @@ -157,7 +158,7 @@ GetValue is ${calculePercentage(dart, getx).round()}% faster than Default ValueN | ||
157 | print('============================================'); | 158 | print('============================================'); |
158 | print('DART STREAM X GET_STREAM X GET_MINI_STREAM TEST'); | 159 | print('DART STREAM X GET_STREAM X GET_MINI_STREAM TEST'); |
159 | print('-----------'); | 160 | print('-----------'); |
160 | - var getx = await getStream(); | 161 | + // var getx = await getStream(); |
161 | var mini = await miniStream(); | 162 | var mini = await miniStream(); |
162 | var dart = await stream(); | 163 | var dart = await stream(); |
163 | print('-----------'); | 164 | print('-----------'); |
@@ -167,16 +168,16 @@ GetStream is ${calculePercentage(dart, mini).round()}% faster than Default Strea | @@ -167,16 +168,16 @@ GetStream is ${calculePercentage(dart, mini).round()}% faster than Default Strea | ||
167 | 168 | ||
168 | times = 30000; | 169 | times = 30000; |
169 | dart = await stream(); | 170 | dart = await stream(); |
170 | - getx = await getStream(); | 171 | + // getx = await getStream(); |
171 | mini = await miniStream(); | 172 | mini = await miniStream(); |
172 | 173 | ||
173 | times = 60000; | 174 | times = 60000; |
174 | dart = await stream(); | 175 | dart = await stream(); |
175 | - getx = await getStream(); | 176 | + // getx = await getStream(); |
176 | mini = await miniStream(); | 177 | mini = await miniStream(); |
177 | print('-----------'); | 178 | print('-----------'); |
178 | print('dart_stream delay $dart ms to made $times requests'); | 179 | print('dart_stream delay $dart ms to made $times requests'); |
179 | - print('getx_stream delay $getx ms to made $times requests'); | 180 | + // print('getx_stream delay $getx ms to made $times requests'); |
180 | print('getx_mini_stream delay $mini ms to made $times requests'); | 181 | print('getx_mini_stream delay $mini ms to made $times requests'); |
181 | print('-----------'); | 182 | print('-----------'); |
182 | print(''' | 183 | print(''' |
-
Please register or login to post a comment