Jonny Borges
Committed by GitHub

Merge pull request #2089 from jonataslaw/refactor-sm

remove StatefulWidgets, refactor GetBuilder from scratch, build a more composable api, prepare to getx 5
Showing 35 changed files with 534 additions and 286 deletions
No preview for this file type
import 'package:get/get.dart';
import '../domain/entity/cases_model.dart';
// ignore: one_member_abstracts
... ... @@ -12,6 +13,7 @@ class HomeProvider extends GetConnect implements IHomeProvider {
httpClient.defaultDecoder =
(val) => CasesModel.fromJson(val as Map<String, dynamic>);
httpClient.baseUrl = 'https://api.covid19api.com';
super.onInit();
}
@override
... ...
... ... @@ -19,10 +19,10 @@ class HomeController extends SuperController<CasesModel> {
Country getCountryById(String id) {
final index = int.tryParse(id);
if (index != null) {
return state!.countries[index];
return state.countries[index];
}
return state!.countries.first;
return state.countries.first;
}
@override
... ...
... ... @@ -28,9 +28,9 @@ class CountryView extends GetView<HomeController> {
),
body: Center(
child: ListView.builder(
itemCount: controller.state!.countries.length,
itemCount: controller.state.countries.length,
itemBuilder: (context, index) {
final country = controller.state!.countries[index];
final country = controller.state.countries[index];
return ListTile(
onTap: () {
//Get.rootDelegate.toNamed('/home/country');
... ...
import 'dart:io';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:get/get.dart';
... ... @@ -81,8 +82,8 @@ void main() {
}
if (controller.status.isSuccess) {
expect(controller.state!.global.totalDeaths, 100);
expect(controller.state!.global.totalConfirmed, 200);
expect(controller.state.global.totalDeaths, 100);
expect(controller.state.global.totalConfirmed, 200);
}
});
... ...
... ... @@ -100,9 +100,7 @@ class GetConnect extends GetConnectInterface {
this.maxAuthRetries = 1,
this.allowAutoSignedCert = false,
this.withCredentials = false,
}) {
$configureLifeCycle();
}
});
bool allowAutoSignedCert;
String userAgent;
... ...
... ... @@ -28,6 +28,7 @@ class GetHttpClient {
int maxAuthRetries;
bool sendUserAgent;
bool sendContentLength;
Decoder? defaultDecoder;
... ... @@ -47,6 +48,7 @@ class GetHttpClient {
this.followRedirects = true,
this.maxRedirects = 5,
this.sendUserAgent = false,
this.sendContentLength = true,
this.maxAuthRetries = 1,
bool allowAutoSignedCert = false,
this.baseUrl,
... ... @@ -111,7 +113,7 @@ class GetHttpClient {
if (body is FormData) {
bodyBytes = await body.toBytes();
headers['content-length'] = bodyBytes.length.toString();
_setContentLenght(headers, bodyBytes.length);
headers['content-type'] =
'multipart/form-data; boundary=${body.boundary}';
} else if (contentType != null &&
... ... @@ -124,21 +126,21 @@ class GetHttpClient {
});
var formData = parts.join('&');
bodyBytes = utf8.encode(formData);
headers['content-length'] = bodyBytes.length.toString();
_setContentLenght(headers, bodyBytes.length);
headers['content-type'] = contentType;
} else if (body is Map || body is List) {
var jsonString = json.encode(body);
bodyBytes = utf8.encode(jsonString);
headers['content-length'] = bodyBytes.length.toString();
_setContentLenght(headers, bodyBytes.length);
headers['content-type'] = contentType ?? defaultContentType;
} else if (body is String) {
bodyBytes = utf8.encode(body);
headers['content-length'] = bodyBytes.length.toString();
_setContentLenght(headers, bodyBytes.length);
headers['content-type'] = contentType ?? defaultContentType;
} else if (body == null) {
_setContentLenght(headers, 0);
headers['content-type'] = contentType ?? defaultContentType;
headers['content-length'] = '0';
} else {
if (!errorSafety) {
throw UnexpectedFormat('body cannot be ${body.runtimeType}');
... ... @@ -162,6 +164,12 @@ class GetHttpClient {
);
}
void _setContentLenght(Map<String, String> headers, int contentLength) {
if (sendContentLength) {
headers['content-length'] = '$contentLength';
}
}
Stream<List<int>> _trackProgress(
List<int> bodyBytes,
Progress? uploadProgress,
... ...
... ... @@ -110,11 +110,12 @@ class BaseWebSocket {
return true;
};
var request = await client.getUrl(Uri.parse(url));
request.headers.add('Connection', 'Upgrade');
request.headers.add('Upgrade', 'websocket');
request.headers.add('Sec-WebSocket-Version', '13');
request.headers.add('Sec-WebSocket-Key', key.toLowerCase());
var request = await client.getUrl(Uri.parse(url))
..headers.add('Connection', 'Upgrade')
..headers.add('Upgrade', 'websocket')
..headers.add('Cache-Control', 'no-cache')
..headers.add('Sec-WebSocket-Version', '13')
..headers.add('Sec-WebSocket-Key', key.toLowerCase());
var response = await request.close();
// ignore: close_sinks
... ...
... ... @@ -205,7 +205,8 @@ class GetInstance {
if (_singl[key]!.isSingleton!) {
_singl[key]!.isInit = true;
if (Get.smartManagement != SmartManagement.onlyBuilder) {
RouterReportManager.reportDependencyLinkedToRoute(_getKey(S, name));
RouterReportManager.instance
.reportDependencyLinkedToRoute(_getKey(S, name));
}
}
}
... ... @@ -257,7 +258,7 @@ class GetInstance {
Get.log('Instance "$S" with tag "$tag" has been initialized');
}
if (!_singl[key]!.isSingleton!) {
RouterReportManager.appendRouteByCreate(i);
RouterReportManager.instance.appendRouteByCreate(i);
}
}
return i;
... ... @@ -323,7 +324,7 @@ class GetInstance {
{@deprecated bool clearFactory = true, bool clearRouteBindings = true}) {
// if (clearFactory) _factory.clear();
// deleteAll(force: true);
if (clearRouteBindings) RouterReportManager.clearRouteKeys();
if (clearRouteBindings) RouterReportManager.instance.clearRouteKeys();
_singl.clear();
return true;
... ...
import '../../get_core/get_core.dart';
/// Special callable class to keep the contract of a regular method, and avoid
/// overrides if you extend the class that uses it, as Dart has no final
/// methods.
/// Used in `DisposableInterface` to avoid the danger of overriding onStart.
class InternalFinalCallback<T> {
ValueUpdater<T>? _callback;
InternalFinalCallback({ValueUpdater<T>? callback}) : _callback = callback;
T call() => _callback!.call();
}
import 'package:flutter/foundation.dart';
import 'package:flutter/scheduler.dart';
/// The [GetLifeCycle]
///
... ... @@ -22,24 +11,10 @@ class InternalFinalCallback<T> {
/// }
/// ```
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.
@protected
@mustCallSuper
void onInit() {}
/// Called 1 frame after onInit(). It is the perfect place to enter
... ... @@ -60,8 +35,14 @@ mixin GetLifeCycleBase {
/// Checks whether the controller has already been initialized.
bool get initialized => _initialized;
// Internal callback that starts the cycle of this controller.
void _onStart() {
/// 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.
// @protected
@mustCallSuper
void onStart() {
// _checkIfAlreadyConfigured();
if (_initialized) return;
onInit();
_initialized = true;
... ... @@ -72,31 +53,28 @@ mixin GetLifeCycleBase {
/// Checks whether the controller has already been closed.
bool get isClosed => _isClosed;
// Internal callback that starts the cycle of this controller.
void _onDelete() {
// Called when the controller is removed from memory.
@mustCallSuper
void onDelete() {
if (_isClosed) return;
_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.""";
}
}
// 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();
@override
void onInit() {
SchedulerBinding.instance?.addPostFrameCallback((_) => onReady());
super.onInit();
}
}
... ...
import 'package:flutter/material.dart';
import '../../../get.dart';
import '../router_report.dart';
... ... @@ -21,7 +22,7 @@ class GetModalBottomSheetRoute<T> extends PopupRoute<T> {
this.enterBottomSheetDuration = const Duration(milliseconds: 250),
this.exitBottomSheetDuration = const Duration(milliseconds: 200),
}) : super(settings: settings) {
RouterReportManager.reportCurrentRoute(this);
RouterReportManager.instance.reportCurrentRoute(this);
}
final bool? isPersistent;
final WidgetBuilder? builder;
... ... @@ -56,7 +57,7 @@ class GetModalBottomSheetRoute<T> extends PopupRoute<T> {
@override
void dispose() {
RouterReportManager.reportRouteDispose(this);
RouterReportManager.instance.reportRouteDispose(this);
super.dispose();
}
... ...
import 'package:flutter/widgets.dart';
import '../router_report.dart';
class GetDialogRoute<T> extends PopupRoute<T> {
... ... @@ -17,7 +18,7 @@ class GetDialogRoute<T> extends PopupRoute<T> {
_transitionDuration = transitionDuration,
_transitionBuilder = transitionBuilder,
super(settings: settings) {
RouterReportManager.reportCurrentRoute(this);
RouterReportManager.instance.reportCurrentRoute(this);
}
final RoutePageBuilder widget;
... ... @@ -28,7 +29,7 @@ class GetDialogRoute<T> extends PopupRoute<T> {
@override
void dispose() {
RouterReportManager.reportRouteDispose(this);
RouterReportManager.instance.reportRouteDispose(this);
super.dispose();
}
... ...
... ... @@ -511,6 +511,7 @@ extension GetNavigation on GetInterface {
Bindings? binding,
bool preventDuplicates = true,
bool? popGesture,
bool showCupertinoParallax = true,
double Function(BuildContext context)? gestureWidth,
}) {
// var routeName = "/${page.runtimeType}";
... ... @@ -525,6 +526,7 @@ extension GetNavigation on GetInterface {
page: _resolvePage(page, 'to'),
routeName: routeName,
gestureWidth: gestureWidth,
showCupertinoParallax: showCupertinoParallax,
settings: RouteSettings(
name: routeName,
arguments: arguments,
... ...
... ... @@ -7,28 +7,30 @@ import '../../get.dart';
class RouterReportManager<T> {
/// Holds a reference to `Get.reference` when the Instance was
/// created to manage the memory.
static final Map<Route?, List<String>> _routesKey = {};
final Map<T?, List<String>> _routesKey = {};
/// Stores the onClose() references of instances created with `Get.create()`
/// using the `Get.reference`.
/// Experimental feature to keep the lifecycle and memory management with
/// non-singleton instances.
static final Map<Route?, HashSet<Function>> _routesByCreate = {};
final Map<T?, HashSet<Function>> _routesByCreate = {};
static late final RouterReportManager instance = RouterReportManager();
void printInstanceStack() {
Get.log(_routesKey.toString());
}
static Route? _current;
T? _current;
// ignore: use_setters_to_change_properties
static void reportCurrentRoute(Route newRoute) {
void reportCurrentRoute(T newRoute) {
_current = newRoute;
}
/// Links a Class instance [S] (or [tag]) to the current route.
/// Requires usage of `GetMaterialApp`.
static void reportDependencyLinkedToRoute(String depedencyKey) {
void reportDependencyLinkedToRoute(String depedencyKey) {
if (_current == null) return;
if (_routesKey.containsKey(_current)) {
_routesKey[_current!]!.add(depedencyKey);
... ... @@ -37,18 +39,18 @@ class RouterReportManager<T> {
}
}
static void clearRouteKeys() {
void clearRouteKeys() {
_routesKey.clear();
_routesByCreate.clear();
}
static void appendRouteByCreate(GetLifeCycleBase i) {
void appendRouteByCreate(GetLifeCycleBase i) {
_routesByCreate[_current] ??= HashSet<Function>();
// _routesByCreate[Get.reference]!.add(i.onDelete as Function);
_routesByCreate[_current]!.add(i.onDelete);
}
static void reportRouteDispose(Route disposed) {
void reportRouteDispose(T disposed) {
if (Get.smartManagement != SmartManagement.onlyBuilder) {
WidgetsBinding.instance!.addPostFrameCallback((_) {
_removeDependencyByRoute(disposed);
... ... @@ -56,7 +58,7 @@ class RouterReportManager<T> {
}
}
static void reportRouteWillDispose(Route disposed) {
void reportRouteWillDispose(T disposed) {
final keysToRemove = <String>[];
_routesKey[disposed]?.forEach(keysToRemove.add);
... ... @@ -85,7 +87,7 @@ class RouterReportManager<T> {
/// using `Get.smartManagement` as [SmartManagement.full] or
/// [SmartManagement.keepFactory]
/// Meant for internal usage of `GetPageRoute` and `GetDialogRoute`
static void _removeDependencyByRoute(Route routeName) {
void _removeDependencyByRoute(T routeName) {
final keysToRemove = <String>[];
_routesKey[routeName]?.forEach(keysToRemove.add);
... ...
... ... @@ -8,18 +8,20 @@ mixin PageRouteReportMixin<T> on Route<T> {
@override
void install() {
super.install();
RouterReportManager.reportCurrentRoute(this);
RouterReportManager.instance.reportCurrentRoute(this);
}
@override
void dispose() {
super.dispose();
RouterReportManager.reportRouteDispose(this);
RouterReportManager.instance.reportRouteDispose(this);
}
}
class GetPageRoute<T> extends PageRoute<T>
with GetPageRouteTransitionMixin<T>, PageRouteReportMixin {
class GetPageRoute<T> extends PageRoute<T> //MaterialPageRoute<T>
with
GetPageRouteTransitionMixin<T>,
PageRouteReportMixin {
/// Creates a page route for use in an iOS designed app.
///
/// The [builder], [maintainState], and [fullscreenDialog] arguments must not
... ... @@ -47,7 +49,11 @@ class GetPageRoute<T> extends PageRoute<T>
this.maintainState = true,
bool fullscreenDialog = false,
this.middlewares,
}) : super(settings: settings, fullscreenDialog: fullscreenDialog);
}) : super(
settings: settings,
fullscreenDialog: fullscreenDialog,
// builder: (context) => Container(),
);
@override
final Duration transitionDuration;
... ...
... ... @@ -322,7 +322,6 @@ Cannot read the previousTitle for a route that has not yet been installed''',
bool canTransitionTo(TransitionRoute<dynamic> nextRoute) {
// Don't perform outgoing animation if the next route is a
// fullscreen dialog.
return (nextRoute is GetPageRouteTransitionMixin &&
!nextRoute.fullscreenDialog &&
nextRoute.showCupertinoParallax) ||
... ...
// import 'package:flutter/material.dart';
// import 'package:get/get_navigation/src/router_report.dart';
// import 'package:get/instance_manager.dart';
// class Dependencies {
// void lazyPut<S>(InstanceBuilderCallback<S> builder,
// {String? tag, bool fenix = false}) {
// GetInstance().lazyPut<S>(builder, tag: tag, fenix: fenix);
// }
// S call<S>() {
// return find<S>();
// }
// Future<S> putAsync<S>(AsyncInstanceBuilderCallback<S> builder,
// {String? tag, bool permanent = false}) async =>
// GetInstance().putAsync<S>(builder, tag: tag, permanent: permanent);
// void create<S>(InstanceBuilderCallback<S> builder,
// {String? tag, bool permanent = true}) =>
// GetInstance().create<S>(builder, tag: tag, permanent: permanent);
// S find<S>({String? tag}) => GetInstance().find<S>(tag: tag);
// S put<S>(S dependency,
// {String? tag,
// bool permanent = false,
// InstanceBuilderCallback<S>? builder}) =>
// GetInstance().put<S>(dependency, tag: tag, permanent: permanent);
// Future<bool> delete<S>({String? tag, bool force = false}) async =>
// GetInstance().delete<S>(tag: tag, force: force);
// Future<void> deleteAll({bool force = false}) async =>
// GetInstance().deleteAll(force: force);
// void reloadAll({bool force = false}) => GetInstance().reloadAll(force: force);
// void reload<S>({String? tag, String? key, bool force = false}) =>
// GetInstance().reload<S>(tag: tag, key: key, force: force);
// bool isRegistered<S>({String? tag}) =>
// GetInstance().isRegistered<S>(tag: tag);
// bool isPrepared<S>({String? tag}) => GetInstance().isPrepared<S>(tag: tag);
// void replace<P>(P child, {String? tag}) {
// final info = GetInstance().getInstanceInfo<P>(tag: tag);
// final permanent = (info.isPermanent ?? false);
// delete<P>(tag: tag, force: permanent);
// put(child, tag: tag, permanent: permanent);
// }
// void lazyReplace<P>(InstanceBuilderCallback<P> builder,
// {String? tag, bool? fenix}) {
// final info = GetInstance().getInstanceInfo<P>(tag: tag);
// final permanent = (info.isPermanent ?? false);
// delete<P>(tag: tag, force: permanent);
// lazyPut(builder, tag: tag, fenix: fenix ?? permanent);
// }
// }
// abstract class Module extends StatefulWidget {
// Module({Key? key}) : super(key: key);
// Widget view(BuildContext context);
// void dependencies(Dependencies i);
// @override
// _ModuleState createState() => _ModuleState();
// }
// class _ModuleState extends State<Module> {
// @override
// void initState() {
// RouterReportManager.instance.reportCurrentRoute(this);
// widget.dependencies(Dependencies());
// super.initState();
// }
// @override
// void dispose() {
// RouterReportManager.instance.reportRouteDispose(this);
// super.dispose();
// }
// @override
// Widget build(BuildContext context) {
// return widget.view(context);
// }
// }
... ...
... ... @@ -52,7 +52,7 @@ class GetObserver extends NavigatorObserver {
Get.log("CLOSE TO ROUTE ${currentRoute.name}");
}
if (previousRoute != null) {
RouterReportManager.reportCurrentRoute(previousRoute);
RouterReportManager.instance.reportCurrentRoute(previousRoute);
}
// Here we use a 'inverse didPush set', meaning that we use
... ... @@ -97,7 +97,7 @@ class GetObserver extends NavigatorObserver {
Get.log("GOING TO ROUTE ${newRoute.name}");
}
RouterReportManager.reportCurrentRoute(route);
RouterReportManager.instance.reportCurrentRoute(route);
_routeSend?.update((value) {
// Only PageRoute is allowed to change current value
if (route is PageRoute) {
... ... @@ -142,7 +142,7 @@ class GetObserver extends NavigatorObserver {
});
if (route is GetPageRoute) {
RouterReportManager.reportRouteWillDispose(route);
RouterReportManager.instance.reportRouteWillDispose(route);
}
routing?.call(_routeSend);
}
... ... @@ -158,7 +158,7 @@ class GetObserver extends NavigatorObserver {
Get.log("NEW ROUTE $newName");
if (newRoute != null) {
RouterReportManager.reportCurrentRoute(newRoute);
RouterReportManager.instance.reportCurrentRoute(newRoute);
}
_routeSend?.update((value) {
... ... @@ -178,7 +178,7 @@ class GetObserver extends NavigatorObserver {
value.isDialog = currentRoute.isDialog ? false : value.isDialog;
});
if (oldRoute is GetPageRoute) {
RouterReportManager.reportRouteWillDispose(oldRoute);
RouterReportManager.instance.reportRouteWillDispose(oldRoute);
}
routing?.call(_routeSend);
... ...
... ... @@ -87,11 +87,19 @@ class SnackbarController {
}
}
bool _isTesting = false;
void _configureOverlay() {
_overlayState = Overlay.of(Get.overlayContext!);
final overlayContext = Get.overlayContext;
_isTesting = overlayContext == null;
_overlayState =
_isTesting ? OverlayState() : Overlay.of(Get.overlayContext!);
_overlayEntries.clear();
_overlayEntries.addAll(_createOverlayEntries(_getBodyWidget()));
_overlayState!.insertAll(_overlayEntries);
if (!_isTesting) {
_overlayState!.insertAll(_overlayEntries);
}
_configureSnackBarDisplay();
}
... ... @@ -316,8 +324,10 @@ class SnackbarController {
}
void _removeOverlay() {
for (var element in _overlayEntries) {
element.remove();
if (!_isTesting) {
for (var element in _overlayEntries) {
element.remove();
}
}
assert(!_transitionCompleter.isCompleted,
... ...
... ... @@ -14,6 +14,22 @@ class GetStream<T> {
FutureOr<void> Function()? onCancel;
GetStream({this.onListen, this.onPause, this.onResume, this.onCancel});
factory GetStream.fromValue(T value,
{Function()? onListen,
Function()? onPause,
Function()? onResume,
FutureOr<void> Function()? onCancel}) {
final valuedStream = GetStream<T>(
onListen: onListen,
onPause: onPause,
onResume: onResume,
onCancel: onCancel)
.._value = value;
return valuedStream;
}
List<LightSubscription<T>>? _onData = <LightSubscription<T>>[];
bool? _isBusy = false;
... ... @@ -87,9 +103,12 @@ class GetStream<T> {
_isBusy = false;
}
T? _value;
late T _value;
T? get value => _value;
T get value {
RxInterface.proxy?.addListener(this);
return _value;
}
void add(T event) {
assert(!isClosed, 'You cannot add event to closed Stream');
... ... @@ -109,7 +128,7 @@ class GetStream<T> {
_notifyDone();
_onData = null;
_isBusy = null;
_value = null;
// _value = null;
}
LightSubscription<T> listen(void Function(T event) onData,
... ...
... ... @@ -2,7 +2,10 @@ library rx_stream;
import 'dart:async';
import 'package:get/get_state_manager/src/simple/list_notifier.dart';
import '../rx_typedefs/rx_typedefs.dart';
import '../rx_types/rx_types.dart';
part 'get_stream.dart';
part 'mini_stream.dart';
... ...
... ... @@ -5,7 +5,7 @@ part of rx_types;
/// of those `Widgets` and Rx values.
mixin RxObjectMixin<T> on NotifyManager<T> {
late T _value;
//late 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.
... ... @@ -91,24 +91,25 @@ mixin RxObjectMixin<T> on NotifyManager<T> {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
int get hashCode => _value.hashCode;
int get hashCode => value.hashCode;
/// 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) {
if (subject.isClosed) return;
sentToStream = false;
if (_value == val && !firstRebuild) return;
if (value == val && !firstRebuild) return;
firstRebuild = false;
_value = val;
// _value = val;
sentToStream = true;
subject.add(_value);
subject.add(val);
}
/// Returns the current [value]
T get value {
RxInterface.proxy?.addListener(subject);
return _value;
return subject.value;
//RxInterface.proxy?.addListener(subject);
// return _value;
}
Stream<T> get stream => subject.stream;
... ... @@ -192,7 +193,7 @@ mixin NotifyManager<T> {
/// Base Rx class that manages all the stream logic for any Type.
abstract class _RxImpl<T> extends RxNotifier<T> with RxObjectMixin<T> {
_RxImpl(T initial) {
_value = initial;
subject = GetStream.fromValue(initial);
}
void addError(Object error, [StackTrace? stackTrace]) {
... ... @@ -222,8 +223,8 @@ abstract class _RxImpl<T> extends RxNotifier<T> with RxObjectMixin<T> {
/// print( person );
/// ```
void update(void fn(T? val)) {
fn(_value);
subject.add(_value);
fn(value);
subject.add(value);
}
/// Following certain practices on Rx data, we might want to react to certain
... ... @@ -295,7 +296,7 @@ extension RxBoolExt on Rx<bool> {
/// not really a dart thing since we have '..' operator
// ignore: avoid_returning_this
Rx<bool> toggle() {
subject.add(_value = !_value);
subject.add(!value);
return this;
}
}
... ... @@ -327,8 +328,8 @@ extension RxnBoolExt on Rx<bool?> {
/// not really a dart thing since we have '..' operator
// ignore: avoid_returning_this
Rx<bool?>? toggle() {
if (_value != null) {
subject.add(_value = !_value!);
if (value != null) {
subject.add(!value!);
return this;
}
}
... ...
part of rx_types;
extension RxStringExt on Rx<String> {
String operator +(String val) => _value + val;
String operator +(String val) => value + val;
int compareTo(String other) {
return value.compareTo(other);
... ... @@ -125,7 +125,7 @@ extension RxStringExt on Rx<String> {
}
extension RxnStringExt on Rx<String?> {
String operator +(String val) => (_value ?? '') + val;
String operator +(String val) => (value ?? '') + val;
int? compareTo(String other) {
return value?.compareTo(other);
... ...
... ... @@ -5,7 +5,7 @@ class RxList<E> extends ListMixin<E>
with NotifyManager<List<E>>, RxObjectMixin<List<E>>
implements RxInterface<List<E>> {
RxList([List<E> initial = const []]) {
_value = List.from(initial);
subject = GetStream.fromValue(List.from(initial));
}
factory RxList.filled(int length, E fill, {bool growable = false}) {
... ... @@ -42,7 +42,7 @@ class RxList<E> extends ListMixin<E>
@override
void operator []=(int index, E val) {
_value[index] = val;
value[index] = val;
refresh();
}
... ... @@ -62,25 +62,25 @@ class RxList<E> extends ListMixin<E>
@override
void add(E item) {
_value.add(item);
value.add(item);
refresh();
}
@override
void addAll(Iterable<E> item) {
_value.addAll(item);
value.addAll(item);
refresh();
}
@override
void removeWhere(bool test(E element)) {
_value.removeWhere(test);
value.removeWhere(test);
refresh();
}
@override
void retainWhere(bool test(E element)) {
_value.retainWhere(test);
value.retainWhere(test);
refresh();
}
... ... @@ -91,18 +91,18 @@ class RxList<E> extends ListMixin<E>
@protected
List<E> get value {
RxInterface.proxy?.addListener(subject);
return _value;
return subject.value;
}
@override
set length(int newLength) {
_value.length = newLength;
value.length = newLength;
refresh();
}
@override
void insertAll(int index, Iterable<E> iterable) {
_value.insertAll(index, iterable);
value.insertAll(index, iterable);
refresh();
}
... ... @@ -121,7 +121,7 @@ class RxList<E> extends ListMixin<E>
@override
void sort([int compare(E a, E b)?]) {
_value.sort(compare);
value.sort(compare);
refresh();
}
}
... ...
... ... @@ -4,7 +4,7 @@ class RxMap<K, V> extends MapMixin<K, V>
with NotifyManager<Map<K, V>>, RxObjectMixin<Map<K, V>>
implements RxInterface<Map<K, V>> {
RxMap([Map<K, V> initial = const {}]) {
_value = Map.from(initial);
subject = GetStream.fromValue(Map.from(initial));
}
factory RxMap.from(Map<K, V> other) {
... ... @@ -32,14 +32,14 @@ class RxMap<K, V> extends MapMixin<K, V>
}
@override
void operator []=(K key, V value) {
_value[key] = value;
void operator []=(K key, V val) {
value[key] = val;
refresh();
}
@override
void clear() {
_value.clear();
value.clear();
refresh();
}
... ... @@ -48,7 +48,7 @@ class RxMap<K, V> extends MapMixin<K, V>
@override
V? remove(Object? key) {
final val = _value.remove(key);
final val = value.remove(key);
refresh();
return val;
}
... ... @@ -56,8 +56,9 @@ class RxMap<K, V> extends MapMixin<K, V>
@override
@protected
Map<K, V> get value {
RxInterface.proxy?.addListener(subject);
return _value;
return subject.value;
// RxInterface.proxy?.addListener(subject);
// return _value;
}
}
... ... @@ -82,7 +83,7 @@ extension MapExtension<K, V> on Map<K, V> {
if (this is RxMap) {
final map = (this as RxMap);
// map._value;
map._value.clear();
map.value.clear();
this[key] = val;
} else {
clear();
... ... @@ -92,12 +93,12 @@ extension MapExtension<K, V> on Map<K, V> {
void assignAll(Map<K, V> val) {
if (val is RxMap && this is RxMap) {
if ((val as RxMap)._value == (this as RxMap)._value) return;
if ((val as RxMap).value == (this as RxMap).value) return;
}
if (this is RxMap) {
final map = (this as RxMap);
if (map._value == val) return;
map._value = val;
if (map.value == val) return;
map.value = val;
map.refresh();
} else {
if (this == val) return;
... ...
... ... @@ -4,7 +4,7 @@ class RxSet<E> extends SetMixin<E>
with NotifyManager<Set<E>>, RxObjectMixin<Set<E>>
implements RxInterface<Set<E>> {
RxSet([Set<E> initial = const {}]) {
_value = Set.from(initial);
subject = GetStream.fromValue(Set.from(initial));
}
/// Special override to push() element(s) in a reactive way
... ... @@ -23,21 +23,22 @@ class RxSet<E> extends SetMixin<E>
@override
@protected
Set<E> get value {
RxInterface.proxy?.addListener(subject);
return _value;
return subject.value;
// RxInterface.proxy?.addListener(subject);
// return _value;
}
@override
@protected
set value(Set<E> val) {
if (_value == val) return;
_value = val;
if (value == val) return;
value = val;
refresh();
}
@override
bool add(E value) {
final val = _value.add(value);
bool add(E newValue) {
final val = value.add(newValue);
refresh();
return val;
}
... ... @@ -60,7 +61,7 @@ class RxSet<E> extends SetMixin<E>
@override
bool remove(Object? item) {
var hasRemoved = _value.remove(item);
var hasRemoved = value.remove(item);
if (hasRemoved) {
refresh();
}
... ... @@ -74,31 +75,31 @@ class RxSet<E> extends SetMixin<E>
@override
void addAll(Iterable<E> item) {
_value.addAll(item);
value.addAll(item);
refresh();
}
@override
void clear() {
_value.clear();
value.clear();
refresh();
}
@override
void removeAll(Iterable<Object?> elements) {
_value.removeAll(elements);
value.removeAll(elements);
refresh();
}
@override
void retainAll(Iterable<Object?> elements) {
_value.retainAll(elements);
value.retainAll(elements);
refresh();
}
@override
void retainWhere(bool Function(E) E) {
_value.retainWhere(E);
value.retainWhere(E);
refresh();
}
}
... ...
... ... @@ -4,6 +4,7 @@ import 'dart:async';
import 'dart:collection';
import 'package:flutter/foundation.dart';
import 'package:get/get_state_manager/src/simple/list_notifier.dart';
import '../rx_stream/rx_stream.dart';
import '../rx_typedefs/rx_typedefs.dart';
... ...
... ... @@ -14,9 +14,6 @@ typedef GetXControllerBuilder<T extends DisposableInterface> = Widget Function(
class GetX<T extends DisposableInterface> extends StatefulWidget {
final GetXControllerBuilder<T> builder;
final bool global;
// final Stream Function(T) stream;
// final StreamController Function(T) streamController;
final bool autoRemove;
final bool assignId;
final void Function(GetXState<T> state)? initState,
... ...
... ... @@ -7,7 +7,7 @@ import '../../get_state_manager.dart';
import '../simple/list_notifier.dart';
mixin StateMixin<T> on ListNotifierMixin {
T? _value;
late T _value;
RxStatus? _status;
bool _isNullOrEmpty(dynamic val) {
... ... @@ -32,23 +32,23 @@ mixin StateMixin<T> on ListNotifierMixin {
return _status ??= _status = RxStatus.loading();
}
T? get state => value;
T get state => value;
@protected
T? get value {
T get value {
notifyChildrens();
return _value;
}
@protected
set value(T? newValue) {
set value(T newValue) {
if (_value == newValue) return;
_value = newValue;
refresh();
}
@protected
void change(T? newState, {RxStatus? status}) {
void change(T newState, {RxStatus? status}) {
var _canUpdate = false;
if (status != null) {
_status = status;
... ... @@ -82,13 +82,13 @@ class Value<T> extends ListNotifier
}
@override
T? get value {
T get value {
notifyChildrens();
return _value;
}
@override
set value(T? newValue) {
set value(T newValue) {
if (_value == newValue) return;
_value = newValue;
refresh();
... ... @@ -119,9 +119,7 @@ extension ReactiveT<T> on T {
typedef Condition = bool Function();
abstract class GetNotifier<T> extends Value<T> with GetLifeCycleBase {
GetNotifier(T initial) : super(initial) {
$configureLifeCycle();
}
GetNotifier(T initial) : super(initial);
@override
@mustCallSuper
... ... @@ -194,3 +192,44 @@ class RxStatus {
}
typedef NotifierBuilder<T> = Widget Function(T state);
abstract class GState<T> {
const GState();
factory GState.loading() => GLoading();
factory GState.error(String message) => GError(message);
factory GState.empty() => GEmpty();
factory GState.success(T data) => GSuccess(data);
}
class GLoading<T> extends GState<T> {}
class GSuccess<T> extends GState<T> {
final T data;
GSuccess(this.data);
}
class GError<T, S> extends GState<T> {
final S? error;
GError([this.error]);
}
class GEmpty<T> extends GState<T> {}
extension StatusDataExt<T> on GState<T> {
bool get isLoading => this is GLoading;
bool get isSuccess => this is GSuccess;
bool get isError => this is GError;
bool get isEmpty => this is GEmpty;
String get errorMessage {
final isError = this is GError;
if (isError) {
final err = this as GError;
if (err.error != null && err.error is String) {
return err.error as String;
}
}
return '';
}
}
... ...
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import '../../../get_rx/src/rx_types/rx_types.dart';
typedef WidgetCallback = Widget Function();
... ... @@ -54,6 +56,15 @@ class _ObxState extends State<ObxWidget> {
RxInterface.notifyChildren(_observer, widget.build);
}
/// The simplest reactive widget in GetX.
///
/// Just pass your Rx variable in the root scope of the callback to have it
... ...
import 'package:flutter/material.dart';
import '../../../get_instance/src/get_instance.dart';
import '../../../instance_manager.dart';
import '../../get_state_manager.dart';
import 'list_notifier.dart';
/// Complies with `GetStateUpdater`
///
/// This mixin's function represents a `GetStateUpdater`, and might be used
/// by `GetBuilder()`, `SimpleBuilder()` (or similar) to comply
/// with [GetStateUpdate] signature. REPLACING the [StateSetter].
/// Avoids the potential (but extremely unlikely) issue of having
/// the Widget in a dispose() state, and abstracts the
/// API from the ugly fn((){}).
mixin GetStateUpdaterMixin<T extends StatefulWidget> on State<T> {
// To avoid the creation of an anonym function to be GC later.
// ignore: prefer_function_declarations_over_variables
/// Experimental method to replace setState((){});
/// Used with GetStateUpdate.
void getUpdate() {
if (mounted) setState(() {});
}
}
typedef GetControllerBuilder<T extends DisposableInterface> = Widget Function(
T controller);
// class _InheritedGetxController<T extends GetxController>
// extends InheritedWidget {
// final T model;
// final int version;
// _InheritedGetxController({
// Key key,
// @required Widget child,
// @required this.model,
// }) : version = model.notifierVersion,
// super(key: key, child: child);
// @override
// bool updateShouldNotify(_InheritedGetxController<T> oldWidget) =>
// (oldWidget.version != version);
// }
extension WatchExt on BuildContext {
T listen<T extends GetxController>() {
return Scope.of(this, rebuild: true);
}
}
extension ReadExt on BuildContext {
T find<T extends GetxController>() {
return Scope.of(this);
}
}
// extension WatchEtx on GetxController {
// T watch<T extends GetxController>() {
// final instance = Get.find<T>();
// _GetBuilderState._currentState.watch(instance.update);
// return instance;
// extension FilterExt on BuildContext {
// T filter<T extends GetxController>(Object Function(T value)? filter) {
// return Scope.of(this, filter: filter, rebuild: true);
// }
// }
class GetBuilder<T extends GetxController> extends StatefulWidget {
class GetBuilder<T extends GetxController> extends StatelessWidget {
final GetControllerBuilder<T> builder;
final bool global;
final Object? id;
... ... @@ -59,10 +33,10 @@ class GetBuilder<T extends GetxController> extends StatefulWidget {
final bool autoRemove;
final bool assignId;
final Object Function(T value)? filter;
final void Function(GetBuilderState<T> state)? initState,
final void Function(ScopeElement<T> state)? initState,
dispose,
didChangeDependencies;
final void Function(GetBuilder oldWidget, GetBuilderState<T> state)?
final void Function(Scope<T> oldWidget, ScopeElement<T> state)?
didUpdateWidget;
final T? init;
... ... @@ -82,40 +56,127 @@ class GetBuilder<T extends GetxController> extends StatefulWidget {
this.didUpdateWidget,
}) : super(key: key);
// static T of<T extends GetxController>(
// BuildContext context, {
// bool rebuild = false,
// }) {
// var widget = rebuild
// ? context
// .dependOnInheritedWidgetOfExactType<_InheritedGetxController<T>>()
// : context
// .getElementForInheritedWidgetOfExactType<
// _InheritedGetxController<T>>()
// ?.widget;
// if (widget == null) {
// throw 'Error: Could not find the correct dependency.';
// } else {
// return (widget as _InheritedGetxController<T>).model;
// }
// }
@override
Widget build(BuildContext context) {
return Scope<T>(
init: init,
global: global,
autoRemove: autoRemove,
assignId: assignId,
initState: initState,
filter: filter,
tag: tag,
dispose: dispose,
id: id,
didChangeDependencies: didChangeDependencies,
didUpdateWidget: didUpdateWidget,
child: Builder(builder: (context) {
final controller = Scope.of<T>(context, rebuild: true);
return builder(controller);
}),
);
// return widget.builder(controller!);
}
}
class Scope<T extends GetxController> extends InheritedWidget {
/// Create an inherited widget that updates its dependents when [controller]
/// sends notifications.
///
/// The [child] argument is required
const Scope({
Key? key,
required Widget child,
this.init,
this.global = true,
this.autoRemove = true,
this.assignId = false,
this.initState,
this.filter,
this.tag,
this.dispose,
this.id,
this.didChangeDependencies,
this.didUpdateWidget,
}) : super(key: key, child: child);
/// The [Listenable] object to which to listen.
///
/// Whenever this object sends change notifications, the dependents of this
/// widget are triggered.
///
/// By default, whenever the [controller] is changed (including when changing to
/// or from null), if the old controller is not equal to the new controller (as
/// determined by the `==` operator), notifications are sent. This behavior
/// can be overridden by overriding [updateShouldNotify].
///
/// While the [controller] is null, no notifications are sent, since the null
/// object cannot itself send notifications.
final T? init;
final bool global;
final Object? id;
final String? tag;
final bool autoRemove;
final bool assignId;
final Object Function(T value)? filter;
final void Function(ScopeElement<T> state)? initState,
dispose,
didChangeDependencies;
final void Function(Scope<T> oldWidget, ScopeElement<T> state)?
didUpdateWidget;
static T of<T extends GetxController>(
BuildContext context, {
bool rebuild = false,
// Object Function(T value)? filter,
}) {
final inheritedElement = context
.getElementForInheritedWidgetOfExactType<Scope<T>>() as ScopeElement<T>;
if (rebuild) {
// var newFilter = filter?.call(inheritedElement.controller!);
// if (newFilter != null) {
// context.dependOnInheritedElement(inheritedElement, aspect: newFilter);
// } else {
context.dependOnInheritedElement(inheritedElement);
// }
}
var widget = inheritedElement.controller;
if (widget == null) {
throw 'Error: Could not find the correct dependency.';
} else {
return widget;
}
}
@override
GetBuilderState<T> createState() => GetBuilderState<T>();
bool updateShouldNotify(Scope<T> oldWidget) {
return oldWidget.id != id ||
oldWidget.global != global ||
oldWidget.autoRemove != autoRemove ||
oldWidget.assignId != assignId;
}
@override
InheritedElement createElement() => ScopeElement<T>(this);
}
class GetBuilderState<T extends GetxController> extends State<GetBuilder<T>>
with GetStateUpdaterMixin {
/// The ScopeElement is responsible for injecting dependencies into the widget
/// tree so that they can be observed
class ScopeElement<T extends GetxController> extends InheritedElement {
ScopeElement(Scope<T> widget) : super(widget) {
initState();
}
T? controller;
bool? _isCreator = false;
VoidCallback? _remove;
Object? _filter;
@override
void initState() {
// _GetBuilderState._currentState = this;
super.initState();
widget.initState?.call(this);
var isRegistered = GetInstance().isRegistered<T>(tag: widget.tag);
... ... @@ -169,9 +230,7 @@ class GetBuilderState<T extends GetxController> extends State<GetBuilder<T>>
}
}
@override
void dispose() {
super.dispose();
widget.dispose?.call(this);
if (_isCreator! || widget.assignId) {
if (widget.autoRemove && GetInstance().isRegistered<T>(tag: widget.tag)) {
... ... @@ -188,39 +247,49 @@ class GetBuilderState<T extends GetxController> extends State<GetBuilder<T>>
}
@override
Scope<T> get widget => super.widget as Scope<T>;
var _dirty = false;
@override
void update(Scope<T> newWidget) {
final oldNotifier = widget.id;
final newNotifier = newWidget.id;
if (oldNotifier != newNotifier) {
_subscribeToController();
}
widget.didUpdateWidget?.call(widget, this);
super.update(newWidget);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
widget.didChangeDependencies?.call(this);
}
@override
void didUpdateWidget(GetBuilder oldWidget) {
super.didUpdateWidget(oldWidget as GetBuilder<T>);
// to avoid conflicts when modifying a "grouped" id list.
if (oldWidget.id != widget.id) {
_subscribeToController();
Widget build() {
if (_dirty) {
notifyClients(widget);
}
widget.didUpdateWidget?.call(oldWidget, this);
return super.build();
}
@override
Widget build(BuildContext context) {
// return _InheritedGetxController<T>(
// model: controller,
// child: widget.builder(controller),
// );
return widget.builder(controller!);
void getUpdate() {
_dirty = true;
markNeedsBuild();
}
}
// extension FindExt on BuildContext {
// T find<T extends GetxController>() {
// return GetBuilder.of<T>(this, rebuild: false);
// }
// }
@override
void notifyClients(Scope<T> oldWidget) {
super.notifyClients(oldWidget);
_dirty = false;
}
// extension ObserverEtx on BuildContext {
// T obs<T extends GetxController>() {
// return GetBuilder.of<T>(this, rebuild: true);
// }
// }
@override
void unmount() {
dispose();
super.unmount();
}
}
... ...
... ... @@ -159,15 +159,11 @@ class TaskManager {
}
}
Widget exchange(
List<VoidCallback> disposers,
GetStateUpdate setState,
Widget Function(BuildContext) builder,
BuildContext context,
) {
T exchange<T>(List<VoidCallback> disposers, GetStateUpdate setState,
T Function() builder) {
_remove = disposers;
_setter = setState;
final result = builder(context);
final result = builder();
_remove = null;
_setter = null;
return result;
... ...
... ... @@ -8,8 +8,11 @@ class MixinBuilder<T extends GetxController> extends StatelessWidget {
final bool global;
final String? id;
final bool autoRemove;
final void Function(State state)? initState, dispose, didChangeDependencies;
final void Function(GetBuilder oldWidget, State state)? didUpdateWidget;
final void Function(ScopeElement<T> state)? initState,
dispose,
didChangeDependencies;
final void Function(Scope<T> oldWidget, ScopeElement<T> state)?
didUpdateWidget;
final T? init;
const MixinBuilder({
... ...
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'get_state.dart';
import 'list_notifier.dart';
typedef ValueBuilderUpdateCallback<T> = void Function(T snapshot);
... ... @@ -74,35 +75,39 @@ class _ValueBuilderState<T> extends State<ValueBuilder<T?>> {
}
}
class ObxElement = StatelessElement with ObserverComponent;
// It's a experimental feature
class SimpleBuilder extends StatefulWidget {
final Widget Function(BuildContext) builder;
class SimpleBuilder extends ObxStatelessWidget {
final WidgetBuilder builder;
const SimpleBuilder({Key? key, required this.builder}) : super(key: key);
@override
_SimpleBuilderState createState() => _SimpleBuilderState();
Widget build(BuildContext context) => builder(context);
}
class _SimpleBuilderState extends State<SimpleBuilder>
with GetStateUpdaterMixin {
/// A StatelessWidget than can listen reactive changes.
abstract class ObxStatelessWidget extends StatelessWidget {
/// Initializes [key] for subclasses.
const ObxStatelessWidget({Key? key}) : super(key: key);
@override
StatelessElement createElement() => ObxElement(this);
}
/// a Component that can track changes in a reactive variable
mixin ObserverComponent on ComponentElement {
final disposers = <Disposer>[];
@override
void dispose() {
super.dispose();
Widget build() =>
TaskManager.instance.exchange(disposers, markNeedsBuild, super.build);
@override
void unmount() {
super.unmount();
for (final disposer in disposers) {
disposer();
}
}
@override
Widget build(BuildContext context) {
return TaskManager.instance.exchange(
disposers,
getUpdate,
widget.builder,
context,
);
}
}
... ...
No preview for this file type