Showing
24 changed files
with
295 additions
and
162 deletions
| @@ -20,6 +20,10 @@ version: 1.0.0+1 | @@ -20,6 +20,10 @@ version: 1.0.0+1 | ||
| 20 | environment: | 20 | environment: | 
| 21 | sdk: ">=2.12.0 <3.0.0" | 21 | sdk: ">=2.12.0 <3.0.0" | 
| 22 | 22 | ||
| 23 | +dependency_overrides: | ||
| 24 | + get: | ||
| 25 | + path: ../ | ||
| 26 | + | ||
| 23 | dependencies: | 27 | dependencies: | 
| 24 | flutter: | 28 | flutter: | 
| 25 | sdk: flutter | 29 | sdk: flutter | 
| @@ -33,6 +37,7 @@ dependencies: | @@ -33,6 +37,7 @@ dependencies: | ||
| 33 | dev_dependencies: | 37 | dev_dependencies: | 
| 34 | flutter_test: | 38 | flutter_test: | 
| 35 | sdk: flutter | 39 | sdk: flutter | 
| 40 | + get_test: 4.0.1 | ||
| 36 | 41 | ||
| 37 | # For information on the generic Dart part of this file, see the | 42 | # For information on the generic Dart part of this file, see the | 
| 38 | # following page: https://dart.dev/tools/pub/pubspec | 43 | # following page: https://dart.dev/tools/pub/pubspec | 
example/test/widget_test.dart
deleted
100644 → 0
| 1 | - | 
| 1 | import 'dart:async'; | 1 | import 'dart:async'; | 
| 2 | 2 | ||
| 3 | -import 'package:get/get.dart'; | ||
| 4 | import 'package:async/async.dart'; | 3 | import 'package:async/async.dart'; | 
| 4 | +import 'package:get/get.dart'; | ||
| 5 | 5 | ||
| 6 | class SplashService extends GetxService { | 6 | class SplashService extends GetxService { | 
| 7 | final welcomeStr = ['GetX', 'Rules!']; | 7 | final welcomeStr = ['GetX', 'Rules!']; | 
| 1 | -import 'package:example_nav2/app/modules/splash/controllers/splash_service.dart'; | ||
| 2 | -import 'package:example_nav2/app/modules/splash/views/splash_view.dart'; | ||
| 3 | import 'package:flutter/material.dart'; | 1 | import 'package:flutter/material.dart'; | 
| 4 | import 'package:get/get.dart'; | 2 | import 'package:get/get.dart'; | 
| 5 | 3 | ||
| 4 | +import 'app/modules/splash/controllers/splash_service.dart'; | ||
| 5 | +import 'app/modules/splash/views/splash_view.dart'; | ||
| 6 | import 'app/routes/app_pages.dart'; | 6 | import 'app/routes/app_pages.dart'; | 
| 7 | import 'services/auth_service.dart'; | 7 | import 'services/auth_service.dart'; | 
| 8 | 8 | 
| @@ -76,3 +76,11 @@ mixin GetLifeCycleMixin { | @@ -76,3 +76,11 @@ mixin GetLifeCycleMixin { | ||
| 76 | 76 | ||
| 77 | /// Allow track difference between GetxServices and GetxControllers | 77 | /// Allow track difference between GetxServices and GetxControllers | 
| 78 | mixin GetxServiceMixin {} | 78 | mixin GetxServiceMixin {} | 
| 79 | + | ||
| 80 | +/// Unlike GetxController, which serves to control events on each of its pages, | ||
| 81 | +/// GetxService is not automatically disposed (nor can be removed with | ||
| 82 | +/// Get.delete()). | ||
| 83 | +/// It is ideal for situations where, once started, that service will | ||
| 84 | +/// remain in memory, such as Auth control for example. Only way to remove | ||
| 85 | +/// it is Get.reset(). | ||
| 86 | +abstract class GetxService with GetLifeCycleMixin, GetxServiceMixin {} | 
| @@ -2,9 +2,9 @@ import 'dart:async'; | @@ -2,9 +2,9 @@ import 'dart:async'; | ||
| 2 | 2 | ||
| 3 | import 'package:flutter/foundation.dart'; | 3 | import 'package:flutter/foundation.dart'; | 
| 4 | import 'package:flutter/material.dart'; | 4 | import 'package:flutter/material.dart'; | 
| 5 | + | ||
| 5 | import '../../../get.dart'; | 6 | import '../../../get.dart'; | 
| 6 | import '../../../get_state_manager/src/simple/list_notifier.dart'; | 7 | import '../../../get_state_manager/src/simple/list_notifier.dart'; | 
| 7 | -import 'get_navigator.dart'; | ||
| 8 | 8 | ||
| 9 | /// Enables the user to customize the intended pop behavior | 9 | /// Enables the user to customize the intended pop behavior | 
| 10 | /// | 10 | /// | 
| @@ -4,6 +4,21 @@ import '../../../get.dart'; | @@ -4,6 +4,21 @@ import '../../../get.dart'; | ||
| 4 | import '../router_report.dart'; | 4 | import '../router_report.dart'; | 
| 5 | import 'get_transition_mixin.dart'; | 5 | import 'get_transition_mixin.dart'; | 
| 6 | 6 | ||
| 7 | +@optionalTypeArgs | ||
| 8 | +mixin RouteReportMixin<T extends StatefulWidget> on State<T> { | ||
| 9 | + @override | ||
| 10 | + void initState() { | ||
| 11 | + super.initState(); | ||
| 12 | + RouterReportManager.instance.reportCurrentRoute(this); | ||
| 13 | + } | ||
| 14 | + | ||
| 15 | + @override | ||
| 16 | + void dispose() { | ||
| 17 | + super.dispose(); | ||
| 18 | + RouterReportManager.instance.reportRouteDispose(this); | ||
| 19 | + } | ||
| 20 | +} | ||
| 21 | + | ||
| 7 | mixin PageRouteReportMixin<T> on Route<T> { | 22 | mixin PageRouteReportMixin<T> on Route<T> { | 
| 8 | @override | 23 | @override | 
| 9 | void install() { | 24 | void install() { | 
| 1 | +import 'package:flutter/material.dart'; | ||
| 2 | + | ||
| 3 | +import '../router_report.dart'; | ||
| 4 | +import 'default_route.dart'; | ||
| 5 | + | ||
| 6 | +class RouteReport extends StatefulWidget { | ||
| 7 | + RouteReport({Key? key, required this.builder}) : super(key: key); | ||
| 8 | + final WidgetBuilder builder; | ||
| 9 | + | ||
| 10 | + @override | ||
| 11 | + _RouteReportState createState() => _RouteReportState(); | ||
| 12 | +} | ||
| 13 | + | ||
| 14 | +class _RouteReportState extends State<RouteReport> with RouteReportMixin { | ||
| 15 | + @override | ||
| 16 | + void initState() { | ||
| 17 | + RouterReportManager.instance.reportCurrentRoute(this); | ||
| 18 | + super.initState(); | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + @override | ||
| 22 | + void dispose() { | ||
| 23 | + RouterReportManager.instance.reportRouteDispose(this); | ||
| 24 | + super.dispose(); | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + @override | ||
| 28 | + Widget build(BuildContext context) { | ||
| 29 | + return widget.builder(context); | ||
| 30 | + } | ||
| 31 | +} | 
| @@ -102,9 +102,7 @@ mixin RxObjectMixin<T> on GetListenable<T> { | @@ -102,9 +102,7 @@ mixin RxObjectMixin<T> on GetListenable<T> { | ||
| 102 | sentToStream = false; | 102 | sentToStream = false; | 
| 103 | if (value == val && !firstRebuild) return; | 103 | if (value == val && !firstRebuild) return; | 
| 104 | firstRebuild = false; | 104 | firstRebuild = false; | 
| 105 | - // _value = val; | ||
| 106 | sentToStream = true; | 105 | sentToStream = true; | 
| 107 | - //TODO: Check this | ||
| 108 | super.value = val; | 106 | super.value = val; | 
| 109 | } | 107 | } | 
| 110 | 108 | ||
| @@ -121,7 +119,6 @@ mixin RxObjectMixin<T> on GetListenable<T> { | @@ -121,7 +119,6 @@ mixin RxObjectMixin<T> on GetListenable<T> { | ||
| 121 | cancelOnError: cancelOnError, | 119 | cancelOnError: cancelOnError, | 
| 122 | ); | 120 | ); | 
| 123 | 121 | ||
| 124 | - //TODO: Change to refresh???? | ||
| 125 | subject.add(value); | 122 | subject.add(value); | 
| 126 | 123 | ||
| 127 | return subscription; | 124 | return subscription; | 
| @@ -140,53 +137,6 @@ mixin RxObjectMixin<T> on GetListenable<T> { | @@ -140,53 +137,6 @@ mixin RxObjectMixin<T> on GetListenable<T> { | ||
| 140 | } | 137 | } | 
| 141 | } | 138 | } | 
| 142 | 139 | ||
| 143 | -//class RxNotifier<T> = RxInterface<T> with NotifyManager<T>; | ||
| 144 | - | ||
| 145 | -// mixin NotifyManager<T> { | ||
| 146 | -// GetStream<T> subject = GetStream<T>(); | ||
| 147 | -// final _subscriptions = <GetStream, List<StreamSubscription>>{}; | ||
| 148 | - | ||
| 149 | -// bool get canUpdate => _subscriptions.isNotEmpty; | ||
| 150 | - | ||
| 151 | -// /// This is an internal method. | ||
| 152 | -// /// Subscribe to changes on the inner stream. | ||
| 153 | -// void addListener(GetStream<T> rxGetx) { | ||
| 154 | -// if (!_subscriptions.containsKey(rxGetx)) { | ||
| 155 | -// final subs = rxGetx.listen((data) { | ||
| 156 | -// if (!subject.isClosed) subject.add(data); | ||
| 157 | -// }); | ||
| 158 | -// final listSubscriptions = | ||
| 159 | -// _subscriptions[rxGetx] ??= <StreamSubscription>[]; | ||
| 160 | -// listSubscriptions.add(subs); | ||
| 161 | -// } | ||
| 162 | -// } | ||
| 163 | - | ||
| 164 | -// StreamSubscription<T> listen( | ||
| 165 | -// void Function(T) onData, { | ||
| 166 | -// Function? onError, | ||
| 167 | -// void Function()? onDone, | ||
| 168 | -// bool? cancelOnError, | ||
| 169 | -// }) => | ||
| 170 | -// subject.listen( | ||
| 171 | -// onData, | ||
| 172 | -// onError: onError, | ||
| 173 | -// onDone: onDone, | ||
| 174 | -// cancelOnError: cancelOnError ?? false, | ||
| 175 | -// ); | ||
| 176 | - | ||
| 177 | -// /// Closes the subscriptions for this Rx, releasing the resources. | ||
| 178 | -// void close() { | ||
| 179 | -// _subscriptions.forEach((getStream, _subscriptions) { | ||
| 180 | -// for (final subscription in _subscriptions) { | ||
| 181 | -// subscription.cancel(); | ||
| 182 | -// } | ||
| 183 | -// }); | ||
| 184 | - | ||
| 185 | -// _subscriptions.clear(); | ||
| 186 | -// subject.close(); | ||
| 187 | -// } | ||
| 188 | -// } | ||
| 189 | - | ||
| 190 | /// Base Rx class that manages all the stream logic for any Type. | 140 | /// Base Rx class that manages all the stream logic for any Type. | 
| 191 | abstract class _RxImpl<T> extends GetListenable<T> with RxObjectMixin<T> { | 141 | abstract class _RxImpl<T> extends GetListenable<T> with RxObjectMixin<T> { | 
| 192 | _RxImpl(T initial) : super(initial); | 142 | _RxImpl(T initial) : super(initial); | 
| @@ -2,33 +2,15 @@ part of rx_types; | @@ -2,33 +2,15 @@ part of rx_types; | ||
| 2 | 2 | ||
| 3 | /// This class is the foundation for all reactive (Rx) classes that makes Get | 3 | /// This class is the foundation for all reactive (Rx) classes that makes Get | 
| 4 | /// so powerful. | 4 | /// so powerful. | 
| 5 | -/// This interface is the contract that _RxImpl]<T> uses in all it's | 5 | +/// This interface is the contract that [_RxImpl]<T> uses in all it's | 
| 6 | /// subclass. | 6 | /// subclass. | 
| 7 | -abstract class RxInterface<T> { | ||
| 8 | - //bool get canUpdate; | ||
| 9 | - | ||
| 10 | - /// Adds a listener to stream | ||
| 11 | - void addListener(VoidCallback listener); | ||
| 12 | - | 7 | +abstract class RxInterface<T> implements ValueListenable<T> { | 
| 13 | /// Close the Rx Variable | 8 | /// Close the Rx Variable | 
| 14 | void close(); | 9 | void close(); | 
| 15 | 10 | ||
| 16 | /// Calls `callback` with current value, when the value changes. | 11 | /// Calls `callback` with current value, when the value changes. | 
| 17 | StreamSubscription<T> listen(void Function(T event) onData, | 12 | StreamSubscription<T> listen(void Function(T event) onData, | 
| 18 | {Function? onError, void Function()? onDone, bool? cancelOnError}); | 13 | {Function? onError, void Function()? onDone, bool? cancelOnError}); | 
| 19 | - | ||
| 20 | - /// Avoids an unsafe usage of the `proxy` | ||
| 21 | - // static T notifyChildren<T>(RxNotifier observer, ValueGetter<T> builder) { | ||
| 22 | - // final _observer = RxInterface.proxy; | ||
| 23 | - // RxInterface.proxy = observer; | ||
| 24 | - // final result = builder(); | ||
| 25 | - // if (!observer.canUpdate) { | ||
| 26 | - // RxInterface.proxy = _observer; | ||
| 27 | - // throw ObxError(); | ||
| 28 | - // } | ||
| 29 | - // RxInterface.proxy = _observer; | ||
| 30 | - // return result; | ||
| 31 | - // } | ||
| 32 | } | 14 | } | 
| 33 | 15 | ||
| 34 | class ObxError { | 16 | class ObxError { | 
| 1 | library get_state_manager; | 1 | library get_state_manager; | 
| 2 | 2 | ||
| 3 | -export 'src/rx_flutter/rx_disposable.dart'; | ||
| 4 | export 'src/rx_flutter/rx_getx_widget.dart'; | 3 | export 'src/rx_flutter/rx_getx_widget.dart'; | 
| 5 | export 'src/rx_flutter/rx_notifier.dart'; | 4 | export 'src/rx_flutter/rx_notifier.dart'; | 
| 6 | export 'src/rx_flutter/rx_obx_widget.dart'; | 5 | export 'src/rx_flutter/rx_obx_widget.dart'; | 
| @@ -9,5 +8,4 @@ export 'src/simple/get_controllers.dart'; | @@ -9,5 +8,4 @@ export 'src/simple/get_controllers.dart'; | ||
| 9 | export 'src/simple/get_responsive.dart'; | 8 | export 'src/simple/get_responsive.dart'; | 
| 10 | export 'src/simple/get_state.dart'; | 9 | export 'src/simple/get_state.dart'; | 
| 11 | export 'src/simple/get_view.dart'; | 10 | export 'src/simple/get_view.dart'; | 
| 12 | -export 'src/simple/mixin_state.dart'; | ||
| 13 | export 'src/simple/simple_builder.dart'; | 11 | export 'src/simple/simple_builder.dart'; | 
| 1 | -import '../../../get_instance/src/lifecycle.dart'; | ||
| 2 | - | ||
| 3 | -/// Unlike GetxController, which serves to control events on each of its pages, | ||
| 4 | -/// GetxService is not automatically disposed (nor can be removed with | ||
| 5 | -/// Get.delete()). | ||
| 6 | -/// It is ideal for situations where, once started, that service will | ||
| 7 | -/// remain in memory, such as Auth control for example. Only way to remove | ||
| 8 | -/// it is Get.reset(). | ||
| 9 | -abstract class GetxService with GetLifeCycleMixin, GetxServiceMixin {} | ||
| 10 | - | ||
| 11 | -// abstract class DisposableInterface with GetLifeCycleMixin {} | 
| @@ -113,20 +113,25 @@ class GetXState<T extends GetLifeCycleMixin> extends State<GetX<T>> { | @@ -113,20 +113,25 @@ class GetXState<T extends GetLifeCycleMixin> extends State<GetX<T>> { | ||
| 113 | disposer(); | 113 | disposer(); | 
| 114 | } | 114 | } | 
| 115 | 115 | ||
| 116 | + disposers.clear(); | ||
| 117 | + | ||
| 116 | controller = null; | 118 | controller = null; | 
| 117 | _isCreator = null; | 119 | _isCreator = null; | 
| 118 | super.dispose(); | 120 | super.dispose(); | 
| 119 | } | 121 | } | 
| 120 | 122 | ||
| 121 | void _update() { | 123 | void _update() { | 
| 122 | - setState(() {}); | 124 | + if (mounted) { | 
| 125 | + setState(() {}); | ||
| 126 | + } | ||
| 123 | } | 127 | } | 
| 124 | 128 | ||
| 125 | final disposers = <Disposer>[]; | 129 | final disposers = <Disposer>[]; | 
| 126 | 130 | ||
| 127 | @override | 131 | @override | 
| 128 | - Widget build(BuildContext context) => NotifierManager.instance | ||
| 129 | - .exchange(disposers, _update, () => widget.builder(controller!)); | 132 | + Widget build(BuildContext context) => Notifier.instance.append( | 
| 133 | + NotifyData(disposers: disposers, updater: _update), | ||
| 134 | + () => widget.builder(controller!)); | ||
| 130 | 135 | ||
| 131 | @override | 136 | @override | 
| 132 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { | 137 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { | 
| @@ -3,6 +3,7 @@ import 'dart:async'; | @@ -3,6 +3,7 @@ import 'dart:async'; | ||
| 3 | import 'package:flutter/foundation.dart'; | 3 | import 'package:flutter/foundation.dart'; | 
| 4 | import 'package:flutter/material.dart'; | 4 | import 'package:flutter/material.dart'; | 
| 5 | 5 | ||
| 6 | +import '../../../get_rx/src/rx_types/rx_types.dart'; | ||
| 6 | import '../../../instance_manager.dart'; | 7 | import '../../../instance_manager.dart'; | 
| 7 | import '../../get_state_manager.dart'; | 8 | import '../../get_state_manager.dart'; | 
| 8 | import '../simple/list_notifier.dart'; | 9 | import '../simple/list_notifier.dart'; | 
| @@ -78,8 +79,7 @@ mixin StateMixin<T> on ListNotifier { | @@ -78,8 +79,7 @@ mixin StateMixin<T> on ListNotifier { | ||
| 78 | } | 79 | } | 
| 79 | } | 80 | } | 
| 80 | 81 | ||
| 81 | -class GetListenable<T> extends ListNotifierSingle | ||
| 82 | - implements ValueListenable<T> { | 82 | +class GetListenable<T> extends ListNotifierSingle implements RxInterface<T> { | 
| 83 | GetListenable(T val) : _value = val; | 83 | GetListenable(T val) : _value = val; | 
| 84 | 84 | ||
| 85 | StreamController<T>? _controller; | 85 | StreamController<T>? _controller; | 
| @@ -96,6 +96,7 @@ class GetListenable<T> extends ListNotifierSingle | @@ -96,6 +96,7 @@ class GetListenable<T> extends ListNotifierSingle | ||
| 96 | _controller?.add(_value); | 96 | _controller?.add(_value); | 
| 97 | } | 97 | } | 
| 98 | 98 | ||
| 99 | + @override | ||
| 99 | @mustCallSuper | 100 | @mustCallSuper | 
| 100 | void close() { | 101 | void close() { | 
| 101 | removeListener(_streamListener); | 102 | removeListener(_streamListener); | 
| @@ -132,6 +133,7 @@ class GetListenable<T> extends ListNotifierSingle | @@ -132,6 +133,7 @@ class GetListenable<T> extends ListNotifierSingle | ||
| 132 | return value; | 133 | return value; | 
| 133 | } | 134 | } | 
| 134 | 135 | ||
| 136 | + @override | ||
| 135 | StreamSubscription<T> listen( | 137 | StreamSubscription<T> listen( | 
| 136 | void Function(T)? onData, { | 138 | void Function(T)? onData, { | 
| 137 | Function? onError, | 139 | Function? onError, | 
| @@ -188,6 +190,8 @@ class Value<T> extends ListNotifier | @@ -188,6 +190,8 @@ class Value<T> extends ListNotifier | ||
| 188 | dynamic toJson() => (value as dynamic)?.toJson(); | 190 | dynamic toJson() => (value as dynamic)?.toJson(); | 
| 189 | } | 191 | } | 
| 190 | 192 | ||
| 193 | +/// GetNotifier has a native status and state implementation, with the | ||
| 194 | +/// Get Lifecycle | ||
| 191 | abstract class GetNotifier<T> extends Value<T> with GetLifeCycleMixin { | 195 | abstract class GetNotifier<T> extends Value<T> with GetLifeCycleMixin { | 
| 192 | GetNotifier(T initial) : super(initial); | 196 | GetNotifier(T initial) : super(initial); | 
| 193 | } | 197 | } | 
| @@ -198,6 +202,7 @@ extension StateExt<T> on StateMixin<T> { | @@ -198,6 +202,7 @@ extension StateExt<T> on StateMixin<T> { | ||
| 198 | Widget Function(String? error)? onError, | 202 | Widget Function(String? error)? onError, | 
| 199 | Widget? onLoading, | 203 | Widget? onLoading, | 
| 200 | Widget? onEmpty, | 204 | Widget? onEmpty, | 
| 205 | + WidgetBuilder? onCustom, | ||
| 201 | }) { | 206 | }) { | 
| 202 | return Observer(builder: (_) { | 207 | return Observer(builder: (_) { | 
| 203 | if (status.isLoading) { | 208 | if (status.isLoading) { | 
| @@ -210,6 +215,11 @@ extension StateExt<T> on StateMixin<T> { | @@ -210,6 +215,11 @@ extension StateExt<T> on StateMixin<T> { | ||
| 210 | return onEmpty != null | 215 | return onEmpty != null | 
| 211 | ? onEmpty | 216 | ? onEmpty | 
| 212 | : SizedBox.shrink(); // Also can be widget(null); but is risky | 217 | : SizedBox.shrink(); // Also can be widget(null); but is risky | 
| 218 | + } else if (status.isSuccess) { | ||
| 219 | + return widget(value); | ||
| 220 | + } else if (status.isCustom) { | ||
| 221 | + return onCustom?.call(_) ?? | ||
| 222 | + SizedBox.shrink(); // Also can be widget(null); but is risky | ||
| 213 | } | 223 | } | 
| 214 | return widget(value); | 224 | return widget(value); | 
| 215 | }); | 225 | }); | 
| @@ -246,6 +256,7 @@ extension StatusDataExt<T> on GetState<T> { | @@ -246,6 +256,7 @@ extension StatusDataExt<T> on GetState<T> { | ||
| 246 | bool get isSuccess => this is SuccessState; | 256 | bool get isSuccess => this is SuccessState; | 
| 247 | bool get isError => this is ErrorState; | 257 | bool get isError => this is ErrorState; | 
| 248 | bool get isEmpty => this is EmptyState; | 258 | bool get isEmpty => this is EmptyState; | 
| 259 | + bool get isCustom => !isLoading && !isSuccess && !isError && !isEmpty; | ||
| 249 | String get errorMessage { | 260 | String get errorMessage { | 
| 250 | final isError = this is ErrorState; | 261 | final isError = this is ErrorState; | 
| 251 | if (isError) { | 262 | if (isError) { | 
| @@ -26,6 +26,8 @@ abstract class GetxController extends ListNotifier with GetLifeCycleMixin { | @@ -26,6 +26,8 @@ abstract class GetxController extends ListNotifier with GetLifeCycleMixin { | ||
| 26 | } | 26 | } | 
| 27 | } | 27 | } | 
| 28 | 28 | ||
| 29 | +/// this mixin allow to fetch data when the scroll is at the bottom or on the | ||
| 30 | +/// top | ||
| 29 | mixin ScrollMixin on GetLifeCycleMixin { | 31 | mixin ScrollMixin on GetLifeCycleMixin { | 
| 30 | final ScrollController scroll = ScrollController(); | 32 | final ScrollController scroll = ScrollController(); | 
| 31 | 33 | ||
| @@ -59,8 +61,10 @@ mixin ScrollMixin on GetLifeCycleMixin { | @@ -59,8 +61,10 @@ mixin ScrollMixin on GetLifeCycleMixin { | ||
| 59 | } | 61 | } | 
| 60 | } | 62 | } | 
| 61 | 63 | ||
| 64 | + /// this method is called when the scroll is at the bottom | ||
| 62 | Future<void> onEndScroll(); | 65 | Future<void> onEndScroll(); | 
| 63 | 66 | ||
| 67 | + /// this method is called when the scroll is at the top | ||
| 64 | Future<void> onTopScroll(); | 68 | Future<void> onTopScroll(); | 
| 65 | 69 | ||
| 66 | @override | 70 | @override | 
| @@ -70,13 +74,17 @@ mixin ScrollMixin on GetLifeCycleMixin { | @@ -70,13 +74,17 @@ mixin ScrollMixin on GetLifeCycleMixin { | ||
| 70 | } | 74 | } | 
| 71 | } | 75 | } | 
| 72 | 76 | ||
| 77 | +/// A clean controller to be used with only Rx variables | ||
| 73 | abstract class RxController with GetLifeCycleMixin {} | 78 | abstract class RxController with GetLifeCycleMixin {} | 
| 74 | 79 | ||
| 80 | +/// A recommended way to use Getx with Future fetching | ||
| 75 | abstract class StateController<T> extends GetxController with StateMixin<T> {} | 81 | abstract class StateController<T> extends GetxController with StateMixin<T> {} | 
| 76 | 82 | ||
| 83 | +/// A controller with super lifecycles (including native lifecycles) and StateMixins | ||
| 77 | abstract class SuperController<T> extends FullLifeCycleController | 84 | abstract class SuperController<T> extends FullLifeCycleController | 
| 78 | with FullLifeCycleMixin, StateMixin<T> {} | 85 | with FullLifeCycleMixin, StateMixin<T> {} | 
| 79 | 86 | ||
| 87 | +/// A controller with super lifecycles (including native lifecycles) | ||
| 80 | abstract class FullLifeCycleController extends GetxController | 88 | abstract class FullLifeCycleController extends GetxController | 
| 81 | with | 89 | with | 
| 82 | // ignore: prefer_mixin | 90 | // ignore: prefer_mixin | 
| @@ -116,8 +124,8 @@ mixin FullLifeCycleMixin on FullLifeCycleController { | @@ -116,8 +124,8 @@ mixin FullLifeCycleMixin on FullLifeCycleController { | ||
| 116 | } | 124 | } | 
| 117 | } | 125 | } | 
| 118 | 126 | ||
| 119 | - void onResumed(); | ||
| 120 | - void onPaused(); | ||
| 121 | - void onInactive(); | ||
| 122 | - void onDetached(); | 127 | + void onResumed() {} | 
| 128 | + void onPaused() {} | ||
| 129 | + void onInactive() {} | ||
| 130 | + void onDetached() {} | ||
| 123 | } | 131 | } | 
| @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; | @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; | ||
| 2 | 2 | ||
| 3 | import '../../../instance_manager.dart'; | 3 | import '../../../instance_manager.dart'; | 
| 4 | import '../../get_state_manager.dart'; | 4 | import '../../get_state_manager.dart'; | 
| 5 | +import 'list_notifier.dart'; | ||
| 5 | 6 | ||
| 6 | typedef InitBuilder<T> = T Function(); | 7 | typedef InitBuilder<T> = T Function(); | 
| 7 | 8 | ||
| @@ -37,7 +38,7 @@ class GetBuilder<T extends GetxController> extends StatelessWidget { | @@ -37,7 +38,7 @@ class GetBuilder<T extends GetxController> extends StatelessWidget { | ||
| 37 | final void Function(BindElement<T> state)? initState, | 38 | final void Function(BindElement<T> state)? initState, | 
| 38 | dispose, | 39 | dispose, | 
| 39 | didChangeDependencies; | 40 | didChangeDependencies; | 
| 40 | - final void Function(BindWrapper<T> oldWidget, BindElement<T> state)? | 41 | + final void Function(Binder<T> oldWidget, BindElement<T> state)? | 
| 41 | didUpdateWidget; | 42 | didUpdateWidget; | 
| 42 | final T? init; | 43 | final T? init; | 
| 43 | 44 | ||
| @@ -59,7 +60,7 @@ class GetBuilder<T extends GetxController> extends StatelessWidget { | @@ -59,7 +60,7 @@ class GetBuilder<T extends GetxController> extends StatelessWidget { | ||
| 59 | 60 | ||
| 60 | @override | 61 | @override | 
| 61 | Widget build(BuildContext context) { | 62 | Widget build(BuildContext context) { | 
| 62 | - return BindWrapper( | 63 | + return Binder( | 
| 63 | init: init == null ? null : () => init!, | 64 | init: init == null ? null : () => init!, | 
| 64 | global: global, | 65 | global: global, | 
| 65 | autoRemove: autoRemove, | 66 | autoRemove: autoRemove, | 
| @@ -108,7 +109,7 @@ abstract class Bind<T> extends StatelessWidget { | @@ -108,7 +109,7 @@ abstract class Bind<T> extends StatelessWidget { | ||
| 108 | final void Function(BindElement<T> state)? initState, | 109 | final void Function(BindElement<T> state)? initState, | 
| 109 | dispose, | 110 | dispose, | 
| 110 | didChangeDependencies; | 111 | didChangeDependencies; | 
| 111 | - final void Function(BindWrapper<T> oldWidget, BindElement<T> state)? | 112 | + final void Function(Binder<T> oldWidget, BindElement<T> state)? | 
| 112 | didUpdateWidget; | 113 | didUpdateWidget; | 
| 113 | 114 | ||
| 114 | final Widget? child; | 115 | final Widget? child; | 
| @@ -193,8 +194,7 @@ abstract class Bind<T> extends StatelessWidget { | @@ -193,8 +194,7 @@ abstract class Bind<T> extends StatelessWidget { | ||
| 193 | void Function(BindElement<T> state)? initState, | 194 | void Function(BindElement<T> state)? initState, | 
| 194 | void Function(BindElement<T> state)? dispose, | 195 | void Function(BindElement<T> state)? dispose, | 
| 195 | void Function(BindElement<T> state)? didChangeDependencies, | 196 | void Function(BindElement<T> state)? didChangeDependencies, | 
| 196 | - void Function(BindWrapper<T> oldWidget, BindElement<T> state)? | ||
| 197 | - didUpdateWidget, | 197 | + void Function(Binder<T> oldWidget, BindElement<T> state)? didUpdateWidget, | 
| 198 | }) => | 198 | }) => | 
| 199 | _FactoryBind<T>( | 199 | _FactoryBind<T>( | 
| 200 | // key: key, | 200 | // key: key, | 
| @@ -218,7 +218,7 @@ abstract class Bind<T> extends StatelessWidget { | @@ -218,7 +218,7 @@ abstract class Bind<T> extends StatelessWidget { | ||
| 218 | // Object Function(T value)? filter, | 218 | // Object Function(T value)? filter, | 
| 219 | }) { | 219 | }) { | 
| 220 | final inheritedElement = | 220 | final inheritedElement = | 
| 221 | - context.getElementForInheritedWidgetOfExactType<BindWrapper<T>>() | 221 | + context.getElementForInheritedWidgetOfExactType<Binder<T>>() | 
| 222 | as BindElement<T>?; | 222 | as BindElement<T>?; | 
| 223 | 223 | ||
| 224 | if (inheritedElement == null) { | 224 | if (inheritedElement == null) { | 
| @@ -265,7 +265,7 @@ class _FactoryBind<T> extends Bind<T> { | @@ -265,7 +265,7 @@ class _FactoryBind<T> extends Bind<T> { | ||
| 265 | dispose, | 265 | dispose, | 
| 266 | didChangeDependencies; | 266 | didChangeDependencies; | 
| 267 | @override | 267 | @override | 
| 268 | - final void Function(BindWrapper<T> oldWidget, BindElement<T> state)? | 268 | + final void Function(Binder<T> oldWidget, BindElement<T> state)? | 
| 269 | didUpdateWidget; | 269 | didUpdateWidget; | 
| 270 | 270 | ||
| 271 | @override | 271 | @override | 
| @@ -307,7 +307,7 @@ class _FactoryBind<T> extends Bind<T> { | @@ -307,7 +307,7 @@ class _FactoryBind<T> extends Bind<T> { | ||
| 307 | 307 | ||
| 308 | @override | 308 | @override | 
| 309 | Widget build(BuildContext context) { | 309 | Widget build(BuildContext context) { | 
| 310 | - return BindWrapper<T>( | 310 | + return Binder<T>( | 
| 311 | init: init, | 311 | init: init, | 
| 312 | global: global, | 312 | global: global, | 
| 313 | autoRemove: autoRemove, | 313 | autoRemove: autoRemove, | 
| @@ -340,12 +340,12 @@ class Binds extends StatelessWidget { | @@ -340,12 +340,12 @@ class Binds extends StatelessWidget { | ||
| 340 | binds.reversed.fold(child, (acc, e) => e._copyWithChild(acc)); | 340 | binds.reversed.fold(child, (acc, e) => e._copyWithChild(acc)); | 
| 341 | } | 341 | } | 
| 342 | 342 | ||
| 343 | -class BindWrapper<T> extends InheritedWidget { | 343 | +class Binder<T> extends InheritedWidget { | 
| 344 | /// Create an inherited widget that updates its dependents when [controller] | 344 | /// Create an inherited widget that updates its dependents when [controller] | 
| 345 | /// sends notifications. | 345 | /// sends notifications. | 
| 346 | /// | 346 | /// | 
| 347 | /// The [child] argument is required | 347 | /// The [child] argument is required | 
| 348 | - const BindWrapper({ | 348 | + const Binder({ | 
| 349 | Key? key, | 349 | Key? key, | 
| 350 | required Widget child, | 350 | required Widget child, | 
| 351 | this.init, | 351 | this.init, | 
| @@ -371,11 +371,11 @@ class BindWrapper<T> extends InheritedWidget { | @@ -371,11 +371,11 @@ class BindWrapper<T> extends InheritedWidget { | ||
| 371 | final void Function(BindElement<T> state)? initState, | 371 | final void Function(BindElement<T> state)? initState, | 
| 372 | dispose, | 372 | dispose, | 
| 373 | didChangeDependencies; | 373 | didChangeDependencies; | 
| 374 | - final void Function(BindWrapper<T> oldWidget, BindElement<T> state)? | 374 | + final void Function(Binder<T> oldWidget, BindElement<T> state)? | 
| 375 | didUpdateWidget; | 375 | didUpdateWidget; | 
| 376 | 376 | ||
| 377 | @override | 377 | @override | 
| 378 | - bool updateShouldNotify(BindWrapper<T> oldWidget) { | 378 | + bool updateShouldNotify(Binder<T> oldWidget) { | 
| 379 | return oldWidget.id != id || | 379 | return oldWidget.id != id || | 
| 380 | oldWidget.global != global || | 380 | oldWidget.global != global || | 
| 381 | oldWidget.autoRemove != autoRemove || | 381 | oldWidget.autoRemove != autoRemove || | 
| @@ -389,10 +389,12 @@ class BindWrapper<T> extends InheritedWidget { | @@ -389,10 +389,12 @@ class BindWrapper<T> extends InheritedWidget { | ||
| 389 | /// The BindElement is responsible for injecting dependencies into the widget | 389 | /// The BindElement is responsible for injecting dependencies into the widget | 
| 390 | /// tree so that they can be observed | 390 | /// tree so that they can be observed | 
| 391 | class BindElement<T> extends InheritedElement { | 391 | class BindElement<T> extends InheritedElement { | 
| 392 | - BindElement(BindWrapper<T> widget) : super(widget) { | 392 | + BindElement(Binder<T> widget) : super(widget) { | 
| 393 | initState(); | 393 | initState(); | 
| 394 | } | 394 | } | 
| 395 | 395 | ||
| 396 | + final disposers = <Disposer>[]; | ||
| 397 | + | ||
| 396 | InitBuilder<T>? _controllerBuilder; | 398 | InitBuilder<T>? _controllerBuilder; | 
| 397 | 399 | ||
| 398 | T? _controller; | 400 | T? _controller; | 
| @@ -486,6 +488,12 @@ class BindElement<T> extends InheritedElement { | @@ -486,6 +488,12 @@ class BindElement<T> extends InheritedElement { | ||
| 486 | } | 488 | } | 
| 487 | } | 489 | } | 
| 488 | 490 | ||
| 491 | + for (final disposer in disposers) { | ||
| 492 | + disposer(); | ||
| 493 | + } | ||
| 494 | + | ||
| 495 | + disposers.clear(); | ||
| 496 | + | ||
| 489 | _remove?.call(); | 497 | _remove?.call(); | 
| 490 | _controller = null; | 498 | _controller = null; | 
| 491 | _isCreator = null; | 499 | _isCreator = null; | 
| @@ -497,12 +505,12 @@ class BindElement<T> extends InheritedElement { | @@ -497,12 +505,12 @@ class BindElement<T> extends InheritedElement { | ||
| 497 | } | 505 | } | 
| 498 | 506 | ||
| 499 | @override | 507 | @override | 
| 500 | - BindWrapper<T> get widget => super.widget as BindWrapper<T>; | 508 | + Binder<T> get widget => super.widget as Binder<T>; | 
| 501 | 509 | ||
| 502 | var _dirty = false; | 510 | var _dirty = false; | 
| 503 | 511 | ||
| 504 | @override | 512 | @override | 
| 505 | - void update(BindWrapper<T> newWidget) { | 513 | + void update(Binder<T> newWidget) { | 
| 506 | final oldNotifier = widget.id; | 514 | final oldNotifier = widget.id; | 
| 507 | final newNotifier = newWidget.id; | 515 | final newNotifier = newWidget.id; | 
| 508 | if (oldNotifier != newNotifier && _wasStarted) { | 516 | if (oldNotifier != newNotifier && _wasStarted) { | 
| @@ -523,7 +531,11 @@ class BindElement<T> extends InheritedElement { | @@ -523,7 +531,11 @@ class BindElement<T> extends InheritedElement { | ||
| 523 | if (_dirty) { | 531 | if (_dirty) { | 
| 524 | notifyClients(widget); | 532 | notifyClients(widget); | 
| 525 | } | 533 | } | 
| 534 | + // return Notifier.instance.notifyAppend( | ||
| 535 | + // NotifyData( | ||
| 536 | + // disposers: disposers, updater: getUpdate, throwException: false), | ||
| 526 | return super.build(); | 537 | return super.build(); | 
| 538 | + //); | ||
| 527 | } | 539 | } | 
| 528 | 540 | ||
| 529 | void getUpdate() { | 541 | void getUpdate() { | 
| @@ -532,7 +544,7 @@ class BindElement<T> extends InheritedElement { | @@ -532,7 +544,7 @@ class BindElement<T> extends InheritedElement { | ||
| 532 | } | 544 | } | 
| 533 | 545 | ||
| 534 | @override | 546 | @override | 
| 535 | - void notifyClients(BindWrapper<T> oldWidget) { | 547 | + void notifyClients(Binder<T> oldWidget) { | 
| 536 | super.notifyClients(oldWidget); | 548 | super.notifyClients(oldWidget); | 
| 537 | _dirty = false; | 549 | _dirty = false; | 
| 538 | } | 550 | } | 
| @@ -12,10 +12,14 @@ typedef GetStateUpdate = void Function(); | @@ -12,10 +12,14 @@ typedef GetStateUpdate = void Function(); | ||
| 12 | class ListNotifier extends Listenable | 12 | class ListNotifier extends Listenable | 
| 13 | with ListNotifierSingleMixin, ListNotifierGroupMixin {} | 13 | with ListNotifierSingleMixin, ListNotifierGroupMixin {} | 
| 14 | 14 | ||
| 15 | +/// A Notifier with single listeners | ||
| 15 | class ListNotifierSingle = ListNotifier with ListNotifierSingleMixin; | 16 | class ListNotifierSingle = ListNotifier with ListNotifierSingleMixin; | 
| 16 | 17 | ||
| 18 | +/// A notifier with group of listeners identified by id | ||
| 17 | class ListNotifierGroup = ListNotifier with ListNotifierGroupMixin; | 19 | class ListNotifierGroup = ListNotifier with ListNotifierGroupMixin; | 
| 18 | 20 | ||
| 21 | +/// This mixin add to Listenable the addListener, removerListener and | ||
| 22 | +/// containsListener implementation | ||
| 19 | mixin ListNotifierSingleMixin on Listenable { | 23 | mixin ListNotifierSingleMixin on Listenable { | 
| 20 | List<GetStateUpdate?>? _updaters = <GetStateUpdate?>[]; | 24 | List<GetStateUpdate?>? _updaters = <GetStateUpdate?>[]; | 
| 21 | 25 | ||
| @@ -44,12 +48,12 @@ mixin ListNotifierSingleMixin on Listenable { | @@ -44,12 +48,12 @@ mixin ListNotifierSingleMixin on Listenable { | ||
| 44 | 48 | ||
| 45 | @protected | 49 | @protected | 
| 46 | void reportRead() { | 50 | void reportRead() { | 
| 47 | - NotifierManager.instance.notify(this); | 51 | + Notifier.instance.read(this); | 
| 48 | } | 52 | } | 
| 49 | 53 | ||
| 50 | @protected | 54 | @protected | 
| 51 | void reportAdd(VoidCallback disposer) { | 55 | void reportAdd(VoidCallback disposer) { | 
| 52 | - NotifierManager.instance.reportAdd(disposer); | 56 | + Notifier.instance.add(disposer); | 
| 53 | } | 57 | } | 
| 54 | 58 | ||
| 55 | void _notifyUpdate() { | 59 | void _notifyUpdate() { | 
| @@ -96,7 +100,7 @@ mixin ListNotifierGroupMixin on Listenable { | @@ -96,7 +100,7 @@ mixin ListNotifierGroupMixin on Listenable { | ||
| 96 | @protected | 100 | @protected | 
| 97 | void notifyGroupChildrens(Object id) { | 101 | void notifyGroupChildrens(Object id) { | 
| 98 | assert(_debugAssertNotDisposed()); | 102 | assert(_debugAssertNotDisposed()); | 
| 99 | - NotifierManager.instance.notify(_updatersGroupIds![id]!); | 103 | + Notifier.instance.read(_updatersGroupIds![id]!); | 
| 100 | } | 104 | } | 
| 101 | 105 | ||
| 102 | bool containsId(Object id) { | 106 | bool containsId(Object id) { | 
| @@ -148,44 +152,47 @@ mixin ListNotifierGroupMixin on Listenable { | @@ -148,44 +152,47 @@ mixin ListNotifierGroupMixin on Listenable { | ||
| 148 | } | 152 | } | 
| 149 | } | 153 | } | 
| 150 | 154 | ||
| 151 | -class NotifierManager { | ||
| 152 | - NotifierManager._(); | 155 | +class Notifier { | 
| 156 | + Notifier._(); | ||
| 153 | 157 | ||
| 154 | - static NotifierManager? _instance; | 158 | + static Notifier? _instance; | 
| 159 | + static Notifier get instance => _instance ??= Notifier._(); | ||
| 155 | 160 | ||
| 156 | - static NotifierManager get instance => _instance ??= NotifierManager._(); | 161 | + NotifyData? _notifyData; | 
| 157 | 162 | ||
| 158 | - GetStateUpdate? _setter; | ||
| 159 | - List<VoidCallback>? _remove; | ||
| 160 | - | ||
| 161 | - void reportAdd(VoidCallback listener) { | ||
| 162 | - _remove?.add(listener); | 163 | + void add(VoidCallback listener) { | 
| 164 | + _notifyData?.disposers.add(listener); | ||
| 163 | } | 165 | } | 
| 164 | 166 | ||
| 165 | - void notify(ListNotifierSingleMixin _updaters) { | ||
| 166 | - final listener = _setter; | ||
| 167 | - if (listener != null) { | ||
| 168 | - if (!_updaters.containsListener(listener)) { | ||
| 169 | - _updaters.addListener(listener); | ||
| 170 | - reportAdd(() => _updaters.removeListener(listener)); | ||
| 171 | - } | 167 | + void read(ListNotifierSingleMixin _updaters) { | 
| 168 | + final listener = _notifyData?.updater; | ||
| 169 | + if (listener != null && !_updaters.containsListener(listener)) { | ||
| 170 | + _updaters.addListener(listener); | ||
| 171 | + add(() => _updaters.removeListener(listener)); | ||
| 172 | } | 172 | } | 
| 173 | } | 173 | } | 
| 174 | 174 | ||
| 175 | - T exchange<T>(List<VoidCallback> disposers, GetStateUpdate setState, | ||
| 176 | - T Function() builder) { | ||
| 177 | - _remove = disposers; | ||
| 178 | - _setter = setState; | 175 | + T append<T>(NotifyData data, T Function() builder) { | 
| 176 | + _notifyData = data; | ||
| 179 | final result = builder(); | 177 | final result = builder(); | 
| 180 | - if (disposers.isEmpty) { | 178 | + if (data.disposers.isEmpty && data.throwException) { | 
| 181 | throw ObxError(); | 179 | throw ObxError(); | 
| 182 | } | 180 | } | 
| 183 | - _remove = null; | ||
| 184 | - _setter = null; | 181 | + _notifyData = data; | 
| 185 | return result; | 182 | return result; | 
| 186 | } | 183 | } | 
| 187 | } | 184 | } | 
| 188 | 185 | ||
| 186 | +class NotifyData { | ||
| 187 | + const NotifyData( | ||
| 188 | + {required this.updater, | ||
| 189 | + required this.disposers, | ||
| 190 | + this.throwException = true}); | ||
| 191 | + final GetStateUpdate updater; | ||
| 192 | + final List<VoidCallback> disposers; | ||
| 193 | + final bool throwException; | ||
| 194 | +} | ||
| 195 | + | ||
| 189 | class ObxError { | 196 | class ObxError { | 
| 190 | const ObxError(); | 197 | const ObxError(); | 
| 191 | @override | 198 | @override | 
| 1 | import 'package:flutter/material.dart'; | 1 | import 'package:flutter/material.dart'; | 
| 2 | 2 | ||
| 3 | -import '../../get_state_manager.dart'; | 3 | +import '../rx_flutter/rx_obx_widget.dart'; | 
| 4 | +import 'get_controllers.dart'; | ||
| 5 | +import 'get_state.dart'; | ||
| 4 | 6 | ||
| 5 | class MixinBuilder<T extends GetxController> extends StatelessWidget { | 7 | class MixinBuilder<T extends GetxController> extends StatelessWidget { | 
| 6 | @required | 8 | @required | 
| @@ -11,7 +13,7 @@ class MixinBuilder<T extends GetxController> extends StatelessWidget { | @@ -11,7 +13,7 @@ class MixinBuilder<T extends GetxController> extends StatelessWidget { | ||
| 11 | final void Function(BindElement<T> state)? initState, | 13 | final void Function(BindElement<T> state)? initState, | 
| 12 | dispose, | 14 | dispose, | 
| 13 | didChangeDependencies; | 15 | didChangeDependencies; | 
| 14 | - final void Function(BindWrapper<T> oldWidget, BindElement<T> state)? | 16 | + final void Function(Binder<T> oldWidget, BindElement<T> state)? | 
| 15 | didUpdateWidget; | 17 | didUpdateWidget; | 
| 16 | final T? init; | 18 | final T? init; | 
| 17 | 19 | 
| @@ -45,7 +45,6 @@ class _ValueBuilderState<T> extends State<ValueBuilder<T>> { | @@ -45,7 +45,6 @@ class _ValueBuilderState<T> extends State<ValueBuilder<T>> { | ||
| 45 | T value; | 45 | T value; | 
| 46 | _ValueBuilderState(this.value); | 46 | _ValueBuilderState(this.value); | 
| 47 | 47 | ||
| 48 | - | ||
| 49 | @override | 48 | @override | 
| 50 | Widget build(BuildContext context) => widget.builder(value, updater); | 49 | Widget build(BuildContext context) => widget.builder(value, updater); | 
| 51 | 50 | ||
| @@ -92,17 +91,27 @@ abstract class ObxStatelessWidget extends StatelessWidget { | @@ -92,17 +91,27 @@ abstract class ObxStatelessWidget extends StatelessWidget { | ||
| 92 | 91 | ||
| 93 | /// a Component that can track changes in a reactive variable | 92 | /// a Component that can track changes in a reactive variable | 
| 94 | mixin ObserverComponent on ComponentElement { | 93 | mixin ObserverComponent on ComponentElement { | 
| 95 | - final disposers = <Disposer>[]; | 94 | + List<Disposer>? disposers = <Disposer>[]; | 
| 95 | + | ||
| 96 | + void getUpdate() { | ||
| 97 | + if (disposers != null) { | ||
| 98 | + markNeedsBuild(); | ||
| 99 | + } | ||
| 100 | + } | ||
| 96 | 101 | ||
| 97 | @override | 102 | @override | 
| 98 | - Widget build() => | ||
| 99 | - NotifierManager.instance.exchange(disposers, markNeedsBuild, super.build); | 103 | + Widget build() { | 
| 104 | + return Notifier.instance.append( | ||
| 105 | + NotifyData(disposers: disposers!, updater: getUpdate), super.build); | ||
| 106 | + } | ||
| 100 | 107 | ||
| 101 | @override | 108 | @override | 
| 102 | void unmount() { | 109 | void unmount() { | 
| 103 | super.unmount(); | 110 | super.unmount(); | 
| 104 | - for (final disposer in disposers) { | 111 | + for (final disposer in disposers!) { | 
| 105 | disposer(); | 112 | disposer(); | 
| 106 | } | 113 | } | 
| 114 | + disposers!.clear(); | ||
| 115 | + disposers = null; | ||
| 107 | } | 116 | } | 
| 108 | } | 117 | } | 
| 1 | import 'package:collection/collection.dart'; | 1 | import 'package:collection/collection.dart'; | 
| 2 | import 'package:flutter/material.dart'; | 2 | import 'package:flutter/material.dart'; | 
| 3 | 3 | ||
| 4 | -extension ContextExtensionss on BuildContext { | 4 | +extension ContextExt on BuildContext { | 
| 5 | /// The same of [MediaQuery.of(context).size] | 5 | /// The same of [MediaQuery.of(context).size] | 
| 6 | Size get mediaQuerySize => MediaQuery.of(this).size; | 6 | Size get mediaQuerySize => MediaQuery.of(this).size; | 
| 7 | 7 | ||
| @@ -105,8 +105,8 @@ extension ContextExtensionss on BuildContext { | @@ -105,8 +105,8 @@ extension ContextExtensionss on BuildContext { | ||
| 105 | /// True if the width is higher than 600p | 105 | /// True if the width is higher than 600p | 
| 106 | bool get isPhoneOrWider => width >= 600; | 106 | bool get isPhoneOrWider => width >= 600; | 
| 107 | 107 | ||
| 108 | - /// same as [isPhoneOrLess] | ||
| 109 | - bool get isPhone => isPhoneOrLess; | 108 | + /// True if the shortestSide is smaller than 600p | 
| 109 | + bool get isPhone => (mediaQueryShortestSide < 600); | ||
| 110 | 110 | ||
| 111 | /// True if the width is smaller than 600p | 111 | /// True if the width is smaller than 600p | 
| 112 | bool get isSmallTabletOrLess => width <= 600; | 112 | bool get isSmallTabletOrLess => width <= 600; | 
| @@ -114,8 +114,11 @@ extension ContextExtensionss on BuildContext { | @@ -114,8 +114,11 @@ extension ContextExtensionss on BuildContext { | ||
| 114 | /// True if the width is higher than 600p | 114 | /// True if the width is higher than 600p | 
| 115 | bool get isSmallTabletOrWider => width >= 600; | 115 | bool get isSmallTabletOrWider => width >= 600; | 
| 116 | 116 | ||
| 117 | - /// same as [isSmallTabletOrLess] | ||
| 118 | - bool get isSmallTablet => isSmallTabletOrLess; | 117 | + /// True if the shortestSide is largest than 600p | 
| 118 | + bool get isSmallTablet => (mediaQueryShortestSide >= 600); | ||
| 119 | + | ||
| 120 | + /// True if the shortestSide is largest than 720p | ||
| 121 | + bool get isLargeTablet => (mediaQueryShortestSide >= 720); | ||
| 119 | 122 | ||
| 120 | /// True if the width is smaller than 720p | 123 | /// True if the width is smaller than 720p | 
| 121 | bool get isLargeTabletOrLess => width <= 720; | 124 | bool get isLargeTabletOrLess => width <= 720; | 
| @@ -123,11 +126,8 @@ extension ContextExtensionss on BuildContext { | @@ -123,11 +126,8 @@ extension ContextExtensionss on BuildContext { | ||
| 123 | /// True if the width is higher than 720p | 126 | /// True if the width is higher than 720p | 
| 124 | bool get isLargeTabletOrWider => width >= 720; | 127 | bool get isLargeTabletOrWider => width >= 720; | 
| 125 | 128 | ||
| 126 | - /// same as [isLargeTabletOrLess] | ||
| 127 | - bool get isLargeTablet => isLargeTabletOrLess; | ||
| 128 | - | ||
| 129 | /// True if the current device is Tablet | 129 | /// True if the current device is Tablet | 
| 130 | - bool get isTablet => isSmallTablet; | 130 | + bool get isTablet => isSmallTablet || isLargeTablet; | 
| 131 | 131 | ||
| 132 | /// True if the width is smaller than 1200p | 132 | /// True if the width is smaller than 1200p | 
| 133 | bool get isDesktopOrLess => width <= 1200; | 133 | bool get isDesktopOrLess => width <= 1200; | 
| 1 | import '../get_utils/get_utils.dart'; | 1 | import '../get_utils/get_utils.dart'; | 
| 2 | 2 | ||
| 3 | extension GetDynamicUtils on dynamic { | 3 | extension GetDynamicUtils on dynamic { | 
| 4 | - @Deprecated('isNull is deprecated and cannot be used, use "==" operator') | ||
| 5 | - bool get isNull => GetUtils.isNull(this); | ||
| 6 | - | ||
| 7 | bool? get isBlank => GetUtils.isBlank(this); | 4 | bool? get isBlank => GetUtils.isBlank(this); | 
| 8 | 5 | ||
| 9 | @Deprecated( | 6 | @Deprecated( | 
| 1 | import '../get_utils/get_utils.dart'; | 1 | import '../get_utils/get_utils.dart'; | 
| 2 | 2 | ||
| 3 | extension GetStringUtils on String { | 3 | extension GetStringUtils on String { | 
| 4 | + /// Discover if the String is a valid number | ||
| 4 | bool get isNum => GetUtils.isNum(this); | 5 | bool get isNum => GetUtils.isNum(this); | 
| 5 | 6 | ||
| 7 | + /// Discover if the String is numeric only | ||
| 6 | bool get isNumericOnly => GetUtils.isNumericOnly(this); | 8 | bool get isNumericOnly => GetUtils.isNumericOnly(this); | 
| 7 | 9 | ||
| 10 | + String numericOnly({bool firstWordOnly = false}) => | ||
| 11 | + GetUtils.numericOnly(this, firstWordOnly: firstWordOnly); | ||
| 12 | + | ||
| 13 | + /// Discover if the String is alphanumeric only | ||
| 8 | bool get isAlphabetOnly => GetUtils.isAlphabetOnly(this); | 14 | bool get isAlphabetOnly => GetUtils.isAlphabetOnly(this); | 
| 9 | 15 | ||
| 16 | + /// Discover if the String is a boolean | ||
| 10 | bool get isBool => GetUtils.isBool(this); | 17 | bool get isBool => GetUtils.isBool(this); | 
| 11 | 18 | ||
| 19 | + /// Discover if the String is a vector | ||
| 12 | bool get isVectorFileName => GetUtils.isVector(this); | 20 | bool get isVectorFileName => GetUtils.isVector(this); | 
| 13 | 21 | ||
| 22 | + /// Discover if the String is a ImageFileName | ||
| 14 | bool get isImageFileName => GetUtils.isImage(this); | 23 | bool get isImageFileName => GetUtils.isImage(this); | 
| 15 | 24 | ||
| 25 | + /// Discover if the String is a AudioFileName | ||
| 16 | bool get isAudioFileName => GetUtils.isAudio(this); | 26 | bool get isAudioFileName => GetUtils.isAudio(this); | 
| 17 | 27 | ||
| 28 | + /// Discover if the String is a VideoFileName | ||
| 18 | bool get isVideoFileName => GetUtils.isVideo(this); | 29 | bool get isVideoFileName => GetUtils.isVideo(this); | 
| 19 | 30 | ||
| 31 | + /// Discover if the String is a TxtFileName | ||
| 20 | bool get isTxtFileName => GetUtils.isTxt(this); | 32 | bool get isTxtFileName => GetUtils.isTxt(this); | 
| 21 | 33 | ||
| 34 | + /// Discover if the String is a Document Word | ||
| 22 | bool get isDocumentFileName => GetUtils.isWord(this); | 35 | bool get isDocumentFileName => GetUtils.isWord(this); | 
| 23 | 36 | ||
| 37 | + /// Discover if the String is a Document Excel | ||
| 24 | bool get isExcelFileName => GetUtils.isExcel(this); | 38 | bool get isExcelFileName => GetUtils.isExcel(this); | 
| 25 | 39 | ||
| 40 | + /// Discover if the String is a Document Powerpoint | ||
| 26 | bool get isPPTFileName => GetUtils.isPPT(this); | 41 | bool get isPPTFileName => GetUtils.isPPT(this); | 
| 27 | 42 | ||
| 43 | + /// Discover if the String is a APK File | ||
| 28 | bool get isAPKFileName => GetUtils.isAPK(this); | 44 | bool get isAPKFileName => GetUtils.isAPK(this); | 
| 29 | 45 | ||
| 46 | + /// Discover if the String is a PDF file | ||
| 30 | bool get isPDFFileName => GetUtils.isPDF(this); | 47 | bool get isPDFFileName => GetUtils.isPDF(this); | 
| 31 | 48 | ||
| 49 | + /// Discover if the String is a HTML file | ||
| 32 | bool get isHTMLFileName => GetUtils.isHTML(this); | 50 | bool get isHTMLFileName => GetUtils.isHTML(this); | 
| 33 | 51 | ||
| 52 | + /// Discover if the String is a URL file | ||
| 34 | bool get isURL => GetUtils.isURL(this); | 53 | bool get isURL => GetUtils.isURL(this); | 
| 35 | 54 | ||
| 55 | + /// Discover if the String is a Email | ||
| 36 | bool get isEmail => GetUtils.isEmail(this); | 56 | bool get isEmail => GetUtils.isEmail(this); | 
| 37 | 57 | ||
| 58 | + /// Discover if the String is a Phone Number | ||
| 38 | bool get isPhoneNumber => GetUtils.isPhoneNumber(this); | 59 | bool get isPhoneNumber => GetUtils.isPhoneNumber(this); | 
| 39 | 60 | ||
| 61 | + /// Discover if the String is a DateTime | ||
| 40 | bool get isDateTime => GetUtils.isDateTime(this); | 62 | bool get isDateTime => GetUtils.isDateTime(this); | 
| 41 | 63 | ||
| 64 | + /// Discover if the String is a MD5 Hash | ||
| 42 | bool get isMD5 => GetUtils.isMD5(this); | 65 | bool get isMD5 => GetUtils.isMD5(this); | 
| 43 | 66 | ||
| 67 | + /// Discover if the String is a SHA1 Hash | ||
| 44 | bool get isSHA1 => GetUtils.isSHA1(this); | 68 | bool get isSHA1 => GetUtils.isSHA1(this); | 
| 45 | 69 | ||
| 70 | + /// Discover if the String is a SHA256 Hash | ||
| 46 | bool get isSHA256 => GetUtils.isSHA256(this); | 71 | bool get isSHA256 => GetUtils.isSHA256(this); | 
| 47 | 72 | ||
| 73 | + /// Discover if the String is a bynary value | ||
| 48 | bool get isBinary => GetUtils.isBinary(this); | 74 | bool get isBinary => GetUtils.isBinary(this); | 
| 49 | 75 | ||
| 76 | + /// Discover if the String is a ipv4 | ||
| 50 | bool get isIPv4 => GetUtils.isIPv4(this); | 77 | bool get isIPv4 => GetUtils.isIPv4(this); | 
| 51 | 78 | ||
| 52 | bool get isIPv6 => GetUtils.isIPv6(this); | 79 | bool get isIPv6 => GetUtils.isIPv6(this); | 
| 53 | 80 | ||
| 81 | + /// Discover if the String is a Hexadecimal | ||
| 54 | bool get isHexadecimal => GetUtils.isHexadecimal(this); | 82 | bool get isHexadecimal => GetUtils.isHexadecimal(this); | 
| 55 | 83 | ||
| 84 | + /// Discover if the String is a palindrom | ||
| 56 | bool get isPalindrom => GetUtils.isPalindrom(this); | 85 | bool get isPalindrom => GetUtils.isPalindrom(this); | 
| 57 | 86 | ||
| 87 | + /// Discover if the String is a passport number | ||
| 58 | bool get isPassport => GetUtils.isPassport(this); | 88 | bool get isPassport => GetUtils.isPassport(this); | 
| 59 | 89 | ||
| 90 | + /// Discover if the String is a currency | ||
| 60 | bool get isCurrency => GetUtils.isCurrency(this); | 91 | bool get isCurrency => GetUtils.isCurrency(this); | 
| 61 | 92 | ||
| 93 | + /// Discover if the String is a CPF number | ||
| 62 | bool get isCpf => GetUtils.isCpf(this); | 94 | bool get isCpf => GetUtils.isCpf(this); | 
| 63 | 95 | ||
| 96 | + /// Discover if the String is a CNPJ number | ||
| 64 | bool get isCnpj => GetUtils.isCnpj(this); | 97 | bool get isCnpj => GetUtils.isCnpj(this); | 
| 65 | 98 | ||
| 99 | + /// Discover if the String is a case insensitive | ||
| 66 | bool isCaseInsensitiveContains(String b) => | 100 | bool isCaseInsensitiveContains(String b) => | 
| 67 | GetUtils.isCaseInsensitiveContains(this, b); | 101 | GetUtils.isCaseInsensitiveContains(this, b); | 
| 68 | 102 | ||
| 103 | + /// Discover if the String is a case sensitive and contains any value | ||
| 69 | bool isCaseInsensitiveContainsAny(String b) => | 104 | bool isCaseInsensitiveContainsAny(String b) => | 
| 70 | GetUtils.isCaseInsensitiveContainsAny(this, b); | 105 | GetUtils.isCaseInsensitiveContainsAny(this, b); | 
| 71 | 106 | ||
| 107 | + /// capitalize the String | ||
| 72 | String? get capitalize => GetUtils.capitalize(this); | 108 | String? get capitalize => GetUtils.capitalize(this); | 
| 73 | 109 | ||
| 110 | + /// Capitalize the first letter of the String | ||
| 74 | String? get capitalizeFirst => GetUtils.capitalizeFirst(this); | 111 | String? get capitalizeFirst => GetUtils.capitalizeFirst(this); | 
| 75 | 112 | ||
| 113 | + /// remove all whitespace from the String | ||
| 76 | String get removeAllWhitespace => GetUtils.removeAllWhitespace(this); | 114 | String get removeAllWhitespace => GetUtils.removeAllWhitespace(this); | 
| 77 | 115 | ||
| 116 | + /// converter the String | ||
| 78 | String? get camelCase => GetUtils.camelCase(this); | 117 | String? get camelCase => GetUtils.camelCase(this); | 
| 79 | 118 | ||
| 119 | + /// Discover if the String is a valid URL | ||
| 80 | String? get paramCase => GetUtils.paramCase(this); | 120 | String? get paramCase => GetUtils.paramCase(this); | 
| 81 | 121 | ||
| 82 | - String numericOnly({bool firstWordOnly = false}) => | ||
| 83 | - GetUtils.numericOnly(this, firstWordOnly: firstWordOnly); | ||
| 84 | - | 122 | + /// add segments to the String | 
| 85 | String createPath([Iterable? segments]) { | 123 | String createPath([Iterable? segments]) { | 
| 86 | final path = startsWith('/') ? this : '/$this'; | 124 | final path = startsWith('/') ? this : '/$this'; | 
| 87 | return GetUtils.createPath(path, segments); | 125 | return GetUtils.createPath(path, segments); | 
| 1 | +import 'package:flutter/material.dart'; | ||
| 2 | + | ||
| 3 | +class OtimizedListView<T> extends StatelessWidget { | ||
| 4 | + final List<T> list; | ||
| 5 | + final Axis scrollDirection; | ||
| 6 | + final bool reverse; | ||
| 7 | + final ScrollController? controller; | ||
| 8 | + final bool? primary; | ||
| 9 | + final ScrollPhysics? physics; | ||
| 10 | + final bool shrinkWrap; | ||
| 11 | + final Widget onEmpty; | ||
| 12 | + final int lenght; | ||
| 13 | + final Widget Function(BuildContext context, ValueKey key, T item) builder; | ||
| 14 | + const OtimizedListView({ | ||
| 15 | + Key? key, | ||
| 16 | + required this.list, | ||
| 17 | + required this.builder, | ||
| 18 | + this.scrollDirection = Axis.vertical, | ||
| 19 | + this.reverse = false, | ||
| 20 | + this.controller, | ||
| 21 | + this.primary, | ||
| 22 | + this.physics, | ||
| 23 | + this.onEmpty = const SizedBox.shrink(), | ||
| 24 | + this.shrinkWrap = false, | ||
| 25 | + }) : lenght = list.length, | ||
| 26 | + super(key: key); | ||
| 27 | + @override | ||
| 28 | + Widget build(BuildContext context) { | ||
| 29 | + if (list.isEmpty) return onEmpty; | ||
| 30 | + | ||
| 31 | + return CustomScrollView( | ||
| 32 | + controller: controller, | ||
| 33 | + reverse: reverse, | ||
| 34 | + scrollDirection: scrollDirection, | ||
| 35 | + primary: primary, | ||
| 36 | + physics: physics, | ||
| 37 | + shrinkWrap: shrinkWrap, | ||
| 38 | + slivers: <Widget>[ | ||
| 39 | + SliverList( | ||
| 40 | + delegate: SliverChildBuilderDelegate( | ||
| 41 | + (context, i) { | ||
| 42 | + final item = list[i]; | ||
| 43 | + final key = ValueKey(item); | ||
| 44 | + return builder(context, key, item); | ||
| 45 | + }, | ||
| 46 | + childCount: list.length, | ||
| 47 | + addAutomaticKeepAlives: true, | ||
| 48 | + findChildIndexCallback: (key) { | ||
| 49 | + return list.indexWhere((m) => m == (key as ValueKey<T>).value); | ||
| 50 | + }, | ||
| 51 | + ), | ||
| 52 | + ), | ||
| 53 | + ], | ||
| 54 | + ); | ||
| 55 | + } | ||
| 56 | +} | 
| 1 | import 'package:flutter/material.dart'; | 1 | import 'package:flutter/material.dart'; | 
| 2 | import 'package:flutter_test/flutter_test.dart'; | 2 | import 'package:flutter_test/flutter_test.dart'; | 
| 3 | import 'package:get/get.dart'; | 3 | import 'package:get/get.dart'; | 
| 4 | +import 'package:get/get_state_manager/src/simple/mixin_builder.dart'; | ||
| 4 | 5 | ||
| 5 | void main() { | 6 | void main() { | 
| 6 | - testWidgets("MixinBuilder smoke test", (tester) async { | 7 | + testWidgets("MixinBuilder with reactive and not reactive", (tester) async { | 
| 7 | await tester.pumpWidget( | 8 | await tester.pumpWidget( | 
| 8 | MaterialApp( | 9 | MaterialApp( | 
| 9 | home: MixinBuilder<Controller>( | 10 | home: MixinBuilder<Controller>( | 
| @@ -35,6 +36,10 @@ void main() { | @@ -35,6 +36,10 @@ void main() { | ||
| 35 | TextButton( | 36 | TextButton( | 
| 36 | child: Text("increment"), | 37 | child: Text("increment"), | 
| 37 | onPressed: () => controller.increment(), | 38 | onPressed: () => controller.increment(), | 
| 39 | + ), | ||
| 40 | + TextButton( | ||
| 41 | + child: Text("increment2"), | ||
| 42 | + onPressed: () => controller.increment2(), | ||
| 38 | ) | 43 | ) | 
| 39 | ], | 44 | ], | 
| 40 | ); | 45 | ); | 
| @@ -62,6 +67,12 @@ void main() { | @@ -62,6 +67,12 @@ void main() { | ||
| 62 | await tester.pump(); | 67 | await tester.pump(); | 
| 63 | 68 | ||
| 64 | expect(find.text("Count: 2"), findsOneWidget); | 69 | expect(find.text("Count: 2"), findsOneWidget); | 
| 70 | + | ||
| 71 | + await tester.tap(find.text('increment2')); | ||
| 72 | + | ||
| 73 | + await tester.pump(); | ||
| 74 | + | ||
| 75 | + expect(find.text("Count2: 1"), findsOneWidget); | ||
| 65 | }); | 76 | }); | 
| 66 | 77 | ||
| 67 | // testWidgets( | 78 | // testWidgets( | 
- 
Please register or login to post a comment