Renat Fakhrutdinov
Committed by GitHub

Merge pull request #16 from jonataslaw/master

Update
Showing 43 changed files with 2080 additions and 1405 deletions
... ... @@ -23,7 +23,7 @@ jobs:
# https://github.com/marketplace/actions/flutter-action
- uses: subosito/flutter-action@v1
with:
flutter-version: "1.22.2"
flutter-version: "1.22.3"
channel: "stable"
- run: flutter pub get
#- run: flutter analyze
... ...
## [3.16.0]
- Documentation translated into Russian language. (@Renat Fakhrutdinov, @Doaxan and @BatttA)
- Added error message callback for StateMixin (@eduardoflorence)
- Fix incorrect Get.reference when pop route (@4mb1t)
- Added Uppercase/Capital letter on GetUtils (@AleFachini)
- Redraw the Streams api to use GetStream instead of StreamControllers. Why this change? GetStream is as light as a ValueNotifier, has very low latency and low consumption of RAM and CPU. There are cases where the performance gain exceeds 9000%, making Get unique when it comes to low latency and resource savings. We did this because new devices are being equipped with 120hz refresh rate, and to provide an even smoother reconstruction of the widgets, it was necessary to create a low latency solution from scratch. GetStream was then created, released in the previous version, and improved in that version. In the previous version, only a small part of GetX used this low-latency API so that it was possible to verify solidly if the api was mature enough to equip state management, which is the most used feature of this library. After two weeks of unremitting tests, we realized that in addition to being fast, the new api is reliable, and will even equip the Stable version of GetServer, which due to the low latency will have performance of low level languages ​​close to C, C ++, Rust and GO.
## [3.15.0] - Big update
- **Improve Performance**: We made modifications to make GetBuilder even faster. We have improved the structure behind it so that listeners are notified faster. Perhaps in version 4.0 everything will be based on this new structure, but maintaining the power and compatibility with streams. If you want to know how much Getx is faster than pure streams or ChangeNotifier (even after the last update using LinkedList), you can create run the repository tests at: (https://github.com/jonataslaw/getx/blob/master/test/benchmarks/benckmark_test.dart)
- **Added StateMixin**
StateMixin allows you to change the state of the controller, and display a loading, an error message, or a widget you want with 0 boilerplate. This makes things like API / Rest communication or websocket absurdly simple, and it's a real revolution in how state management has behaved so far.
You no longer need to have a ternary in your code, and you don't need a widget like FutureBuilder, StreamBuilder or even Obx / GetBuilder to encompass your Visibility. This will change with the way you manage the state of your controllers, decrease your boilerplate absurdly, and give you more security in your code.
- **Added GetNotifier**
GetNotifier is a super and powerful ValueNotifier, which in addition to having the life cycle of the controllers, is extremely fast, and can manage a single state, as a simplified immutable state management solution.
In theory, the only difference between it and GetxController is the possibility of setting an initial value in the constructor's super (exactly as ValueNotifier does). If the initial value is null, use GetxController. If you need a starting value, GetNotifier can be more useful and have less boilerplate, but both serve the same purpose: to decouple your visualization layer from your presentation logic.
- Other Fixes and improvements:
- Fixed GetxController is closed twice when smartManagement.full is turn on
- Fixed phone number validation
- Fixed some inconsistencies in GetWidget and the life cycle of controllers
- It made controller testing completely safe with navigation.
- Improve docs (@eduardoflorence)
- Improve security types on routes (@unacorbatanegra)
- Improve code structure with less duplicate code: (@kranfix)
- Fix named route erroring when route does not exist (@FiercestT)
## [3.13.2]
- Reunification of the package.
During the 2 week period, we try to keep this package as a compilation of smaller packages. We were successful in separating, getx is well decoupled and it was only necessary to send the internal folders as packages to pub.dev, however, it became very complicated to contribute to the package. This is because it was necessary to clone the repository, replace all pubspec packages with local paths, and after modification, return the original paths to do the PR. With that, the frequency of updates, which was about 4 to 5 days, became almost 2 weeks, and this is not legal for a community as active as Getx, which uses this package precisely in addition to being modern and performance, be constantly improving. This led contributors to the conclusion that getx works best together.
... ...
![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
*Idiomas: Español (este archivo), [Lengua china](README.zh-cn.md), [Inglés](README.md), [Portugués de Brasil](README.pt-br.md), [Polaco](README.pl.md).*
*Idiomas: Español (este archivo), [Lengua china](README.zh-cn.md), [Inglés](README.md), [Portugués de Brasil](README.pt-br.md), [Russo](README-ru.md), [Polaco](README.pl.md).*
[![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
... ...
![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
_Languages: English (this file), [Chinese](README.zh-cn.md), [Brazilian Portuguese](README.pt-br.md), [Spanish](README-es.md),[Polish](README.pl.md)._
_Languages: English (this file), [Chinese](README.zh-cn.md), [Brazilian Portuguese](README.pt-br.md), [Spanish](README-es.md), [Russian](README-ru.md), [Polish](README.pl.md)._
[![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
... ...
![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
*Languages: [English](README.md), [Język chiński](README.zh-cn.md), [Brazilian Portuguese](README.pt-br.md), [Spanish](README-es.md), Polish (Jesteś tu).*
*Languages: [English](README.md), [Język chiński](README.zh-cn.md), [Brazilian Portuguese](README.pt-br.md), [Spanish](README-es.md), [Russian](README-ru.md), Polish (Jesteś tu).*
[![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
... ...
![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
*Idiomas: [Inglês](README.md), [Língua chinesa](README.zh-cn.md), Português Brasileiro (este arquivo), [Espanhol](README-es.md), [Polaco](README.pl.md).*
*Idiomas: [Inglês](README.md), [Chinês](README.zh-cn.md), Português Brasileiro (este arquivo), [Espanhol](README-es.md), [Russo](README-ru.md), [Polonês](README.pl.md).*
[![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
... ...
![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
_语言: 中文, [英文](README.md), [巴西葡萄牙语](README.pt-br.md), [西班牙语](README-es.md), [波兰语](README.pl.md)_
_语言: 中文, [英文](README.md), [巴西葡萄牙语](README.pt-br.md), [俄语](README-ru.md), [西班牙语](README-es.md), [波兰语](README.pl.md)_
[![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
... ...
... ... @@ -49,6 +49,7 @@ linter:
prefer_equal_for_default_values: true
avoid_init_to_null: true
unnecessary_getters_setters: true
annotate_overrides: true
#- unnecessary_getters # prefer # Disabled pending fix: https://github.com/dart-lang/linter/issues/23
#- prefer_expression_function_bodies # consider
unnecessary_this: true
... ...
... ... @@ -3,43 +3,22 @@ import 'package:get/get.dart';
import '../../domain/adapters/repository_adapter.dart';
import '../../domain/entity/cases_model.dart';
enum Status { loading, success, error }
class HomeController extends GetxController {
class HomeController extends GetxController with StateMixin<CasesModel> {
HomeController({this.homeRepository});
/// inject repo abstraction dependency
final IHomeRepository homeRepository;
/// create a reactive status from request with initial value = loading
final status = Status.loading.obs;
/// create a reactive CasesModel. CasesModel().obs has same result
final cases = Rx<CasesModel>();
/// When the controller is initialized, make the http request
@override
void onInit() {
super.onInit();
fetchDataFromApi();
}
/// fetch cases from Api
Future<void> fetchDataFromApi() async {
/// When the repository returns the value, change the status to success,
/// and fill in "cases"
return homeRepository.getCases().then(
(data) {
cases(data);
status(Status.success);
},
/// In case of error, print the error and change the status
/// to Status.error
onError: (err) {
print("$err");
return status(Status.error);
},
);
// show loading on start, data on success
// and error message on error with 0 boilerplate
homeRepository.getCases().then((data) {
change(data, status: RxStatus.success());
}, onError: (err) {
change(null, status: RxStatus.error(err.toString()));
});
}
}
... ...
... ... @@ -29,9 +29,9 @@ class CountryView extends GetView<HomeController> {
),
body: Center(
child: ListView.builder(
itemCount: controller.cases.value.countries.length,
itemCount: controller.state.countries.length,
itemBuilder: (context, index) {
final country = controller.cases.value.countries[index];
final country = controller.state.countries[index];
return ListTile(
onTap: () {
Get.toNamed('/details', arguments: country);
... ...
... ... @@ -27,11 +27,8 @@ class HomeView extends GetView<HomeController> {
centerTitle: true,
),
body: Center(
child: Obx(
() {
final status = controller.status.value;
if (status == Status.loading) return CircularProgressIndicator();
if (status == Status.error) return Text('Error on connection :(');
child: controller.obx(
(state) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
... ... @@ -45,7 +42,7 @@ class HomeView extends GetView<HomeController> {
),
),
Text(
'${controller.cases.value.global.totalConfirmed}',
'${state.global.totalConfirmed}',
style: TextStyle(fontSize: 45, fontWeight: FontWeight.bold),
),
SizedBox(
... ... @@ -58,7 +55,7 @@ class HomeView extends GetView<HomeController> {
),
),
Text(
'${controller.cases.value.global.totalDeaths}',
'${state.global.totalDeaths}',
style: TextStyle(fontSize: 45, fontWeight: FontWeight.bold),
),
SizedBox(
... ...
... ... @@ -59,18 +59,18 @@ void main() {
expect(controller.initialized, true);
/// check initial Status
expect(controller.status.value, Status.loading);
expect(controller.status.isLoading, true);
/// await time request
await Future.delayed(Duration(milliseconds: 100));
if (controller.status.value == Status.error) {
expect(controller.cases.value, null);
if (controller.status.isError) {
expect(controller.state, null);
}
if (controller.status.value == Status.success) {
expect(controller.cases.value.global.totalDeaths, 100);
expect(controller.cases.value.global.totalConfirmed, 200);
if (controller.status.isSuccess) {
expect(controller.state.global.totalDeaths, 100);
expect(controller.state.global.totalConfirmed, 200);
}
});
... ...
import 'package:meta/meta.dart';
import '../../get_core/get_core.dart';
/// Special callable class to keep the contract of a regular method, and avoid
... ... @@ -18,37 +17,34 @@ class _InternalFinalCallback<T> {
/// ```dart
/// class SomeController with GetLifeCycle {
/// SomeController() {
/// initLifeCycle();
/// configureLifeCycle();
/// }
/// }
/// ```
mixin GetLifeCycle {
/// The `initLifeCycle` works as a constructor for the [GetLifeCycle]
///
/// This method must be invoked in the constructor of the implementation
void initLifeCycle() {
onStart._callback = _onStart;
onDelete._callback = _onDelete;
}
mixin GetLifeCycleBase {
/// Called at the exact moment the widget is allocated in memory.
/// It uses an internal "callable" type, to avoid any @overrides in subclases.
/// This method should be internal and is required to define the
/// lifetime cycle of the subclass.
final onStart = _InternalFinalCallback<void>();
// /// The `configureLifeCycle` works as a constructor for the [GetLifeCycle]
// ///
// /// This method must be invoked in the constructor of the implementation
// void configureLifeCycle() {
// if (_initialized) return;
// }
/// Internal callback that starts the cycle of this controller.
final onDelete = _InternalFinalCallback<void>();
/// Called immediately after the widget is allocated in memory.
/// You might use this to initialize something for the controller.
@mustCallSuper
void onInit() {}
/// Called 1 frame after onInit(). It is the perfect place to enter
/// navigation events, like snackbar, dialogs, or a new route, or
/// async request.
@mustCallSuper
void onReady() {}
/// Called before [onDelete] method. [onClose] might be used to
... ... @@ -57,7 +53,6 @@ mixin GetLifeCycle {
/// Or dispose objects that can potentially create some memory leaks,
/// like TextEditingControllers, AnimationControllers.
/// Might be useful as well to persist some data on disk.
@mustCallSuper
void onClose() {}
bool _initialized = false;
... ... @@ -83,6 +78,26 @@ mixin GetLifeCycle {
_isClosed = true;
onClose();
}
void $configureLifeCycle() {
_checkIfAlreadyConfigured();
onStart._callback = _onStart;
onDelete._callback = _onDelete;
}
void _checkIfAlreadyConfigured() {
if (_initialized) {
throw """You can only call configureLifeCycle once.
The proper place to insert it is in your class's constructor
that inherits GetLifeCycle.""";
}
}
}
abstract class GetLifeCycle with GetLifeCycleBase {
GetLifeCycle() {
$configureLifeCycle();
}
}
/// Allow track difference between GetxServices and GetxControllers
... ...
... ... @@ -11,9 +11,6 @@ import 'root/parse_route.dart';
import 'root/root_controller.dart';
import 'routes/transitions_type.dart';
//TODO: Split this class on "Snackbar" "Dialog" "bottomSheet"
//and "navigation" extensions
extension ExtensionSnackbar on GetInterface {
void rawSnackbar({
String title,
... ...
... ... @@ -127,14 +127,15 @@ class GetObserver extends NavigatorObserver {
@override
void didPop(Route route, Route previousRoute) {
super.didPop(route, previousRoute);
final newRoute = _RouteData.ofRoute(route);
if (newRoute.isSnackbar) {
Get.log("CLOSE SNACKBAR ${newRoute.name}");
} else if (newRoute.isBottomSheet || newRoute.isDialog) {
Get.log("CLOSE ${newRoute.name}");
} else if (newRoute.isGetPageRoute) {
Get.log("CLOSE TO ROUTE ${newRoute.name}");
final currentRoute = _RouteData.ofRoute(route);
final newRoute = _RouteData.ofRoute(previousRoute);
if (currentRoute.isSnackbar) {
Get.log("CLOSE SNACKBAR ${currentRoute.name}");
} else if (currentRoute.isBottomSheet || currentRoute.isDialog) {
Get.log("CLOSE ${currentRoute.name}");
} else if (currentRoute.isGetPageRoute) {
Get.log("CLOSE TO ROUTE ${currentRoute.name}");
}
Get.reference = newRoute.name;
... ...
library get_rx;
export 'src/rx_core/rx_impl.dart';
export 'src/rx_core/rx_interface.dart';
export 'src/rx_iterables/rx_list.dart';
export 'src/rx_iterables/rx_map.dart';
export 'src/rx_iterables/rx_set.dart';
export 'src/rx_stream/rx_stream.dart';
export 'src/rx_types/rx_types.dart';
export 'src/rx_workers/rx_workers.dart';
... ...
import 'dart:async';
import 'dart:collection';
import 'dart:math';
import 'package:flutter/foundation.dart';
import '../rx_core/rx_impl.dart';
import '../rx_core/rx_interface.dart';
import '../rx_typedefs/rx_typedefs.dart';
/// Create a list similar to `List<T>`
class RxList<E> implements List<E>, RxInterface<List<E>> {
RxList([List<E> initial]) {
if (initial != null) _list = initial;
}
List<E> _list = <E>[];
@override
Iterator<E> get iterator => value.iterator;
@override
bool get isEmpty => value.isEmpty;
bool get canUpdate {
return _subscriptions.length > 0;
}
@override
bool get isNotEmpty => value.isNotEmpty;
@override
StreamController<List<E>> subject = StreamController.broadcast();
final _subscriptions = HashMap<Stream<List<E>>, StreamSubscription>();
void operator []=(int index, E val) {
_list[index] = val;
refresh();
}
void refresh() {
subject.add(_list);
}
/// Special override to push() element(s) in a reactive way
/// inside the List,
RxList<E> operator +(Iterable<E> val) {
addAll(val);
refresh();
return this;
}
E operator [](int index) {
return value[index];
}
void add(E item) {
_list.add(item);
refresh();
}
@override
void addAll(Iterable<E> item) {
_list.addAll(item);
refresh();
}
/// Add [item] to [List<E>] only if [item] is not null.
void addNonNull(E item) {
if (item != null) add(item);
}
/// Add [Iterable<E>] to [List<E>] only if [Iterable<E>] is not null.
void addAllNonNull(Iterable<E> item) {
if (item != null) addAll(item);
}
/// Add [item] to [List<E>] only if [condition] is true.
void addIf(dynamic condition, E item) {
if (condition is Condition) condition = condition();
if (condition is bool && condition) add(item);
}
/// Adds [Iterable<E>] to [List<E>] only if [condition] is true.
void addAllIf(dynamic condition, Iterable<E> items) {
if (condition is Condition) condition = condition();
if (condition is bool && condition) addAll(items);
}
@override
void insert(int index, E item) {
_list.insert(index, item);
refresh();
}
@override
void insertAll(int index, Iterable<E> iterable) {
_list.insertAll(index, iterable);
refresh();
}
@override
int get length => value.length;
/// Removes an item from the list.
///
/// This is O(N) in the number of items in the list.
///
/// Returns whether the item was present in the list.
@override
bool remove(Object item) {
final hasRemoved = _list.remove(item);
if (hasRemoved) {
refresh();
}
return hasRemoved;
}
@override
E removeAt(int index) {
final item = _list.removeAt(index);
refresh();
return item;
}
@override
E removeLast() {
final item = _list.removeLast();
refresh();
return item;
}
@override
void removeRange(int start, int end) {
_list.removeRange(start, end);
refresh();
}
@override
void removeWhere(bool Function(E) test) {
_list.removeWhere(test);
refresh();
}
@override
void clear() {
_list.clear();
refresh();
}
@override
void sort([int compare(E a, E b)]) {
_list.sort(compare);
refresh();
}
@override
void close() {
_subscriptions.forEach((observable, subscription) {
subscription.cancel();
});
_subscriptions.clear();
subject.close();
}
/// Replaces all existing items of this list with [item]
void assign(E item) {
clear();
add(item);
}
void update(void fn(Iterable<E> value)) {
fn(value);
refresh();
}
/// Replaces all existing items of this list with [items]
void assignAll(Iterable<E> items) {
clear();
addAll(items);
}
@protected
List<E> get value {
if (getObs != null) {
getObs.addListener(subject.stream);
}
return _list;
}
String get string => value.toString();
void addListener(Stream<List<E>> rxGetX) {
if (_subscriptions.containsKey(rxGetX)) {
return;
}
_subscriptions[rxGetX] = rxGetX.listen(subject.add);
}
set value(List<E> val) {
if (_list == val) return;
_list = val;
refresh();
}
Stream<List<E>> get stream => subject.stream;
StreamSubscription<List<E>> listen(
void Function(List<E>) onData, {
Function onError,
void Function() onDone,
bool cancelOnError,
}) =>
stream.listen(onData, onError: onError, onDone: onDone);
/// Binds an existing [Stream<List>] to this [RxList].
/// You can bind multiple sources to update the value.
/// Closing the subscription will happen automatically when the observer
/// Widget ([GetX] or [Obx]) gets unmounted from the Widget tree.
void bindStream(Stream<List<E>> stream) {
_subscriptions[stream] = stream.listen((va) => value = va);
}
@override
E get first => value.first;
@override
E get last => value.last;
@override
bool any(bool Function(E) test) {
return value.any(test);
}
@override
Map<int, E> asMap() {
return value.asMap();
}
@override
List<R> cast<R>() {
return value.cast<R>();
}
@override
bool contains(Object element) {
return value.contains(element);
}
@override
E elementAt(int index) {
return value.elementAt(index);
}
@override
bool every(bool Function(E) test) {
return value.every(test);
}
@override
Iterable<T> expand<T>(Iterable<T> Function(E) f) {
return value.expand(f);
}
@override
void fillRange(int start, int end, [E fillValue]) {
_list.fillRange(start, end, fillValue);
refresh();
}
@override
E firstWhere(bool Function(E) test, {E Function() orElse}) {
return value.firstWhere(test, orElse: orElse);
}
@override
T fold<T>(T initialValue, T Function(T, E) combine) {
return value.fold(initialValue, combine);
}
@override
Iterable<E> followedBy(Iterable<E> other) {
return value.followedBy(other);
}
@override
void forEach(void Function(E) f) {
value.forEach(f);
}
@override
Iterable<E> getRange(int start, int end) {
return value.getRange(start, end);
}
@override
int indexOf(E element, [int start = 0]) {
return value.indexOf(element, start);
}
@override
int indexWhere(bool Function(E) test, [int start = 0]) {
return value.indexWhere(test, start);
}
@override
String join([String separator = ""]) {
return value.join(separator);
}
@override
int lastIndexOf(E element, [int start]) {
return value.lastIndexOf(element, start);
}
@override
int lastIndexWhere(bool Function(E) test, [int start]) {
return value.lastIndexWhere(test, start);
}
@override
E lastWhere(bool Function(E) test, {E Function() orElse}) {
return value.lastWhere(test, orElse: orElse);
}
@override
set length(int newLength) {
_list.length = newLength;
refresh();
}
@override
Iterable<T> map<T>(T Function(E) f) {
return value.map(f);
}
@override
E reduce(E Function(E, E) combine) {
return value.reduce(combine);
}
@override
void replaceRange(int start, int end, Iterable<E> replacement) {
_list.replaceRange(start, end, replacement);
refresh();
}
@override
void retainWhere(bool Function(E) test) {
_list.retainWhere(test);
refresh();
}
@override
Iterable<E> get reversed => value.reversed;
@override
void setAll(int index, Iterable<E> iterable) {
_list.setAll(index, iterable);
refresh();
}
@override
void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) {
_list.setRange(start, end, iterable, skipCount);
refresh();
}
@override
void shuffle([Random random]) {
_list.shuffle(random);
refresh();
}
@override
E get single => value.single;
@override
E singleWhere(bool Function(E) test, {E Function() orElse}) {
return value.singleWhere(test, orElse: orElse);
}
@override
Iterable<E> skip(int count) {
return value.skip(count);
}
@override
Iterable<E> skipWhile(bool Function(E) test) {
return value.skipWhile(test);
}
@override
List<E> sublist(int start, [int end]) {
return value.sublist(start, end);
}
@override
Iterable<E> take(int count) {
return value.take(count);
}
@override
Iterable<E> takeWhile(bool Function(E) test) {
return value.takeWhile(test);
}
@override
List<E> toList({bool growable = true}) {
return value.toList(growable: growable);
}
@override
Set<E> toSet() {
return value.toSet();
}
@override
Iterable<E> where(bool Function(E) test) {
return value.where(test);
}
@override
Iterable<T> whereType<T>() {
return value.whereType<T>();
}
@override
set first(E value) {
_list.first = value;
refresh();
}
@override
set last(E value) {
_list.last = value;
refresh();
}
}
extension ListExtension<E> on List<E> {
RxList<E> get obs {
if (this != null) {
return RxList<E>(<E>[])..addAllNonNull(this);
} else {
return RxList<E>(null);
}
}
}
import 'dart:async';
import 'dart:collection';
import 'package:flutter/foundation.dart';
import '../rx_core/rx_impl.dart';
import '../rx_core/rx_interface.dart';
import '../rx_typedefs/rx_typedefs.dart';
class RxSet<E> implements Set<E>, RxInterface<Set<E>> {
RxSet([Set<E> initial]) {
if (initial != null) _set = initial;
}
Set<E> _set = <E>{};
@override
Iterator<E> get iterator => value.iterator;
@override
bool get isEmpty => value.isEmpty;
bool get canUpdate {
return _subscriptions.length > 0;
}
@override
bool get isNotEmpty => value.isNotEmpty;
StreamController<Set<E>> subject = StreamController<Set<E>>.broadcast();
final _subscriptions = HashMap<Stream<Set<E>>, StreamSubscription>();
/// Adds [item] only if [condition] resolves to true.
void addIf(dynamic condition, E item) {
if (condition is Condition) condition = condition();
if (condition is bool && condition) add(item);
}
/// Adds all [items] only if [condition] resolves to true.
void addAllIf(dynamic condition, Iterable<E> items) {
if (condition is Condition) condition = condition();
if (condition is bool && condition) addAll(items);
}
void refresh() {
subject.add(_set);
}
/// Special override to push() element(s) in a reactive way
/// inside the List,
RxSet<E> operator +(Set<E> val) {
addAll(val);
refresh();
return this;
}
@override
bool add(E value) {
final val = _set.add(value);
refresh();
return val;
}
@override
void addAll(Iterable<E> item) {
_set.addAll(item);
refresh();
}
/// Adds only if [item] is not null.
void addNonNull(E item) {
if (item != null) add(item);
}
/// Adds only if [item] is not null.
void addAllNonNull(Iterable<E> item) {
if (item != null) addAll(item);
}
int get length => value.length;
/// Removes an item from the list.
///
/// This is O(N) in the number of items in the list.
///
/// Returns whether the item was present in the list.
bool remove(Object item) {
var hasRemoved = _set.remove(item);
if (hasRemoved) {
refresh();
}
return hasRemoved;
}
void removeWhere(bool Function(E) test) {
_set.removeWhere(test);
refresh();
}
void clear() {
_set.clear();
refresh();
}
void close() {
_subscriptions.forEach((observable, subscription) {
subscription.cancel();
});
_subscriptions.clear();
subject.close();
}
/// Replaces all existing items of this list with [item]
void assign(E item) {
clear();
add(item);
}
void update(void fn(Iterable<E> value)) {
fn(value);
refresh();
}
/// Replaces all existing items of this list with [items]
void assignAll(Iterable<E> items) {
clear();
addAll(items);
}
@protected
Set<E> get value {
if (getObs != null) {
getObs.addListener(subject.stream);
}
return _set;
}
String get string => value.toString();
void addListener(Stream<Set<E>> rxGetX) {
if (_subscriptions.containsKey(rxGetX)) {
return;
}
_subscriptions[rxGetX] = rxGetX.listen((data) {
subject.add(data);
});
}
set value(Set<E> val) {
if (_set == val) return;
_set = val;
refresh();
}
Stream<Set<E>> get stream => subject.stream;
StreamSubscription<Set<E>> listen(void Function(Set<E>) onData,
{Function onError, void Function() onDone, bool cancelOnError}) =>
stream.listen(onData, onError: onError, onDone: onDone);
/// Binds an existing [Stream<Set>] to this [RxSet].
/// You can bind multiple sources to update the value.
/// Closing the subscription will happen automatically when the observer
/// Widget ([GetX] or [Obx]) gets unmounted from the Widget tree.
void bindStream(Stream<Set<E>> stream) {
_subscriptions[stream] = stream.listen((va) => value = va);
}
@override
E get first => value.first;
@override
E get last => value.last;
@override
bool any(bool Function(E) test) {
return value.any(test);
}
@override
Set<R> cast<R>() {
return value.cast<R>();
}
@override
bool contains(Object element) {
return value.contains(element);
}
@override
E elementAt(int index) {
return value.elementAt(index);
}
@override
bool every(bool Function(E) test) {
return value.every(test);
}
@override
Iterable<T> expand<T>(Iterable<T> Function(E) f) {
return value.expand(f);
}
@override
E firstWhere(bool Function(E) test, {E Function() orElse}) {
return value.firstWhere(test, orElse: orElse);
}
@override
T fold<T>(T initialValue, T Function(T, E) combine) {
return value.fold(initialValue, combine);
}
@override
Iterable<E> followedBy(Iterable<E> other) {
return value.followedBy(other);
}
@override
void forEach(void Function(E) f) {
value.forEach(f);
}
@override
String join([String separator = ""]) {
return value.join(separator);
}
@override
E lastWhere(bool Function(E) test, {E Function() orElse}) {
return value.lastWhere(test, orElse: orElse);
}
@override
Iterable<T> map<T>(T Function(E) f) {
return value.map(f);
}
@override
E reduce(E Function(E, E) combine) {
return value.reduce(combine);
}
@override
E get single => value.single;
@override
E singleWhere(bool Function(E) test, {E Function() orElse}) {
return value.singleWhere(test, orElse: orElse);
}
@override
Iterable<E> skip(int count) {
return value.skip(count);
}
@override
Iterable<E> skipWhile(bool Function(E) test) {
return value.skipWhile(test);
}
@override
Iterable<E> take(int count) {
return value.take(count);
}
@override
Iterable<E> takeWhile(bool Function(E) test) {
return value.takeWhile(test);
}
@override
List<E> toList({bool growable = true}) {
return value.toList(growable: growable);
}
@override
Set<E> toSet() {
return value.toSet();
}
@override
Iterable<E> where(bool Function(E) test) {
return value.where(test);
}
@override
Iterable<T> whereType<T>() {
return value.whereType<T>();
}
@override
bool containsAll(Iterable<Object> other) {
return value.containsAll(other);
}
@override
Set<E> difference(Set<Object> other) {
return value.difference(other);
}
@override
Set<E> intersection(Set<Object> other) {
return value.intersection(other);
}
@override
E lookup(Object object) {
return value.lookup(object);
}
@override
void removeAll(Iterable<Object> elements) {
_set.removeAll(elements);
refresh();
}
@override
void retainAll(Iterable<Object> elements) {
_set.retainAll(elements);
refresh();
}
@override
void retainWhere(bool Function(E) E) {
_set.retainWhere(E);
refresh();
}
@override
Set<E> union(Set<E> other) {
return value.union(other);
}
}
extension SetExtension<E> on Set<E> {
RxSet<E> get obs {
if (this != null) {
return RxSet<E>(<E>{})..addAllNonNull(this);
} else {
return RxSet<E>(null);
}
}
}
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> {
LightListenable<T> listenable = LightListenable<T>();
T _value;
T get value => _value;
void add(T event) {
_value = event;
_checkIfDisposed();
listenable.notifyData(event);
}
void _checkIfDisposed([bool isClosed = false]) {
if (listenable == null) {
throw '''[LightStream] Error:
You cannot ${isClosed ? "close" : "add events to"} a closed stream.''';
}
}
void addError(Object error, [StackTrace stackTrace]) {
_checkIfDisposed();
listenable.notifyError(error, stackTrace);
}
void close() {
_checkIfDisposed(true);
listenable.notifyDone();
listenable.dispose();
listenable = null;
_value = null;
}
int get length => listenable.length;
bool get hasListeners => listenable.hasListeners;
bool get isClosed => listenable == null;
LightSubscription<T> listen(void Function(T event) onData,
{Function onError, void Function() onDone, bool cancelOnError}) {
final subs = LightSubscription<T>(listenable)
..onData(onData)
..onError(onError)
..onDone(onDone);
listenable.addSubscription(subs);
return subs;
}
Stream<T> get stream => GetStreamTransformation(listenable);
}
class LightListenable<T> {
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) {
assert(!isDisposed, 'You cannot add data to a closed stream.');
_isBusy = true;
for (final item in _onData) {
if (item.isPaused) {
break;
}
item._data?.call(data);
}
_isBusy = false;
}
void notifyError(Object error, [StackTrace stackTrace]) {
assert(!isDisposed, 'You cannot add errors to a closed stream.');
_isBusy = true;
for (final item in _onData) {
if (item.isPaused) {
break;
}
item._onError?.call(error, stackTrace);
if (item.cancelOnError) {
item.cancel?.call();
item._onDone?.call();
}
}
_isBusy = false;
}
void notifyDone() {
assert(!isDisposed, 'You cannot close a closed stream.');
_isBusy = true;
for (final item in _onData) {
if (item.isPaused) {
break;
}
item._onDone?.call();
}
_isBusy = false;
}
void dispose() {
_onData = null;
_isBusy = null;
}
bool get isDisposed => _onData == null;
}
class LightSubscription<T> extends StreamSubscription<T> {
final LightListenable<T> listener;
LightSubscription(this.listener);
bool cancelOnError = false;
@override
Future<void> cancel() {
listener.removeSubscription(this);
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;
@override
void resume() => _isPaused = false;
@override
bool get isPaused => _isPaused;
@override
Future<E> asFuture<E>([E futureValue]) => Future.value(futureValue);
}
class GetStreamTransformation<T> extends Stream<T> {
final LightListenable<T> listenable;
GetStreamTransformation(this.listenable);
@override
LightSubscription<T> listen(void Function(T event) onData,
{Function onError, void Function() onDone, bool cancelOnError}) {
final subs = LightSubscription<T>(listenable)
..onData(onData)
..onError(onError)
..onDone(onDone);
listenable.addSubscription(subs);
return subs;
}
}
... ...
part of rx_stream;
class Node<T> {
T data;
Node<T> next;
Node({this.data, this.next});
}
class MiniSubscription<T> {
const MiniSubscription(
this.data, this.onError, this.onDone, this.cancelOnError, this.listener);
final OnData<T> data;
final Function onError;
final Callback onDone;
final bool cancelOnError;
Future<void> cancel() async => listener.removeListener(this);
final FastList<T> listener;
}
class MiniStream<T> {
FastList<T> listenable = FastList<T>();
T _value;
T get value => _value;
set value(T val) {
add(val);
}
void add(T event) {
assert(listenable != null);
_value = event;
listenable._notifyData(event);
}
void addError(Object error, [StackTrace stackTrace]) {
assert(listenable != null);
listenable._notifyError(error, stackTrace);
}
int get length => listenable.length;
bool get hasListeners => listenable.isNotEmpty;
bool get isClosed => listenable == null;
MiniSubscription<T> listen(void Function(T event) onData,
{Function onError, void Function() onDone, bool cancelOnError = false}) {
final subs = MiniSubscription<T>(
onData,
onError,
onDone,
cancelOnError,
listenable,
);
listenable.addListener(subs);
return subs;
}
void close() {
if (listenable == null) {
throw 'You can not close a closed Stream';
}
listenable._notifyDone();
listenable = null;
_value = null;
}
}
class FastList<T> {
Node<MiniSubscription<T>> _head;
void _notifyData(T data) {
var currentNode = _head;
do {
currentNode.data.data(data);
currentNode = currentNode.next;
} while (currentNode != null);
}
void _notifyDone() {
var currentNode = _head;
do {
currentNode.data.onDone?.call();
currentNode = currentNode.next;
} while (currentNode != null);
}
void _notifyError(Object error, [StackTrace stackTrace]) {
var currentNode = _head;
while (currentNode != null) {
currentNode.data.onError?.call(error, stackTrace);
currentNode = currentNode.next;
}
}
/// Checks if this list is empty
bool get isEmpty => _head == null;
bool get isNotEmpty => !isEmpty;
/// Returns the length of this list
int get length {
var length = 0;
var currentNode = _head;
while (currentNode != null) {
currentNode = currentNode.next;
length++;
}
return length;
}
/// Shows the element at position [position]. `null` for invalid positions.
MiniSubscription<T> _elementAt(int position) {
if (isEmpty || length < position || position < 0) return null;
var node = _head;
var current = 0;
while (current != position) {
node = node.next;
current++;
}
return node.data;
}
/// Inserts [data] at the end of the list.
void addListener(MiniSubscription<T> data) {
var newNode = Node(data: data);
if (isEmpty) {
_head = newNode;
} else {
var currentNode = _head;
while (currentNode.next != null) {
currentNode = currentNode.next;
}
currentNode.next = newNode;
}
}
bool contains(T element) {
var length = this.length;
for (var i = 0; i < length; i++) {
if (_elementAt(i) == element) return true;
if (length != this.length) {
throw ConcurrentModificationError(this);
}
}
return false;
}
void removeListener(MiniSubscription<T> element) {
var length = this.length;
for (var i = 0; i < length; i++) {
if (_elementAt(i) == element) {
_removeAt(i);
break;
}
}
}
MiniSubscription<T> _removeAt(int position) {
var index = 0;
var currentNode = _head;
Node<MiniSubscription<T>> previousNode;
if (isEmpty || length < position || position < 0) {
throw Exception('Invalid position');
} else if (position == 0) {
_head = _head.next;
} else {
while (index != position) {
previousNode = currentNode;
currentNode = currentNode.next;
index++;
}
if (previousNode == null) {
_head = null;
} else {
previousNode.next = currentNode.next;
}
currentNode.next = null;
}
return currentNode.data;
}
}
... ...
library rx_stream;
import 'dart:async';
import '../rx_typedefs/rx_typedefs.dart';
part 'get_stream.dart';
part 'mini_stream.dart';
... ...
typedef Condition = bool Function();
typedef ValueCallback<T> = Function(T v);
typedef OnData<T> = void Function(T data);
typedef Callback = void Function();
... ...
import 'dart:async';
import 'dart:collection';
import '../rx_core/rx_interface.dart';
part 'rx_num.dart';
part of rx_types;
/// global object that registers against `GetX` and `Obx`, and allows the
/// reactivity
/// of those `Widgets` and Rx values.
RxInterface getObs;
/// Base Rx class that manages all the stream logic for any Type.
abstract class _RxImpl<T> implements RxInterface<T> {
_RxImpl(T initial) {
_value = initial;
}
StreamController<T> subject = StreamController<T>.broadcast();
final _subscriptions = HashMap<Stream<T>, StreamSubscription>();
T _value;
mixin RxObjectMixin<T> {
GetStream<T> subject = GetStream<T>();
final _subscriptions = <StreamSubscription>[];
bool get canUpdate => _subscriptions.isNotEmpty;
/// Makes this Rx looks like a function so you can update a new
/// value using [rx(someOtherValue)]. Practical to assign the Rx directly
/// to some Widget that has a signature ::onChange( value )
///
/// Example:
/// ```
/// final myText = 'GetX rocks!'.obs;
///
/// // in your Constructor, just to check it works :P
/// ever( myText, print ) ;
///
/// // in your build(BuildContext) {
/// TextField(
/// onChanged: myText,
/// ),
///```
T call([T v]) {
if (v != null) {
value = v;
}
return value;
}
T _value;
/// Makes a direct update of [value] adding it to the Stream
/// useful when you make use of Rx for custom Types to referesh your UI.
... ... @@ -65,31 +34,6 @@ abstract class _RxImpl<T> implements RxInterface<T> {
subject.add(value);
}
/// Uses a callback to update [value] internally, similar to [refresh],
/// but provides the current value as the argument.
/// Makes sense for custom Rx types (like Models).
///
/// Sample:
/// ```
/// class Person {
/// String name, last;
/// int age;
/// Person({this.name, this.last, this.age});
/// @override
/// String toString() => '$name $last, $age years old';
/// }
///
/// final person = Person(name: 'John', last: 'Doe', age: 18).obs;
/// person.update((person) {
/// person.name = 'Roi';
/// });
/// print( person );
/// ```
void update(void fn(T val)) {
fn(_value);
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
/// [call()] doesn't accept [null] values. For instance,
... ... @@ -104,6 +48,53 @@ abstract class _RxImpl<T> implements RxInterface<T> {
subject.add(_value = null);
}
/// Closes the subscriptions for this Rx, releasing the resources.
void close() {
for (final subscription in _subscriptions) {
subscription?.cancel();
}
_subscriptions.clear();
subject.close();
}
/// Makes this Rx looks like a function so you can update a new
/// value using [rx(someOtherValue)]. Practical to assign the Rx directly
/// to some Widget that has a signature ::onChange( value )
///
/// Example:
/// ```
/// final myText = 'GetX rocks!'.obs;
///
/// // in your Constructor, just to check it works :P
/// ever( myText, print ) ;
///
/// // in your build(BuildContext) {
/// TextField(
/// onChanged: myText,
/// ),
///```
T call([T v]) {
if (v != null) {
value = v;
}
return value;
}
/// This is an internal method.
/// Subscribe to changes on the inner stream.
void addListener(GetStream<T> rxGetx) {
if (_subscriptions.contains(rxGetx.listen)) {
return;
}
final subs = rxGetx.listen((data) {
subject.add(data);
});
_subscriptions.add(subs);
}
bool firstRebuild = true;
/// Same as `toString()` but using a getter.
String get string => value.toString();
... ... @@ -128,26 +119,6 @@ abstract class _RxImpl<T> implements RxInterface<T> {
// ignore: avoid_equals_and_hash_code_on_mutable_classes
int get hashCode => _value.hashCode;
/// Closes the subscriptions for this Rx, releasing the resources.
void close() {
_subscriptions.forEach((observable, subscription) => subscription.cancel());
_subscriptions.clear();
subject.close();
}
/// This is an internal method.
/// Subscribe to changes on the inner stream.
void addListener(Stream<T> rxGetx) {
if (_subscriptions.containsKey(rxGetx)) {
return;
}
_subscriptions[rxGetx] = rxGetx.listen((data) {
subject.add(data);
});
}
bool firstRebuild = true;
/// Updates the [value] and adds it to the stream, updating the observer
/// Widget, only if it's different from the previous value.
set value(T val) {
... ... @@ -160,26 +131,63 @@ abstract class _RxImpl<T> implements RxInterface<T> {
/// Returns the current [value]
T get value {
if (getObs != null) {
getObs.addListener(subject.stream);
getObs.addListener(subject);
}
return _value;
}
Stream<T> get stream => subject.stream;
Stream<T> get stream => GetStreamTransformation<T>(subject.listenable);
StreamSubscription<T> listen(void Function(T) onData,
{Function onError, void Function() onDone, bool cancelOnError}) =>
stream.listen(onData, onError: onError, onDone: onDone);
StreamSubscription<T> listen(
void Function(T) onData, {
Function onError,
void Function() onDone,
bool cancelOnError = false,
}) =>
subject.listen(onData,
onError: onError, onDone: onDone, cancelOnError: cancelOnError);
/// Binds an existing [Stream<T>] to this Rx<T> to keep the values in sync.
/// You can bind multiple sources to update the value.
/// Closing the subscription will happen automatically when the observer
/// Widget ([GetX] or [Obx]) gets unmounted from the Widget tree.
void bindStream(Stream<T> stream) {
_subscriptions[stream] = stream.listen((va) => value = va);
_subscriptions.add(stream.listen((va) => value = va));
}
}
/// Base Rx class that manages all the stream logic for any Type.
abstract class _RxImpl<T> with RxObjectMixin<T> implements RxInterface<T> {
_RxImpl(T initial) {
_value = initial;
}
Stream<R> map<R>(R mapper(T data)) => stream.map(mapper);
/// Uses a callback to update [value] internally, similar to [refresh],
/// but provides the current value as the argument.
/// Makes sense for custom Rx types (like Models).
///
/// Sample:
/// ```
/// class Person {
/// String name, last;
/// int age;
/// Person({this.name, this.last, this.age});
/// @override
/// String toString() => '$name $last, $age years old';
/// }
///
/// final person = Person(name: 'John', last: 'Doe', age: 18).obs;
/// person.update((person) {
/// person.name = 'Roi';
/// });
/// print( person );
/// ```
void update(void fn(T val)) {
fn(_value);
subject.add(_value);
}
}
/// Rx class for `bool` Type.
... ... @@ -202,6 +210,7 @@ class RxBool extends _RxImpl<bool> {
return this;
}
@override
String toString() {
return value ? "true" : "false";
}
... ...
import 'dart:async';
import '../rx_typedefs/rx_typedefs.dart';
part of rx_types;
/// This class is the foundation for all reactive (Rx) classes that makes Get
/// so powerful.
... ... @@ -8,10 +7,10 @@ import '../rx_typedefs/rx_typedefs.dart';
abstract class RxInterface<T> {
RxInterface([T initial]);
StreamController<T> subject;
GetStream<T> subject;
/// Adds a listener to stream
void addListener(Stream<T> rxGetx);
void addListener(GetStream<T> rxGetx);
bool get canUpdate;
... ... @@ -21,8 +20,10 @@ abstract class RxInterface<T> {
/// Closes the stream
// FIXME: shouldn't we expose the returned future?
void close() => subject?.close();
void close();
/// Calls [callback] with current value, when the value changes.
StreamSubscription<T> listen(ValueCallback<T> callback);
StreamSubscription<T> listen(void Function(T event) onData,
{Function onError, void Function() onDone, bool cancelOnError});
}
... ...
part of 'rx_impl.dart';
part of rx_types;
/// Base Rx class for all num Rx's.
abstract class _BaseRxNum<T extends num> extends _RxImpl<T> {
... ... @@ -299,23 +299,29 @@ class RxDouble extends _BaseRxNum<double> {
}
/// Multiplication operator.
@override
double operator *(num other) => value * other;
@override
double operator %(num other) => value % other;
/// Division operator.
@override
double operator /(num other) => value / other;
/// Truncating division operator.
///
/// The result of the truncating division `a ~/ b` is equivalent to
/// `(a / b).truncate()`.
@override
int operator ~/(num other) => value ~/ other;
/// Negate operator. */
@override
double operator -() => -value;
/// Returns the absolute value of this [double].
@override
double abs() => value.abs();
/// Returns the sign of the double's numerical value.
... ... @@ -323,6 +329,7 @@ class RxDouble extends _BaseRxNum<double> {
/// Returns -1.0 if the value is less than zero,
/// +1.0 if the value is greater than zero,
/// and the value itself if it is -0.0, 0.0 or NaN.
@override
double get sign => value.sign;
/// Returns the integer closest to `this`.
... ... @@ -331,22 +338,26 @@ class RxDouble extends _BaseRxNum<double> {
/// `(3.5).round() == 4` and `(-3.5).round() == -4`.
///
/// If `this` is not finite (`NaN` or infinity), throws an [UnsupportedError].
@override
int round() => value.round();
/// Returns the greatest integer no greater than `this`.
///
/// If `this` is not finite (`NaN` or infinity), throws an [UnsupportedError].
@override
int floor() => value.floor();
/// Returns the least integer no smaller than `this`.
///
/// If `this` is not finite (`NaN` or infinity), throws an [UnsupportedError].
@override
int ceil() => value.ceil();
/// Returns the integer obtained by discarding any fractional
/// digits from `this`.
///
/// If `this` is not finite (`NaN` or infinity), throws an [UnsupportedError].
@override
int truncate() => value.truncate();
/// Returns the integer double value closest to `this`.
... ... @@ -361,6 +372,7 @@ class RxDouble extends _BaseRxNum<double> {
/// and `-0.0` is therefore considered closer to negative numbers than `0.0`.
/// This means that for a value, `d` in the range `-0.5 < d < 0.0`,
/// the result is `-0.0`.
@override
double roundToDouble() => value.roundToDouble();
/// Returns the greatest integer double value no greater than `this`.
... ... @@ -370,6 +382,7 @@ class RxDouble extends _BaseRxNum<double> {
///
/// For the purpose of rounding, `-0.0` is considered to be below `0.0`.
/// A number `d` in the range `0.0 < d < 1.0` will return `0.0`.
@override
double floorToDouble() => value.floorToDouble();
/// Returns the least integer double value no smaller than `this`.
... ... @@ -379,6 +392,7 @@ class RxDouble extends _BaseRxNum<double> {
///
/// For the purpose of rounding, `-0.0` is considered to be below `0.0`.
/// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`.
@override
double ceilToDouble() => value.ceilToDouble();
/// Returns the integer double value obtained by discarding any fractional
... ... @@ -390,6 +404,7 @@ class RxDouble extends _BaseRxNum<double> {
/// For the purpose of rounding, `-0.0` is considered to be below `0.0`.
/// A number `d` in the range `-1.0 < d < 0.0` will return `-0.0`, and
/// in the range `0.0 < d < 1.0` it will return 0.0.
@override
double truncateToDouble() => value.truncateToDouble();
}
... ... @@ -578,40 +593,51 @@ class RxInt extends _BaseRxNum<int> {
///
/// The result of negating an integer always has the opposite sign, except
/// for zero, which is its own negation.
@override
int operator -() => -value;
/// Returns the absolute value of this integer.
///
/// For any integer `x`, the result is the same as `x < 0 ? -x : x`.
@override
int abs() => value.abs();
/// Returns the sign of this integer.
///
/// Returns 0 for zero, -1 for values less than zero and
/// +1 for values greater than zero.
@override
int get sign => value.sign;
/// Returns `this`.
@override
int round() => value.round();
/// Returns `this`.
@override
int floor() => value.floor();
/// Returns `this`.
@override
int ceil() => value.ceil();
/// Returns `this`.
@override
int truncate() => value.truncate();
/// Returns `this.toDouble()`.
@override
double roundToDouble() => value.roundToDouble();
/// Returns `this.toDouble()`.
@override
double floorToDouble() => value.floorToDouble();
/// Returns `this.toDouble()`.
@override
double ceilToDouble() => value.ceilToDouble();
/// Returns `this.toDouble()`.
@override
double truncateToDouble() => value.truncateToDouble();
}
... ...
part of rx_types;
/// Create a list similar to `List<T>`
class RxList<E> extends ListMixin<E>
with RxObjectMixin<List<E>>
implements RxInterface<List<E>> {
RxList([List<E> initial]) {
_value = initial;
}
@override
Iterator<E> get iterator => value.iterator;
@override
void operator []=(int index, E val) {
_value[index] = val;
refresh();
}
/// Special override to push() element(s) in a reactive way
/// inside the List,
@override
RxList<E> operator +(Iterable<E> val) {
addAll(val);
refresh();
return this;
}
@override
E operator [](int index) {
return value[index];
}
@override
void add(E item) {
_value.add(item);
refresh();
}
@override
void addAll(Iterable<E> item) {
_value.addAll(item);
refresh();
}
/// Add [item] to [List<E>] only if [item] is not null.
void addNonNull(E item) {
if (item != null) add(item);
}
/// Add [Iterable<E>] to [List<E>] only if [Iterable<E>] is not null.
void addAllNonNull(Iterable<E> item) {
if (item != null) addAll(item);
}
/// Add [item] to [List<E>] only if [condition] is true.
void addIf(dynamic condition, E item) {
if (condition is Condition) condition = condition();
if (condition is bool && condition) add(item);
}
/// Adds [Iterable<E>] to [List<E>] only if [condition] is true.
void addAllIf(dynamic condition, Iterable<E> items) {
if (condition is Condition) condition = condition();
if (condition is bool && condition) addAll(items);
}
@override
int get length => value.length;
/// Replaces all existing items of this list with [item]
void assign(E item) {
clear();
add(item);
}
/// Replaces all existing items of this list with [items]
void assignAll(Iterable<E> items) {
clear();
addAll(items);
}
@override
@protected
List<E> get value {
if (getObs != null) {
getObs.addListener(subject);
}
return _value;
}
@override
@protected
@Deprecated('List.value is deprecated. use [yourList.assignAll(newList)]')
set value(List<E> val) {
if (_value == val) return;
_value = val;
refresh();
}
@override
set length(int newLength) {
_value.length = newLength;
refresh();
}
@override
void insertAll(int index, Iterable<E> iterable) {
_value.insertAll(index, iterable);
refresh();
}
@override
Iterable<E> get reversed => value.reversed;
@override
Iterable<E> where(bool Function(E) test) {
return value.where(test);
}
@override
Iterable<T> whereType<T>() {
return value.whereType<T>();
}
@override
void sort([int compare(E a, E b)]) {
_value.sort(compare);
refresh();
}
}
// /// Create a list similar to `List<T>`
// class RxList<E> implements List<E>, RxInterface<List<E>> {
// RxList([List<E> initial]) {
// if (initial != null) _value = initial;
// }
// List<E> _value = <E>[];
// @override
// Iterator<E> get iterator => value.iterator;
// @override
// bool get isEmpty => value.isEmpty;
// bool get canUpdate {
// return _subscriptions.length > 0;
// }
// @override
// bool get isNotEmpty => value.isNotEmpty;
// @override
// StreamController<List<E>> subject = StreamController.broadcast();
// final _subscriptions = HashMap<Stream<List<E>>, StreamSubscription>();
// void operator []=(int index, E val) {
// _value[index] = val;
// refresh();
// }
// void refresh() {
// subject.add(_value);
// }
// /// Special override to push() element(s) in a reactive way
// /// inside the List,
// RxList<E> operator +(Iterable<E> val) {
// addAll(val);
// refresh();
// return this;
// }
// E operator [](int index) {
// return value[index];
// }
// void add(E item) {
// _value.add(item);
// refresh();
// }
// @override
// void addAll(Iterable<E> item) {
// _value.addAll(item);
// refresh();
// }
// /// Add [item] to [List<E>] only if [item] is not null.
// void addNonNull(E item) {
// if (item != null) add(item);
// }
// /// Add [Iterable<E>] to [List<E>] only if [Iterable<E>] is not null.
// void addAllNonNull(Iterable<E> item) {
// if (item != null) addAll(item);
// }
// /// Add [item] to [List<E>] only if [condition] is true.
// void addIf(dynamic condition, E item) {
// if (condition is Condition) condition = condition();
// if (condition is bool && condition) add(item);
// }
// /// Adds [Iterable<E>] to [List<E>] only if [condition] is true.
// void addAllIf(dynamic condition, Iterable<E> items) {
// if (condition is Condition) condition = condition();
// if (condition is bool && condition) addAll(items);
// }
// @override
// void insert(int index, E item) {
// _value.insert(index, item);
// refresh();
// }
// @override
// void insertAll(int index, Iterable<E> iterable) {
// _value.insertAll(index, iterable);
// refresh();
// }
// @override
// int get length => value.length;
// /// Removes an item from the list.
// ///
// /// This is O(N) in the number of items in the list.
// ///
// /// Returns whether the item was present in the list.
// @override
// bool remove(Object item) {
// final hasRemoved = _value.remove(item);
// if (hasRemoved) {
// refresh();
// }
// return hasRemoved;
// }
// @override
// E removeAt(int index) {
// final item = _value.removeAt(index);
// refresh();
// return item;
// }
// @override
// E removeLast() {
// final item = _value.removeLast();
// refresh();
// return item;
// }
// @override
// void removeRange(int start, int end) {
// _value.removeRange(start, end);
// refresh();
// }
// @override
// void removeWhere(bool Function(E) test) {
// _value.removeWhere(test);
// refresh();
// }
// @override
// void clear() {
// _value.clear();
// refresh();
// }
// @override
// void sort([int compare(E a, E b)]) {
// _value.sort(compare);
// refresh();
// }
// @override
// void close() {
// _subscriptions.forEach((observable, subscription) {
// subscription.cancel();
// });
// _subscriptions.clear();
// subject.close();
// }
// /// Replaces all existing items of this list with [item]
// void assign(E item) {
// clear();
// add(item);
// }
// void update(void fn(Iterable<E> value)) {
// fn(value);
// refresh();
// }
// /// Replaces all existing items of this list with [items]
// void assignAll(Iterable<E> items) {
// clear();
// addAll(items);
// }
// @protected
// List<E> get value {
// if (getObs != null) {
// getObs.addListener(subject.stream);
// }
// return _value;
// }
// String get string => value.toString();
// void addListener(Stream<List<E>> rxGetX) {
// if (_subscriptions.containsKey(rxGetX)) {
// return;
// }
// _subscriptions[rxGetX] = rxGetX.listen(subject.add);
// }
// set value(List<E> val) {
// if (_value == val) return;
// _value = val;
// refresh();
// }
// Stream<List<E>> get stream => subject.stream;
// StreamSubscription<List<E>> listen(
// void Function(List<E>) onData, {
// Function onError,
// void Function() onDone,
// bool cancelOnError,
// }) =>
// stream.listen(onData, onError: onError, onDone: onDone);
// /// Binds an existing [Stream<List>] to this [RxList].
// /// You can bind multiple sources to update the value.
// /// Closing the subscription will happen automatically when the observer
// /// Widget ([GetX] or [Obx]) gets unmounted from the Widget tree.
// void bindStream(Stream<List<E>> stream) {
// _subscriptions[stream] = stream.listen((va) => value = va);
// }
// @override
// E get first => value.first;
// @override
// E get last => value.last;
// @override
// bool any(bool Function(E) test) {
// return value.any(test);
// }
// @override
// Map<int, E> asMap() {
// return value.asMap();
// }
// @override
// List<R> cast<R>() {
// return value.cast<R>();
// }
// @override
// bool contains(Object element) {
// return value.contains(element);
// }
// @override
// E elementAt(int index) {
// return value.elementAt(index);
// }
// @override
// bool every(bool Function(E) test) {
// return value.every(test);
// }
// @override
// Iterable<T> expand<T>(Iterable<T> Function(E) f) {
// return value.expand(f);
// }
// @override
// void fillRange(int start, int end, [E fillValue]) {
// _value.fillRange(start, end, fillValue);
// refresh();
// }
// @override
// E firstWhere(bool Function(E) test, {E Function() orElse}) {
// return value.firstWhere(test, orElse: orElse);
// }
// @override
// T fold<T>(T initialValue, T Function(T, E) combine) {
// return value.fold(initialValue, combine);
// }
// @override
// Iterable<E> followedBy(Iterable<E> other) {
// return value.followedBy(other);
// }
// @override
// void forEach(void Function(E) f) {
// value.forEach(f);
// }
// @override
// Iterable<E> getRange(int start, int end) {
// return value.getRange(start, end);
// }
// @override
// int indexOf(E element, [int start = 0]) {
// return value.indexOf(element, start);
// }
// @override
// int indexWhere(bool Function(E) test, [int start = 0]) {
// return value.indexWhere(test, start);
// }
// @override
// String join([String separator = ""]) {
// return value.join(separator);
// }
// @override
// int lastIndexOf(E element, [int start]) {
// return value.lastIndexOf(element, start);
// }
// @override
// int lastIndexWhere(bool Function(E) test, [int start]) {
// return value.lastIndexWhere(test, start);
// }
// @override
// E lastWhere(bool Function(E) test, {E Function() orElse}) {
// return value.lastWhere(test, orElse: orElse);
// }
// @override
// set length(int newLength) {
// _value.length = newLength;
// refresh();
// }
// @override
// Iterable<T> map<T>(T Function(E) f) {
// return value.map(f);
// }
// @override
// E reduce(E Function(E, E) combine) {
// return value.reduce(combine);
// }
// @override
// void replaceRange(int start, int end, Iterable<E> replacement) {
// _value.replaceRange(start, end, replacement);
// refresh();
// }
// @override
// void retainWhere(bool Function(E) test) {
// _value.retainWhere(test);
// refresh();
// }
// @override
// Iterable<E> get reversed => value.reversed;
// @override
// void setAll(int index, Iterable<E> iterable) {
// _value.setAll(index, iterable);
// refresh();
// }
// @override
// void setRange(int start, int end,
// Iterable<E> iterable, [int skipCount = 0],) {
// _value.setRange(start, end, iterable, skipCount);
// refresh();
// }
// @override
// void shuffle([Random random]) {
// _value.shuffle(random);
// refresh();
// }
// @override
// E get single => value.single;
// @override
// E singleWhere(bool Function(E) test, {E Function() orElse}) {
// return value.singleWhere(test, orElse: orElse);
// }
// @override
// Iterable<E> skip(int count) {
// return value.skip(count);
// }
// @override
// Iterable<E> skipWhile(bool Function(E) test) {
// return value.skipWhile(test);
// }
// @override
// List<E> sublist(int start, [int end]) {
// return value.sublist(start, end);
// }
// @override
// Iterable<E> take(int count) {
// return value.take(count);
// }
// @override
// Iterable<E> takeWhile(bool Function(E) test) {
// return value.takeWhile(test);
// }
// @override
// List<E> toList({bool growable = true}) {
// return value.toList(growable: growable);
// }
// @override
// Set<E> toSet() {
// return value.toSet();
// }
// @override
// Iterable<E> where(bool Function(E) test) {
// return value.where(test);
// }
// @override
// Iterable<T> whereType<T>() {
// return value.whereType<T>();
// }
// @override
// set first(E value) {
// _value.first = value;
// refresh();
// }
// @override
// set last(E value) {
// _value.last = value;
// refresh();
// }
// }
extension ListExtension<E> on List<E> {
RxList<E> get obs {
if (this != null) {
return RxList<E>(<E>[])..addAllNonNull(this);
} else {
return RxList<E>(null);
}
}
}
... ...
import 'dart:async';
import 'dart:collection';
import 'package:flutter/foundation.dart';
import '../rx_core/rx_impl.dart';
import '../rx_core/rx_interface.dart';
import '../rx_typedefs/rx_typedefs.dart';
part of rx_types;
class RxMap<K, V> implements RxInterface<Map<K, V>>, Map<K, V> {
class RxMap<K, V> extends MapMixin<K, V>
with RxObjectMixin<Map<K, V>>
implements RxInterface<Map<K, V>> {
RxMap([Map<K, V> initial]) {
if (initial != null) _value = initial;
}
@override
StreamController<Map<K, V>> subject = StreamController<Map<K, V>>.broadcast();
final _subscriptions = HashMap<Stream<Map<K, V>>, StreamSubscription>();
Map<K, V> _value;
@protected
Map<K, V> get value {
if (getObs != null) {
getObs.addListener(subject.stream);
}
return _value;
}
void refresh() {
subject.add(_value);
}
String get string => value.toString();
bool get canUpdate {
return _subscriptions.length > 0;
}
@override
void close() {
_subscriptions.forEach((observable, subscription) {
subscription.cancel();
});
_subscriptions.clear();
subject.close();
}
@override
void addListener(Stream<Map<K, V>> rxGetX) {
if (_subscriptions.containsKey(rxGetX)) {
return;
}
_subscriptions[rxGetX] = rxGetX.listen((data) {
subject.add(data);
});
}
set value(Map<K, V> val) {
if (_value == val) return;
_value = val;
refresh();
}
Stream<Map<K, V>> get stream => subject.stream;
StreamSubscription<Map<K, V>> listen(void Function(Map<K, V>) onData,
{Function onError, void Function() onDone, bool cancelOnError}) =>
stream.listen(onData, onError: onError, onDone: onDone);
/// Binds an existing [Stream<Map>] to this [RxMap].
/// You can bind multiple sources to update the value.
/// Closing the subscription will happen automatically when the observer
/// Widget ([GetX] or [Obx]) gets unmounted from the Widget tree.
void bindStream(Stream<Map<K, V>> stream) {
_subscriptions[stream] = stream.listen((va) => value = va);
}
void add(K key, V value) {
_value[key] = value;
refresh();
}
void addIf(dynamic condition, K key, V value) {
if (condition is Condition) condition = condition();
if (condition is bool && condition) {
_value[key] = value;
refresh();
}
}
void addAllIf(dynamic condition, Map<K, V> values) {
if (condition is Condition) condition = condition();
if (condition is bool && condition) addAll(values);
_value = initial;
}
@override
... ... @@ -103,64 +19,15 @@ class RxMap<K, V> implements RxInterface<Map<K, V>>, Map<K, V> {
}
@override
void addAll(Map<K, V> other) {
_value.addAll(other);
refresh();
}
@override
void addEntries(Iterable<MapEntry<K, V>> entries) {
_value.addEntries(entries);
refresh();
}
@override
void clear() {
_value.clear();
refresh();
}
@override
Map<K2, V2> cast<K2, V2>() => value.cast<K2, V2>();
@override
bool containsKey(Object key) => value.containsKey(key);
@override
bool containsValue(Object value) => _value.containsValue(value);
@override
Iterable<MapEntry<K, V>> get entries => value.entries;
@override
void forEach(void Function(K, V) f) {
value.forEach(f);
}
@override
bool get isEmpty => value.isEmpty;
@override
bool get isNotEmpty => value.isNotEmpty;
@override
Iterable<K> get keys => value.keys;
@override
int get length => value.length;
@override
Map<K2, V2> map<K2, V2>(MapEntry<K2, V2> Function(K, V) transform) =>
value.map(transform);
@override
V putIfAbsent(K key, V Function() ifAbsent) {
final val = _value.putIfAbsent(key, ifAbsent);
refresh();
return val;
}
@override
V remove(Object key) {
final val = _value.remove(key);
refresh();
... ... @@ -168,37 +35,51 @@ class RxMap<K, V> implements RxInterface<Map<K, V>>, Map<K, V> {
}
@override
void removeWhere(bool Function(K, V) test) {
_value.removeWhere(test);
refresh();
@protected
Map<K, V> get value {
if (getObs != null) {
getObs.addListener(subject);
}
return _value;
}
@override
Iterable<V> get values => value.values;
@override
String toString() => _value.toString();
void assign(K key, V val) {
_value.clear();
_value[key] = val;
refresh();
}
@override
V update(K key, V Function(V) update, {V Function() ifAbsent}) {
final val = _value.update(key, update, ifAbsent: ifAbsent);
void assignAll(Map<K, V> val) {
if (_value == val) return;
_value = val;
refresh();
return val;
}
@override
void updateAll(V Function(K, V) update) {
_value.updateAll(update);
@protected
@Deprecated('Map.value is deprecated. use [yourMap.assignAll(newMap)]')
set value(Map<K, V> val) {
if (_value == val) return;
_value = val;
refresh();
}
void addIf(dynamic condition, K key, V value) {
if (condition is Condition) condition = condition();
if (condition is bool && condition) {
_value[key] = value;
refresh();
}
}
void addAllIf(dynamic condition, Map<K, V> values) {
if (condition is Condition) condition = condition();
if (condition is bool && condition) addAll(values);
}
}
extension MapExtension<K, V> on Map<K, V> {
RxMap<K, V> get obs {
if (this != null) {
return RxMap<K, V>(<K, V>{})..addAll(this);
} else {
return RxMap<K, V>(null);
}
return RxMap<K, V>(this);
}
}
... ...
part of rx_types;
class RxSet<E> extends SetMixin<E>
with RxObjectMixin<Set<E>>
implements RxInterface<Set<E>> {
RxSet([Set<E> initial]) {
if (initial != null) _value = initial;
}
/// Adds [item] only if [condition] resolves to true.
void addIf(dynamic condition, E item) {
if (condition is Condition) condition = condition();
if (condition is bool && condition) add(item);
}
/// Adds all [items] only if [condition] resolves to true.
void addAllIf(dynamic condition, Iterable<E> items) {
if (condition is Condition) condition = condition();
if (condition is bool && condition) addAll(items);
}
/// Special override to push() element(s) in a reactive way
/// inside the List,
RxSet<E> operator +(Set<E> val) {
addAll(val);
refresh();
return this;
}
/// Adds only if [item] is not null.
void addNonNull(E item) {
if (item != null) add(item);
}
/// Adds only if [item] is not null.
void addAllNonNull(Iterable<E> item) {
if (item != null) addAll(item);
}
/// Replaces all existing items of this list with [item]
void assign(E item) {
clear();
add(item);
}
void update(void fn(Iterable<E> value)) {
fn(value);
refresh();
}
/// Replaces all existing items of this list with [items]
void assignAll(Iterable<E> items) {
clear();
addAll(items);
}
@override
@protected
Set<E> get value {
if (getObs != null) {
getObs.addListener(subject);
}
return _value;
}
@override
@protected
set value(Set<E> val) {
if (_value == val) return;
_value = val;
refresh();
}
@override
bool add(E value) {
final val = _value.add(value);
refresh();
return val;
}
@override
bool contains(Object element) {
return value.contains(element);
}
@override
Iterator<E> get iterator => value.iterator;
@override
int get length => value.length;
@override
E lookup(Object object) {
return value.lookup(object);
}
@override
bool remove(Object item) {
var hasRemoved = _value.remove(item);
if (hasRemoved) {
refresh();
}
return hasRemoved;
}
@override
Set<E> toSet() {
return value.toSet();
}
@override
void addAll(Iterable<E> item) {
_value.addAll(item);
refresh();
}
@override
void clear() {
_value.clear();
refresh();
}
@override
void removeAll(Iterable<Object> elements) {
_value.removeAll(elements);
refresh();
}
@override
void retainAll(Iterable<Object> elements) {
_value.retainAll(elements);
refresh();
}
@override
void retainWhere(bool Function(E) E) {
_value.retainWhere(E);
refresh();
}
}
// class RxSet<E> implements Set<E>, RxInterface<Set<E>> {
// RxSet([Set<E> initial]) {
// if (initial != null) _value = initial;
// }
// Set<E> _value = <E>{};
// @override
// Iterator<E> get iterator => value.iterator;
// @override
// bool get isEmpty => value.isEmpty;
// bool get canUpdate {
// return _subscriptions.length > 0;
// }
// @override
// bool get isNotEmpty => value.isNotEmpty;
// StreamController<Set<E>> subject = StreamController<Set<E>>.broadcast();
// final _subscriptions = HashMap<Stream<Set<E>>, StreamSubscription>();
// /// Adds [item] only if [condition] resolves to true.
// void addIf(dynamic condition, E item) {
// if (condition is Condition) condition = condition();
// if (condition is bool && condition) add(item);
// }
// /// Adds all [items] only if [condition] resolves to true.
// void addAllIf(dynamic condition, Iterable<E> items) {
// if (condition is Condition) condition = condition();
// if (condition is bool && condition) addAll(items);
// }
// void refresh() {
// subject.add(_value);
// }
// /// Special override to push() element(s) in a reactive way
// /// inside the List,
// RxSet<E> operator +(Set<E> val) {
// addAll(val);
// refresh();
// return this;
// }
// @override
// bool add(E value) {
// final val = _value.add(value);
// refresh();
// return val;
// }
// @override
// void addAll(Iterable<E> item) {
// _value.addAll(item);
// refresh();
// }
// /// Adds only if [item] is not null.
// void addNonNull(E item) {
// if (item != null) add(item);
// }
// /// Adds only if [item] is not null.
// void addAllNonNull(Iterable<E> item) {
// if (item != null) addAll(item);
// }
// int get length => value.length;
// /// Removes an item from the list.
// ///
// /// This is O(N) in the number of items in the list.
// ///
// /// Returns whether the item was present in the list.
// bool remove(Object item) {
// var hasRemoved = _value.remove(item);
// if (hasRemoved) {
// refresh();
// }
// return hasRemoved;
// }
// void removeWhere(bool Function(E) test) {
// _value.removeWhere(test);
// refresh();
// }
// void clear() {
// _value.clear();
// refresh();
// }
// void close() {
// _subscriptions.forEach((observable, subscription) {
// subscription.cancel();
// });
// _subscriptions.clear();
// subject.close();
// }
// /// Replaces all existing items of this list with [item]
// void assign(E item) {
// clear();
// add(item);
// }
// void update(void fn(Iterable<E> value)) {
// fn(value);
// refresh();
// }
// /// Replaces all existing items of this list with [items]
// void assignAll(Iterable<E> items) {
// clear();
// addAll(items);
// }
// @protected
// Set<E> get value {
// if (getObs != null) {
// getObs.addListener(subject.stream);
// }
// return _value;
// }
// String get string => value.toString();
// void addListener(Stream<Set<E>> rxGetX) {
// if (_subscriptions.containsKey(rxGetX)) {
// return;
// }
// _subscriptions[rxGetX] = rxGetX.listen((data) {
// subject.add(data);
// });
// }
// set value(Set<E> val) {
// if (_value == val) return;
// _value = val;
// refresh();
// }
// Stream<Set<E>> get stream => subject.stream;
// StreamSubscription<Set<E>> listen(void Function(Set<E>) onData,
// {Function onError, void Function() onDone, bool cancelOnError}) =>
// stream.listen(onData, onError: onError, onDone: onDone);
// /// Binds an existing [Stream<Set>] to this [RxSet].
// /// You can bind multiple sources to update the value.
// /// Closing the subscription will happen automatically when the observer
// /// Widget ([GetX] or [Obx]) gets unmounted from the Widget tree.
// void bindStream(Stream<Set<E>> stream) {
// _subscriptions[stream] = stream.listen((va) => value = va);
// }
// @override
// E get first => value.first;
// @override
// E get last => value.last;
// @override
// bool any(bool Function(E) test) {
// return value.any(test);
// }
// @override
// Set<R> cast<R>() {
// return value.cast<R>();
// }
// @override
// bool contains(Object element) {
// return value.contains(element);
// }
// @override
// E elementAt(int index) {
// return value.elementAt(index);
// }
// @override
// bool every(bool Function(E) test) {
// return value.every(test);
// }
// @override
// Iterable<T> expand<T>(Iterable<T> Function(E) f) {
// return value.expand(f);
// }
// @override
// E firstWhere(bool Function(E) test, {E Function() orElse}) {
// return value.firstWhere(test, orElse: orElse);
// }
// @override
// T fold<T>(T initialValue, T Function(T, E) combine) {
// return value.fold(initialValue, combine);
// }
// @override
// Iterable<E> followedBy(Iterable<E> other) {
// return value.followedBy(other);
// }
// @override
// void forEach(void Function(E) f) {
// value.forEach(f);
// }
// @override
// String join([String separator = ""]) {
// return value.join(separator);
// }
// @override
// E lastWhere(bool Function(E) test, {E Function() orElse}) {
// return value.lastWhere(test, orElse: orElse);
// }
// @override
// Iterable<T> map<T>(T Function(E) f) {
// return value.map(f);
// }
// @override
// E reduce(E Function(E, E) combine) {
// return value.reduce(combine);
// }
// @override
// E get single => value.single;
// @override
// E singleWhere(bool Function(E) test, {E Function() orElse}) {
// return value.singleWhere(test, orElse: orElse);
// }
// @override
// Iterable<E> skip(int count) {
// return value.skip(count);
// }
// @override
// Iterable<E> skipWhile(bool Function(E) test) {
// return value.skipWhile(test);
// }
// @override
// Iterable<E> take(int count) {
// return value.take(count);
// }
// @override
// Iterable<E> takeWhile(bool Function(E) test) {
// return value.takeWhile(test);
// }
// @override
// List<E> toList({bool growable = true}) {
// return value.toList(growable: growable);
// }
// @override
// Set<E> toSet() {
// return value.toSet();
// }
// @override
// Iterable<E> where(bool Function(E) test) {
// return value.where(test);
// }
// @override
// Iterable<T> whereType<T>() {
// return value.whereType<T>();
// }
// @override
// bool containsAll(Iterable<Object> other) {
// return value.containsAll(other);
// }
// @override
// Set<E> difference(Set<Object> other) {
// return value.difference(other);
// }
// @override
// Set<E> intersection(Set<Object> other) {
// return value.intersection(other);
// }
// @override
// E lookup(Object object) {
// return value.lookup(object);
// }
// @override
// void removeAll(Iterable<Object> elements) {
// _value.removeAll(elements);
// refresh();
// }
// @override
// void retainAll(Iterable<Object> elements) {
// _value.retainAll(elements);
// refresh();
// }
// @override
// void retainWhere(bool Function(E) E) {
// _value.retainWhere(E);
// refresh();
// }
// @override
// Set<E> union(Set<E> other) {
// return value.union(other);
// }
// }
extension SetExtension<E> on Set<E> {
RxSet<E> get obs {
if (this != null) {
return RxSet<E>(<E>{})..addAllNonNull(this);
} else {
return RxSet<E>(null);
}
}
}
... ...
library rx_types;
import 'dart:async';
import 'dart:collection';
import 'package:flutter/foundation.dart';
import '../rx_stream/rx_stream.dart';
import '../rx_typedefs/rx_typedefs.dart';
part 'rx_core/rx_impl.dart';
part 'rx_core/rx_interface.dart';
part 'rx_core/rx_num.dart';
part 'rx_iterables/rx_list.dart';
part 'rx_iterables/rx_set.dart';
part 'rx_iterables/rx_map.dart';
... ...
import 'dart:async';
import '../../../get_core/get_core.dart';
import '../rx_core/rx_interface.dart';
import '../rx_types/rx_types.dart';
import 'utils/debouncer.dart';
bool _conditional(dynamic condition) {
... ... @@ -10,6 +11,8 @@ bool _conditional(dynamic condition) {
return true;
}
typedef WorkerCallback<T> = Function(T callback);
///
/// Called every time [listener] changes. As long as the [condition]
/// returns true.
... ... @@ -40,9 +43,9 @@ bool _conditional(dynamic condition) {
/// void increment() => count + 1;
/// }
/// ```
Worker ever<T>(RxInterface<T> listener, Function(T) callback,
Worker ever<T>(RxInterface<T> listener, WorkerCallback<T> callback,
{dynamic condition = true}) {
StreamSubscription sub = listener.subject.stream.listen((event) {
StreamSubscription sub = listener.subject.listen((event) {
if (_conditional(condition)) callback(event);
});
return Worker(sub.cancel, '[ever]');
... ... @@ -52,11 +55,11 @@ Worker ever<T>(RxInterface<T> listener, Function(T) callback,
/// for the [callback] is common to all [listeners],
/// and the [callback] is executed to each one of them. The [Worker] is
/// common to all, so [worker.dispose()] will cancel all streams.
Worker everAll(List<RxInterface> listeners, Function(dynamic) callback,
Worker everAll(List<RxInterface> listeners, WorkerCallback callback,
{dynamic condition = true}) {
final evers = <StreamSubscription>[];
for (var i in listeners) {
final sub = i.subject.stream.listen((event) {
final sub = i.subject.listen((event) {
if (_conditional(condition)) callback(event);
});
evers.add(sub);
... ... @@ -93,11 +96,11 @@ Worker everAll(List<RxInterface> listeners, Function(dynamic) callback,
/// void increment() => count + 1;
/// }
///```
Worker once<T>(RxInterface<T> listener, Function(T) callback,
Worker once<T>(RxInterface<T> listener, WorkerCallback<T> callback,
{dynamic condition}) {
Worker ref;
StreamSubscription sub;
sub = listener.subject.stream.listen((event) {
sub = listener.subject.listen((event) {
if (!_conditional(condition)) return;
ref._disposed = true;
ref._log('called');
... ... @@ -125,11 +128,11 @@ Worker once<T>(RxInterface<T> listener, Function(T) callback,
/// condition: () => count < 20,
/// );
/// ```
Worker interval<T>(RxInterface<T> listener, Function(T) callback,
Worker interval<T>(RxInterface<T> listener, WorkerCallback<T> callback,
{Duration time = const Duration(seconds: 1), dynamic condition = true}) {
var debounceActive = false;
time ??= const Duration(seconds: 1);
StreamSubscription sub = listener.subject.stream.listen((event) async {
StreamSubscription sub = listener.subject.listen((event) async {
if (debounceActive || !_conditional(condition)) return;
debounceActive = true;
await Future.delayed(time);
... ... @@ -158,11 +161,11 @@ Worker interval<T>(RxInterface<T> listener, Function(T) callback,
/// );
/// }
/// ```
Worker debounce<T>(RxInterface<T> listener, Function(T) callback,
Worker debounce<T>(RxInterface<T> listener, WorkerCallback<T> callback,
{Duration time}) {
final _debouncer =
Debouncer(delay: time ?? const Duration(milliseconds: 800));
StreamSubscription sub = listener.subject.stream.listen((event) {
StreamSubscription sub = listener.subject.listen((event) {
_debouncer(() {
callback(event);
});
... ...
... ... @@ -10,11 +10,7 @@ import '../../../get_instance/src/lifecycle.dart';
/// it is Get.reset().
abstract class GetxService extends DisposableInterface with GetxServiceMixin {}
abstract class DisposableInterface with GetLifeCycle {
DisposableInterface() {
initLifeCycle();
}
abstract class DisposableInterface extends GetLifeCycle {
/// Called immediately after the widget is allocated in memory.
/// You might use this to initialize something for the controller.
@override
... ... @@ -29,7 +25,9 @@ abstract class DisposableInterface with GetLifeCycle {
/// async request.
@override
@mustCallSuper
void onReady() {}
void onReady() {
super.onReady();
}
/// Called before [onDelete] method. [onClose] might be used to
/// dispose resources used by the controller. Like closing events,
... ... @@ -38,5 +36,7 @@ abstract class DisposableInterface with GetLifeCycle {
/// like TextEditingControllers, AnimationControllers.
/// Might be useful as well to persist some data on disk.
@override
void onClose() {}
void onClose() {
super.onClose();
}
}
... ...
... ... @@ -4,7 +4,7 @@ import 'package:flutter/widgets.dart';
import '../../../get_core/get_core.dart';
import '../../../get_instance/src/get_instance.dart';
import '../../../get_rx/get_rx.dart';
import '../../../get_rx/src/rx_types/rx_types.dart';
import '../../get_state_manager.dart';
typedef GetXControllerBuilder<T extends DisposableInterface> = Widget Function(
... ... @@ -38,10 +38,11 @@ class GetX<T extends DisposableInterface> extends StatefulWidget {
// this.streamController
});
GetImplXState<T> createState() => GetImplXState<T>();
@override
GetXState<T> createState() => GetXState<T>();
}
class GetImplXState<T extends DisposableInterface> extends State<GetX<T>> {
class GetXState<T extends DisposableInterface> extends State<GetX<T>> {
RxInterface _observer;
T controller;
bool isCreator = false;
... ... @@ -76,7 +77,7 @@ class GetImplXState<T extends DisposableInterface> extends State<GetX<T>> {
if (widget.global && Get.smartManagement == SmartManagement.onlyBuilder) {
controller?.onStart();
}
subs = _observer.subject.stream.listen((data) => setState(() {}));
subs = _observer.subject.listen((data) => setState(() {}));
super.initState();
}
... ...
... ... @@ -5,29 +5,101 @@ import '../../../instance_manager.dart';
import '../../get_state_manager.dart';
import '../simple/list_notifier.dart';
class Value<T> extends ListNotifier implements ValueListenable<T> {
Value(this._value);
mixin StateMixin<T> on ListNotifier {
T _value;
RxStatus _status;
bool _isNullOrEmpty(dynamic val) {
if (val == null) return true;
var result = false;
if (val is Iterable) {
result = val.isEmpty;
} else if (val is String) {
result = val.isEmpty;
} else if (val is Map) {
result = val.isEmpty;
}
return result;
}
void _fillEmptyStatus() {
_status = _isNullOrEmpty(_value) ? RxStatus.loading() : RxStatus.success();
}
RxStatus get status {
notifyChildrens();
return _status ??= _status = RxStatus.loading();
}
T get state => value;
@protected
T get value {
notifyChildrens();
return _value;
}
@override
String toString() => value.toString();
@protected
set value(T newValue) {
if (_value == newValue) return;
_value = newValue;
refresh();
}
T _value;
@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();
}
}
}
class Value<T> extends ListNotifier
with StateMixin<T>
implements ValueListenable<T> {
Value(T val) {
_value = val;
_fillEmptyStatus();
}
@override
T get value {
notifyChildrens();
return _value;
}
@override
set value(T newValue) {
if (_value == newValue) return;
_value = newValue;
updater();
refresh();
}
T call([T v]) {
if (v != null) {
value = v;
}
return value;
}
void update(void fn(T value)) {
fn(value);
updater();
refresh();
}
@override
String toString() => value.toString();
dynamic toJson() => (value as dynamic)?.toJson();
}
extension ReactiveT<T> on T {
... ... @@ -36,10 +108,9 @@ extension ReactiveT<T> on T {
typedef Condition = bool Function();
abstract class GetNotifier<T> extends Value<T> with GetLifeCycle {
abstract class GetNotifier<T> extends Value<T> with GetLifeCycleBase {
GetNotifier(T initial) : super(initial) {
initLifeCycle();
_fillEmptyStatus();
$configureLifeCycle();
}
@override
... ... @@ -48,62 +119,27 @@ abstract class GetNotifier<T> extends Value<T> with GetLifeCycle {
super.onInit();
SchedulerBinding.instance?.addPostFrameCallback((_) => onReady());
}
}
RxStatus _status;
bool get isNullOrEmpty {
if (_value == null) return true;
dynamic val = _value;
var result = false;
if (val is Iterable) {
result = val.isEmpty;
} else if (val is String) {
result = val.isEmpty;
} else if (val is Map) {
result = val.isEmpty;
}
return result;
}
void _fillEmptyStatus() {
_status = isNullOrEmpty ? RxStatus.loading() : RxStatus.success();
}
RxStatus get status {
notifyChildrens();
return _status;
}
Widget call(NotifierBuilder<T> widget, {Widget onError, Widget onLoading}) {
extension StateExt<T> on StateMixin<T> {
Widget obx(
NotifierBuilder<T> widget, {
Widget Function(String error) onError,
Widget onLoading,
}) {
assert(widget != null);
return SimpleBuilder(builder: (_) {
if (status.isLoading) {
return onLoading ?? CircularProgressIndicator();
return onLoading ?? Center(child: CircularProgressIndicator());
} else if (status.isError) {
return onError ?? Text('A error occured: ${status.errorMessage}');
return onError != null
? onError(status.errorMessage)
: Center(child: Text('A error occured: ${status.errorMessage}'));
} else {
return widget(value);
}
});
}
@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) {
updater();
}
}
dynamic toJson() => (value as dynamic)?.toJson();
}
class RxStatus {
... ...
import 'dart:async';
import 'package:flutter/widgets.dart';
import '../../../get_rx/get_rx.dart';
import '../../../get_rx/src/rx_types/rx_types.dart';
typedef WidgetCallback = Widget Function();
... ... @@ -12,6 +12,7 @@ typedef WidgetCallback = Widget Function();
abstract class ObxWidget extends StatefulWidget {
const ObxWidget({Key key}) : super(key: key);
@override
_ObxState createState() => _ObxState();
@protected
... ... @@ -28,7 +29,7 @@ class _ObxState extends State<ObxWidget> {
@override
void initState() {
subs = _observer.subject.stream.listen((data) => setState(() {}));
subs = _observer.subject.listen((data) => setState(() {}));
super.initState();
}
... ...
... ... @@ -21,5 +21,6 @@ import '../../get_state_manager.dart';
/// ```
mixin SingleGetTickerProviderMixin on DisposableInterface
implements TickerProvider {
@override
Ticker createTicker(TickerCallback onTick) => Ticker(onTick);
}
... ...
import 'dart:collection';
import 'package:flutter/material.dart';
import '../../../get_core/get_core.dart';
import '../../../get_instance/src/get_instance.dart';
import '../../get_state_manager.dart';
// Changed to VoidCallback.
//typedef Disposer = void Function();
// replacing StateSetter, return if the Widget is mounted for extra validation.
// if it brings overhead the extra call,
typedef GetStateUpdate = void Function();
//typedef GetStateUpdate = void Function(VoidCallback fn);
import 'list_notifier.dart';
/// Complies with [GetStateUpdater]
///
... ... @@ -31,14 +23,8 @@ mixin GetStateUpdaterMixin<T extends StatefulWidget> on State<T> {
}
}
class GetxController extends DisposableInterface {
final _updaters = <GetStateUpdate>[];
// final _updatersIds = HashMap<String, StateSetter>(); //<old>
final _updatersIds = HashMap<String, GetStateUpdate>();
final _updatersGroupIds = HashMap<String, List<GetStateUpdate>>();
// ignore: prefer_mixin
class GetxController extends DisposableInterface with ListNotifier {
/// Rebuilds [GetBuilder] each time you call [update()];
/// Can take a List of [ids], that will only update the matching
/// `GetBuilder( id: )`,
... ... @@ -49,73 +35,13 @@ class GetxController extends DisposableInterface {
return;
}
if (ids == null) {
// _updaters?.forEach((rs) => rs(() {})); //<old>
for (final updater in _updaters) {
updater();
}
refresh();
} else {
// @jonny, remove this commented code if it's not more optimized.
// for (final id in ids) {
// if (_updatersIds[id] != null) _updatersIds[id]();
// if (_updatersGroupIds[id] != null)
// for (final rs in _updatersGroupIds[id]) rs();
// }
for (final id in ids) {
_updatersIds[id]?.call();
// ignore: avoid_function_literals_in_foreach_calls
_updatersGroupIds[id]?.forEach((rs) => rs());
refreshGroup(id);
}
}
}
// VoidCallback addListener(StateSetter listener) {//<old>
VoidCallback addListener(GetStateUpdate listener) {
_updaters.add(listener);
return () => _updaters.remove(listener);
}
// VoidCallback addListenerId(String key, StateSetter listener) {//<old>
VoidCallback addListenerId(String key, GetStateUpdate listener) {
// _printCurrentIds();
if (_updatersIds.containsKey(key)) {
_updatersGroupIds[key] ??= <GetStateUpdate>[];
_updatersGroupIds[key].add(listener);
return () {
_updatersGroupIds[key].remove(listener);
};
} else {
_updatersIds[key] = listener;
return () => _updatersIds.remove(key);
}
}
/// 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(String id) {
_updatersIds.remove(id);
_updatersGroupIds.remove(id);
}
/// Remove this after checking the new implementation makes sense.
/// Uncomment this if you wanna control the removal of ids..
/// bool _debugging = false;
/// Future<void> _printCurrentIds() async {
/// if (_debugging) return;
/// _debugging = true;
/// print('about to debug...');
/// await Future.delayed(Duration(milliseconds: 10));
/// int totalGroups = 0;
/// _updatersGroupIds.forEach((key, value) {
/// totalGroups += value.length;
/// });
/// int totalIds = _updatersIds.length;
/// print(
/// 'Total: ${totalIds + totalGroups},'+
/// 'in groups:$totalGroups, solo ids:$totalIds',);
/// _debugging = false;
/// }
}
typedef GetControllerBuilder<T extends DisposableInterface> = Widget Function(
... ... @@ -187,10 +113,6 @@ class _GetBuilderState<T extends GetxController> extends State<GetBuilder<T>>
controller?.onStart();
}
// if (widget.global && Get.smartManagement ==
//SmartManagement.onlyBuilder) {
// controller?.onStart();
// }
_subscribeToController();
}
... ... @@ -200,20 +122,10 @@ class _GetBuilderState<T extends GetxController> extends State<GetBuilder<T>>
void _subscribeToController() {
remove?.call();
remove = (widget.id == null)
// ? controller?.addListener(setState) //<old>
// : controller?.addListenerId(widget.id, setState); //<old>
? controller?.addListener(getUpdate)
: controller?.addListenerId(widget.id, getUpdate);
}
/// Sample for [GetStateUpdate] when you don't wanna
/// use [GetStateHelper mixin].
/// bool _getUpdater() {
/// final _mounted = mounted;
/// if (_mounted) setState(() {});
/// return _mounted;
/// }
@override
void dispose() {
super.dispose();
... ... @@ -249,26 +161,6 @@ class _GetBuilderState<T extends GetxController> extends State<GetBuilder<T>>
Widget build(BuildContext context) => widget.builder(controller);
}
/// This is a experimental feature.
/// Meant to be used with SimpleBuilder, it auto-registers the variable
/// like Rx() does with Obx().
// class Value<T> extends GetxController {
// Value([this._value]);
// T _value;
// T get value {
// TaskManager.instance.notify(_updaters);
// return _value;
// }
// set value(T newValue) {
// if (_value == newValue) return;
// _value = newValue;
// update();
// }
// }
/// It's Experimental class, the Api can be change
abstract class GetState<T> extends GetxController {
GetState(T initialValue) {
... ...
... ... @@ -146,5 +146,3 @@
// return widget.builder(controller.state);
// }
//}
... ...
import 'dart:collection';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'simple_builder.dart';
// This callback remove the listener on addListener function
typedef Disposer = void Function();
// replacing StateSetter, return if the Widget is mounted for extra validation.
// if it brings overhead the extra call,
typedef GetStateUpdate = void Function();
class ListNotifier implements Listenable {
List<VoidCallback> _listeners = <VoidCallback>[];
List<GetStateUpdate> _updaters = <GetStateUpdate>[];
HashMap<String, List<GetStateUpdate>> _updatersGroupIds =
HashMap<String, List<GetStateUpdate>>();
@protected
void updater() {
void refresh() {
assert(_debugAssertNotDisposed());
for (var element in _listeners) {
for (var element in _updaters) {
element();
}
}
@protected
void refreshGroup(String id) {
assert(_debugAssertNotDisposed());
if (_updatersGroupIds.containsKey(id)) {
for (var item in _updatersGroupIds[id]) {
item();
}
}
}
bool _debugAssertNotDisposed() {
assert(() {
if (_listeners == null) {
if (_updaters == 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.''');
}
... ... @@ -26,29 +46,87 @@ class ListNotifier implements Listenable {
@protected
void notifyChildrens() {
TaskManager.instance.notify(_listeners);
TaskManager.instance.notify(_updaters);
}
bool get hasListeners {
assert(_debugAssertNotDisposed());
return _listeners.isNotEmpty;
return _updaters.isNotEmpty;
}
@override
void addListener(VoidCallback listener) {
void removeListener(VoidCallback listener) {
assert(_debugAssertNotDisposed());
_listeners.add(listener);
_updaters.remove(listener);
}
@override
void removeListener(VoidCallback listener) {
void removeListenerId(String id, VoidCallback listener) {
assert(_debugAssertNotDisposed());
_listeners.remove(listener);
if (_updatersGroupIds.containsKey(id)) {
_updatersGroupIds[id].remove(listener);
}
_updaters.remove(listener);
}
@mustCallSuper
void dispose() {
assert(_debugAssertNotDisposed());
_listeners = null;
_updaters = null;
_updatersGroupIds = null;
}
@override
Disposer addListener(GetStateUpdate listener) {
assert(_debugAssertNotDisposed());
_updaters.add(listener);
return () => _updaters.remove(listener);
}
Disposer addListenerId(String key, GetStateUpdate listener) {
_updatersGroupIds[key] ??= <GetStateUpdate>[];
_updatersGroupIds[key].add(listener);
return () => _updatersGroupIds[key].remove(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(String id) {
_updatersGroupIds.remove(id);
}
}
class TaskManager {
TaskManager._();
static TaskManager _instance;
static TaskManager get instance => _instance ??= 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));
}
}
}
Widget exchange(
List<VoidCallback> disposers,
GetStateUpdate setState,
Widget Function(BuildContext) builder,
BuildContext context,
) {
_remove = disposers;
_setter = setState;
final result = builder(context);
_remove = null;
_setter = null;
return result;
}
}
... ...
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'get_state.dart';
import 'list_notifier.dart';
typedef ValueBuilderUpdateCallback<T> = void Function(T snapshot);
typedef ValueBuilderBuilder<T> = Widget Function(
... ... @@ -87,7 +88,7 @@ class SimpleBuilder extends StatefulWidget {
class _SimpleBuilderState extends State<SimpleBuilder>
with GetStateUpdaterMixin {
final disposers = <VoidCallback>[];
final disposers = <Disposer>[];
@override
void dispose() {
... ... @@ -107,38 +108,3 @@ class _SimpleBuilderState extends State<SimpleBuilder>
);
}
}
class TaskManager {
TaskManager._();
static TaskManager _instance;
static TaskManager get instance => _instance ??= 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));
}
}
}
Widget exchange(
List<VoidCallback> disposers,
GetStateUpdate setState,
Widget Function(BuildContext) builder,
BuildContext context,
) {
_remove = disposers;
_setter = setState;
final result = builder(context);
_remove = null;
_setter = null;
return result;
}
}
... ...
... ... @@ -94,6 +94,9 @@ class GetUtils {
/// Checks if string consist only Alphabet. (No Whitespace)
static bool isAlphabetOnly(String s) => hasMatch(s, r'^[a-zA-Z]+$');
/// Checks if string contains at least one Capital Letter
static bool hasCapitalletter(String s) => hasMatch(s, r'[A-Z]');
/// Checks if string is boolean.
static bool isBool(String value) {
... ...
name: get
description: Open screens/snackbars/dialogs/bottomSheets without context, manage states and inject dependencies easily with GetX.
version: 3.13.2
version: 3.15.0
homepage: https://github.com/jonataslaw/getx
environment:
... ... @@ -9,7 +9,6 @@ environment:
dependencies:
flutter:
sdk: flutter
meta: 1.3.0-nullsafety.3
dev_dependencies:
flutter_test:
... ...
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:get/state_manager.dart';
int times = 3;
int get last => times - 1;
int times = 30;
Future<String> valueNotifier() {
final c = Completer<String>();
Future<int> valueNotifier() {
final c = Completer<int>();
final value = ValueNotifier<int>(0);
final timer = Stopwatch();
timer.start();
value.addListener(() {
if (last == value.value) {
if (times == value.value) {
timer.stop();
c.complete("""${value.value} item value notifier
objs time: ${timer.elapsedMicroseconds}ms""");
print(
"""${value.value} listeners notified | [VALUE_NOTIFIER] time: ${timer.elapsedMicroseconds}ms""");
c.complete(timer.elapsedMicroseconds);
}
});
for (var i = 0; i < times; i++) {
for (var i = 0; i < times + 1; i++) {
value.value = i;
}
return c.future;
}
Future<String> getValue() {
final c = Completer<String>();
Future<int> getValue() {
final c = Completer<int>();
final value = Value<int>(0);
final timer = Stopwatch();
timer.start();
value.addListener(() {
if (last == value.value) {
if (times == value.value) {
timer.stop();
c.complete("""${value.value} item get value objs
time: ${timer.elapsedMicroseconds}ms""");
print(
"""${value.value} listeners notified | [GETX_VALUE] time: ${timer.elapsedMicroseconds}ms""");
c.complete(timer.elapsedMicroseconds);
}
});
for (var i = 0; i < times; i++) {
for (var i = 0; i < times + 1; i++) {
value.value = i;
}
return c.future;
}
Future<String> getStream() {
final c = Completer<String>();
Future<int> stream() {
final c = Completer<int>();
final value = StreamController<int>();
final timer = Stopwatch();
timer.start();
value.stream.listen((v) {
if (last == v) {
if (times == v) {
timer.stop();
print(
"""$v listeners notified | [STREAM] time: ${timer.elapsedMicroseconds}ms""");
c.complete(timer.elapsedMicroseconds);
}
});
for (var i = 0; i < times + 1; i++) {
value.add(i);
}
return c.future;
}
Future<int> getStream() {
final c = Completer<int>();
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);
}
});
for (var i = 0; i < times + 1; i++) {
value.add(i);
}
return c.future;
}
Future<int> miniStream() {
final c = Completer<int>();
final value = MiniStream<int>();
final timer = Stopwatch();
timer.start();
value.listen((v) {
if (times == v) {
timer.stop();
c.complete("$v item stream objs time: ${timer.elapsedMicroseconds}ms");
print(
"""$v listeners notified | [MINI_STREAM] time: ${timer.elapsedMicroseconds}ms""");
c.complete(timer.elapsedMicroseconds);
}
});
for (var i = 0; i < times; i++) {
for (var i = 0; i < times + 1; i++) {
value.add(i);
}
return c.future;
}
void main() async {
print(await getValue());
print(await valueNotifier());
print(await getStream());
times = 30000;
print(await getValue());
print(await valueNotifier());
print(await getStream());
void main() {
test('percentage test', () {
print('============================================');
print('PERCENTAGE TEST');
final referenceValue = 200;
final requestedValue = 100;
print('''
referenceValue is ${calculePercentage(referenceValue, requestedValue)}% more than requestedValue''');
expect(calculePercentage(referenceValue, requestedValue), 100);
});
test('run benchmarks from ValueNotifier', () async {
times = 30;
print('============================================');
print('VALUE_NOTIFIER X GETX_VALUE TEST');
print('-----------');
await getValue();
await valueNotifier();
print('-----------');
times = 30000;
final getx = await getValue();
final dart = await valueNotifier();
print('-----------');
print('ValueNotifier delay $dart ms to made $times requests');
print('GetValue delay $getx ms to made $times requests');
print('-----------');
print('''
GetValue is ${calculePercentage(dart, getx).round()}% more fast than Default ValueNotifier with $times listeners''');
});
test('run benchmarks from Streams', () async {
times = 30;
print('============================================');
print('DART STREAM X GET_STREAM X GET_MINI_STREAM TEST');
print('-----------');
var getx = await getStream();
var mini = await miniStream();
var dart = await stream();
print('-----------');
print('''
GetStream is ${calculePercentage(dart, mini).round()}% more fast than Default Stream with $times listeners''');
print('-----------');
times = 30000;
dart = await stream();
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_mini_stream delay $mini ms to made $times requests');
print('-----------');
print('''
GetStream is ${calculePercentage(dart, getx).round()}% more fast than Default Stream with $times listeners''');
});
}
typedef VoidCallback = void Function();
int calculePercentage(int dart, int getx) {
return (dart / getx * 100).round() - 100;
}
... ...
... ... @@ -9,11 +9,7 @@ class Mock {
}
}
class DisposableController with GetLifeCycle {
DisposableController() {
initLifeCycle();
}
}
class DisposableController extends GetLifeCycle {}
// ignore: one_member_abstracts
abstract class Service {
... ...