Jonny Borges
Committed by GitHub

Merge pull request #2095 from jonataslaw/sm-refactor

Sm refactor
... ... @@ -3,7 +3,7 @@ import 'package:get/get.dart';
import '../../domain/adapters/repository_adapter.dart';
import '../../domain/entity/cases_model.dart';
class HomeController extends SuperController<CasesModel> {
class HomeController extends StateController<CasesModel> {
HomeController({required this.homeRepository});
final IHomeRepository homeRepository;
... ... @@ -11,74 +11,12 @@ class HomeController extends SuperController<CasesModel> {
@override
void onInit() {
super.onInit();
//Loading, Success, Error handle with 1 line of code
append(() => homeRepository.getCases);
futurize(() => homeRepository.getCases);
}
Country getCountryById(String id) {
final index = int.tryParse(id);
if (index != null) {
return state.countries[index];
}
return state.countries.first;
}
@override
void onReady() {
print('The build method is done. '
'Your controller is ready to call dialogs and snackbars');
super.onReady();
}
@override
void onClose() {
print('onClose called');
super.onClose();
}
@override
void didChangeMetrics() {
print('the window size did change');
super.didChangeMetrics();
}
@override
void didChangePlatformBrightness() {
print('platform change ThemeMode');
super.didChangePlatformBrightness();
}
@override
Future<bool> didPushRoute(String route) {
print('the route $route will be open');
return super.didPushRoute(route);
}
@override
Future<bool> didPopRoute() {
print('the current route will be closed');
return super.didPopRoute();
}
@override
void onDetached() {
print('onDetached called');
}
@override
void onInactive() {
print('onInative called');
}
@override
void onPaused() {
print('onPaused called');
}
@override
void onResumed() {
print('onResumed called');
return index != null ? state.countries[index] : state.countries.first;
}
}
... ...
import 'dart:io';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
... ... @@ -11,28 +10,29 @@ import 'package:get_demo/pages/home/presentation/controllers/home_controller.dar
// import 'package:get_test/get_test.dart';
import 'package:matcher/matcher.dart' as m;
class MockRepository implements IHomeRepository {
class MockRepositorySuccess implements IHomeRepository {
@override
Future<CasesModel> getCases() async {
await Future.delayed(Duration(milliseconds: 100));
if (Random().nextBool()) {
return CasesModel(
global: Global(
totalDeaths: 100,
totalConfirmed: 200,
date: DateTime.now(),
newConfirmed: 0,
newDeaths: 0,
newRecovered: 0,
totalRecovered: 0),
countries: [],
date: DateTime.now(),
id: '',
message: '',
);
}
return CasesModel(
global: Global(
totalDeaths: 100,
totalConfirmed: 200,
date: DateTime.now(),
newConfirmed: 0,
newDeaths: 0,
newRecovered: 0,
totalRecovered: 0),
countries: [],
date: DateTime.now(),
id: '',
message: '',
);
}
}
class MockRepositoryFailure implements IHomeRepository {
@override
Future<CasesModel> getCases() async {
return Future<CasesModel>.error('error');
}
}
... ... @@ -41,28 +41,18 @@ void main() {
WidgetsFlutterBinding.ensureInitialized();
setUpAll(() => HttpOverrides.global = null);
final binding = BindingsBuilder(() {
Get.lazyPut<IHomeRepository>(() => MockRepository());
Get.lazyPut<IHomeRepository>(() => MockRepositorySuccess());
Get.lazyPut<HomeController>(
() => HomeController(homeRepository: Get.find()));
() => HomeController(homeRepository: Get.find()),
);
});
test('Test Binding', () {
expect(Get.isPrepared<HomeController>(), false);
expect(Get.isPrepared<IHomeRepository>(), false);
/// test you Binding class with BindingsBuilder
binding.builder();
expect(Get.isPrepared<HomeController>(), true);
expect(Get.isPrepared<IHomeRepository>(), true);
Get.reset();
});
test('Test Controller', () async {
/// Controller can't be on memory
expect(() => Get.find<HomeController>(), throwsA(m.TypeMatcher<String>()));
expect(() => Get.find<HomeController>(tag: 'success'),
throwsA(m.TypeMatcher<String>()));
/// build Binding
/// binding will put the controller on memory
binding.builder();
/// recover your controller
... ... @@ -77,24 +67,15 @@ void main() {
/// await time request
await Future.delayed(Duration(milliseconds: 100));
if (controller.status.isError) {
expect(controller.state, null);
}
if (controller.status.isSuccess) {
expect(controller.state.global.totalDeaths, 100);
expect(controller.state.global.totalConfirmed, 200);
}
});
/// test if status is success
expect(controller.status.isSuccess, true);
expect(controller.state.global.totalDeaths, 100);
expect(controller.state.global.totalConfirmed, 200);
test('ever', () async {
final count = ''.obs;
var result = '';
ever<String>(count, (value) {
result = value;
});
count.value = '1';
expect('1', result);
/// test if status is error
Get.lazyReplace<IHomeRepository>(() => MockRepositoryFailure());
expect(controller.status.isError, true);
expect(controller.state, null);
});
/// Tests with GetTests
... ... @@ -151,26 +132,3 @@ void main() {
},
);*/
}
class Controller extends GetxController {
final count = 0.obs;
void increment() => count.value++;
@override
void onInit() {
print('inittt');
super.onInit();
}
@override
void onReady() {
print('onReady');
super.onReady();
}
@override
void onClose() {
super.onClose();
print('onClose');
}
}
... ...
... ... @@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>8.0</string>
<string>9.0</string>
</dict>
</plist>
... ...
... ... @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
... ... @@ -127,7 +127,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1020;
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
... ...
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
... ...
... ... @@ -6,7 +6,8 @@ import 'package:flutter/material.dart';
import '../../../get.dart';
import '../../../get_state_manager/src/simple/list_notifier.dart';
class GetDelegate extends RouterDelegate<GetNavConfig> with ListNotifierMixin {
class GetDelegate extends RouterDelegate<GetNavConfig>
with ListNotifierSingleMixin {
final List<GetNavConfig> history = <GetNavConfig>[];
final PopMode backButtonPopMode;
final PreventDuplicateHandlingMode preventDuplicateHandlingMode;
... ...
part of rx_stream;
/// [GetStream] is the lightest and most performative way of working
/// with events at Dart. You sintaxe is like StreamController, but it works
/// with simple callbacks. In this way, every event calls only one function.
/// There is no buffering, to very low memory consumption.
/// event [add] will add a object to stream. [addError] will add a error
/// to stream. [listen] is a very light StreamSubscription interface.
/// Is possible take the last value with [value] property.
class GetStream<T> {
void Function()? onListen;
void Function()? onPause;
void Function()? onResume;
FutureOr<void> Function()? onCancel;
GetStream({this.onListen, this.onPause, this.onResume, this.onCancel});
factory GetStream.fromValue(T value,
{Function()? onListen,
Function()? onPause,
Function()? onResume,
FutureOr<void> Function()? onCancel}) {
final valuedStream = GetStream<T>(
onListen: onListen,
onPause: onPause,
onResume: onResume,
onCancel: onCancel)
.._value = value;
return valuedStream;
}
List<LightSubscription<T>>? _onData = <LightSubscription<T>>[];
bool? _isBusy = false;
FutureOr<bool?> removeSubscription(LightSubscription<T> subs) async {
if (!_isBusy!) {
return _onData!.remove(subs);
} else {
await Future.delayed(Duration.zero);
return _onData?.remove(subs);
}
}
FutureOr<void> addSubscription(LightSubscription<T> subs) async {
if (!_isBusy!) {
return _onData!.add(subs);
} else {
await Future.delayed(Duration.zero);
return _onData!.add(subs);
}
}
int? get length => _onData?.length;
bool get hasListeners => _onData!.isNotEmpty;
void _notifyData(T data) {
_isBusy = true;
for (final item in _onData!) {
if (!item.isPaused) {
item._data?.call(data);
}
}
_isBusy = false;
}
void _notifyError(Object error, [StackTrace? stackTrace]) {
assert(!isClosed, 'You cannot add errors to a closed stream.');
_isBusy = true;
var itemsToRemove = <LightSubscription<T>>[];
for (final item in _onData!) {
if (!item.isPaused) {
if (stackTrace != null) {
item._onError?.call(error, stackTrace);
} else {
item._onError?.call(error);
}
if (item.cancelOnError ?? false) {
//item.cancel?.call();
itemsToRemove.add(item);
item.pause();
item._onDone?.call();
}
}
}
for (final item in itemsToRemove) {
_onData!.remove(item);
}
_isBusy = false;
}
void _notifyDone() {
assert(!isClosed, 'You cannot close a closed stream.');
_isBusy = true;
for (final item in _onData!) {
if (!item.isPaused) {
item._onDone?.call();
}
}
_isBusy = false;
}
late T _value;
T get value {
RxInterface.proxy?.addListener(this);
return _value;
}
void add(T event) {
assert(!isClosed, 'You cannot add event to closed Stream');
_value = event;
_notifyData(event);
}
bool get isClosed => _onData == null;
void addError(Object error, [StackTrace? stackTrace]) {
assert(!isClosed, 'You cannot add error to closed Stream');
_notifyError(error, stackTrace);
}
void close() {
assert(!isClosed, 'You cannot close a closed Stream');
_notifyDone();
_onData = null;
_isBusy = null;
// _value = null;
}
LightSubscription<T> listen(void Function(T event) onData,
{Function? onError, void Function()? onDone, bool? cancelOnError}) {
final subs = LightSubscription<T>(
removeSubscription,
onPause: onPause,
onResume: onResume,
onCancel: onCancel,
)
..onData(onData)
..onError(onError)
..onDone(onDone)
..cancelOnError = cancelOnError;
addSubscription(subs);
onListen?.call();
return subs;
}
Stream<T> get stream =>
GetStreamTransformation(addSubscription, removeSubscription);
}
class LightSubscription<T> extends StreamSubscription<T> {
final RemoveSubscription<T> _removeSubscription;
LightSubscription(this._removeSubscription,
{this.onPause, this.onResume, this.onCancel});
final void Function()? onPause;
final void Function()? onResume;
final FutureOr<void> Function()? onCancel;
bool? cancelOnError = false;
@override
Future<void> cancel() {
_removeSubscription(this);
onCancel?.call();
return Future.value();
}
OnData<T>? _data;
Function? _onError;
Callback? _onDone;
bool _isPaused = false;
@override
void onData(OnData<T>? handleData) => _data = handleData;
@override
void onError(Function? handleError) => _onError = handleError;
@override
void onDone(Callback? handleDone) => _onDone = handleDone;
@override
void pause([Future<void>? resumeSignal]) {
_isPaused = true;
onPause?.call();
}
@override
void resume() {
_isPaused = false;
onResume?.call();
}
@override
bool get isPaused => _isPaused;
@override
Future<E> asFuture<E>([E? futureValue]) => Future.value(futureValue);
}
class GetStreamTransformation<T> extends Stream<T> {
final AddSubscription<T> _addSubscription;
final RemoveSubscription<T> _removeSubscription;
GetStreamTransformation(this._addSubscription, this._removeSubscription);
@override
LightSubscription<T> listen(void Function(T event)? onData,
{Function? onError, void Function()? onDone, bool? cancelOnError}) {
final subs = LightSubscription<T>(_removeSubscription)
..onData(onData)
..onError(onError)
..onDone(onDone);
_addSubscription(subs);
return subs;
}
}
typedef RemoveSubscription<T> = FutureOr<bool?> Function(
LightSubscription<T> subs);
typedef AddSubscription<T> = FutureOr<void> Function(LightSubscription<T> subs);
// part of rx_stream;
// /// [GetStream] is the lightest and most performative way of working
// /// with events at Dart. You sintaxe is like StreamController, but it works
// /// with simple callbacks. In this way, every event calls only one function.
// /// There is no buffering, to very low memory consumption.
// /// event [add] will add a object to stream. [addError] will add a error
// /// to stream. [listen] is a very light StreamSubscription interface.
// /// Is possible take the last value with [value] property.
// class GetStream<T> {
// void Function()? onListen;
// void Function()? onPause;
// void Function()? onResume;
// FutureOr<void> Function()? onCancel;
// GetStream({this.onListen, this.onPause, this.onResume, this.onCancel});
// factory GetStream.fromValue(T value,
// {Function()? onListen,
// Function()? onPause,
// Function()? onResume,
// FutureOr<void> Function()? onCancel}) {
// final valuedStream = GetStream<T>(
// onListen: onListen,
// onPause: onPause,
// onResume: onResume,
// onCancel: onCancel)
// .._value = value;
// return valuedStream;
// }
// List<LightSubscription<T>>? _onData = <LightSubscription<T>>[];
// bool? _isBusy = false;
// FutureOr<bool?> removeSubscription(LightSubscription<T> subs) async {
// if (!_isBusy!) {
// return _onData!.remove(subs);
// } else {
// await Future.delayed(Duration.zero);
// return _onData?.remove(subs);
// }
// }
// FutureOr<void> addSubscription(LightSubscription<T> subs) async {
// if (!_isBusy!) {
// return _onData!.add(subs);
// } else {
// await Future.delayed(Duration.zero);
// return _onData!.add(subs);
// }
// }
// int? get length => _onData?.length;
// bool get hasListeners => _onData!.isNotEmpty;
// void _notifyData(T data) {
// _isBusy = true;
// for (final item in _onData!) {
// if (!item.isPaused) {
// item._data?.call(data);
// }
// }
// _isBusy = false;
// }
// void _notifyError(Object error, [StackTrace? stackTrace]) {
// assert(!isClosed, 'You cannot add errors to a closed stream.');
// _isBusy = true;
// var itemsToRemove = <LightSubscription<T>>[];
// for (final item in _onData!) {
// if (!item.isPaused) {
// if (stackTrace != null) {
// item._onError?.call(error, stackTrace);
// } else {
// item._onError?.call(error);
// }
// if (item.cancelOnError ?? false) {
// //item.cancel?.call();
// itemsToRemove.add(item);
// item.pause();
// item._onDone?.call();
// }
// }
// }
// for (final item in itemsToRemove) {
// _onData!.remove(item);
// }
// _isBusy = false;
// }
// void _notifyDone() {
// assert(!isClosed, 'You cannot close a closed stream.');
// _isBusy = true;
// for (final item in _onData!) {
// if (!item.isPaused) {
// item._onDone?.call();
// }
// }
// _isBusy = false;
// }
// late T _value;
// T get value {
// // RxInterface.proxy?.addListener(this);
// return _value;
// }
// void add(T event) {
// assert(!isClosed, 'You cannot add event to closed Stream');
// _value = event;
// _notifyData(event);
// }
// bool get isClosed => _onData == null;
// void addError(Object error, [StackTrace? stackTrace]) {
// assert(!isClosed, 'You cannot add error to closed Stream');
// _notifyError(error, stackTrace);
// }
// void close() {
// assert(!isClosed, 'You cannot close a closed Stream');
// _notifyDone();
// _onData = null;
// _isBusy = null;
// // _value = null;
// }
// LightSubscription<T> listen(void Function(T event) onData,
// {Function? onError, void Function()? onDone, bool? cancelOnError}) {
// final subs = LightSubscription<T>(
// removeSubscription,
// onPause: onPause,
// onResume: onResume,
// onCancel: onCancel,
// )
// ..onData(onData)
// ..onError(onError)
// ..onDone(onDone)
// ..cancelOnError = cancelOnError;
// addSubscription(subs);
// onListen?.call();
// return subs;
// }
// Stream<T> get stream =>
// GetStreamTransformation(addSubscription, removeSubscription);
// }
// class LightSubscription<T> extends StreamSubscription<T> {
// final RemoveSubscription<T> _removeSubscription;
// LightSubscription(this._removeSubscription,
// {this.onPause, this.onResume, this.onCancel});
// final void Function()? onPause;
// final void Function()? onResume;
// final FutureOr<void> Function()? onCancel;
// bool? cancelOnError = false;
// @override
// Future<void> cancel() {
// _removeSubscription(this);
// onCancel?.call();
// return Future.value();
// }
// OnData<T>? _data;
// Function? _onError;
// Callback? _onDone;
// bool _isPaused = false;
// @override
// void onData(OnData<T>? handleData) => _data = handleData;
// @override
// void onError(Function? handleError) => _onError = handleError;
// @override
// void onDone(Callback? handleDone) => _onDone = handleDone;
// @override
// void pause([Future<void>? resumeSignal]) {
// _isPaused = true;
// onPause?.call();
// }
// @override
// void resume() {
// _isPaused = false;
// onResume?.call();
// }
// @override
// bool get isPaused => _isPaused;
// @override
// Future<E> asFuture<E>([E? futureValue]) => Future.value(futureValue);
// }
// class GetStreamTransformation<T> extends Stream<T> {
// final AddSubscription<T> _addSubscription;
// final RemoveSubscription<T> _removeSubscription;
// GetStreamTransformation(this._addSubscription, this._removeSubscription);
// @override
// LightSubscription<T> listen(void Function(T event)? onData,
// {Function? onError, void Function()? onDone, bool? cancelOnError}) {
// final subs = LightSubscription<T>(_removeSubscription)
// ..onData(onData)
// ..onError(onError)
// ..onDone(onDone);
// _addSubscription(subs);
// return subs;
// }
// }
// typedef RemoveSubscription<T> = FutureOr<bool?> Function(
// LightSubscription<T> subs);
// typedef AddSubscription<T> =
//FutureOr<void> Function(LightSubscription<T> subs);
... ...
... ... @@ -3,7 +3,6 @@ library rx_stream;
import 'dart:async';
import '../rx_typedefs/rx_typedefs.dart';
import '../rx_types/rx_types.dart';
part 'get_stream.dart';
//part 'get_stream.dart';
part 'mini_stream.dart';
... ...
... ... @@ -4,7 +4,7 @@ part of rx_types;
/// reactivity
/// of those `Widgets` and Rx values.
mixin RxObjectMixin<T> on NotifyManager<T> {
mixin RxObjectMixin<T> on GetListenable<T> {
//late T _value;
/// Makes a direct update of [value] adding it to the Stream
... ... @@ -25,9 +25,9 @@ mixin RxObjectMixin<T> on NotifyManager<T> {
/// person.refresh();
/// print( person );
/// ```
void refresh() {
subject.add(value);
}
// void refresh() {
// subject.add(value);
// }
/// updates the value to `null` and adds it to the Stream.
/// Even with null-safety coming, is still an important feature to support, as
... ... @@ -59,6 +59,7 @@ mixin RxObjectMixin<T> on NotifyManager<T> {
/// onChanged: myText,
/// ),
///```
@override
T call([T? v]) {
if (v != null) {
value = v;
... ... @@ -95,25 +96,18 @@ mixin RxObjectMixin<T> on NotifyManager<T> {
/// Updates the [value] and adds it to the stream, updating the observer
/// Widget, only if it's different from the previous value.
@override
set value(T val) {
if (subject.isClosed) return;
if (isDisposed) return;
sentToStream = false;
if (value == val && !firstRebuild) return;
firstRebuild = false;
// _value = val;
sentToStream = true;
subject.add(val);
//TODO: Check this
super.value = val;
}
/// Returns the current [value]
T get value {
return subject.value;
//RxInterface.proxy?.addListener(subject);
// return _value;
}
Stream<T> get stream => subject.stream;
/// Returns a [StreamSubscription] similar to [listen], but with the
/// added benefit that it primes the stream with the current [value], rather
/// than waiting for the next [value]. This should not be called in [onInit]
... ... @@ -127,6 +121,7 @@ mixin RxObjectMixin<T> on NotifyManager<T> {
cancelOnError: cancelOnError,
);
//TODO: Change to refresh????
subject.add(value);
return subscription;
... ... @@ -137,64 +132,64 @@ mixin RxObjectMixin<T> on NotifyManager<T> {
/// Closing the subscription will happen automatically when the observer
/// Widget (`GetX` or `Obx`) gets unmounted from the Widget tree.
void bindStream(Stream<T> stream) {
final listSubscriptions =
_subscriptions[subject] ??= <StreamSubscription>[];
listSubscriptions.add(stream.listen((va) => value = va));
}
}
class RxNotifier<T> = RxInterface<T> with NotifyManager<T>;
mixin NotifyManager<T> {
GetStream<T> subject = GetStream<T>();
final _subscriptions = <GetStream, List<StreamSubscription>>{};
// final listSubscriptions =
// _subscriptions[subject] ??= <StreamSubscription>[];
bool get canUpdate => _subscriptions.isNotEmpty;
/// This is an internal method.
/// Subscribe to changes on the inner stream.
void addListener(GetStream<T> rxGetx) {
if (!_subscriptions.containsKey(rxGetx)) {
final subs = rxGetx.listen((data) {
if (!subject.isClosed) subject.add(data);
});
final listSubscriptions =
_subscriptions[rxGetx] ??= <StreamSubscription>[];
listSubscriptions.add(subs);
}
}
StreamSubscription<T> listen(
void Function(T) onData, {
Function? onError,
void Function()? onDone,
bool? cancelOnError,
}) =>
subject.listen(
onData,
onError: onError,
onDone: onDone,
cancelOnError: cancelOnError ?? false,
);
/// Closes the subscriptions for this Rx, releasing the resources.
void close() {
_subscriptions.forEach((getStream, _subscriptions) {
for (final subscription in _subscriptions) {
subscription.cancel();
}
});
_subscriptions.clear();
subject.close();
final sub = stream.listen((va) => value = va);
reportAdd(sub.cancel);
}
}
//class RxNotifier<T> = RxInterface<T> with NotifyManager<T>;
// mixin NotifyManager<T> {
// GetStream<T> subject = GetStream<T>();
// final _subscriptions = <GetStream, List<StreamSubscription>>{};
// bool get canUpdate => _subscriptions.isNotEmpty;
// /// This is an internal method.
// /// Subscribe to changes on the inner stream.
// void addListener(GetStream<T> rxGetx) {
// if (!_subscriptions.containsKey(rxGetx)) {
// final subs = rxGetx.listen((data) {
// if (!subject.isClosed) subject.add(data);
// });
// final listSubscriptions =
// _subscriptions[rxGetx] ??= <StreamSubscription>[];
// listSubscriptions.add(subs);
// }
// }
// StreamSubscription<T> listen(
// void Function(T) onData, {
// Function? onError,
// void Function()? onDone,
// bool? cancelOnError,
// }) =>
// subject.listen(
// onData,
// onError: onError,
// onDone: onDone,
// cancelOnError: cancelOnError ?? false,
// );
// /// Closes the subscriptions for this Rx, releasing the resources.
// void close() {
// _subscriptions.forEach((getStream, _subscriptions) {
// for (final subscription in _subscriptions) {
// subscription.cancel();
// }
// });
// _subscriptions.clear();
// subject.close();
// }
// }
/// Base Rx class that manages all the stream logic for any Type.
abstract class _RxImpl<T> extends RxNotifier<T> with RxObjectMixin<T> {
_RxImpl(T initial) {
subject = GetStream.fromValue(initial);
}
abstract class _RxImpl<T> extends GetListenable<T> with RxObjectMixin<T> {
_RxImpl(T initial) : super(initial);
void addError(Object error, [StackTrace? stackTrace]) {
subject.addError(error, stackTrace);
... ...
... ... @@ -5,12 +5,10 @@ part of rx_types;
/// This interface is the contract that _RxImpl]<T> uses in all it's
/// subclass.
abstract class RxInterface<T> {
static RxInterface? proxy;
bool get canUpdate;
//bool get canUpdate;
/// Adds a listener to stream
void addListener(GetStream<T> rxGetx);
void addListener(VoidCallback listener);
/// Close the Rx Variable
void close();
... ... @@ -20,13 +18,24 @@ abstract class RxInterface<T> {
{Function? onError, void Function()? onDone, bool? cancelOnError});
/// Avoids an unsafe usage of the `proxy`
static T notifyChildren<T>(RxNotifier observer, ValueGetter<T> builder) {
final _observer = RxInterface.proxy;
RxInterface.proxy = observer;
final result = builder();
if (!observer.canUpdate) {
RxInterface.proxy = _observer;
throw """
// static T notifyChildren<T>(RxNotifier observer, ValueGetter<T> builder) {
// final _observer = RxInterface.proxy;
// RxInterface.proxy = observer;
// final result = builder();
// if (!observer.canUpdate) {
// RxInterface.proxy = _observer;
// throw ObxError();
// }
// RxInterface.proxy = _observer;
// return result;
// }
}
class ObxError {
const ObxError();
@override
String toString() {
return """
[Get] the improper use of a GetX has been detected.
You should only use GetX or Obx for the specific widget that will be updated.
If you are seeing this error, you probably did not insert any observable variables into GetX/Obx
... ... @@ -34,8 +43,5 @@ abstract class RxInterface<T> {
(example: GetX => HeavyWidget => variableObservable).
If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX.
""";
}
RxInterface.proxy = _observer;
return result;
}
}
... ...
part of rx_types;
/// Create a list similar to `List<T>`
class RxList<E> extends ListMixin<E>
with NotifyManager<List<E>>, RxObjectMixin<List<E>>
implements RxInterface<List<E>> {
RxList([List<E> initial = const []]) {
subject = GetStream.fromValue(List.from(initial));
}
class RxList<E> extends GetListenable<List<E>>
with ListMixin<E>, RxObjectMixin<List<E>> {
RxList([List<E> initial = const []]) : super(initial);
factory RxList.filled(int length, E fill, {bool growable = false}) {
return RxList(List.filled(length, fill, growable: growable));
... ... @@ -87,12 +84,12 @@ class RxList<E> extends ListMixin<E>
@override
int get length => value.length;
@override
@protected
List<E> get value {
RxInterface.proxy?.addListener(subject);
return subject.value;
}
// @override
// @protected
// List<E> get value {
// RxInterface.proxy?.addListener(subject);
// return subject.value;
// }
@override
set length(int newLength) {
... ...
part of rx_types;
class RxMap<K, V> extends MapMixin<K, V>
with NotifyManager<Map<K, V>>, RxObjectMixin<Map<K, V>>
implements RxInterface<Map<K, V>> {
RxMap([Map<K, V> initial = const {}]) {
subject = GetStream.fromValue(Map.from(initial));
}
class RxMap<K, V> extends GetListenable<Map<K, V>>
with MapMixin<K, V>, RxObjectMixin<Map<K, V>> {
RxMap([Map<K, V> initial = const {}]) : super(initial);
factory RxMap.from(Map<K, V> other) {
return RxMap(Map.from(other));
... ... @@ -53,13 +50,13 @@ class RxMap<K, V> extends MapMixin<K, V>
return val;
}
@override
@protected
Map<K, V> get value {
return subject.value;
// RxInterface.proxy?.addListener(subject);
// return _value;
}
// @override
// @protected
// Map<K, V> get value {
// return subject.value;
// // RxInterface.proxy?.addListener(subject);
// // return _value;
// }
}
extension MapExtension<K, V> on Map<K, V> {
... ... @@ -99,6 +96,7 @@ extension MapExtension<K, V> on Map<K, V> {
final map = (this as RxMap);
if (map.value == val) return;
map.value = val;
// ignore: invalid_use_of_protected_member
map.refresh();
} else {
if (this == val) return;
... ...
part of rx_types;
class RxSet<E> extends SetMixin<E>
with NotifyManager<Set<E>>, RxObjectMixin<Set<E>>
implements RxInterface<Set<E>> {
RxSet([Set<E> initial = const {}]) {
subject = GetStream.fromValue(Set.from(initial));
}
class RxSet<E> extends GetListenable<Set<E>>
with SetMixin<E>, RxObjectMixin<Set<E>> {
RxSet([Set<E> initial = const {}]) : super(initial);
/// Special override to push() element(s) in a reactive way
/// inside the List,
... ... @@ -20,13 +17,13 @@ class RxSet<E> extends SetMixin<E>
refresh();
}
@override
@protected
Set<E> get value {
return subject.value;
// RxInterface.proxy?.addListener(subject);
// return _value;
}
// @override
// @protected
// Set<E> get value {
// return subject.value;
// // RxInterface.proxy?.addListener(subject);
// // return _value;
// }
@override
@protected
... ...
... ... @@ -5,7 +5,7 @@ import 'dart:collection';
import 'package:flutter/foundation.dart';
import '../rx_stream/rx_stream.dart';
import '../../../get_state_manager/src/rx_flutter/rx_notifier.dart';
import '../rx_typedefs/rx_typedefs.dart';
part 'rx_core/rx_impl.dart';
... ...
import 'dart:async';
import '../../../get_core/get_core.dart';
import '../../../get_state_manager/src/rx_flutter/rx_notifier.dart';
import '../rx_types/rx_types.dart';
import 'utils/debouncer.dart';
... ... @@ -57,7 +58,7 @@ class Workers {
/// }
/// ```
Worker ever<T>(
RxInterface<T> listener,
GetListenable<T> listener,
WorkerCallback<T> callback, {
dynamic condition = true,
Function? onError,
... ... @@ -132,7 +133,7 @@ Worker everAll(
/// }
///```
Worker once<T>(
RxInterface<T> listener,
GetListenable<T> listener,
WorkerCallback<T> callback, {
dynamic condition = true,
Function? onError,
... ... @@ -175,7 +176,7 @@ Worker once<T>(
/// );
/// ```
Worker interval<T>(
RxInterface<T> listener,
GetListenable<T> listener,
WorkerCallback<T> callback, {
Duration time = const Duration(seconds: 1),
dynamic condition = true,
... ... @@ -219,7 +220,7 @@ Worker interval<T>(
/// }
/// ```
Worker debounce<T>(
RxInterface<T> listener,
GetListenable<T> listener,
WorkerCallback<T> callback, {
Duration? time,
Function? onError,
... ...
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import '../../../get_core/get_core.dart';
import '../../../get_instance/src/get_instance.dart';
import '../../../get_instance/src/lifecycle.dart';
import '../../../get_rx/src/rx_types/rx_types.dart';
import '../simple/list_notifier.dart';
import '../simple/simple_builder.dart';
typedef GetXControllerBuilder<T extends GetLifeCycleMixin> = Widget Function(
T controller);
class StatefulObserverComponent = StatefulElement with ObserverComponent;
class GetX<T extends GetLifeCycleMixin> extends StatefulWidget {
final GetXControllerBuilder<T> builder;
final bool global;
... ... @@ -39,6 +40,9 @@ class GetX<T extends GetLifeCycleMixin> extends StatefulWidget {
});
@override
StatefulElement createElement() => StatefulElement(this);
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
... ... @@ -55,10 +59,8 @@ class GetX<T extends GetLifeCycleMixin> extends StatefulWidget {
}
class GetXState<T extends GetLifeCycleMixin> extends State<GetX<T>> {
final _observer = RxNotifier();
T? controller;
bool? _isCreator = false;
late StreamSubscription _subs;
@override
void initState() {
... ... @@ -83,7 +85,7 @@ class GetXState<T extends GetLifeCycleMixin> extends State<GetX<T>> {
if (widget.global && Get.smartManagement == SmartManagement.onlyBuilder) {
controller?.onStart();
}
_subs = _observer.listen((data) => setState(() {}), cancelOnError: false);
super.initState();
}
... ... @@ -109,22 +111,29 @@ class GetXState<T extends GetLifeCycleMixin> extends State<GetX<T>> {
GetInstance().delete<T>(tag: widget.tag);
}
}
_subs.cancel();
_observer.close();
for (final disposer in disposers) {
disposer();
}
controller = null;
_isCreator = null;
super.dispose();
}
void _update() {
setState(() {});
}
final disposers = <Disposer>[];
@override
Widget build(BuildContext context) => TaskManager.instance
.exchange(disposers, _update, () => widget.builder(controller!));
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<T>('controller', controller));
}
@override
Widget build(BuildContext context) => RxInterface.notifyChildren(
_observer,
() => widget.builder(controller!),
);
}
... ...
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
... ... @@ -5,37 +7,52 @@ import '../../../instance_manager.dart';
import '../../get_state_manager.dart';
import '../simple/list_notifier.dart';
mixin StateMixin<T> on ListNotifierMixin {
late T _value;
RxStatus? _status;
bool _isNullOrEmpty(dynamic val) {
if (val == null) return true;
extension _Empty on Object {
bool _isEmpty() {
final val = this;
// if (val == null) return true;
var result = false;
if (val is Iterable) {
result = val.isEmpty;
} else if (val is String) {
result = val.isEmpty;
result = val.trim().isEmpty;
} else if (val is Map) {
result = val.isEmpty;
}
return result;
}
}
void _fillEmptyStatus() {
_status = _isNullOrEmpty(_value) ? RxStatus.loading() : RxStatus.success();
mixin StateMixin<T> on ListNotifier {
late T _value;
GetState<T>? _status;
void _fillInitialStatus() {
_status = (value == null || value!._isEmpty())
? GetState<T>.loading()
: GetState<T>.success(_value);
}
RxStatus get status {
notifyChildrens();
return _status ??= _status = RxStatus.loading();
GetState<T> get status {
reportRead();
return _status ??= _status = GetState.loading();
}
T get state => value;
set status(GetState<T> newStatus) {
if (newStatus == status) return;
_status = newStatus;
if (newStatus is SuccessState<T>) {
_value = newStatus.data!;
return;
}
refresh();
}
@protected
T get value {
notifyChildrens();
reportRead();
return _value;
}
... ... @@ -46,43 +63,103 @@ mixin StateMixin<T> on ListNotifierMixin {
refresh();
}
@protected
void change(T newState, {RxStatus? status}) {
var _canUpdate = false;
if (status != null) {
_status = status;
_canUpdate = true;
}
if (newState != _value) {
_value = newState;
_canUpdate = true;
}
if (_canUpdate) {
refresh();
}
}
void append(Future<T> Function() body(), {String? errorMessage}) {
void futurize(Future<T> Function() body(),
{String? errorMessage, bool useEmpty = true}) {
final compute = body();
compute().then((newValue) {
change(newValue, status: RxStatus.success());
if ((newValue == null || newValue._isEmpty()) && useEmpty) {
status = GetState<T>.loading();
} else {
status = GetState<T>.success(newValue);
}
}, onError: (err) {
change(state, status: RxStatus.error(errorMessage ?? err.toString()));
status = GetState.error(errorMessage ?? err.toString());
});
}
}
class GetListenable<T> extends ListNotifierSingle
implements ValueListenable<T> {
GetListenable(T val) : _value = val;
StreamController<T>? _controller;
StreamController<T> get subject {
if (_controller == null) {
_controller = StreamController<T>.broadcast();
addListener(_streamListener);
}
return _controller!;
}
void _streamListener() {
_controller?.add(_value);
}
@mustCallSuper
void close() {
removeListener(_streamListener);
_controller?.close();
dispose();
}
Stream<T> get stream {
return subject.stream;
}
T _value;
@override
T get value {
reportRead();
return _value;
}
void _notify() {
refresh();
}
set value(T newValue) {
if (_value == newValue) return;
_value = newValue;
_notify();
}
T? call([T? v]) {
if (v != null) {
value = v;
}
return value;
}
StreamSubscription<T> listen(
void Function(T)? onData, {
Function? onError,
void Function()? onDone,
bool? cancelOnError,
}) =>
stream.listen(
onData,
onError: onError,
onDone: onDone,
cancelOnError: cancelOnError ?? false,
);
@override
String toString() => value.toString();
}
class Value<T> extends ListNotifier
with StateMixin<T>
implements ValueListenable<T?> {
Value(T val) {
_value = val;
_fillEmptyStatus();
_fillInitialStatus();
}
@override
T get value {
notifyChildrens();
reportRead();
return _value;
}
... ... @@ -111,12 +188,6 @@ class Value<T> extends ListNotifier
dynamic toJson() => (value as dynamic)?.toJson();
}
extension ReactiveT<T> on T {
Value<T> get reactive => Value<T>(this);
}
typedef Condition = bool Function();
abstract class GetNotifier<T> extends Value<T> with GetLifeCycleMixin {
GetNotifier(T initial) : super(initial);
}
... ... @@ -128,7 +199,7 @@ extension StateExt<T> on StateMixin<T> {
Widget? onLoading,
Widget? onEmpty,
}) {
return SimpleBuilder(builder: (_) {
return Observer(builder: (_) {
if (status.isLoading) {
return onLoading ?? const Center(child: CircularProgressIndicator());
} else if (status.isError) {
... ... @@ -145,78 +216,40 @@ extension StateExt<T> on StateMixin<T> {
}
}
class RxStatus {
final bool isLoading;
final bool isError;
final bool isSuccess;
final bool isEmpty;
final bool isLoadingMore;
final String? errorMessage;
RxStatus._({
this.isEmpty = false,
this.isLoading = false,
this.isError = false,
this.isSuccess = false,
this.errorMessage,
this.isLoadingMore = false,
});
factory RxStatus.loading() {
return RxStatus._(isLoading: true);
}
factory RxStatus.loadingMore() {
return RxStatus._(isSuccess: true, isLoadingMore: true);
}
factory RxStatus.success() {
return RxStatus._(isSuccess: true);
}
factory RxStatus.error([String? message]) {
return RxStatus._(isError: true, errorMessage: message);
}
factory RxStatus.empty() {
return RxStatus._(isEmpty: true);
}
}
typedef NotifierBuilder<T> = Widget Function(T state);
abstract class GState<T> {
const GState();
factory GState.loading() => GLoading();
factory GState.error(String message) => GError(message);
factory GState.empty() => GEmpty();
factory GState.success(T data) => GSuccess(data);
abstract class GetState<T> {
const GetState();
factory GetState.loading() => LoadingState();
factory GetState.error(String message) => ErrorState(message);
factory GetState.empty() => EmptyState();
factory GetState.success(T data) => SuccessState(data);
}
class GLoading<T> extends GState<T> {}
class LoadingState<T> extends GetState<T> {}
class GSuccess<T> extends GState<T> {
class SuccessState<T> extends GetState<T> {
final T data;
GSuccess(this.data);
SuccessState(this.data);
}
class GError<T, S> extends GState<T> {
class ErrorState<T, S> extends GetState<T> {
final S? error;
GError([this.error]);
ErrorState([this.error]);
}
class GEmpty<T> extends GState<T> {}
class EmptyState<T> extends GetState<T> {}
extension StatusDataExt<T> on GState<T> {
bool get isLoading => this is GLoading;
bool get isSuccess => this is GSuccess;
bool get isError => this is GError;
bool get isEmpty => this is GEmpty;
extension StatusDataExt<T> on GetState<T> {
bool get isLoading => this is LoadingState;
bool get isSuccess => this is SuccessState;
bool get isError => this is ErrorState;
bool get isEmpty => this is EmptyState;
String get errorMessage {
final isError = this is GError;
final isError = this is ErrorState;
if (isError) {
final err = this as GError;
final err = this as ErrorState;
if (err.error != null && err.error is String) {
return err.error as String;
}
... ... @@ -224,4 +257,12 @@ extension StatusDataExt<T> on GState<T> {
return '';
}
T? get data {
if (this is SuccessState<T>) {
final success = this as SuccessState<T>;
return success.data;
}
return null;
}
}
... ...
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import '../../../get_rx/src/rx_types/rx_types.dart';
import '../simple/simple_builder.dart';
typedef WidgetCallback = Widget Function();
... ... @@ -12,48 +10,8 @@ typedef WidgetCallback = Widget Function();
/// See also:
/// - [Obx]
/// - [ObxValue]
abstract class ObxWidget extends StatefulWidget {
abstract class ObxWidget extends ObxStatelessWidget {
const ObxWidget({Key? key}) : super(key: key);
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties..add(ObjectFlagProperty<Function>.has('builder', build));
}
@override
_ObxState createState() => _ObxState();
@protected
Widget build();
}
class _ObxState extends State<ObxWidget> {
final _observer = RxNotifier();
late StreamSubscription subs;
@override
void initState() {
super.initState();
subs = _observer.subject.stream.listen(_updateTree, cancelOnError: false);
}
void _updateTree(_) {
if (mounted) {
setState(() {});
}
}
@override
void dispose() {
subs.cancel();
_observer.close();
super.dispose();
}
@override
Widget build(BuildContext context) =>
RxInterface.notifyChildren(_observer, widget.build);
}
/// The simplest reactive widget in GetX.
... ... @@ -69,7 +27,9 @@ class Obx extends ObxWidget {
const Obx(this.builder);
@override
Widget build() => builder();
Widget build(BuildContext context) {
return builder();
}
}
/// Similar to Obx, but manages a local state.
... ... @@ -90,5 +50,5 @@ class ObxValue<T extends RxInterface> extends ObxWidget {
const ObxValue(this.builder, this.data, {Key? key}) : super(key: key);
@override
Widget build() => builder(data);
Widget build(BuildContext context) => builder(data);
}
... ...
... ... @@ -6,8 +6,7 @@ import '../rx_flutter/rx_notifier.dart';
import 'list_notifier.dart';
// ignore: prefer_mixin
abstract class GetxController extends Listenable
with GetLifeCycleMixin, ListNotifierMixin {
abstract class GetxController extends ListNotifier with GetLifeCycleMixin {
/// Rebuilds `GetBuilder` each time you call `update()`;
/// Can take a List of [ids], that will only update the matching
/// `GetBuilder( id: )`,
... ... @@ -73,6 +72,8 @@ mixin ScrollMixin on GetLifeCycleMixin {
abstract class RxController with GetLifeCycleMixin {}
abstract class StateController<T> extends GetxController with StateMixin<T> {}
abstract class SuperController<T> extends FullLifeCycleController
with FullLifeCycleMixin, StateMixin<T> {}
... ...
import 'dart:collection';
import 'package:flutter/widgets.dart';
import 'package:flutter/foundation.dart';
// This callback remove the listener on addListener function
typedef Disposer = void Function();
... ... @@ -9,45 +9,60 @@ typedef Disposer = void Function();
// if it brings overhead the extra call,
typedef GetStateUpdate = void Function();
class ListNotifier extends Listenable with ListNotifierMixin {}
class ListNotifier extends Listenable
with ListNotifierSingleMixin, ListNotifierGroupMixin {}
mixin ListNotifierMixin on Listenable {
class ListNotifierSingle = ListNotifier with ListNotifierSingleMixin;
class ListNotifierGroup = ListNotifier with ListNotifierGroupMixin;
mixin ListNotifierSingleMixin on Listenable {
List<GetStateUpdate?>? _updaters = <GetStateUpdate?>[];
HashMap<Object?, List<GetStateUpdate>>? _updatersGroupIds =
HashMap<Object?, List<GetStateUpdate>>();
@override
Disposer addListener(GetStateUpdate listener) {
assert(_debugAssertNotDisposed());
_updaters!.add(listener);
return () => _updaters!.remove(listener);
}
bool containsListener(GetStateUpdate listener) {
return _updaters?.contains(listener) ?? false;
}
@override
void removeListener(VoidCallback listener) {
assert(_debugAssertNotDisposed());
_updaters!.remove(listener);
}
@protected
void refresh() {
assert(_debugAssertNotDisposed());
_notifyUpdate();
}
@protected
void reportRead() {
TaskManager.instance.notify(this);
}
@protected
void reportAdd(VoidCallback disposer) {
TaskManager.instance.reportAdd(disposer);
}
void _notifyUpdate() {
for (var element in _updaters!) {
element!();
}
}
void _notifyIdUpdate(Object id) {
if (_updatersGroupIds!.containsKey(id)) {
final listGroup = _updatersGroupIds![id]!;
for (var item in listGroup) {
item();
}
}
}
@protected
void refreshGroup(Object id) {
assert(_debugAssertNotDisposed());
_notifyIdUpdate(id);
}
bool get isDisposed => _updaters == null;
bool _debugAssertNotDisposed() {
assert(() {
if (_updaters == null) {
if (isDisposed) {
throw FlutterError('''A $runtimeType was used after being disposed.\n
'Once you have called dispose() on a $runtimeType, it can no longer be used.''');
}
... ... @@ -56,59 +71,79 @@ mixin ListNotifierMixin on Listenable {
return true;
}
@protected
void notifyChildrens() {
TaskManager.instance.notify(_updaters);
int get listenersLength {
assert(_debugAssertNotDisposed());
return _updaters!.length;
}
bool get hasListeners {
@mustCallSuper
void dispose() {
assert(_debugAssertNotDisposed());
return _updaters!.isNotEmpty;
_updaters = null;
}
}
mixin ListNotifierGroupMixin on Listenable {
HashMap<Object?, ListNotifierSingleMixin>? _updatersGroupIds =
HashMap<Object?, ListNotifierSingleMixin>();
void _notifyGroupUpdate(Object id) {
if (_updatersGroupIds!.containsKey(id)) {
_updatersGroupIds![id]!._notifyUpdate();
}
}
int get listeners {
@protected
void notifyGroupChildrens(Object id) {
assert(_debugAssertNotDisposed());
return _updaters!.length;
TaskManager.instance.notify(_updatersGroupIds![id]!);
}
@override
void removeListener(VoidCallback listener) {
bool containsId(Object id) {
return _updatersGroupIds?.containsKey(id) ?? false;
}
@protected
void refreshGroup(Object id) {
assert(_debugAssertNotDisposed());
_updaters!.remove(listener);
_notifyGroupUpdate(id);
}
bool _debugAssertNotDisposed() {
assert(() {
if (_updatersGroupIds == null) {
throw FlutterError('''A $runtimeType was used after being disposed.\n
'Once you have called dispose() on a $runtimeType, it can no longer be used.''');
}
return true;
}());
return true;
}
void removeListenerId(Object id, VoidCallback listener) {
assert(_debugAssertNotDisposed());
if (_updatersGroupIds!.containsKey(id)) {
_updatersGroupIds![id]!.remove(listener);
_updatersGroupIds![id]!.removeListener(listener);
}
_updaters!.remove(listener);
}
@mustCallSuper
void dispose() {
assert(_debugAssertNotDisposed());
_updaters = null;
_updatersGroupIds?.forEach((key, value) => value.dispose());
_updatersGroupIds = null;
}
@override
Disposer addListener(GetStateUpdate listener) {
assert(_debugAssertNotDisposed());
_updaters!.add(listener);
return () => _updaters!.remove(listener);
}
Disposer addListenerId(Object? key, GetStateUpdate listener) {
_updatersGroupIds![key] ??= <GetStateUpdate>[];
_updatersGroupIds![key]!.add(listener);
return () => _updatersGroupIds![key]!.remove(listener);
_updatersGroupIds![key] ??= ListNotifierSingle();
return _updatersGroupIds![key]!.addListener(listener);
}
/// To dispose an [id] from future updates(), this ids are registered
/// by `GetBuilder()` or similar, so is a way to unlink the state change with
/// the Widget from the Controller.
void disposeId(Object id) {
_updatersGroupIds?[id]?.dispose();
_updatersGroupIds!.remove(id);
}
}
... ... @@ -123,11 +158,16 @@ class TaskManager {
GetStateUpdate? _setter;
List<VoidCallback>? _remove;
void notify(List<GetStateUpdate?>? _updaters) {
if (_setter != null) {
if (!_updaters!.contains(_setter)) {
_updaters.add(_setter);
_remove!.add(() => _updaters.remove(_setter));
void reportAdd(VoidCallback listener) {
_remove?.add(listener);
}
void notify(ListNotifierSingleMixin _updaters) {
final listener = _setter;
if (listener != null) {
if (!_updaters.containsListener(listener)) {
_updaters.addListener(listener);
reportAdd(() => _updaters.removeListener(listener));
}
}
}
... ... @@ -136,9 +176,29 @@ class TaskManager {
T Function() builder) {
_remove = disposers;
_setter = setState;
final result = builder();
print(disposers.isEmpty);
if (disposers.isEmpty) {
throw ObxError();
}
_remove = null;
_setter = null;
return result;
}
}
class ObxError {
const ObxError();
@override
String toString() {
return """
[Get] the improper use of a GetX has been detected.
You should only use GetX or Obx for the specific widget that will be updated.
If you are seeing this error, you probably did not insert any observable variables into GetX/Obx
or insert them outside the scope that GetX considers suitable for an update
(example: GetX => HeavyWidget => variableObservable).
If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX.
""";
}
}
... ...
... ... @@ -78,10 +78,10 @@ class _ValueBuilderState<T> extends State<ValueBuilder<T?>> {
class ObxElement = StatelessElement with ObserverComponent;
// It's a experimental feature
class SimpleBuilder extends ObxStatelessWidget {
class Observer extends ObxStatelessWidget {
final WidgetBuilder builder;
const SimpleBuilder({Key? key, required this.builder}) : super(key: key);
const Observer({Key? key, required this.builder}) : super(key: key);
@override
Widget build(BuildContext context) => builder(context);
... ...
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:get/state_manager.dart';
... ... @@ -73,28 +74,28 @@ Future<int> stream() {
return c.future;
}
Future<int> getStream() {
final c = Completer<int>();
// Future<int> getStream() {
// final c = Completer<int>();
final value = GetStream<int>();
final timer = Stopwatch();
timer.start();
// final value = GetStream<int>();
// final timer = Stopwatch();
// timer.start();
value.listen((v) {
if (times == v) {
timer.stop();
print(
"""$v listeners notified | [GET_STREAM] time: ${timer.elapsedMicroseconds}ms""");
c.complete(timer.elapsedMicroseconds);
}
});
// value.listen((v) {
// if (times == v) {
// timer.stop();
// print(
// """$v listeners notified | [GET_STREAM] time: ${timer.elapsedMicroseconds}ms""");
// c.complete(timer.elapsedMicroseconds);
// }
// });
for (var i = 0; i < times + 1; i++) {
value.add(i);
}
// for (var i = 0; i < times + 1; i++) {
// value.add(i);
// }
return c.future;
}
// return c.future;
// }
Future<int> miniStream() {
final c = Completer<int>();
... ... @@ -157,7 +158,7 @@ GetValue is ${calculePercentage(dart, getx).round()}% faster than Default ValueN
print('============================================');
print('DART STREAM X GET_STREAM X GET_MINI_STREAM TEST');
print('-----------');
var getx = await getStream();
// var getx = await getStream();
var mini = await miniStream();
var dart = await stream();
print('-----------');
... ... @@ -167,16 +168,16 @@ GetStream is ${calculePercentage(dart, mini).round()}% faster than Default Strea
times = 30000;
dart = await stream();
getx = await getStream();
// getx = await getStream();
mini = await miniStream();
times = 60000;
dart = await stream();
getx = await getStream();
// getx = await getStream();
mini = await miniStream();
print('-----------');
print('dart_stream delay $dart ms to made $times requests');
print('getx_stream delay $getx ms to made $times requests');
// print('getx_stream delay $getx ms to made $times requests');
print('getx_mini_stream delay $mini ms to made $times requests');
print('-----------');
print('''
... ...