Renat Fakhrutdinov
Committed by GitHub

Merge pull request #14 from jonataslaw/master

Update
... ... @@ -3,12 +3,11 @@ name: build
# Trigger the workflow on push or pull request
on:
push:
branches: [ master ]
branches: [master]
pull_request:
branches: [ master ]
branches: [master]
#A workflow run is made up of one or more jobs. Jobs run in parallel by default.
jobs:
test:
#The type of machine to run the job on. [windows,macos, ubuntu , self-hosted]
defaults:
... ... @@ -24,8 +23,8 @@ jobs:
# https://github.com/marketplace/actions/flutter-action
- uses: subosito/flutter-action@v1
with:
flutter-version: '1.20.4'
channel: 'stable'
flutter-version: "1.22.2"
channel: "stable"
- run: flutter pub get
#- run: flutter analyze
# run flutter widgets tests and unit tests
... ... @@ -33,4 +32,3 @@ jobs:
# Upload coverage reports to Codecov
# https://github.com/marketplace/actions/codecov
- uses: codecov/codecov-action@v1.0.7
... ...
... ... @@ -19,7 +19,10 @@ class HomeController extends GetxController {
/// When the controller is initialized, make the http request
@override
void onInit() => fetchDataFromApi();
void onInit() {
super.onInit();
fetchDataFromApi();
}
/// fetch cases from Api
Future<void> fetchDataFromApi() async {
... ...
... ... @@ -170,7 +170,9 @@ class Controller extends GetxController {
super.onReady();
}
@override
void onClose() {
super.onClose();
print('onClose');
}
}
... ...
... ... @@ -5,3 +5,5 @@ export 'src/get_main.dart';
export 'src/log.dart';
export 'src/smart_management.dart';
export 'src/typedefs.dart';
... ...
typedef ValueUpdater<T> = T Function();
... ...
import 'package:meta/meta.dart';
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> {
T Function() callback;
ValueUpdater<T> _callback;
_InternalFinalCallback();
_InternalFinalCallback({ValueUpdater<T> callback}) : _callback = callback;
T call() => callback.call();
T call() => _callback.call();
}
/// The [GetLifeCycle]
///
/// ```dart
/// class SomeController with GetLifeCycle {
/// SomeController() {
/// initLifeCycle();
/// }
/// }
/// ```
mixin GetLifeCycle {
/// The `initLifeCycle` works as a constructor for the [GetLifeCycle]
///
/// This method must be invoked in the constructor of the implementation
void initLifeCycle() {
onStart._callback = _onStart;
onDelete._callback = _onDelete;
}
/// 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>();
/// 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.
@mustCallSuper
void onInit() {}
/// Called 1 frame after onInit(). It is the perfect place to enter
/// navigation events, like snackbar, dialogs, or a new route, or
/// async request.
@mustCallSuper
void onReady() {}
/// Called before [onDelete] method. [onClose] might be used to
... ... @@ -35,7 +57,32 @@ mixin GetLifeCycle {
/// Or dispose objects that can potentially create some memory leaks,
/// like TextEditingControllers, AnimationControllers.
/// Might be useful as well to persist some data on disk.
@mustCallSuper
void onClose() {}
bool _initialized = false;
/// Checks whether the controller has already been initialized.
bool get initialized => _initialized;
// Internal callback that starts the cycle of this controller.
void _onStart() {
if (_initialized) return;
onInit();
_initialized = true;
}
bool _isClosed = false;
/// Checks whether the controller has already been closed.
bool get isClosed => _isClosed;
// Internal callback that starts the cycle of this controller.
void _onDelete() {
if (_isClosed) return;
_isClosed = true;
onClose();
}
}
/// Allow track difference between GetxServices and GetxControllers
... ...
... ... @@ -5,7 +5,6 @@ export 'src/extension_navigation.dart';
export 'src/root/root_widget.dart';
export 'src/routes/custom_transition.dart';
export 'src/routes/default_route.dart';
export 'src/routes/default_route.dart';
export 'src/routes/get_route.dart';
export 'src/routes/observers/route_observer.dart';
export 'src/routes/transitions_type.dart';
... ...
... ... @@ -104,7 +104,7 @@ extension ExtensionSnackbar on GetInterface {
return key?.currentState?.push(SnackRoute<T>(snack: snackbar));
}
void snackbar(
void snackbar<T>(
String title,
String message, {
Color colorText,
... ... @@ -199,11 +199,11 @@ extension ExtensionSnackbar on GetInterface {
userInputForm: userInputForm);
if (instantInit) {
showSnackbar(getBar);
showSnackbar<T>(getBar);
} else {
routing.isSnackbar = true;
SchedulerBinding.instance.addPostFrameCallback((_) {
showSnackbar(getBar);
showSnackbar<T>(getBar);
});
}
}
... ... @@ -220,9 +220,11 @@ extension ExtensionDialog on GetInterface {
Color barrierColor,
bool useSafeArea = true,
bool useRootNavigator = true,
RouteSettings routeSettings,
Object arguments,
Duration transitionDuration,
Curve transitionCurve,
String name,
RouteSettings routeSettings,
}) {
assert(widget != null);
assert(barrierDismissible != null);
... ... @@ -231,7 +233,7 @@ extension ExtensionDialog on GetInterface {
assert(debugCheckHasMaterialLocalizations(context));
final theme = Theme.of(context, shadowThemeOnly: true);
return generalDialog(
return generalDialog<T>(
pageBuilder: (buildContext, animation, secondaryAnimation) {
final pageChild = widget;
Widget dialog = Builder(builder: (context) {
... ... @@ -258,7 +260,8 @@ extension ExtensionDialog on GetInterface {
);
},
useRootNavigator: useRootNavigator,
routeSettings: routeSettings,
routeSettings:
routeSettings ?? RouteSettings(arguments: arguments, name: name),
);
}
... ... @@ -360,7 +363,7 @@ extension ExtensionDialog on GetInterface {
}
}
return dialog(
return dialog<T>(
AlertDialog(
titlePadding: EdgeInsets.all(8),
contentPadding: EdgeInsets.all(8),
... ... @@ -484,8 +487,8 @@ extension GetNavigation on GetInterface {
if (preventDuplicates && routeName == currentRoute) {
return null;
}
return global(id)?.currentState?.push(
GetPageRoute(
return global(id)?.currentState?.push<T>(
GetPageRoute<T>(
opaque: opaque ?? true,
page: () => page,
routeName: routeName,
... ... @@ -528,7 +531,7 @@ extension GetNavigation on GetInterface {
if (preventDuplicates && page == currentRoute) {
return null;
}
return global(id)?.currentState?.pushNamed(page, arguments: arguments);
return global(id)?.currentState?.pushNamed<T>(page, arguments: arguments);
}
/// **Navigation.pushReplacementNamed()** shortcut.<br><br>
... ... @@ -601,7 +604,7 @@ extension GetNavigation on GetInterface {
Future<T> offUntil<T>(Route<T> page, RoutePredicate predicate, {int id}) {
// if (key.currentState.mounted) // add this if appear problems on future with route navigate
// when widget don't mounted
return global(id)?.currentState?.pushAndRemoveUntil(page, predicate);
return global(id)?.currentState?.pushAndRemoveUntil<T>(page, predicate);
}
/// **Navigation.pushNamedAndRemoveUntil()** shortcut.<br><br>
... ... @@ -629,7 +632,7 @@ extension GetNavigation on GetInterface {
}) {
return global(id)
?.currentState
?.pushNamedAndRemoveUntil(page, predicate, arguments: arguments);
?.pushNamedAndRemoveUntil<T>(page, predicate, arguments: arguments);
}
/// **Navigation.popAndPushNamed()** shortcut.<br><br>
... ... @@ -690,7 +693,7 @@ extension GetNavigation on GetInterface {
dynamic arguments,
int id,
}) {
return global(id)?.currentState?.pushNamedAndRemoveUntil(
return global(id)?.currentState?.pushNamedAndRemoveUntil<T>(
newRouteName,
predicate ?? (_) => false,
arguments: arguments,
... ... @@ -717,8 +720,8 @@ extension GetNavigation on GetInterface {
///
/// It has the advantage of not needing context, so you can call
/// from your business logic.
void back({
dynamic result,
void back<T>({
T result,
bool closeOverlays = false,
bool canPop = true,
int id,
... ... @@ -730,10 +733,10 @@ extension GetNavigation on GetInterface {
}
if (canPop) {
if (global(id)?.currentState?.canPop() == true) {
global(id)?.currentState?.pop(result);
global(id)?.currentState?.pop<T>(result);
}
} else {
global(id)?.currentState?.pop(result);
global(id)?.currentState?.pop<T>(result);
}
}
... ... @@ -854,8 +857,8 @@ extension GetNavigation on GetInterface {
}) {
var routeName = "/${page.runtimeType.toString()}";
return global(id)?.currentState?.pushAndRemoveUntil(
GetPageRoute(
return global(id)?.currentState?.pushAndRemoveUntil<T>(
GetPageRoute<T>(
opaque: opaque ?? true,
popGesture: popGesture ?? defaultPopGesture,
page: () => page,
... ...
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import '../../../get_instance/src/lifecycle.dart';
... ... @@ -10,45 +11,24 @@ import '../../../get_instance/src/lifecycle.dart';
abstract class GetxService extends DisposableInterface with GetxServiceMixin {}
abstract class DisposableInterface with GetLifeCycle {
bool _initialized = false;
/// Checks whether the controller has already been initialized.
bool get initialized => _initialized;
bool _isClosed = false;
/// Checks whether the controller has already been closed.
bool get isClosed => _isClosed;
DisposableInterface() {
onStart.callback = _onStart;
onDelete.callback = _onDelete;
}
// Internal callback that starts the cycle of this controller.
void _onStart() {
if (_initialized) return;
onInit();
_initialized = true;
SchedulerBinding.instance?.addPostFrameCallback((_) => onReady());
}
// Internal callback that starts the cycle of this controller.
void _onDelete() {
if (_isClosed) return;
_isClosed = true;
onClose();
initLifeCycle();
}
/// Called immediately after the widget is allocated in memory.
/// You might use this to initialize something for the controller.
@override
void onInit() {}
@mustCallSuper
void onInit() {
super.onInit();
SchedulerBinding.instance?.addPostFrameCallback((_) => onReady());
}
/// Called 1 frame after onInit(). It is the perfect place to enter
/// navigation events, like snackbar, dialogs, or a new route, or
/// async request.
@override
@mustCallSuper
void onReady() {}
/// Called before [onDelete] method. [onClose] might be used to
... ...
... ... @@ -38,36 +38,17 @@ typedef Condition = bool Function();
abstract class GetNotifier<T> extends Value<T> with GetLifeCycle {
GetNotifier(T initial) : super(initial) {
onStart.callback = _onStart;
onDelete.callback = _onDelete;
initLifeCycle();
_fillEmptyStatus();
}
bool _initialized = false;
/// Checks whether the controller has already been initialized.
bool get initialized => _initialized;
bool _isClosed = false;
/// Checks whether the controller has already been closed.
bool get isClosed => _isClosed;
// Internal callback that starts the cycle of this controller.
void _onStart() {
if (_initialized) return;
onInit();
_initialized = true;
@override
@mustCallSuper
void onInit() {
super.onInit();
SchedulerBinding.instance?.addPostFrameCallback((_) => onReady());
}
// Internal callback that starts the cycle of this controller.
void _onDelete() {
if (_isClosed) return;
_isClosed = true;
onClose();
}
RxStatus _status;
bool get isNullOrEmpty {
... ...
... ... @@ -4,22 +4,21 @@ import '../../../get_rx/get_rx.dart';
typedef WidgetCallback = Widget Function();
/// The simplest reactive widget in GetX.
///
/// Just pass your Rx variable in the root scope of the callback to have it
/// automatically registered for changes.
/// The [ObxWidget] is the base for all GetX reactive widgets
///
/// final _name = "GetX".obs;
/// Obx(() => Text( _name.value )),... ;
class Obx extends StatefulWidget {
final WidgetCallback builder;
const Obx(this.builder);
/// See also:
/// - [Obx]
/// - [ObxValue]
abstract class ObxWidget extends StatefulWidget {
const ObxWidget({Key key}) : super(key: key);
_ObxState createState() => _ObxState();
@protected
Widget build();
}
class _ObxState extends State<Obx> {
class _ObxState extends State<ObxWidget> {
RxInterface _observer;
StreamSubscription subs;
... ... @@ -43,7 +42,7 @@ class _ObxState extends State<Obx> {
Widget get notifyChilds {
final observer = getObs;
getObs = _observer;
final result = widget.builder();
final result = widget.build();
if (!_observer.canUpdate) {
throw """
[Get] the improper use of a GetX has been detected.
... ... @@ -62,6 +61,22 @@ class _ObxState extends State<Obx> {
Widget build(BuildContext context) => notifyChilds;
}
/// The simplest reactive widget in GetX.
///
/// Just pass your Rx variable in the root scope of the callback to have it
/// automatically registered for changes.
///
/// final _name = "GetX".obs;
/// Obx(() => Text( _name.value )),... ;
class Obx extends ObxWidget {
final WidgetCallback builder;
const Obx(this.builder);
@override
Widget build() => builder();
}
/// Similar to Obx, but manages a local state.
/// Pass the initial data in constructor.
/// Useful for simple local states, like toggles, visibility, themes,
... ... @@ -76,45 +91,12 @@ class _ObxState extends State<Obx> {
// TODO: change T to a proper Rx interface, that includes the accessor
// for ::value
class ObxValue<T extends RxInterface> extends StatefulWidget {
class ObxValue<T extends RxInterface> extends ObxWidget {
final Widget Function(T) builder;
final T data;
const ObxValue(this.builder, this.data, {Key key}) : super(key: key);
_ObxValueState createState() => _ObxValueState();
}
class _ObxValueState extends State<ObxValue> {
RxInterface _observer;
StreamSubscription subs;
_ObxValueState() {
_observer = Rx();
}
@override
void initState() {
subs = _observer.subject.stream.listen((data) => setState(() {}));
super.initState();
}
@override
void dispose() {
subs.cancel();
_observer.close();
super.dispose();
}
Widget get notifyChilds {
final observer = getObs;
getObs = _observer;
// observable is implicity taken from the constructor.
final result = widget.builder(widget.data);
getObs = observer;
return result;
}
@override
Widget build(BuildContext context) => notifyChilds;
Widget build() => builder(data);
}
... ...
... ... @@ -39,17 +39,21 @@ abstract class GetView<T> extends StatelessWidget {
Widget build(BuildContext context);
}
class _Wrapper<T> {
T data;
}
abstract class GetWidget<T extends DisposableInterface>
extends StatelessWidget {
GetWidget({Key key}) : super(key: key);
final Set<T> _value = <T>{};
final _value = _Wrapper<T>();
final String tag = null;
T get controller {
if (_value.isEmpty) _value.add(GetInstance().find<T>(tag: tag));
return _value.first;
_value.data ??= GetInstance().find<T>(tag: tag);
return _value.data;
}
@override
... ...
... ... @@ -203,8 +203,10 @@ class GetUtils {
r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$');
/// Checks if string is phone number.
static bool isPhoneNumber(String s) => hasMatch(s,
r'^(0|\+|(\+[0-9]{2,4}|\(\+?[0-9]{2,4}\)) ?)([0-9]*|\d{2,4}-\d{2,4}(-\d{2,4})?)$');
static bool isPhoneNumber(String s) {
if (s == null || s.length > 16 || s.length < 9) return false;
return hasMatch(s, r'^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\s\./0-9]*$');
}
/// Checks if string is DateTime (UTC or Iso8601).
static bool isDateTime(String s) =>
... ...
... ... @@ -4,11 +4,12 @@ version: 3.13.2
homepage: https://github.com/jonataslaw/getx
environment:
sdk: ">=2.8.0 <3.0.0"
sdk: ">=2.10.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
meta: 1.3.0-nullsafety.3
dev_dependencies:
flutter_test:
... ...
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:get/state_manager.dart';
int times = 3;
int get last => times - 1;
Future<String> valueNotifier() {
final c = Completer<String>();
final value = ValueNotifier<int>(0);
final timer = Stopwatch();
timer.start();
value.addListener(() {
if (last == value.value) {
timer.stop();
c.complete("""${value.value} item value notifier
objs time: ${timer.elapsedMicroseconds}ms""");
}
});
for (var i = 0; i < times; i++) {
value.value = i;
}
return c.future;
}
Future<String> getValue() {
final c = Completer<String>();
final value = Value<int>(0);
final timer = Stopwatch();
timer.start();
value.addListener(() {
if (last == value.value) {
timer.stop();
c.complete("""${value.value} item get value objs
time: ${timer.elapsedMicroseconds}ms""");
}
});
for (var i = 0; i < times; i++) {
value.value = i;
}
return c.future;
}
Future<String> getStream() {
final c = Completer<String>();
final value = StreamController<int>();
final timer = Stopwatch();
timer.start();
value.stream.listen((v) {
if (last == v) {
timer.stop();
c.complete("$v item stream objs time: ${timer.elapsedMicroseconds}ms");
}
});
for (var i = 0; i < times; i++) {
value.add(i);
}
return c.future;
}
void main() async {
print(await getValue());
print(await valueNotifier());
print(await getStream());
times = 30000;
print(await getValue());
print(await valueNotifier());
print(await getStream());
}
typedef VoidCallback = void Function();
... ...
... ... @@ -11,29 +11,7 @@ class Mock {
class DisposableController with GetLifeCycle {
DisposableController() {
onStart.callback = _onStart;
onDelete.callback = _onDelete;
}
// Internal callback that starts the cycle of this controller.
void _onStart() {
if (initialized) return;
onInit();
}
// Internal callback that starts the cycle of this controller.
void _onDelete() {
if (isClosed) return;
isClosed = true;
onClose();
}
bool initialized = false;
bool isClosed = false;
void onInit() async {
initialized = true;
initLifeCycle();
}
}
... ...
... ... @@ -73,7 +73,6 @@ void main() {
var expected = '';
void logFunction(String prefix, dynamic value, String info,
{bool isError = false}) {
print('algo');
expected = '$prefix $value $info'.trim();
}
... ...
... ... @@ -239,14 +239,12 @@ void main() {
'(455) 443-8171',
'(915) 685-8658',
'(572) 207-1898',
// TODO those are failing, but they shouldn't
// '(81) 6 2499-9538',
// '(31) 32304-4263',
// '(64) 25242-6375',
// '(41) 19308-7925',
// '(67) 61684-0395',
// '(60) 54706-3569',
'(81) 6 2499-9538',
'(31) 32304-4263',
'(64) 25242-6375',
'(41) 19308-7925',
'(67) 61684-0395',
'(60) 54706-3569',
'(31) 33110055',
'(11) 3344-5599',
'(31) 977447788',
... ...