Rodrigo Lopez Peker

- Rename typedefs FcBuilderFunc to InstanceBuilderCallback to be more meaningful.

- cleanup more comments.
- promote most classes in parse_route.dart (except ParseRouteTree) to be private. I still believe we should use Uri to parse the "url".
- Added _RxImpl::refresh() in rx_impl.dart and added some comments/samples.
- Some cleanup in rx_interface.dart, added more comments, missing types and move vars to the top.
... ... @@ -3,11 +3,12 @@ import 'package:get/src/core/get_interface.dart';
import 'get_instance.dart';
extension Inst on GetInterface {
void lazyPut<S>(FcBuilderFunc builder, {String tag, bool fenix = false}) {
void lazyPut<S>(InstanceBuilderCallback builder,
{String tag, bool fenix = false}) {
return GetInstance().lazyPut<S>(builder, tag: tag, fenix: fenix);
}
Future<S> putAsync<S>(FcBuilderFuncAsync<S> builder,
Future<S> putAsync<S>(AsyncInstanceBuilderCallback<S> builder,
{String tag, bool permanent = false}) async =>
GetInstance().putAsync<S>(builder, tag: tag, permanent: permanent);
... ... @@ -21,15 +22,13 @@ extension Inst on GetInterface {
/// Repl a = find();
/// Repl b = find();
/// print(a==b); (false)```
///
void create<S>(FcBuilderFunc<S> builder,
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]
///
S find<S>({String tag}) => GetInstance().find<S>(tag: tag);
/// Injects a Instance [S] in [GetInstance].
... ... @@ -40,17 +39,24 @@ extension Inst on GetInterface {
/// - [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
///
/// - [builder] If defined, the [dependency] must be returned from here
S put<S>(S dependency,
{String tag, bool permanent = false, FcBuilderFunc<S> builder}) =>
{String tag,
bool permanent = false,
InstanceBuilderCallback<S> builder}) =>
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 [lazyPut]
/// [clearRouteBindings] clears Instances associated with routes.
bool reset({bool clearFactory = true, bool clearRouteBindings = true}) =>
GetInstance().reset(
clearFactory: clearFactory, clearRouteBindings: clearRouteBindings);
/// Delete class instance on [S] and clean memory
/// Delete class instance on [S], cleaning the memory
Future<bool> delete<S>({String tag, String key}) async =>
GetInstance().delete<S>(tag: tag, key: key);
... ...
... ... @@ -12,12 +12,14 @@ 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 = {};
static Map<String, _InstanceBuilderFactory> _singl = {};
/// Holds a reference to every registered callback when using
/// Get.[lazyPut]
... ... @@ -27,12 +29,13 @@ class GetInstance {
static GetQueue _queue = GetQueue();
void lazyPut<S>(FcBuilderFunc builder, {String tag, bool fenix = false}) {
void lazyPut<S>(InstanceBuilderCallback builder,
{String tag, bool fenix = false}) {
String key = _getKey(S, tag);
_factory.putIfAbsent(key, () => _Lazy(builder, fenix));
}
Future<S> putAsync<S>(FcBuilderFuncAsync<S> builder,
Future<S> putAsync<S>(AsyncInstanceBuilderCallback<S> builder,
{String tag, bool permanent = false}) async {
return put<S>(await builder(), tag: tag, permanent: permanent);
}
... ... @@ -49,7 +52,7 @@ class GetInstance {
S dependency, {
String tag,
bool permanent = false,
FcBuilderFunc<S> builder,
InstanceBuilderCallback<S> builder,
}) {
_insert(
isSingleton: true,
... ... @@ -70,7 +73,7 @@ class GetInstance {
/// Repl b = find();
/// print(a==b); (false)```
void create<S>(
FcBuilderFunc<S> builder, {
InstanceBuilderCallback<S> builder, {
String name,
bool permanent = true,
}) {
... ... @@ -83,12 +86,14 @@ class GetInstance {
bool isSingleton,
String name,
bool permanent = false,
FcBuilderFunc<S> builder,
InstanceBuilderCallback<S> builder,
}) {
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
... ... @@ -180,8 +185,7 @@ class GetInstance {
S find<S>({String tag}) {
String key = _getKey(S, tag);
if (isRegistered<S>(tag: tag)) {
_FcBuilder builder = _singl[key] as _FcBuilder;
if (builder == null) {
if (_singl[key] == null) {
if (tag == null) {
throw 'Class "$S" is not register';
} else {
... ... @@ -189,7 +193,6 @@ class GetInstance {
}
}
_initDependencies<S>(name: tag);
return _singl[key].getDependency() as S;
} else {
if (!_factory.containsKey(key))
... ... @@ -245,11 +248,12 @@ class GetInstance {
return false;
}
_FcBuilder builder = _singl[newKey] as _FcBuilder;
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;
... ... @@ -280,12 +284,12 @@ class GetInstance {
bool isPrepared<S>({String tag}) => _factory.containsKey(_getKey(S, tag));
}
typedef FcBuilderFunc<S> = S Function();
typedef InstanceBuilderCallback<S> = S Function();
typedef FcBuilderFuncAsync<S> = Future<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;
... ... @@ -295,7 +299,7 @@ class _FcBuilder<S> {
/// Generates (and regenerates) the instance when [isSingleton]=false.
/// Usually used by factory methods
FcBuilderFunc<S> builderFunc;
InstanceBuilderCallback<S> builderFunc;
/// Flag to persist the instance in memory,
/// without considering [GetConfig.smartManagement]
... ... @@ -303,7 +307,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() {
... ... @@ -315,6 +320,6 @@ class _FcBuilder<S> {
/// keeps a reference to the callback to be called.
class _Lazy {
bool fenix;
FcBuilderFunc builder;
InstanceBuilderCallback builder;
_Lazy(this.builder, this.fenix);
}
... ...
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);
... ... @@ -55,14 +57,18 @@ 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 {}
}
... ...