Jonatas

update Rx Structure from Inherited to Composition

Showing 53 changed files with 2360 additions and 1085 deletions
... ... @@ -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
... ...
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
... ...
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 29
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.example"
minSdkVersion 16
targetSdkVersion 29
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
... ...
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.example">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
... ...
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.example">
<application
android:label="example"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<!-- Displays an Android View that continues showing the launch screen
Drawable until Flutter paints its first frame, then this splash
screen fades out. A splash screen is useful to avoid any visual
gap between the end of Android's launch screen and the painting of
Flutter's first frame. -->
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
... ...
package com.example.example
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}
... ...
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
... ...
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
... ...
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
... ...
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.example">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
... ...
buildscript {
ext.kotlin_version = '1.3.50'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
jcenter()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
delete rootProject.buildDir
}
... ...
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
... ...
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
... ...
include ':app'
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
... ...
flutter/ephemeral
... ...
cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX)
set(BINARY_NAME "example")
set(APPLICATION_ID "com.example.example")
cmake_policy(SET CMP0063 NEW)
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
# Configure build options.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release")
endif()
# Compilation settings that should be applied to most targets.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_14)
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
endfunction()
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
# Flutter library and tool build rules.
add_subdirectory(${FLUTTER_MANAGED_DIR})
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
# Application build
add_executable(${BINARY_NAME}
"main.cc"
"my_application.cc"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
)
apply_standard_settings(${BINARY_NAME})
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
add_dependencies(${BINARY_NAME} flutter_assemble)
# Only the install-generated bundle's copy of the executable will launch
# correctly, since the resources must in the right relative locations. To avoid
# people trying to run the unbundled copy, put it in a subdirectory instead of
# the default top-level location.
set_target_properties(${BINARY_NAME}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
)
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
# === Installation ===
# By default, "installing" just makes a relocatable bundle in the build
# directory.
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()
# Start with a clean build bundle directory every time.
install(CODE "
file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
" COMPONENT Runtime)
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime)
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
if(PLUGIN_BUNDLED_LIBRARIES)
install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()
# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
# Install the AOT library on non-Debug builds only.
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()
... ...
cmake_minimum_required(VERSION 3.10)
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
# Configuration provided via flutter tool.
include(${EPHEMERAL_DIR}/generated_config.cmake)
# TODO: Move the rest of this into files in ephemeral. See
# https://github.com/flutter/flutter/issues/57146.
# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
# which isn't available in 3.10.
function(list_prepend LIST_NAME PREFIX)
set(NEW_LIST "")
foreach(element ${${LIST_NAME}})
list(APPEND NEW_LIST "${PREFIX}${element}")
endforeach(element)
set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
endfunction()
# === Flutter Library ===
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
pkg_check_modules(BLKID REQUIRED IMPORTED_TARGET blkid)
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
# Published to parent scope for install step.
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
list(APPEND FLUTTER_LIBRARY_HEADERS
"fl_basic_message_channel.h"
"fl_binary_codec.h"
"fl_binary_messenger.h"
"fl_dart_project.h"
"fl_engine.h"
"fl_json_message_codec.h"
"fl_json_method_codec.h"
"fl_message_codec.h"
"fl_method_call.h"
"fl_method_channel.h"
"fl_method_codec.h"
"fl_method_response.h"
"fl_plugin_registrar.h"
"fl_plugin_registry.h"
"fl_standard_message_codec.h"
"fl_standard_method_codec.h"
"fl_string_codec.h"
"fl_value.h"
"fl_view.h"
"flutter_linux.h"
)
list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
add_library(flutter INTERFACE)
target_include_directories(flutter INTERFACE
"${EPHEMERAL_DIR}"
)
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
target_link_libraries(flutter INTERFACE
PkgConfig::GTK
PkgConfig::GLIB
PkgConfig::GIO
PkgConfig::BLKID
)
add_dependencies(flutter flutter_assemble)
# === Flutter tool backend ===
# _phony_ is a non-existent file to force this command to run every time,
# since currently there's no way to get a full input/output list from the
# flutter tool.
add_custom_command(
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
${CMAKE_CURRENT_BINARY_DIR}/_phony_
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
linux-x64 ${CMAKE_BUILD_TYPE}
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS}
)
... ...
//
// Generated file. Do not edit.
//
#include "generated_plugin_registrant.h"
void fl_register_plugins(FlPluginRegistry* registry) {
}
... ...
//
// Generated file. Do not edit.
//
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter_linux/flutter_linux.h>
// Registers Flutter plugins.
void fl_register_plugins(FlPluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_
... ...
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
... ...
#include "my_application.h"
int main(int argc, char** argv) {
g_autoptr(MyApplication) app = my_application_new();
return g_application_run(G_APPLICATION(app), argc, argv);
}
... ...
#include "my_application.h"
#include <flutter_linux/flutter_linux.h>
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif
#include "flutter/generated_plugin_registrant.h"
struct _MyApplication {
GtkApplication parent_instance;
};
G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
// Implements GApplication::activate.
static void my_application_activate(GApplication* application) {
GtkWindow* window =
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
// Use a header bar when running in GNOME as this is the common style used
// by applications and is the setup most users will be using (e.g. Ubuntu
// desktop).
// If running on X and not using GNOME then just use a traditional title bar
// in case the window manager does more exotic layout, e.g. tiling.
// If running on Wayland assume the header bar will work (may need changing
// if future cases occur).
gboolean use_header_bar = TRUE;
#ifdef GDK_WINDOWING_X11
GdkScreen *screen = gtk_window_get_screen(window);
if (GDK_IS_X11_SCREEN(screen)) {
const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
use_header_bar = FALSE;
}
}
#endif
if (use_header_bar) {
GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
gtk_widget_show(GTK_WIDGET(header_bar));
gtk_header_bar_set_title(header_bar, "example");
gtk_header_bar_set_show_close_button(header_bar, TRUE);
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
}
else {
gtk_window_set_title(window, "example");
}
gtk_window_set_default_size(window, 1280, 720);
gtk_widget_show(GTK_WIDGET(window));
g_autoptr(FlDartProject) project = fl_dart_project_new();
FlView* view = fl_view_new(project);
gtk_widget_show(GTK_WIDGET(view));
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
fl_register_plugins(FL_PLUGIN_REGISTRY(view));
gtk_widget_grab_focus(GTK_WIDGET(view));
}
static void my_application_class_init(MyApplicationClass* klass) {
G_APPLICATION_CLASS(klass)->activate = my_application_activate;
}
static void my_application_init(MyApplication* self) {}
MyApplication* my_application_new() {
return MY_APPLICATION(g_object_new(my_application_get_type(),
"application-id", APPLICATION_ID,
nullptr));
}
... ...
#ifndef FLUTTER_MY_APPLICATION_H_
#define FLUTTER_MY_APPLICATION_H_
#include <gtk/gtk.h>
G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
GtkApplication)
/**
* my_application_new:
*
* Creates a new Flutter-based application.
*
* Returns: a new #MyApplication.
*/
MyApplication* my_application_new();
#endif // FLUTTER_MY_APPLICATION_H_
... ...
<!DOCTYPE html>
<html>
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
Fore more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
-->
<base href="/">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="example">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>example</title>
<link rel="manifest" href="manifest.json">
</head>
<body>
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('flutter-first-frame', function () {
navigator.serviceWorker.register('flutter_service_worker.js');
});
}
</script>
<script src="main.dart.js" type="application/javascript"></script>
</body>
</html>
... ...
{
"name": "example",
"short_name": "example",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "A new Flutter project.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
... ...
... ... @@ -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,
... ...
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;
}
}
class LightListenable<T> {
List<LightSubscription<T>> _onData = <LightSubscription<T>>[];
void removeSubscription(LightSubscription<T> subs) {
_onData.remove(subs);
}
void addSubscription(LightSubscription<T> subs) {
_onData.add(subs);
}
int get length => _onData?.length;
bool get hasListeners => _onData.isNotEmpty;
void notifyData(T data) {
_checkIfDisposed();
for (final item in _onData) {
if (item.isPaused) {
break;
}
item._data?.call(data);
}
}
void _checkIfDisposed() {
if (isDisposed) {
throw '[LightStream] Error: You cannot add events to a closed stream.';
}
}
void notifyError(Object error, [StackTrace stackTrace]) {
_checkIfDisposed();
for (final item in _onData) {
if (item.isPaused) {
break;
}
item._onError?.call(error, stackTrace);
if (item.cancelOnError) {
item.cancel?.call();
item._onDone?.call();
}
}
}
void notifyDone() {
_checkIfDisposed();
for (final item in _onData) {
if (item.isPaused) {
break;
}
item._onDone?.call();
}
}
void dispose() => _onData = 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() async => listener.removeSubscription(this);
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;
void assign(K key, V val) {
_value.clear();
_value[key] = val;
refresh();
}
@override
String toString() => _value.toString();
void assignAll(Map<K, V> val) {
if (_value == val) return;
_value = val;
refresh();
}
@override
V update(K key, V Function(V) update, {V Function() ifAbsent}) {
final val = _value.update(key, update, ifAbsent: ifAbsent);
@protected
@Deprecated('Map.value is deprecated. use [yourMap.assignAll(newMap)]')
set value(Map<K, V> val) {
if (_value == val) return;
_value = val;
refresh();
return val;
}
@override
void updateAll(V Function(K, V) update) {
_value.updateAll(update);
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) {
... ... @@ -42,7 +43,7 @@ bool _conditional(dynamic condition) {
/// ```
Worker ever<T>(RxInterface<T> listener, Function(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]');
... ... @@ -56,7 +57,7 @@ Worker everAll(List<RxInterface> listeners, Function(dynamic) 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);
... ... @@ -97,7 +98,7 @@ Worker once<T>(RxInterface<T> listener, Function(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');
... ... @@ -129,7 +130,7 @@ Worker interval<T>(RxInterface<T> listener, Function(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);
... ... @@ -162,7 +163,7 @@ Worker debounce<T>(RxInterface<T> listener, Function(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);
});
... ...
... ... @@ -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();
}
... ...
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:async';
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:get/state_manager.dart';
... ... @@ -7,8 +6,8 @@ import 'package:get/state_manager.dart';
int times = 3;
int get last => times - 1;
Future<String> valueNotifier() {
final c = Completer<String>();
Future<int> valueNotifier() {
final c = Completer<int>();
final value = ValueNotifier<int>(0);
final timer = Stopwatch();
timer.start();
... ... @@ -16,8 +15,9 @@ Future<String> valueNotifier() {
value.addListener(() {
if (last == value.value) {
timer.stop();
c.complete(
"""${value.value} listeners notified | [VALUENOTIFIER] objs time: ${timer.elapsedMicroseconds}ms""");
print(
"""${value.value} listeners notified | [VALUE_NOTIFIER] time: ${timer.elapsedMicroseconds}ms""");
c.complete(timer.elapsedMicroseconds);
}
});
... ... @@ -28,8 +28,8 @@ Future<String> valueNotifier() {
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();
... ... @@ -37,8 +37,9 @@ Future<String> getValue() {
value.addListener(() {
if (last == value.value) {
timer.stop();
c.complete(
"""${value.value} listeners notified | [GETX VALUE] objs time: ${timer.elapsedMicroseconds}ms""");
print(
"""${value.value} listeners notified | [GETX_VALUE] time: ${timer.elapsedMicroseconds}ms""");
c.complete(timer.elapsedMicroseconds);
}
});
... ... @@ -49,8 +50,8 @@ Future<String> getValue() {
return c.future;
}
Future<String> getStream() {
final c = Completer<String>();
Future<int> stream() {
final c = Completer<int>();
final value = StreamController<int>();
final timer = Stopwatch();
... ... @@ -59,8 +60,55 @@ Future<String> getStream() {
value.stream.listen((v) {
if (last == v) {
timer.stop();
c.complete(
"""$v listeners notified | [STREAM] objs time: ${timer.elapsedMicroseconds}ms""");
print(
"""$v listeners notified | [STREAM] time: ${timer.elapsedMicroseconds}ms""");
c.complete(timer.elapsedMicroseconds);
}
});
for (var i = 0; i < times; 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 (last == v) {
timer.stop();
print(
"""$v listeners notified | [GET_STREAM] time: ${timer.elapsedMicroseconds}ms""");
c.complete(timer.elapsedMicroseconds);
}
});
for (var i = 0; i < times; 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 (last == v) {
timer.stop();
print(
"""$v listeners notified | [MINI_STREAM] time: ${timer.elapsedMicroseconds}ms""");
c.complete(timer.elapsedMicroseconds);
}
});
... ... @@ -72,15 +120,43 @@ Future<String> getStream() {
}
void main() async {
test('run benchmarks', () async {
print(await getValue());
print(await valueNotifier());
print(await getStream());
test('run benchmarks from ValueNotifier', () async {
await getValue();
await valueNotifier();
times = 30000;
await getValue();
await valueNotifier();
});
test('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 Streams', () async {
var dart = await stream();
var getx = await getStream();
var mini = await miniStream();
print('''
GetStream is ${calculePercentage(dart, getx).round()}% more fast than Default Stream with $last listeners''');
times = 30000;
print(await getValue());
print(await valueNotifier());
print(await getStream());
dart = await stream();
getx = await getStream();
mini = await miniStream();
print('dart is $dart');
print('getx is $getx');
print('mini is $mini');
print('''
GetStream is ${calculePercentage(dart, getx).round()}% more fast than Default Stream with $last listeners''');
});
}
typedef VoidCallback = void Function();
int calculePercentage(int dart, int getx) {
return (dart / getx * 100).round() - 100;
}
... ...