Jonny Borges
Committed by GitHub

Merge pull request #523 from roipeker/master

General cleanup, more docs.
... ... @@ -3,11 +3,33 @@ import 'package:get/src/core/get_interface.dart';
import 'get_instance.dart';
extension Inst on GetInterface {
/// Creates a new Instance<S> lazily from the [<S>builder()] callback.
///
/// The first time you call [Get.find()], the [builder()] callback will create
/// the Instance and persisted as a Singleton (like you would use [Get.put()]).
///
/// Using [GetConfig.smartManagement] as [SmartManagement.keepFactory] has the same outcome
/// as using [fenix:true] :
/// The internal register of [builder()] will remain in memory to recreate the Instance
/// if the Instance has been removed with [Get.delete()].
/// Therefore, future calls to [Get.find()] will return the same Instance.
///
/// If you need to make use of GetxController's life-cycle ([onInit(), onStart(), onClose()])
/// [fenix] is a great choice to mix with [GetBuilder()] and [GetX()] widgets, and/or [GetMaterialApp] Navigation.
///
/// You could use [Get.lazyPut(fenix:true)] in your app's [main()] instead of [Bindings()] for each [GetPage].
/// And the memory management will be similar.
///
/// Subsequent calls to [Get.lazyPut()] with the same parameters (<[S]> and optionally [tag]
/// will **not** override the original).
void lazyPut<S>(InstanceBuilderCallback<S> builder,
{String tag, bool fenix = false}) {
return GetInstance().lazyPut<S>(builder, tag: tag, fenix: fenix);
GetInstance().lazyPut<S>(builder, tag: tag, fenix: fenix);
}
/// async version of [Get.put()].
/// Awaits for the resolution of the Future from [builder()] parameter and
/// stores the Instance returned.
Future<S> putAsync<S>(AsyncInstanceBuilderCallback<S> builder,
{String tag, bool permanent = false}) async =>
GetInstance().putAsync<S>(builder, tag: tag, permanent: permanent);
... ... @@ -22,26 +44,26 @@ extension Inst on GetInterface {
/// Repl a = find();
/// Repl b = find();
/// print(a==b); (false)```
///
void create<S>(InstanceBuilderCallback<S> builder,
{String name, bool permanent = true}) =>
GetInstance().create<S>(builder, name: name, permanent: permanent);
/// Finds a instance of the required Class<[S]> (or [tag])
/// In the case of using Get.[create], it will create an instance
/// each time you call [find]
///
/// Finds a Instance of the required Class <[S]>(or [tag])
/// In the case of using [Get.create()], it will generate an Instance
/// each time you call [Get.find()].
S find<S>({String tag}) => GetInstance().find<S>(tag: tag);
/// Injects a Instance [S] in [GetInstance].
/// Injects an [Instance<S>] in memory.
///
/// No need to define the generic type <[S]> as it's inferred from the [dependency]
/// parameter.
///
/// - [dependency] The Instance to be injected.
/// - [tag] optionally, use a [tag] as an "id" to create multiple records of the same Type<[S]>
/// - [permanent] keeps the Instance in memory, not following [GetConfig.smartManagement]
/// rules
///
/// the [tag] does **not** conflict with the same tags used by other [dependencies] Types.
/// - [permanent] keeps the Instance in memory and persist it, not following [GetConfig.smartManagement]
/// rules. Although, can be removed by [GetInstance.reset()] and [Get.delete()]
/// - [builder] If defined, the [dependency] must be returned from here
S put<S>(S dependency,
{String tag,
bool permanent = false,
... ... @@ -49,14 +71,25 @@ extension Inst on GetInterface {
GetInstance()
.put<S>(dependency, tag: tag, permanent: permanent, builder: builder);
/// Clears all registered instances (and/or tags).
/// Even the persistent ones.
///
/// - [clearFactory] clears the callbacks registered by [Get.lazyPut()]
/// - [clearRouteBindings] clears Instances associated with Routes when using
/// [GetMaterialApp].
bool reset({bool clearFactory = true, bool clearRouteBindings = true}) =>
GetInstance().reset(
clearFactory: clearFactory, clearRouteBindings: clearRouteBindings);
/// Delete class instance on [S] and clean memory
Future<bool> delete<S>({String tag, String key}) async =>
GetInstance().delete<S>(tag: tag, key: key);
/// Deletes the Instance<[S]>, cleaning the memory and closes any open
/// controllers ([DisposableInterface]).
///
/// - [tag] Optional "tag" used to register the Instance
Future<bool> delete<S>({String tag}) async =>
GetInstance().delete<S>(tag: tag);
/// Check if a Class Instance<[S]> (or [tag]) is registered in memory.
/// - [tag] optional, if you use a [tag] to register the Instance.
bool isRegistered<S>({String tag}) => GetInstance().isRegistered<S>(tag: tag);
bool isPrepared<S>({String tag}) => GetInstance().isPrepared<S>(tag: tag);
... ...
... ... @@ -12,27 +12,53 @@ class GetConfig {
class GetInstance {
factory GetInstance() => _getInstance ??= GetInstance._();
const GetInstance._();
static GetInstance _getInstance;
/// Holds references to every registered Instance when using
/// Get.[put]
static Map<String, _FcBuilder> _singl = {};
/// [Get.put()]
static Map<String, _InstanceBuilderFactory> _singl = {};
/// Holds a reference to every registered callback when using
/// Get.[lazyPut]
/// [Get.lazyPut()]
static Map<String, _Lazy> _factory = {};
/// Holds a reference to [GetConfig.currentRoute] when the Instance was
/// created to manage the memory.
static Map<String, String> _routesKey = {};
static GetQueue _queue = GetQueue();
/// Creates a new Instance<S> lazily from the [<S>builder()] callback.
///
/// The first time you call [Get.find()], the [builder()] callback will create
/// the Instance and persisted as a Singleton (like you would use [Get.put()]).
///
/// Using [GetConfig.smartManagement] as [SmartManagement.keepFactory] has the same outcome
/// as using [fenix:true] :
/// The internal register of [builder()] will remain in memory to recreate the Instance
/// if the Instance has been removed with [Get.delete()].
/// Therefore, future calls to [Get.find()] will return the same Instance.
///
/// If you need to make use of GetxController's life-cycle ([onInit(), onStart(), onClose()])
/// [fenix] is a great choice to mix with [GetBuilder()] and [GetX()] widgets, and/or [GetMaterialApp] Navigation.
///
/// You could use [Get.lazyPut(fenix:true)] in your app's [main()] instead of [Bindings()] for each [GetPage].
/// And the memory management will be similar.
///
/// Subsequent calls to [Get.lazyPut()] with the same parameters (<[S]> and optionally [tag]
/// will **not** override the original).
void lazyPut<S>(InstanceBuilderCallback<S> builder,
{String tag, bool fenix = false}) {
String key = _getKey(S, tag);
_factory.putIfAbsent(key, () => _Lazy(builder, fenix));
}
/// async version of [Get.put()].
/// Awaits for the resolution of the Future from [builder()] parameter and
/// stores the Instance returned.
Future<S> putAsync<S>(AsyncInstanceBuilderCallback<S> builder,
{String tag, bool permanent = false}) async {
return put<S>(await builder(), tag: tag, permanent: permanent);
... ... @@ -89,7 +115,9 @@ class GetInstance {
assert(builder != null);
final key = _getKey(S, name);
_singl.putIfAbsent(
key, () => _FcBuilder<S>(isSingleton, builder, permanent, false));
key,
() =>
_InstanceBuilderFactory<S>(isSingleton, builder, permanent, false));
}
/// Clears from memory registered Instances associated with [routeName] when
... ... @@ -181,8 +209,8 @@ class GetInstance {
S find<S>({String tag}) {
String key = _getKey(S, tag);
if (isRegistered<S>(tag: tag)) {
_FcBuilder builder = _singl[key];
if (builder == null) {
if (_singl[key] == null) {
if (tag == null) {
throw 'Class "$S" is not register';
} else {
... ... @@ -190,14 +218,12 @@ class GetInstance {
}
}
_initDependencies<S>(name: tag);
return _singl[key].getDependency() as S;
} else {
if (!_factory.containsKey(key))
throw '"$S" not found. You need to call "Get.put<$S>($S())"';
throw '"$S" not found. You need to call "Get.put($S())" or "Get.lazyPut(()=>$S())"';
// TODO: This message is not clear
GetConfig.log('"$S" instance was created at that time');
GetConfig.log('Lazy instance "$S" created');
S _value = put<S>(_factory[key].builder() as S);
... ... @@ -212,6 +238,8 @@ class GetInstance {
}
}
/// Generates the key based on [type] (and optionally a [name])
/// to register an Instance Builder in the hashmap.
String _getKey(Type type, String name) {
return name == null ? type.toString() : type.toString() + name;
}
... ... @@ -237,6 +265,20 @@ class GetInstance {
/// Delete registered Class Instance [S] (or [tag]) and, closes any open
/// controllers [DisposableInterface], cleans up the memory
///
/// /// Deletes the Instance<[S]>, cleaning the memory.
// ///
// /// - [tag] Optional "tag" used to register the Instance
// /// - [key] For internal usage, is the processed key used to register
// /// the Instance. **don't use** it unless you know what you are doing.
/// Deletes the Instance<[S]>, cleaning the memory and closes any open
/// controllers ([DisposableInterface]).
///
/// - [tag] Optional "tag" used to register the Instance
/// - [key] For internal usage, is the processed key used to register
/// the Instance. **don't use** it unless you know what you are doing.
/// - [force] Will delete an Instance even if marked as [permanent].
Future<bool> delete<S>({String tag, String key, bool force = false}) async {
final newKey = key ?? _getKey(S, tag);
... ... @@ -246,11 +288,12 @@ class GetInstance {
return false;
}
_FcBuilder builder = _singl[newKey];
final builder = _singl[newKey];
if (builder.permanent && !force) {
GetConfig.log(
'"$newKey" has been marked as permanent, SmartManagement is not authorized to delete it.',
isError: true);
isError: true,
);
return false;
}
final i = builder.dependency;
... ... @@ -274,10 +317,13 @@ class GetInstance {
});
}
/// Check if a Class instance [S] (or [tag]) is registered.
/// Check if a Class Instance<[S]> (or [tag]) is registered in memory.
/// - [tag] optional, if you use a [tag] to register the Instance.
bool isRegistered<S>({String tag}) => _singl.containsKey(_getKey(S, tag));
/// Check if Class instance [S] (or [tag]) is prepared to be used.
/// Checks if a lazy factory callback that returns an Instance<[S]>
/// is registered.
/// - [tag] optional, if you use a [tag] to register the Instance.
bool isPrepared<S>({String tag}) => _factory.containsKey(_getKey(S, tag));
}
... ... @@ -286,7 +332,7 @@ typedef InstanceBuilderCallback<S> = S Function();
typedef AsyncInstanceBuilderCallback<S> = Future<S> Function();
/// Internal class to register instances with Get.[put]<[S]>().
class _FcBuilder<S> {
class _InstanceBuilderFactory<S> {
/// Marks the Builder as a single instance.
/// For reusing [dependency] instead of [builderFunc]
bool isSingleton;
... ... @@ -304,7 +350,8 @@ class _FcBuilder<S> {
bool isInit = false;
_FcBuilder(this.isSingleton, this.builderFunc, this.permanent, this.isInit);
_InstanceBuilderFactory(
this.isSingleton, this.builderFunc, this.permanent, this.isInit);
/// Gets the actual instance by it's [builderFunc] or the persisted instance.
S getDependency() {
... ...
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:get/src/core/get_interface.dart';
import 'package:get/instance_manager.dart';
import 'package:get/route_manager.dart';
import 'package:get/src/core/get_interface.dart';
import 'package:get/src/core/log.dart';
import 'dialog/dialog_route.dart';
import 'root/parse_route.dart';
import 'routes/bindings_interface.dart';
... ... @@ -13,6 +14,8 @@ import 'routes/bindings_interface.dart';
NavigatorState get navigator => Get.key.currentState;
extension GetNavigation on GetInterface {
/// **Navigation.push()** shortcut.<br><br>
///
/// Pushes a new [page] to the stack
///
/// It has the advantage of not needing context,
... ... @@ -69,7 +72,9 @@ extension GetNavigation on GetInterface {
);
}
/// Pushes a new named [page] to the stack
/// **Navigation.pushNamed()** shortcut.<br><br>
///
/// Pushes a new named [page] to the stack.
///
/// It has the advantage of not needing context, so you can call
/// from your business logic.
... ... @@ -95,6 +100,8 @@ extension GetNavigation on GetInterface {
return global(id).currentState.pushNamed(page, arguments: arguments);
}
/// **Navigation.pushReplacementNamed()** shortcut.<br><br>
///
/// Pop the current named [page] in the stack and push a new one in its place
///
/// It has the advantage of not needing context, so you can call
... ... @@ -123,6 +130,8 @@ extension GetNavigation on GetInterface {
.pushReplacementNamed(page, arguments: arguments);
}
/// **Navigation.popUntil()** shortcut.<br><br>
///
/// Calls pop several times in the stack until [predicate] returns true
///
/// [id] is for when you are using nested navigation,
... ... @@ -139,6 +148,8 @@ extension GetNavigation on GetInterface {
return global(id).currentState.popUntil(predicate);
}
/// **Navigation.pushAndRemoveUntil()** shortcut.<br><br>
///
/// Push the given [page], and then pop several pages in the stack until
/// [predicate] returns true
///
... ... @@ -160,6 +171,8 @@ extension GetNavigation on GetInterface {
return global(id).currentState.pushAndRemoveUntil(page, predicate);
}
/// **Navigation.pushNamedAndRemoveUntil()** shortcut.<br><br>
///
/// Push the given named [page], and then pop several pages in the stack
/// until [predicate] returns true
///
... ... @@ -185,6 +198,8 @@ extension GetNavigation on GetInterface {
.pushNamedAndRemoveUntil(page, predicate, arguments: arguments);
}
/// **Navigation.popAndPushNamed()** shortcut.<br><br>
///
/// Pop the current named page and pushes a new [page] to the stack in its place
///
/// You can send any type of value to the other route in the [arguments].
... ... @@ -200,6 +215,8 @@ extension GetNavigation on GetInterface {
.popAndPushNamed(page, arguments: arguments, result: result);
}
/// **Navigation.removeRoute()** shortcut.<br><br>
///
/// Remove a specific [route] from the stack
///
/// [id] is for when you are using nested navigation,
... ... @@ -208,6 +225,8 @@ extension GetNavigation on GetInterface {
return global(id).currentState.removeRoute(route);
}
/// **Navigation.pushNamedAndRemoveUntil()** shortcut.<br><br>
///
/// Push a named [page] and pop several pages in the stack
/// until [predicate] returns true. [predicate] is optional
///
... ... @@ -234,14 +253,16 @@ extension GetNavigation on GetInterface {
arguments: arguments);
}
/// Returns true if a snackbar, dialog or bottomsheet is currently showing in the screen
/// Returns true if a Snackbar, Dialog or BottomSheet is currently showing
bool get isOverlaysOpen =>
(isSnackbarOpen || isDialogOpen || isBottomSheetOpen);
/// returns true if there is no snackbar, dialog or bottomsheet open
/// Returns true if there is no Snackbar, Dialog or BottomSheet open
bool get isOverlaysClosed =>
(!isSnackbarOpen && !isDialogOpen && !isBottomSheetOpen);
/// **Navigation.popUntil()** shortcut.<br><br>
///
/// Pop the current page, snackbar, dialog or bottomsheet in the stack
///
/// if your set [closeOverlays] to true, Get.back() will close the currently open
... ... @@ -272,6 +293,8 @@ extension GetNavigation on GetInterface {
}
}
/// **Navigation.popUntil()** (with predicate) shortcut .<br><br>
///
/// Close as many routes as defined by [times]
///
/// [id] is for when you are using nested navigation,
... ... @@ -287,6 +310,8 @@ extension GetNavigation on GetInterface {
return back;
}
/// **Navigation.pushReplacement()** shortcut .<br><br>
///
/// Pop the current page and pushes a new [page] to the stack
///
/// It has the advantage of not needing context,
... ... @@ -337,6 +362,8 @@ extension GetNavigation on GetInterface {
transitionDuration: duration ?? defaultDurationTransition));
}
/// **Navigation.pushAndRemoveUntil()** shortcut .<br><br>
///
/// Push a [page] and pop several pages in the stack
/// until [predicate] returns true. [predicate] is optional
///
... ... @@ -470,6 +497,7 @@ extension GetNavigation on GetInterface {
));
}
/// Custom UI Dialog.
Future<T> defaultDialog<T>({
String title = "Alert",
Widget content,
... ... @@ -884,7 +912,7 @@ extension GetNavigation on GetInterface {
return key;
}
if (!keys.containsKey(k)) {
throw 'route id not found';
throw 'Route id ($k) not found';
}
return keys[k];
}
... ... @@ -892,7 +920,7 @@ extension GetNavigation on GetInterface {
RouteSettings get routeSettings => settings;
void setSettings(RouteSettings settings) {
settings = settings;
this.settings = settings;
}
/// give current arguments
... ...
import 'package:flutter/widgets.dart';
import 'package:get/src/navigation/routes/get_route.dart';
class GetPageMatch {
GetPageMatch(this.route);
GetPage route;
Map<String, String> parameters = <String, String>{};
}
class ParseRouteTree {
final List<ParseRouteTreeNode> _nodes = <ParseRouteTreeNode>[];
final List<_ParseRouteTreeNode> _nodes = <_ParseRouteTreeNode>[];
// bool _hasDefaultRoute = false;
... ... @@ -20,7 +13,7 @@ class ParseRouteTree {
// if (_hasDefaultRoute) {
// throw ("Default route was already defined");
// }
var node = ParseRouteTreeNode(path, ParseRouteTreeNodeType.component);
var node = _ParseRouteTreeNode(path, _ParseRouteTreeNodeType.component);
node.routes = [route];
_nodes.add(node);
// _hasDefaultRoute = true;
... ... @@ -30,13 +23,13 @@ class ParseRouteTree {
path = path.substring(1);
}
List<String> pathComponents = path.split('/');
ParseRouteTreeNode parent;
_ParseRouteTreeNode parent;
for (int i = 0; i < pathComponents.length; i++) {
String component = pathComponents[i];
ParseRouteTreeNode node = _nodeForComponent(component, parent);
_ParseRouteTreeNode node = _nodeForComponent(component, parent);
if (node == null) {
ParseRouteTreeNodeType type = _typeForComponent(component);
node = ParseRouteTreeNode(component, type);
_ParseRouteTreeNodeType type = _typeForComponent(component);
node = _ParseRouteTreeNode(component, type);
node.parent = parent;
if (parent == null) {
_nodes.add(node);
... ... @@ -55,7 +48,7 @@ class ParseRouteTree {
}
}
GetPageMatch matchRoute(String path) {
_GetPageMatch matchRoute(String path) {
String usePath = path;
if (usePath.startsWith("/")) {
usePath = path.substring(1);
... ... @@ -68,14 +61,14 @@ class ParseRouteTree {
if (path == Navigator.defaultRouteName) {
components = ["/"];
}
Map<ParseRouteTreeNode, ParseRouteTreeNodeMatch> nodeMatches =
<ParseRouteTreeNode, ParseRouteTreeNodeMatch>{};
List<ParseRouteTreeNode> nodesToCheck = _nodes;
Map<_ParseRouteTreeNode, _ParseRouteTreeNodeMatch> nodeMatches =
<_ParseRouteTreeNode, _ParseRouteTreeNodeMatch>{};
List<_ParseRouteTreeNode> nodesToCheck = _nodes;
for (String checkComponent in components) {
Map<ParseRouteTreeNode, ParseRouteTreeNodeMatch> currentMatches =
<ParseRouteTreeNode, ParseRouteTreeNodeMatch>{};
List<ParseRouteTreeNode> nextNodes = <ParseRouteTreeNode>[];
for (ParseRouteTreeNode node in nodesToCheck) {
Map<_ParseRouteTreeNode, _ParseRouteTreeNodeMatch> currentMatches =
<_ParseRouteTreeNode, _ParseRouteTreeNodeMatch>{};
List<_ParseRouteTreeNode> nextNodes = <_ParseRouteTreeNode>[];
for (_ParseRouteTreeNode node in nodesToCheck) {
String pathPart = checkComponent;
Map<String, String> queryMap = {};
... ... @@ -104,9 +97,9 @@ class ParseRouteTree {
bool isMatch = (node.part == pathPart || node.isParameter());
if (isMatch) {
ParseRouteTreeNodeMatch parentMatch = nodeMatches[node.parent];
ParseRouteTreeNodeMatch match =
ParseRouteTreeNodeMatch.fromMatch(parentMatch, node);
_ParseRouteTreeNodeMatch parentMatch = nodeMatches[node.parent];
_ParseRouteTreeNodeMatch match =
_ParseRouteTreeNodeMatch.fromMatch(parentMatch, node);
// TODO: find a way to clean this implementation.
match.parameters.addAll(uri.queryParameters);
... ... @@ -131,16 +124,16 @@ class ParseRouteTree {
return null;
}
}
List<ParseRouteTreeNodeMatch> matches = nodeMatches.values.toList();
List<_ParseRouteTreeNodeMatch> matches = nodeMatches.values.toList();
if (matches.length > 0) {
ParseRouteTreeNodeMatch match = matches.first;
ParseRouteTreeNode nodeToUse = match.node;
_ParseRouteTreeNodeMatch match = matches.first;
_ParseRouteTreeNode nodeToUse = match.node;
if (nodeToUse != null &&
nodeToUse.routes != null &&
nodeToUse.routes.length > 0) {
List<GetPage> routes = nodeToUse.routes;
GetPageMatch routeMatch = GetPageMatch(routes[0]);
_GetPageMatch routeMatch = _GetPageMatch(routes[0]);
routeMatch.parameters = match.parameters;
... ... @@ -150,13 +143,13 @@ class ParseRouteTree {
return null;
}
ParseRouteTreeNode _nodeForComponent(
String component, ParseRouteTreeNode parent) {
List<ParseRouteTreeNode> nodes = _nodes;
_ParseRouteTreeNode _nodeForComponent(
String component, _ParseRouteTreeNode parent) {
List<_ParseRouteTreeNode> nodes = _nodes;
if (parent != null) {
nodes = parent.nodes;
}
for (ParseRouteTreeNode node in nodes) {
for (_ParseRouteTreeNode node in nodes) {
if (node.part == component) {
return node;
}
... ... @@ -164,10 +157,10 @@ class ParseRouteTree {
return null;
}
ParseRouteTreeNodeType _typeForComponent(String component) {
ParseRouteTreeNodeType type = ParseRouteTreeNodeType.component;
_ParseRouteTreeNodeType _typeForComponent(String component) {
_ParseRouteTreeNodeType type = _ParseRouteTreeNodeType.component;
if (_isParameterComponent(component)) {
type = ParseRouteTreeNodeType.parameter;
type = _ParseRouteTreeNodeType.parameter;
}
return type;
}
... ... @@ -190,35 +183,43 @@ class ParseRouteTree {
}
}
class ParseRouteTreeNodeMatch {
ParseRouteTreeNodeMatch(this.node);
class _ParseRouteTreeNodeMatch {
_ParseRouteTreeNodeMatch(this.node);
ParseRouteTreeNodeMatch.fromMatch(ParseRouteTreeNodeMatch match, this.node) {
_ParseRouteTreeNodeMatch.fromMatch(
_ParseRouteTreeNodeMatch match, this.node) {
parameters = <String, String>{};
if (match != null) {
parameters.addAll(match.parameters);
}
}
ParseRouteTreeNode node;
_ParseRouteTreeNode node;
Map<String, String> parameters = <String, String>{};
}
class ParseRouteTreeNode {
ParseRouteTreeNode(this.part, this.type);
class _ParseRouteTreeNode {
_ParseRouteTreeNode(this.part, this.type);
String part;
ParseRouteTreeNodeType type;
_ParseRouteTreeNodeType type;
List<GetPage> routes = <GetPage>[];
List<ParseRouteTreeNode> nodes = <ParseRouteTreeNode>[];
ParseRouteTreeNode parent;
List<_ParseRouteTreeNode> nodes = <_ParseRouteTreeNode>[];
_ParseRouteTreeNode parent;
bool isParameter() {
return type == ParseRouteTreeNodeType.parameter;
return type == _ParseRouteTreeNodeType.parameter;
}
}
enum ParseRouteTreeNodeType {
class _GetPageMatch {
_GetPageMatch(this.route);
GetPage route;
Map<String, String> parameters = <String, String>{};
}
enum _ParseRouteTreeNodeType {
component,
parameter,
}
... ...
... ... @@ -38,11 +38,69 @@ class _RxImpl<T> implements RxInterface<T> {
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) this.value = v;
return this.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.
///
/// 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.value.name = 'Roi';
/// person.refresh();
/// print( person );
/// ```
void refresh() {
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 value)) {
fn(value);
subject.add(value);
... ...
... ... @@ -3,20 +3,22 @@ import 'dart:async';
import 'package:flutter/scheduler.dart';
import 'package:get/src/state_manager/rx/rx_callbacks.dart';
/// This class is the foundation for all reactive (Rx) classes that makes Get
/// so powerful.
/// This interface is the contract that [_RxImpl]<[T]> uses in all it's
/// subclass.
abstract class RxInterface<T> {
RxInterface([T initial]);
/// add listener to stream
StreamController<T> subject;
/// Adds a listener to stream
void addListener(Stream<T> rxGetx);
bool get canUpdate;
/// close stream
void close() {
subject?.close();
}
StreamController<T> subject;
/// Closes the stream
void close() => subject?.close();
/// Calls [callback] with current value, when the value changes.
StreamSubscription<T> listen(ValueCallback<T> callback);
... ... @@ -60,14 +62,40 @@ abstract class DisposableInterface {
SchedulerBinding.instance?.addPostFrameCallback((_) => onReady());
}
/// Called Called immediately after the widget is allocated in memory.
/// Called immediately after the widget is allocated in memory.
/// You might use this initialize something for the controller.
void onInit() async {}
/// Called after rendering the screen. It is the perfect place to enter navigation events,
/// be it snackbar, dialogs, or a new route.
/// Called 1 frame after onInit(). It is the perfect place to enter navigation events,
/// like snackbar, dialogs, or a new route, or async request.
void onReady() async {}
/// Called before the onDelete method. onClose is used to close events
/// before the controller is destroyed, such as closing streams, for example.
onClose() async {}
/// Called before [onDelete] method. [onClose] might be used to dispose resources
/// used by the controller. Like closing events, or streams before the controller is destroyed.
/// Or dispose objects that can potentially create some memory leaks,
/// like TextEditingControllers, AnimationControllers.
/// Might be useful as well to persist some data on disk.
void onClose() async {}
}
/// Used like [SingleTickerProviderMixin] but only with Get Controllers.
/// Simplifies AnimationController creation inside GetxController.
///
/// Example:
///```
///class SplashController extends GetxController with SingleGetTickerProviderMixin {
/// AnimationController _ac;
///
/// @override
/// void onInit() {
/// final dur = const Duration(seconds: 2);
/// _ac = AnimationController.unbounded(duration: dur, vsync: this);
/// _ac.repeat();
/// _ac.addListener(() => print("Animation Controller value: ${_ac.value}"));
/// }
/// ...
/// ```
mixin SingleGetTickerProviderMixin on DisposableInterface
implements TickerProvider {
Ticker createTicker(TickerCallback onTick) => Ticker(onTick);
}
... ...
import 'dart:async';
/// This "function" class is the implementation of [debouncer()] Worker.
/// It calls the function passed after specified [delay] parameter.
/// Example:
/// ```
/// final delayed = Debouncer( delay: Duration( seconds: 1 )) ;
/// print( 'the next function will be called after 1 sec' );
/// delayed( () => print( 'called after 1 sec' ));
/// ```
class Debouncer {
final Duration delay;
Timer _timer;
Debouncer({this.delay});
call(void Function() action) {
void call(void Function() action) {
_timer?.cancel();
_timer = Timer(delay, action);
}
/// Notifies if the delayed call is active.
bool get isRunning => _timer?.isActive ?? false;
/// Cancel the current delayed call.
void cancel() => _timer?.cancel();
}
... ...