new change to use intaleq_map sdk 04-16-4
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../get.dart';
|
||||
|
||||
/// Unlike GetxController, which serves to control events on each of its pages,
|
||||
/// GetxService is not automatically disposed (nor can be removed with
|
||||
/// Get.delete()).
|
||||
/// It is ideal for situations where, once started, that service will
|
||||
/// remain in memory, such as Auth control for example. Only way to remove
|
||||
/// it is Get.reset().
|
||||
abstract class GetxService extends DisposableInterface with GetxServiceMixin {}
|
||||
|
||||
abstract class DisposableInterface extends GetLifeCycle {
|
||||
/// Called immediately after the widget is allocated in memory.
|
||||
/// You might use this to initialize something for the controller.
|
||||
@override
|
||||
@mustCallSuper
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
|
||||
Get.engine.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
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
}
|
||||
|
||||
/// 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.
|
||||
@override
|
||||
void onClose() {
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import '../../../get_core/get_core.dart';
|
||||
import '../../../get_instance/src/get_instance.dart';
|
||||
import '../../../get_rx/src/rx_types/rx_types.dart';
|
||||
import '../../get_state_manager.dart';
|
||||
|
||||
typedef GetXControllerBuilder<T extends DisposableInterface> = Widget Function(
|
||||
T controller);
|
||||
|
||||
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,
|
||||
dispose,
|
||||
didChangeDependencies;
|
||||
final void Function(GetX oldWidget, GetXState<T> state)? didUpdateWidget;
|
||||
final T? init;
|
||||
final String? tag;
|
||||
|
||||
const GetX({
|
||||
this.tag,
|
||||
required this.builder,
|
||||
this.global = true,
|
||||
this.autoRemove = true,
|
||||
this.initState,
|
||||
this.assignId = false,
|
||||
// this.stream,
|
||||
this.dispose,
|
||||
this.didChangeDependencies,
|
||||
this.didUpdateWidget,
|
||||
this.init,
|
||||
// this.streamController
|
||||
});
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties
|
||||
..add(
|
||||
DiagnosticsProperty<T>('controller', init),
|
||||
)
|
||||
..add(DiagnosticsProperty<String>('tag', tag))
|
||||
..add(
|
||||
ObjectFlagProperty<GetXControllerBuilder<T>>.has('builder', builder));
|
||||
}
|
||||
|
||||
@override
|
||||
GetXState<T> createState() => GetXState<T>();
|
||||
}
|
||||
|
||||
class GetXState<T extends DisposableInterface> extends State<GetX<T>> {
|
||||
final _observer = RxNotifier();
|
||||
T? controller;
|
||||
bool? _isCreator = false;
|
||||
late StreamSubscription _subs;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
// var isPrepared = GetInstance().isPrepared<T>(tag: widget.tag);
|
||||
final isRegistered = GetInstance().isRegistered<T>(tag: widget.tag);
|
||||
|
||||
if (widget.global) {
|
||||
if (isRegistered) {
|
||||
_isCreator = GetInstance().isPrepared<T>(tag: widget.tag);
|
||||
controller = GetInstance().find<T>(tag: widget.tag);
|
||||
} else {
|
||||
controller = widget.init;
|
||||
_isCreator = true;
|
||||
GetInstance().put<T>(controller!, tag: widget.tag);
|
||||
}
|
||||
} else {
|
||||
controller = widget.init;
|
||||
_isCreator = true;
|
||||
controller?.onStart();
|
||||
}
|
||||
widget.initState?.call(this);
|
||||
if (widget.global && Get.smartManagement == SmartManagement.onlyBuilder) {
|
||||
controller?.onStart();
|
||||
}
|
||||
_subs = _observer.listen((data) => setState(() {}), cancelOnError: false);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
if (widget.didChangeDependencies != null) {
|
||||
widget.didChangeDependencies!(this);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(GetX oldWidget) {
|
||||
super.didUpdateWidget(oldWidget as GetX<T>);
|
||||
widget.didUpdateWidget?.call(oldWidget, this);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (widget.dispose != null) widget.dispose!(this);
|
||||
if (_isCreator! || widget.assignId) {
|
||||
if (widget.autoRemove && GetInstance().isRegistered<T>(tag: widget.tag)) {
|
||||
GetInstance().delete<T>(tag: widget.tag);
|
||||
}
|
||||
}
|
||||
_subs.cancel();
|
||||
_observer.close();
|
||||
controller = null;
|
||||
_isCreator = null;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties.add(DiagnosticsProperty<T>('controller', controller));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => RxInterface.notifyChildren(
|
||||
_observer,
|
||||
() => widget.builder(controller!),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
|
||||
import '../../../instance_manager.dart';
|
||||
import '../../get_state_manager.dart';
|
||||
import '../simple/list_notifier.dart';
|
||||
|
||||
mixin StateMixin<T> on ListNotifierMixin {
|
||||
T? _value;
|
||||
RxStatus? _status;
|
||||
|
||||
bool _isNullOrEmpty(dynamic val) {
|
||||
if (val == null) return true;
|
||||
var result = false;
|
||||
if (val is Iterable) {
|
||||
result = val.isEmpty;
|
||||
} else if (val is String) {
|
||||
result = val.isEmpty;
|
||||
} else if (val is Map) {
|
||||
result = val.isEmpty;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void _fillEmptyStatus() {
|
||||
_status = _isNullOrEmpty(_value) ? RxStatus.loading() : RxStatus.success();
|
||||
}
|
||||
|
||||
RxStatus get status {
|
||||
notifyChildrens();
|
||||
return _status ??= _status = RxStatus.loading();
|
||||
}
|
||||
|
||||
T? get state => value;
|
||||
|
||||
@protected
|
||||
T? get value {
|
||||
notifyChildrens();
|
||||
return _value;
|
||||
}
|
||||
|
||||
@protected
|
||||
set value(T? newValue) {
|
||||
if (_value == newValue) return;
|
||||
_value = newValue;
|
||||
refresh();
|
||||
}
|
||||
|
||||
@protected
|
||||
void change(T? newState, {RxStatus? status}) {
|
||||
var _canUpdate = false;
|
||||
if (status != null) {
|
||||
_status = status;
|
||||
_canUpdate = true;
|
||||
}
|
||||
if (newState != _value) {
|
||||
_value = newState;
|
||||
_canUpdate = true;
|
||||
}
|
||||
if (_canUpdate) {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void append(Future<T> Function() body(), {String? errorMessage}) {
|
||||
final compute = body();
|
||||
compute().then((newValue) {
|
||||
change(newValue, status: RxStatus.success());
|
||||
}, onError: (err) {
|
||||
change(state, status: RxStatus.error(errorMessage ?? err.toString()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class Value<T> extends ListNotifier
|
||||
with StateMixin<T>
|
||||
implements ValueListenable<T?> {
|
||||
Value(T val) {
|
||||
_value = val;
|
||||
_fillEmptyStatus();
|
||||
}
|
||||
|
||||
@override
|
||||
T? get value {
|
||||
notifyChildrens();
|
||||
return _value;
|
||||
}
|
||||
|
||||
@override
|
||||
set value(T? newValue) {
|
||||
if (_value == newValue) return;
|
||||
_value = newValue;
|
||||
refresh();
|
||||
}
|
||||
|
||||
T? call([T? v]) {
|
||||
if (v != null) {
|
||||
value = v;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void update(void fn(T? value)) {
|
||||
fn(value);
|
||||
refresh();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => value.toString();
|
||||
|
||||
dynamic toJson() => (value as dynamic)?.toJson();
|
||||
}
|
||||
|
||||
extension ReactiveT<T> on T {
|
||||
Value<T> get reactive => Value<T>(this);
|
||||
}
|
||||
|
||||
typedef Condition = bool Function();
|
||||
|
||||
abstract class GetNotifier<T> extends Value<T> with GetLifeCycleBase {
|
||||
GetNotifier(T initial) : super(initial) {
|
||||
$configureLifeCycle();
|
||||
}
|
||||
|
||||
@override
|
||||
@mustCallSuper
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
ambiguate(SchedulerBinding.instance)
|
||||
?.addPostFrameCallback((_) => onReady());
|
||||
}
|
||||
}
|
||||
|
||||
extension StateExt<T> on StateMixin<T> {
|
||||
Widget obx(
|
||||
NotifierBuilder<T?> widget, {
|
||||
Widget Function(String? error)? onError,
|
||||
Widget? onLoading,
|
||||
Widget? onEmpty,
|
||||
}) {
|
||||
return SimpleBuilder(builder: (_) {
|
||||
if (status.isLoading) {
|
||||
return onLoading ?? const Center(child: CircularProgressIndicator());
|
||||
} else if (status.isError) {
|
||||
return onError != null
|
||||
? onError(status.errorMessage)
|
||||
: Center(child: Text('A error occurred: ${status.errorMessage}'));
|
||||
} else if (status.isEmpty) {
|
||||
return onEmpty != null
|
||||
? onEmpty
|
||||
: SizedBox.shrink(); // Also can be widget(null); but is risky
|
||||
}
|
||||
return widget(value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class RxStatus {
|
||||
final bool isLoading;
|
||||
final bool isError;
|
||||
final bool isSuccess;
|
||||
final bool isEmpty;
|
||||
final bool isLoadingMore;
|
||||
final String? errorMessage;
|
||||
|
||||
RxStatus._({
|
||||
this.isEmpty = false,
|
||||
this.isLoading = false,
|
||||
this.isError = false,
|
||||
this.isSuccess = false,
|
||||
this.errorMessage,
|
||||
this.isLoadingMore = false,
|
||||
});
|
||||
|
||||
factory RxStatus.loading() {
|
||||
return RxStatus._(isLoading: true);
|
||||
}
|
||||
|
||||
factory RxStatus.loadingMore() {
|
||||
return RxStatus._(isSuccess: true, isLoadingMore: true);
|
||||
}
|
||||
|
||||
factory RxStatus.success() {
|
||||
return RxStatus._(isSuccess: true);
|
||||
}
|
||||
|
||||
factory RxStatus.error([String? message]) {
|
||||
return RxStatus._(isError: true, errorMessage: message);
|
||||
}
|
||||
|
||||
factory RxStatus.empty() {
|
||||
return RxStatus._(isEmpty: true);
|
||||
}
|
||||
}
|
||||
|
||||
typedef NotifierBuilder<T> = Widget Function(T state);
|
||||
@@ -0,0 +1,92 @@
|
||||
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();
|
||||
|
||||
/// The [ObxWidget] is the base for all GetX reactive widgets
|
||||
///
|
||||
/// See also:
|
||||
/// - [Obx]
|
||||
/// - [ObxValue]
|
||||
abstract class ObxWidget extends StatefulWidget {
|
||||
const ObxWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties..add(ObjectFlagProperty<Function>.has('builder', build));
|
||||
}
|
||||
|
||||
@override
|
||||
_ObxState createState() => _ObxState();
|
||||
|
||||
@protected
|
||||
Widget build();
|
||||
}
|
||||
|
||||
class _ObxState extends State<ObxWidget> {
|
||||
final _observer = RxNotifier();
|
||||
late StreamSubscription subs;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
subs = _observer.listen(_updateTree, cancelOnError: false);
|
||||
}
|
||||
|
||||
void _updateTree(_) {
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
subs.cancel();
|
||||
_observer.close();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) =>
|
||||
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
|
||||
/// 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,
|
||||
/// button states, etc.
|
||||
/// Sample:
|
||||
/// ObxValue((data) => Switch(
|
||||
/// value: data.value,
|
||||
/// onChanged: (flag) => data.value = flag,
|
||||
/// ),
|
||||
/// false.obs,
|
||||
/// ),
|
||||
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);
|
||||
|
||||
@override
|
||||
Widget build() => builder(data);
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
|
||||
import '../../get_state_manager.dart';
|
||||
|
||||
/// Used like `SingleTickerProviderMixin` but only with Get Controllers.
|
||||
/// Simplifies AnimationController creation inside GetxController.
|
||||
///
|
||||
/// Example:
|
||||
///```
|
||||
///class SplashController extends GetxController with
|
||||
/// GetSingleTickerProviderStateMixin {
|
||||
/// AnimationController controller;
|
||||
///
|
||||
/// @override
|
||||
/// void onInit() {
|
||||
/// final duration = const Duration(seconds: 2);
|
||||
/// controller =
|
||||
/// AnimationController.unbounded(duration: duration, vsync: this);
|
||||
/// controller.repeat();
|
||||
/// controller.addListener(() =>
|
||||
/// print("Animation Controller value: ${controller.value}"));
|
||||
/// }
|
||||
/// ...
|
||||
/// ```
|
||||
mixin GetSingleTickerProviderStateMixin on GetxController
|
||||
implements TickerProvider {
|
||||
Ticker? _ticker;
|
||||
|
||||
@override
|
||||
Ticker createTicker(TickerCallback onTick) {
|
||||
assert(() {
|
||||
if (_ticker == null) return true;
|
||||
throw FlutterError.fromParts(<DiagnosticsNode>[
|
||||
ErrorSummary(
|
||||
'$runtimeType is a GetSingleTickerProviderStateMixin but multiple tickers were created.'),
|
||||
ErrorDescription(
|
||||
'A GetSingleTickerProviderStateMixin can only be used as a TickerProvider once.'),
|
||||
ErrorHint(
|
||||
'If a State is used for multiple AnimationController objects, or if it is passed to other '
|
||||
'objects and those objects might use it more than one time in total, then instead of '
|
||||
'mixing in a GetSingleTickerProviderStateMixin, use a regular GetTickerProviderStateMixin.',
|
||||
),
|
||||
]);
|
||||
}());
|
||||
_ticker =
|
||||
Ticker(onTick, debugLabel: kDebugMode ? 'created by $this' : null);
|
||||
// We assume that this is called from initState, build, or some sort of
|
||||
// event handler, and that thus TickerMode.of(context) would return true. We
|
||||
// can't actually check that here because if we're in initState then we're
|
||||
// not allowed to do inheritance checks yet.
|
||||
return _ticker!;
|
||||
}
|
||||
|
||||
void didChangeDependencies(BuildContext context) {
|
||||
if (_ticker != null) _ticker!.muted = !TickerMode.of(context);
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
assert(() {
|
||||
if (_ticker == null || !_ticker!.isActive) return true;
|
||||
throw FlutterError.fromParts(<DiagnosticsNode>[
|
||||
ErrorSummary('$this was disposed with an active Ticker.'),
|
||||
ErrorDescription(
|
||||
'$runtimeType created a Ticker via its GetSingleTickerProviderStateMixin, but at the time '
|
||||
'dispose() was called on the mixin, that Ticker was still active. The Ticker must '
|
||||
'be disposed before calling super.dispose().',
|
||||
),
|
||||
ErrorHint(
|
||||
'Tickers used by AnimationControllers '
|
||||
'should be disposed by calling dispose() on the AnimationController itself. '
|
||||
'Otherwise, the ticker will leak.',
|
||||
),
|
||||
_ticker!.describeForError('The offending ticker was'),
|
||||
]);
|
||||
}());
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
/// Used like `TickerProviderMixin` but only with Get Controllers.
|
||||
/// Simplifies multiple AnimationController creation inside GetxController.
|
||||
///
|
||||
/// Example:
|
||||
///```
|
||||
///class SplashController extends GetxController with
|
||||
/// GetTickerProviderStateMixin {
|
||||
/// AnimationController first_controller;
|
||||
/// AnimationController second_controller;
|
||||
///
|
||||
/// @override
|
||||
/// void onInit() {
|
||||
/// final duration = const Duration(seconds: 2);
|
||||
/// first_controller =
|
||||
/// AnimationController.unbounded(duration: duration, vsync: this);
|
||||
/// second_controller =
|
||||
/// AnimationController.unbounded(duration: duration, vsync: this);
|
||||
/// first_controller.repeat();
|
||||
/// first_controller.addListener(() =>
|
||||
/// print("Animation Controller value: ${first_controller.value}"));
|
||||
/// second_controller.addListener(() =>
|
||||
/// print("Animation Controller value: ${second_controller.value}"));
|
||||
/// }
|
||||
/// ...
|
||||
/// ```
|
||||
mixin GetTickerProviderStateMixin on GetxController implements TickerProvider {
|
||||
Set<Ticker>? _tickers;
|
||||
|
||||
@override
|
||||
Ticker createTicker(TickerCallback onTick) {
|
||||
_tickers ??= <_WidgetTicker>{};
|
||||
final result = _WidgetTicker(onTick, this,
|
||||
debugLabel: kDebugMode ? 'created by ${describeIdentity(this)}' : null);
|
||||
_tickers!.add(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void _removeTicker(_WidgetTicker ticker) {
|
||||
assert(_tickers != null);
|
||||
assert(_tickers!.contains(ticker));
|
||||
_tickers!.remove(ticker);
|
||||
}
|
||||
|
||||
void didChangeDependencies(BuildContext context) {
|
||||
final muted = !TickerMode.of(context);
|
||||
if (_tickers != null) {
|
||||
for (final ticker in _tickers!) {
|
||||
ticker.muted = muted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
assert(() {
|
||||
if (_tickers != null) {
|
||||
for (final ticker in _tickers!) {
|
||||
if (ticker.isActive) {
|
||||
throw FlutterError.fromParts(<DiagnosticsNode>[
|
||||
ErrorSummary('$this was disposed with an active Ticker.'),
|
||||
ErrorDescription(
|
||||
'$runtimeType created a Ticker via its GetTickerProviderStateMixin, but at the time '
|
||||
'dispose() was called on the mixin, that Ticker was still active. All Tickers must '
|
||||
'be disposed before calling super.dispose().',
|
||||
),
|
||||
ErrorHint(
|
||||
'Tickers used by AnimationControllers '
|
||||
'should be disposed by calling dispose() on the AnimationController itself. '
|
||||
'Otherwise, the ticker will leak.',
|
||||
),
|
||||
ticker.describeForError('The offending ticker was'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}());
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
class _WidgetTicker extends Ticker {
|
||||
_WidgetTicker(TickerCallback onTick, this._creator, {String? debugLabel})
|
||||
: super(onTick, debugLabel: debugLabel);
|
||||
|
||||
final GetTickerProviderStateMixin _creator;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_creator._removeTicker(this);
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated('use GetSingleTickerProviderStateMixin')
|
||||
|
||||
/// 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 {
|
||||
@override
|
||||
Ticker createTicker(TickerCallback onTick) => Ticker(onTick);
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
// ignore: prefer_mixin
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import '../../../instance_manager.dart';
|
||||
import '../rx_flutter/rx_disposable.dart';
|
||||
import '../rx_flutter/rx_notifier.dart';
|
||||
import 'list_notifier.dart';
|
||||
|
||||
// ignore: prefer_mixin
|
||||
abstract class GetxController extends DisposableInterface
|
||||
with ListenableMixin, ListNotifierMixin {
|
||||
/// Rebuilds `GetBuilder` each time you call `update()`;
|
||||
/// Can take a List of [ids], that will only update the matching
|
||||
/// `GetBuilder( id: )`,
|
||||
/// [ids] can be reused among `GetBuilders` like group tags.
|
||||
/// The update will only notify the Widgets, if [condition] is true.
|
||||
void update([List<Object>? ids, bool condition = true]) {
|
||||
if (!condition) {
|
||||
return;
|
||||
}
|
||||
if (ids == null) {
|
||||
refresh();
|
||||
} else {
|
||||
for (final id in ids) {
|
||||
refreshGroup(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mixin ScrollMixin on GetLifeCycleBase {
|
||||
final ScrollController scroll = ScrollController();
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
scroll.addListener(_listener);
|
||||
}
|
||||
|
||||
bool _canFetchBottom = true;
|
||||
|
||||
bool _canFetchTop = true;
|
||||
|
||||
void _listener() {
|
||||
if (scroll.position.atEdge) {
|
||||
_checkIfCanLoadMore();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _checkIfCanLoadMore() async {
|
||||
if (scroll.position.pixels == 0) {
|
||||
if (!_canFetchTop) return;
|
||||
_canFetchTop = false;
|
||||
await onTopScroll();
|
||||
_canFetchTop = true;
|
||||
} else {
|
||||
if (!_canFetchBottom) return;
|
||||
_canFetchBottom = false;
|
||||
await onEndScroll();
|
||||
_canFetchBottom = true;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> onEndScroll();
|
||||
|
||||
Future<void> onTopScroll();
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
scroll.removeListener(_listener);
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class RxController extends DisposableInterface {}
|
||||
|
||||
abstract class SuperController<T> extends FullLifeCycleController
|
||||
with FullLifeCycleMixin, StateMixin<T> {}
|
||||
|
||||
abstract class FullLifeCycleController extends GetxController
|
||||
with
|
||||
// ignore: prefer_mixin
|
||||
WidgetsBindingObserver {}
|
||||
|
||||
mixin FullLifeCycleMixin on FullLifeCycleController {
|
||||
@mustCallSuper
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
ambiguate(WidgetsBinding.instance)?.addObserver(this);
|
||||
}
|
||||
|
||||
@mustCallSuper
|
||||
@override
|
||||
void onClose() {
|
||||
ambiguate(WidgetsBinding.instance)?.removeObserver(this);
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
@mustCallSuper
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
switch (state) {
|
||||
case AppLifecycleState.resumed:
|
||||
onResumed();
|
||||
break;
|
||||
case AppLifecycleState.inactive:
|
||||
onInactive();
|
||||
break;
|
||||
case AppLifecycleState.paused:
|
||||
onPaused();
|
||||
break;
|
||||
case AppLifecycleState.detached:
|
||||
onDetached();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void onResumed();
|
||||
void onPaused();
|
||||
void onInactive();
|
||||
void onDetached();
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import '../../../get.dart';
|
||||
|
||||
mixin GetResponsiveMixin on Widget {
|
||||
ResponsiveScreen get screen;
|
||||
bool get alwaysUseBuilder;
|
||||
|
||||
@protected
|
||||
Widget build(BuildContext context) {
|
||||
screen.context = context;
|
||||
Widget? widget;
|
||||
if (alwaysUseBuilder) {
|
||||
widget = builder();
|
||||
if (widget != null) return widget;
|
||||
}
|
||||
if (screen.isDesktop) {
|
||||
widget = desktop() ?? widget;
|
||||
if (widget != null) return widget;
|
||||
}
|
||||
if (screen.isTablet) {
|
||||
widget = tablet() ?? desktop();
|
||||
if (widget != null) return widget;
|
||||
}
|
||||
if (screen.isPhone) {
|
||||
widget = phone() ?? tablet() ?? desktop();
|
||||
if (widget != null) return widget;
|
||||
}
|
||||
return watch() ?? phone() ?? tablet() ?? desktop() ?? builder()!;
|
||||
}
|
||||
|
||||
Widget? builder() => null;
|
||||
|
||||
Widget? desktop() => null;
|
||||
|
||||
Widget? phone() => null;
|
||||
|
||||
Widget? tablet() => null;
|
||||
|
||||
Widget? watch() => null;
|
||||
}
|
||||
|
||||
/// Extend this widget to build responsive view.
|
||||
/// this widget contains the `screen` property that have all
|
||||
/// information about the screen size and type.
|
||||
/// You have two options to build it.
|
||||
/// 1- with `builder` method you return the widget to build.
|
||||
/// 2- with methods `desktop`, `tablet`,`phone`, `watch`. the specific
|
||||
/// method will be built when the screen type matches the method
|
||||
/// when the screen is [ScreenType.Tablet] the `tablet` method
|
||||
/// will be exuded and so on.
|
||||
/// Note if you use this method please set the
|
||||
/// property `alwaysUseBuilder` to false
|
||||
/// With `settings` property you can set the width limit for the screen types.
|
||||
class GetResponsiveView<T> extends GetView<T> with GetResponsiveMixin {
|
||||
@override
|
||||
final bool alwaysUseBuilder;
|
||||
|
||||
@override
|
||||
final ResponsiveScreen screen;
|
||||
|
||||
GetResponsiveView({
|
||||
this.alwaysUseBuilder = false,
|
||||
ResponsiveScreenSettings settings = const ResponsiveScreenSettings(),
|
||||
Key? key,
|
||||
}) : screen = ResponsiveScreen(settings),
|
||||
super(key: key);
|
||||
}
|
||||
|
||||
class GetResponsiveWidget<T extends GetLifeCycleBase?> extends GetWidget<T>
|
||||
with GetResponsiveMixin {
|
||||
@override
|
||||
final bool alwaysUseBuilder;
|
||||
|
||||
@override
|
||||
final ResponsiveScreen screen;
|
||||
|
||||
GetResponsiveWidget({
|
||||
this.alwaysUseBuilder = false,
|
||||
ResponsiveScreenSettings settings = const ResponsiveScreenSettings(),
|
||||
Key? key,
|
||||
}) : screen = ResponsiveScreen(settings),
|
||||
super(key: key);
|
||||
}
|
||||
|
||||
class ResponsiveScreenSettings {
|
||||
/// When the width is greater als this value
|
||||
/// the display will be set as [ScreenType.Desktop]
|
||||
final double desktopChangePoint;
|
||||
|
||||
/// When the width is greater als this value
|
||||
/// the display will be set as [ScreenType.Tablet]
|
||||
/// or when width greater als [watchChangePoint] and smaller als this value
|
||||
/// the display will be [ScreenType.Phone]
|
||||
final double tabletChangePoint;
|
||||
|
||||
/// When the width is smaller als this value
|
||||
/// the display will be set as [ScreenType.Watch]
|
||||
/// or when width greater als this value and smaller als [tabletChangePoint]
|
||||
/// the display will be [ScreenType.Phone]
|
||||
final double watchChangePoint;
|
||||
|
||||
const ResponsiveScreenSettings(
|
||||
{this.desktopChangePoint = 1200,
|
||||
this.tabletChangePoint = 600,
|
||||
this.watchChangePoint = 300});
|
||||
}
|
||||
|
||||
class ResponsiveScreen {
|
||||
late BuildContext context;
|
||||
final ResponsiveScreenSettings settings;
|
||||
|
||||
late bool _isPaltformDesktop;
|
||||
ResponsiveScreen(this.settings) {
|
||||
_isPaltformDesktop = GetPlatform.isDesktop;
|
||||
}
|
||||
|
||||
double get height => context.height;
|
||||
double get width => context.width;
|
||||
|
||||
/// Is [screenType] [ScreenType.Desktop]
|
||||
bool get isDesktop => (screenType == ScreenType.Desktop);
|
||||
|
||||
/// Is [screenType] [ScreenType.Tablet]
|
||||
bool get isTablet => (screenType == ScreenType.Tablet);
|
||||
|
||||
/// Is [screenType] [ScreenType.Phone]
|
||||
bool get isPhone => (screenType == ScreenType.Phone);
|
||||
|
||||
/// Is [screenType] [ScreenType.Watch]
|
||||
bool get isWatch => (screenType == ScreenType.Watch);
|
||||
|
||||
double get _getdeviceWidth {
|
||||
if (_isPaltformDesktop) {
|
||||
return width;
|
||||
}
|
||||
return context.mediaQueryShortestSide;
|
||||
}
|
||||
|
||||
ScreenType get screenType {
|
||||
final deviceWidth = _getdeviceWidth;
|
||||
if (deviceWidth >= settings.desktopChangePoint) return ScreenType.Desktop;
|
||||
if (deviceWidth >= settings.tabletChangePoint) return ScreenType.Tablet;
|
||||
if (deviceWidth < settings.watchChangePoint) return ScreenType.Watch;
|
||||
return ScreenType.Phone;
|
||||
}
|
||||
|
||||
/// Return widget according to screen type
|
||||
/// if the [screenType] is [ScreenType.Desktop] and
|
||||
/// `desktop` object is null the `tablet` object will be returned
|
||||
/// and if `tablet` object is null the `mobile` object will be returned
|
||||
/// and if `mobile` object is null the `watch` object will be returned
|
||||
/// also when it is null.
|
||||
T? responsiveValue<T>({
|
||||
T? mobile,
|
||||
T? tablet,
|
||||
T? desktop,
|
||||
T? watch,
|
||||
}) {
|
||||
if (isDesktop && desktop != null) return desktop;
|
||||
if (isTablet && tablet != null) return tablet;
|
||||
if (isPhone && mobile != null) return mobile;
|
||||
return watch;
|
||||
}
|
||||
}
|
||||
|
||||
enum ScreenType {
|
||||
Watch,
|
||||
Phone,
|
||||
Tablet,
|
||||
Desktop,
|
||||
}
|
||||
226
packages/get/lib/get_state_manager/src/simple/get_state.dart
Normal file
226
packages/get/lib/get_state_manager/src/simple/get_state.dart
Normal file
@@ -0,0 +1,226 @@
|
||||
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 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;
|
||||
final bool global;
|
||||
final Object? id;
|
||||
final String? tag;
|
||||
final bool autoRemove;
|
||||
final bool assignId;
|
||||
final Object Function(T value)? filter;
|
||||
final void Function(GetBuilderState<T> state)? initState,
|
||||
dispose,
|
||||
didChangeDependencies;
|
||||
final void Function(GetBuilder oldWidget, GetBuilderState<T> state)?
|
||||
didUpdateWidget;
|
||||
final T? init;
|
||||
|
||||
const GetBuilder({
|
||||
Key? key,
|
||||
this.init,
|
||||
this.global = true,
|
||||
required this.builder,
|
||||
this.autoRemove = true,
|
||||
this.assignId = false,
|
||||
this.initState,
|
||||
this.filter,
|
||||
this.tag,
|
||||
this.dispose,
|
||||
this.id,
|
||||
this.didChangeDependencies,
|
||||
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
|
||||
GetBuilderState<T> createState() => GetBuilderState<T>();
|
||||
}
|
||||
|
||||
class GetBuilderState<T extends GetxController> extends State<GetBuilder<T>>
|
||||
with GetStateUpdaterMixin {
|
||||
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);
|
||||
|
||||
if (widget.global) {
|
||||
if (isRegistered) {
|
||||
if (GetInstance().isPrepared<T>(tag: widget.tag)) {
|
||||
_isCreator = true;
|
||||
} else {
|
||||
_isCreator = false;
|
||||
}
|
||||
controller = GetInstance().find<T>(tag: widget.tag);
|
||||
} else {
|
||||
controller = widget.init;
|
||||
_isCreator = true;
|
||||
GetInstance().put<T>(controller!, tag: widget.tag);
|
||||
}
|
||||
} else {
|
||||
controller = widget.init;
|
||||
_isCreator = true;
|
||||
controller?.onStart();
|
||||
}
|
||||
|
||||
if (widget.filter != null) {
|
||||
_filter = widget.filter!(controller!);
|
||||
}
|
||||
|
||||
_subscribeToController();
|
||||
}
|
||||
|
||||
/// Register to listen Controller's events.
|
||||
/// It gets a reference to the remove() callback, to delete the
|
||||
/// setState "link" from the Controller.
|
||||
void _subscribeToController() {
|
||||
_remove?.call();
|
||||
_remove = (widget.id == null)
|
||||
? controller?.addListener(
|
||||
_filter != null ? _filterUpdate : getUpdate,
|
||||
)
|
||||
: controller?.addListenerId(
|
||||
widget.id,
|
||||
_filter != null ? _filterUpdate : getUpdate,
|
||||
);
|
||||
}
|
||||
|
||||
void _filterUpdate() {
|
||||
var newFilter = widget.filter!(controller!);
|
||||
if (newFilter != _filter) {
|
||||
_filter = newFilter;
|
||||
getUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
widget.dispose?.call(this);
|
||||
if (_isCreator! || widget.assignId) {
|
||||
if (widget.autoRemove && GetInstance().isRegistered<T>(tag: widget.tag)) {
|
||||
GetInstance().delete<T>(tag: widget.tag);
|
||||
}
|
||||
}
|
||||
|
||||
_remove?.call();
|
||||
|
||||
controller = null;
|
||||
_isCreator = null;
|
||||
_remove = null;
|
||||
_filter = null;
|
||||
}
|
||||
|
||||
@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.didUpdateWidget?.call(oldWidget, this);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// return _InheritedGetxController<T>(
|
||||
// model: controller,
|
||||
// child: widget.builder(controller),
|
||||
// );
|
||||
return widget.builder(controller!);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
// }
|
||||
// }
|
||||
105
packages/get/lib/get_state_manager/src/simple/get_view.dart
Normal file
105
packages/get/lib/get_state_manager/src/simple/get_view.dart
Normal file
@@ -0,0 +1,105 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import '../../../instance_manager.dart';
|
||||
import '../../../utils.dart';
|
||||
import 'get_widget_cache.dart';
|
||||
|
||||
/// GetView is a great way of quickly access your Controller
|
||||
/// without having to call Get.find<AwesomeController>() yourself.
|
||||
///
|
||||
/// Sample:
|
||||
/// ```
|
||||
/// class AwesomeController extends GetxController {
|
||||
/// final String title = 'My Awesome View';
|
||||
/// }
|
||||
///
|
||||
/// class AwesomeView extends GetView<AwesomeController> {
|
||||
/// /// if you need you can pass the tag for
|
||||
/// /// Get.find<AwesomeController>(tag:"myTag");
|
||||
/// @override
|
||||
/// final String tag = "myTag";
|
||||
///
|
||||
/// AwesomeView({Key key}):super(key:key);
|
||||
///
|
||||
/// @override
|
||||
/// Widget build(BuildContext context) {
|
||||
/// return Container(
|
||||
/// padding: EdgeInsets.all(20),
|
||||
/// child: Text( controller.title ),
|
||||
/// );
|
||||
/// }
|
||||
/// }
|
||||
///``
|
||||
abstract class GetView<T> extends StatelessWidget {
|
||||
const GetView({Key? key}) : super(key: key);
|
||||
|
||||
final String? tag = null;
|
||||
|
||||
T get controller => GetInstance().find<T>(tag: tag)!;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context);
|
||||
}
|
||||
|
||||
/// GetWidget is a great way of quickly access your individual Controller
|
||||
/// without having to call Get.find<AwesomeController>() yourself.
|
||||
/// Get save you controller on cache, so, you can to use Get.create() safely
|
||||
/// GetWidget is perfect to multiples instance of a same controller. Each
|
||||
/// GetWidget will have your own controller, and will be call events as `onInit`
|
||||
/// and `onClose` when the controller get in/get out on memory.
|
||||
abstract class GetWidget<S extends GetLifeCycleBase?> extends GetWidgetCache {
|
||||
const GetWidget({Key? key}) : super(key: key);
|
||||
|
||||
@protected
|
||||
final String? tag = null;
|
||||
|
||||
S get controller => GetWidget._cache[this] as S;
|
||||
|
||||
// static final _cache = <GetWidget, GetLifeCycleBase>{};
|
||||
|
||||
static final _cache = Expando<GetLifeCycleBase>();
|
||||
|
||||
@protected
|
||||
Widget build(BuildContext context);
|
||||
|
||||
@override
|
||||
WidgetCache createWidgetCache() => _GetCache<S>();
|
||||
}
|
||||
|
||||
class _GetCache<S extends GetLifeCycleBase?> extends WidgetCache<GetWidget<S>> {
|
||||
S? _controller;
|
||||
bool _isCreator = false;
|
||||
InstanceInfo? info;
|
||||
@override
|
||||
void onInit() {
|
||||
info = GetInstance().getInstanceInfo<S>(tag: widget!.tag);
|
||||
|
||||
_isCreator = info!.isPrepared && info!.isCreate;
|
||||
|
||||
if (info!.isRegistered) {
|
||||
_controller = Get.find<S>(tag: widget!.tag);
|
||||
}
|
||||
|
||||
GetWidget._cache[widget!] = _controller;
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
if (_isCreator) {
|
||||
Get.asap(() {
|
||||
widget!.controller!.onDelete();
|
||||
Get.log('"${widget!.controller.runtimeType}" onClose() called');
|
||||
Get.log('"${widget!.controller.runtimeType}" deleted from memory');
|
||||
GetWidget._cache[widget!] = null;
|
||||
});
|
||||
}
|
||||
info = null;
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return widget!.build(context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
abstract class GetWidgetCache extends Widget {
|
||||
const GetWidgetCache({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
GetWidgetCacheElement createElement() => GetWidgetCacheElement(this);
|
||||
|
||||
@protected
|
||||
@factory
|
||||
WidgetCache createWidgetCache();
|
||||
}
|
||||
|
||||
class GetWidgetCacheElement extends ComponentElement {
|
||||
GetWidgetCacheElement(GetWidgetCache widget)
|
||||
: cache = widget.createWidgetCache(),
|
||||
super(widget) {
|
||||
cache._element = this;
|
||||
cache._widget = widget;
|
||||
}
|
||||
|
||||
@override
|
||||
void mount(Element? parent, dynamic newSlot) {
|
||||
cache.onInit();
|
||||
super.mount(parent, newSlot);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build() => cache.build(this);
|
||||
|
||||
final WidgetCache<GetWidgetCache> cache;
|
||||
|
||||
@override
|
||||
void performRebuild() {
|
||||
super.performRebuild();
|
||||
}
|
||||
|
||||
@override
|
||||
void activate() {
|
||||
super.activate();
|
||||
markNeedsBuild();
|
||||
}
|
||||
|
||||
@override
|
||||
void unmount() {
|
||||
super.unmount();
|
||||
cache.onClose();
|
||||
cache._element = null;
|
||||
}
|
||||
}
|
||||
|
||||
@optionalTypeArgs
|
||||
abstract class WidgetCache<T extends GetWidgetCache> {
|
||||
T? get widget => _widget;
|
||||
T? _widget;
|
||||
|
||||
BuildContext? get context => _element;
|
||||
|
||||
GetWidgetCacheElement? _element;
|
||||
|
||||
@protected
|
||||
@mustCallSuper
|
||||
void onInit() {}
|
||||
|
||||
@protected
|
||||
@mustCallSuper
|
||||
void onClose() {}
|
||||
|
||||
@protected
|
||||
Widget build(BuildContext context);
|
||||
}
|
||||
175
packages/get/lib/get_state_manager/src/simple/list_notifier.dart
Normal file
175
packages/get/lib/get_state_manager/src/simple/list_notifier.dart
Normal file
@@ -0,0 +1,175 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
// This callback remove the listener on addListener function
|
||||
typedef Disposer = void Function();
|
||||
|
||||
// replacing StateSetter, return if the Widget is mounted for extra validation.
|
||||
// if it brings overhead the extra call,
|
||||
typedef GetStateUpdate = void Function();
|
||||
|
||||
class ListNotifier extends Listenable with ListenableMixin, ListNotifierMixin {}
|
||||
|
||||
mixin ListenableMixin implements Listenable {}
|
||||
mixin ListNotifierMixin on ListenableMixin {
|
||||
// int _version = 0;
|
||||
// int _microtask = 0;
|
||||
|
||||
// int get notifierVersion => _version;
|
||||
// int get notifierMicrotask => _microtask;
|
||||
|
||||
List<GetStateUpdate?>? _updaters = <GetStateUpdate?>[];
|
||||
|
||||
HashMap<Object?, List<GetStateUpdate>>? _updatersGroupIds =
|
||||
HashMap<Object?, List<GetStateUpdate>>();
|
||||
|
||||
@protected
|
||||
void refresh() {
|
||||
assert(_debugAssertNotDisposed());
|
||||
|
||||
/// This debounce the call to update.
|
||||
/// It prevent errors and duplicates builds
|
||||
// if (_microtask == _version) {
|
||||
// _microtask++;
|
||||
// scheduleMicrotask(() {
|
||||
// _version++;
|
||||
// _microtask = _version;
|
||||
_notifyUpdate();
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
||||
void _notifyUpdate() {
|
||||
for (var element in _updaters!) {
|
||||
element!();
|
||||
}
|
||||
}
|
||||
|
||||
void _notifyIdUpdate(Object id) {
|
||||
if (_updatersGroupIds!.containsKey(id)) {
|
||||
final listGroup = _updatersGroupIds![id]!;
|
||||
for (var item in listGroup) {
|
||||
item();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
void refreshGroup(Object id) {
|
||||
assert(_debugAssertNotDisposed());
|
||||
|
||||
// /// This debounce the call to update.
|
||||
// /// It prevent errors and duplicates builds
|
||||
// if (_microtask == _version) {
|
||||
// _microtask++;
|
||||
// scheduleMicrotask(() {
|
||||
// _version++;
|
||||
// _microtask = _version;
|
||||
_notifyIdUpdate(id);
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
||||
bool _debugAssertNotDisposed() {
|
||||
assert(() {
|
||||
if (_updaters == null) {
|
||||
throw FlutterError('''A $runtimeType was used after being disposed.\n
|
||||
'Once you have called dispose() on a $runtimeType, it can no longer be used.''');
|
||||
}
|
||||
return true;
|
||||
}());
|
||||
return true;
|
||||
}
|
||||
|
||||
@protected
|
||||
void notifyChildrens() {
|
||||
TaskManager.instance.notify(_updaters);
|
||||
}
|
||||
|
||||
bool get hasListeners {
|
||||
assert(_debugAssertNotDisposed());
|
||||
return _updaters!.isNotEmpty;
|
||||
}
|
||||
|
||||
int get listeners {
|
||||
assert(_debugAssertNotDisposed());
|
||||
return _updaters!.length;
|
||||
}
|
||||
|
||||
@override
|
||||
void removeListener(VoidCallback listener) {
|
||||
assert(_debugAssertNotDisposed());
|
||||
_updaters!.remove(listener);
|
||||
}
|
||||
|
||||
void removeListenerId(Object id, VoidCallback listener) {
|
||||
assert(_debugAssertNotDisposed());
|
||||
if (_updatersGroupIds!.containsKey(id)) {
|
||||
_updatersGroupIds![id]!.remove(listener);
|
||||
}
|
||||
_updaters!.remove(listener);
|
||||
}
|
||||
|
||||
@mustCallSuper
|
||||
void dispose() {
|
||||
assert(_debugAssertNotDisposed());
|
||||
_updaters = null;
|
||||
_updatersGroupIds = null;
|
||||
}
|
||||
|
||||
@override
|
||||
Disposer addListener(GetStateUpdate listener) {
|
||||
assert(_debugAssertNotDisposed());
|
||||
_updaters!.add(listener);
|
||||
return () => _updaters!.remove(listener);
|
||||
}
|
||||
|
||||
Disposer addListenerId(Object? key, GetStateUpdate listener) {
|
||||
_updatersGroupIds![key] ??= <GetStateUpdate>[];
|
||||
_updatersGroupIds![key]!.add(listener);
|
||||
return () => _updatersGroupIds![key]!.remove(listener);
|
||||
}
|
||||
|
||||
/// To dispose an [id] from future updates(), this ids are registered
|
||||
/// by `GetBuilder()` or similar, so is a way to unlink the state change with
|
||||
/// the Widget from the Controller.
|
||||
void disposeId(Object id) {
|
||||
_updatersGroupIds!.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
class TaskManager {
|
||||
TaskManager._();
|
||||
|
||||
static TaskManager? _instance;
|
||||
|
||||
static TaskManager get instance => _instance ??= TaskManager._();
|
||||
|
||||
GetStateUpdate? _setter;
|
||||
|
||||
List<VoidCallback>? _remove;
|
||||
|
||||
void notify(List<GetStateUpdate?>? _updaters) {
|
||||
if (_setter != null) {
|
||||
if (!_updaters!.contains(_setter)) {
|
||||
_updaters.add(_setter);
|
||||
_remove!.add(() => _updaters.remove(_setter));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Widget exchange(
|
||||
List<VoidCallback> disposers,
|
||||
GetStateUpdate setState,
|
||||
Widget Function(BuildContext) builder,
|
||||
BuildContext context,
|
||||
) {
|
||||
_remove = disposers;
|
||||
_setter = setState;
|
||||
final result = builder(context);
|
||||
_remove = null;
|
||||
_setter = null;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../get_state_manager.dart';
|
||||
|
||||
class MixinBuilder<T extends GetxController> extends StatelessWidget {
|
||||
@required
|
||||
final Widget Function(T) builder;
|
||||
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 T? init;
|
||||
|
||||
const MixinBuilder({
|
||||
Key? key,
|
||||
this.init,
|
||||
this.global = true,
|
||||
required this.builder,
|
||||
this.autoRemove = true,
|
||||
this.initState,
|
||||
this.dispose,
|
||||
this.id,
|
||||
this.didChangeDependencies,
|
||||
this.didUpdateWidget,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<T>(
|
||||
init: init,
|
||||
global: global,
|
||||
autoRemove: autoRemove,
|
||||
initState: initState,
|
||||
dispose: dispose,
|
||||
id: id,
|
||||
didChangeDependencies: didChangeDependencies,
|
||||
didUpdateWidget: didUpdateWidget,
|
||||
builder: (controller) => Obx(() => builder.call(controller)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'get_state.dart';
|
||||
import 'list_notifier.dart';
|
||||
|
||||
typedef ValueBuilderUpdateCallback<T> = void Function(T snapshot);
|
||||
typedef ValueBuilderBuilder<T> = Widget Function(
|
||||
T snapshot, ValueBuilderUpdateCallback<T> updater);
|
||||
|
||||
/// Manages a local state like ObxValue, but uses a callback instead of
|
||||
/// a Rx value.
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// ValueBuilder<bool>(
|
||||
/// initialValue: false,
|
||||
/// builder: (value, update) => Switch(
|
||||
/// value: value,
|
||||
/// onChanged: (flag) {
|
||||
/// update( flag );
|
||||
/// },),
|
||||
/// onUpdate: (value) => print("Value updated: $value"),
|
||||
/// ),
|
||||
/// ```
|
||||
class ValueBuilder<T> extends StatefulWidget {
|
||||
final T? initialValue;
|
||||
final ValueBuilderBuilder<T> builder;
|
||||
final void Function()? onDispose;
|
||||
final void Function(T)? onUpdate;
|
||||
|
||||
const ValueBuilder({
|
||||
Key? key,
|
||||
this.initialValue,
|
||||
this.onDispose,
|
||||
this.onUpdate,
|
||||
required this.builder,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_ValueBuilderState<T> createState() => _ValueBuilderState<T>();
|
||||
}
|
||||
|
||||
class _ValueBuilderState<T> extends State<ValueBuilder<T?>> {
|
||||
T? value;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
value = widget.initialValue;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => widget.builder(value, updater);
|
||||
|
||||
void updater(T? newValue) {
|
||||
if (widget.onUpdate != null) {
|
||||
widget.onUpdate!(newValue);
|
||||
}
|
||||
setState(() {
|
||||
value = newValue;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
widget.onDispose?.call();
|
||||
if (value is ChangeNotifier) {
|
||||
(value as ChangeNotifier?)?.dispose();
|
||||
} else if (value is StreamController) {
|
||||
(value as StreamController?)?.close();
|
||||
}
|
||||
value = null;
|
||||
}
|
||||
}
|
||||
|
||||
// It's a experimental feature
|
||||
class SimpleBuilder extends StatefulWidget {
|
||||
final Widget Function(BuildContext) builder;
|
||||
|
||||
const SimpleBuilder({Key? key, required this.builder}) : super(key: key);
|
||||
|
||||
@override
|
||||
_SimpleBuilderState createState() => _SimpleBuilderState();
|
||||
}
|
||||
|
||||
class _SimpleBuilderState extends State<SimpleBuilder>
|
||||
with GetStateUpdaterMixin {
|
||||
final disposers = <Disposer>[];
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
for (final disposer in disposers) {
|
||||
disposer();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TaskManager.instance.exchange(
|
||||
disposers,
|
||||
getUpdate,
|
||||
widget.builder,
|
||||
context,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user