Jonatas

prepare to update

## [3.25.0]
## [3.25.0] - Big update
- Added [FullLifeCycleController] - A GetxController capable of observing all the life cycles of your application. FullLifeCycleController has the life cycles:
* onInit: called when the controller enters the application's memory
* onReady: called after onInit, when build method from widget relationed to controller is done.
* onClose: called when controller is deleted from memory.
* onPaused: called when the application is not currently visible to the user, and running in the background.
* onInactive: called when the application is in an inactive state and is not receiving user input, when the user receives a call, for example
* onResumed: The application is now visible and in the foreground
* onDetached: The application is still hosted on a flutter engine but is detached from any host views.
* didChangeMetrics: called when the window size is changed
- Added SuperController, a complete life circle controller with StateMixin
- Improve Iterable Rx Api. Now, you can to use dart List, Map and Set as reactive, like: List<String> names = <String>['juan', 'pedro', 'maria'].obs;
- Added [reload] and [reloadAll] methods to reload your Controller to original values
- Added assign and assignAll extensions to default dart List
- Added parameters options from Get.toNamed, Get.offNamed, and Get.offAllNamed (@enghitalo)
- Improve Rx disposal logic to completely prevent memory leaks
- Improve Capitalize methods from GetUtils (@eduardoflorence)
- Prevent a close snackbar from close a Screen with double tap (@eduardoflorence)
- Includes GetLifeCycleBase mixin on delete/dispose (@saviogrossi)
- Added internacionalization example to sample app (@rodriguesJeff)
- Added headers to Graphql query and mutation(@asalvi0)
- Added translation with parameter extension (@CpdnCristiano)
- Added Get.parameter access to Middleware (@eduardoflorence)
- Fix RxBool typo (@emanuelmutschlechner)
- Added Filter to GetBuilder
- Added debouce to GetBuilder update
- Added ability to insert an Enum, class, or type of an object as a GetBuilder's Id
- Improve upload time from GetConnect
- Create minified version to DartPad(@roipeker)
- Suggested to use `Get.to(() => Page())` instead of `Get.to(Page())`.
- Fix and improve docs: @unacorbatanegra, @lsm, @nivisi, @ThinkDigitalSoftware, @martwozniak, @UsamaElgendy, @@DominusKelvin, @jintak0401,
## [3.24.0]
- GetWidget has been completely redesigned.
... ...
... ... @@ -42,8 +42,9 @@ class CountryView extends GetView<HomeController> {
"https://flagpedia.net/data/flags/normal/${country.countryCode.toLowerCase()}.png"),
),
title: Text(country.country),
subtitle:
Text('total_infecteds'.tr +' ${country.totalConfirmed}'),
subtitle: Text(
// ignore: lines_longer_than_80_chars
'${'total_infecteds'.tr}${' ${country.totalConfirmed}'}'),
);
}),
),
... ...
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import '../lib/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(MyApp());
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}
... ... @@ -50,41 +50,6 @@ class GetInstance {
/// non-singleton instances.
static final Map<String, HashSet<Function>> _routesByCreate = {};
/// 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 [Get.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,
}) {
_insert(
isSingleton: true,
name: tag,
permanent: fenix ?? Get.smartManagement == SmartManagement.keepFactory,
builder: builder,
);
}
void printInstanceStack() {
Get.log(_routesKey.toString());
}
... ... @@ -138,6 +103,43 @@ class GetInstance {
return find<S>(tag: tag);
}
/// 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 [Get.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,
bool permanent = false,
}) {
_insert(
isSingleton: true,
name: tag,
permanent: permanent,
builder: builder,
fenix: fenix ?? Get.smartManagement == SmartManagement.keepFactory,
);
}
/// Creates a new Class Instance [S] from the builder callback[S].
/// Every time [find]<[S]>() is used, it calls the builder method to generate
/// a new Instance [S].
... ... @@ -173,6 +175,7 @@ class GetInstance {
String name,
bool permanent = false,
InstanceBuilderCallback<S> builder,
bool fenix = false,
}) {
assert(builder != null);
final key = _getKey(S, name);
... ... @@ -183,6 +186,7 @@ class GetInstance {
builder,
permanent,
false,
fenix,
),
);
}
... ... @@ -374,6 +378,7 @@ class GetInstance {
}
final builder = _singl[newKey];
if (builder.permanent && !force) {
Get.log(
// ignore: lines_longer_than_80_chars
... ... @@ -393,13 +398,17 @@ class GetInstance {
Get.log('"$newKey" onDelete() called');
}
if (builder.fenix) {
builder.dependency = null;
builder.isInit = false;
} else {
_singl.remove(newKey);
if (_singl.containsKey(newKey)) {
Get.log('Error removing object "$newKey"', isError: true);
} else {
Get.log('"$newKey" deleted from memory');
}
}
return true;
}
... ... @@ -469,6 +478,10 @@ class _InstanceBuilderFactory<S> {
/// For reusing [dependency] instead of [builderFunc]
bool isSingleton;
/// When fenix mode is avaliable, when a new instance is need
/// Instance manager will recreate a new instance of S
bool fenix;
/// Stores the actual object instance when [isSingleton]=true.
S dependency;
... ... @@ -487,6 +500,7 @@ class _InstanceBuilderFactory<S> {
this.builderFunc,
this.permanent,
this.isInit,
this.fenix,
);
/// Gets the actual instance by it's [builderFunc] or the persisted instance.
... ...
... ... @@ -486,7 +486,7 @@ extension GetNavigation on GetInterface {
/// By default, GetX will prevent you from push a route that you already in,
/// if you want to push anyway, set [preventDuplicates] to false
Future<T> to<T>(
Widget page, {
dynamic page, {
bool opaque,
Transition transition,
Curve curve,
... ... @@ -505,7 +505,7 @@ extension GetNavigation on GetInterface {
return global(id)?.currentState?.push<T>(
GetPageRoute<T>(
opaque: opaque ?? true,
page: () => page,
page: _resolve(page, 'to'),
routeName: routeName,
settings: RouteSettings(
// name: forceRouteName ? '${a.runtimeType}' : '',
... ... @@ -521,6 +521,21 @@ extension GetNavigation on GetInterface {
);
}
GetPageBuilder _resolve(dynamic page, String method) {
if (page is GetPageBuilder) {
return page;
} else if (page is Widget) {
Get.log(
'''WARNING, consider using: "Get.$method(() => Page())" instead of "Get.to(Page())".
Using a widget function instead of a widget fully guarantees that the widget and its controllers will be removed from memory when they are no longer used.
''');
return () => page;
} else {
throw '''Unexpected format,
you can only use widgets and widget functions here''';
}
}
/// **Navigation.pushNamed()** shortcut.<br><br>
///
/// Pushes a new named [page] to the stack.
... ... @@ -836,7 +851,7 @@ extension GetNavigation on GetInterface {
/// By default, GetX will prevent you from push a route that you already in,
/// if you want to push anyway, set [preventDuplicates] to false
Future<T> off<T>(
Widget page, {
dynamic page, {
bool opaque = false,
Transition transition,
Curve curve,
... ... @@ -854,7 +869,7 @@ extension GetNavigation on GetInterface {
}
return global(id)?.currentState?.pushReplacement(GetPageRoute(
opaque: opaque ?? true,
page: () => page,
page: _resolve(page, 'off'),
binding: binding,
settings: RouteSettings(arguments: arguments),
routeName: routeName,
... ... @@ -897,7 +912,7 @@ extension GetNavigation on GetInterface {
/// By default, GetX will prevent you from push a route that you already in,
/// if you want to push anyway, set [preventDuplicates] to false
Future<T> offAll<T>(
Widget page, {
dynamic page, {
RoutePredicate predicate,
bool opaque = false,
bool popGesture,
... ... @@ -915,7 +930,7 @@ extension GetNavigation on GetInterface {
GetPageRoute<T>(
opaque: opaque ?? true,
popGesture: popGesture ?? defaultPopGesture,
page: () => page,
page: _resolve(page, 'offAll'),
binding: binding,
settings: RouteSettings(arguments: arguments),
fullscreenDialog: fullscreenDialog,
... ...
... ... @@ -26,30 +26,30 @@ mixin GetStateUpdaterMixin<T extends StatefulWidget> on State<T> {
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 WatchEtx on GetxController {
T watch<T extends GetxController>() {
final instance = Get.find<T>();
_GetBuilderState._currentState.watch(instance.update);
return instance;
}
}
// 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 WatchEtx on GetxController {
// T watch<T extends GetxController>() {
// final instance = Get.find<T>();
// _GetBuilderState._currentState.watch(instance.update);
// return instance;
// }
// }
class GetBuilder<T extends GetxController> extends StatefulWidget {
final GetControllerBuilder<T> builder;
... ... @@ -58,7 +58,7 @@ class GetBuilder<T extends GetxController> extends StatefulWidget {
final String tag;
final bool autoRemove;
final bool assignId;
final Object Function(T value) selector;
final Object Function(T value) filter;
final void Function(State state) initState, dispose, didChangeDependencies;
final void Function(GetBuilder oldWidget, State state) didUpdateWidget;
final T init;
... ... @@ -71,7 +71,7 @@ class GetBuilder<T extends GetxController> extends StatefulWidget {
this.autoRemove = true,
this.assignId = false,
this.initState,
this.selector,
this.filter,
this.tag,
this.dispose,
this.id,
... ... @@ -80,24 +80,24 @@ class GetBuilder<T extends GetxController> extends StatefulWidget {
}) : assert(builder != null),
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;
}
}
// 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
_GetBuilderState<T> createState() => _GetBuilderState<T>();
... ... @@ -108,10 +108,10 @@ class _GetBuilderState<T extends GetxController> extends State<GetBuilder<T>>
T controller;
bool isCreator = false;
VoidCallback remove;
Object _selector;
Object _filter;
List<VoidCallback> _watchs;
static _GetBuilderState _currentState;
// static _GetBuilderState _currentState;
void watch(VoidCallback listener) {
(_watchs ??= <VoidCallback>[]).add(listener);
... ... @@ -119,7 +119,7 @@ class _GetBuilderState<T extends GetxController> extends State<GetBuilder<T>>
@override
void initState() {
_GetBuilderState._currentState = this;
// _GetBuilderState._currentState = this;
super.initState();
widget.initState?.call(this);
... ... @@ -144,8 +144,8 @@ class _GetBuilderState<T extends GetxController> extends State<GetBuilder<T>>
controller?.onStart();
}
if (widget.selector != null) {
_selector = widget.selector(controller);
if (widget.filter != null) {
_filter = widget.filter(controller);
}
_subscribeToController();
... ... @@ -158,18 +158,18 @@ class _GetBuilderState<T extends GetxController> extends State<GetBuilder<T>>
remove?.call();
remove = (widget.id == null)
? controller?.addListener(
_selector != null ? _selectorUpdate : getUpdate,
_filter != null ? _filterUpdate : getUpdate,
)
: controller?.addListenerId(
widget.id,
_selector != null ? _selectorUpdate : getUpdate,
_filter != null ? _filterUpdate : getUpdate,
);
}
void _selectorUpdate() {
var newSelector = widget.selector(controller);
if (newSelector != _selector) {
_selector = newSelector;
void _filterUpdate() {
var newFilter = widget.filter(controller);
if (newFilter != _filter) {
_filter = newFilter;
getUpdate();
}
}
... ... @@ -189,7 +189,7 @@ class _GetBuilderState<T extends GetxController> extends State<GetBuilder<T>>
controller = null;
isCreator = null;
remove = null;
_selector = null;
_filter = null;
_watchs = null;
}
... ... @@ -211,21 +211,22 @@ class _GetBuilderState<T extends GetxController> extends State<GetBuilder<T>>
@override
Widget build(BuildContext context) {
return _InheritedGetxController<T>(
model: controller,
child: widget.builder(controller),
);
}
}
extension FindExt on BuildContext {
T find<T extends GetxController>() {
return GetBuilder.of<T>(this, rebuild: false);
// return _InheritedGetxController<T>(
// model: controller,
// child: widget.builder(controller),
// );
return widget.builder(controller);
}
}
extension ObserverEtx on BuildContext {
T obs<T extends GetxController>() {
return GetBuilder.of<T>(this, rebuild: true);
}
}
// extension FindExt on BuildContext {
// T find<T extends GetxController>() {
// return GetBuilder.of<T>(this, rebuild: false);
// }
// }
// extension ObserverEtx on BuildContext {
// T obs<T extends GetxController>() {
// return GetBuilder.of<T>(this, rebuild: true);
// }
// }
... ...
... ... @@ -79,4 +79,9 @@ extension GetStringUtils on String {
String numericOnly({bool firstWordOnly = false}) =>
GetUtils.numericOnly(this, firstWordOnly: firstWordOnly);
String createPath([Iterable segments]) {
final path = startsWith('/') ? this : '/$this';
return GetUtils.createPath(path, segments);
}
}
... ...
... ... @@ -578,6 +578,14 @@ class GetUtils {
return (value == null) ? false : RegExp(pattern).hasMatch(value);
}
static String createPath(String path, [Iterable segments]) {
if (segments == null || segments.isEmpty) {
return path;
}
final list = segments.map((e) => '/$e');
return path + list.join();
}
static void printFunction(
String prefix,
dynamic value,
... ...
... ... @@ -81,6 +81,26 @@ void main() {
Get.reset();
});
test('Get.lazyPut fenix test', () async {
Get.lazyPut<Controller>(() => Controller(), fenix: true);
Get.find<Controller>().increment();
expect(Get.find<Controller>().count, 1);
Get.delete<Controller>();
expect(Get.find<Controller>().count, 0);
Get.reset();
});
test('Get.lazyPut without fenix', () async {
Get.lazyPut<Controller>(() => Controller());
Get.find<Controller>().increment();
expect(Get.find<Controller>().count, 1);
Get.delete<Controller>();
expect(() => Get.find<Controller>(), throwsA(m.TypeMatcher<String>()));
Get.reset();
});
test('Get.reloadInstance test', () async {
Get.lazyPut<Controller>(() => Controller());
var ct1 = Get.find<Controller>();
... ...