GetxService onInit in an error GetLifeCycle
wxqqh opened this issue · comments
GetxService onInit in an error GetLifeCycle
The problem is that in GetLifeCycleBase, onInit is a FutureFunctional(), but it is executed synchronously. So the completion status of onInit CANNOT be obtained after Get.put is completed outside
the code is at there GetLifeCycleBase
mixin GetLifeCycleBase {
// Internal callback that starts the cycle of this controller.
void _onStart() {
if (_initialized) return;
onInit(); // <--- The problem here is that in GetLifeCycleBase,
_initialized = true;
}
}
example:
class DbService extends GetxService {
Future<DbService> onInit() async { /// onInit is Future<dynamic> Function()
print('$runtimeType delays 2 sec');
await 2.delay();
print('$runtimeType ready!');
return this;
}
}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('FimberLoggerService', () {
setUpAll(() async {
// Get.putAsync(DbService());
/// ⬆️ The signature and usage of this function do not match the documentation https://github.com/jonataslaw/getx/blob/master/README.md#getxservice
/// The argument type 'DbService' can't be assigned to the parameter type 'Future<dynamic> Function()'.
/// must to
Get.put(DbService());
/// or
Get.putAsync(() async => DbService());
})
test("Get DbService", () {
DbService db = Get.find(); // <--- onInit not finish
})
}
To Reproduce
run the test case
Expected behavior
Get.putAsync
may get the onInit finish OR just like doc call onInit MANUAL
Now this code can work, but it is very cumbersome
class DbService extends GetxService {
Future<DbService> onInit() async {
if(_initialized) return this;
print('$runtimeType delays 2 sec');
await 2.delay();
print('$runtimeType ready!');
return this;
_initialized = true;
}
bool _initialized = false;
}
// in test group
setUpAll(() async {
await Get.putAsync(() async {
GetxService service = GetxService();
await service.onInit();
return service;
});
})
Getx Version:
dependencies:
get: ^4.6.6
Describe on which device you found the bug:
In test case
this code is work for me but not elegant:
import 'package:get/get.dart';
import 'package:flutter_test/flutter_test.dart';
abstract class AppService<T extends GetxService> extends GetxService {
@override
Future<T> onInit() async {
if (_initialized) return this as T;
super.onInit();
await init();
_initialized = true;
return this as T;
}
Future<void> init() async => throw UnimplementedError();
bool _initialized = false;
}
class DBService extends AppService {
@override
Future<void> init() async {
/// do sth. only called once
}
}
void main() {
setUpAll(() async {
await Get.putAsync(() => DBService().onInit());
DBService db = Get.find();
/// do sth. with DBService instance
});
}
Thanks for opening this issue, and you're absolutely right!
This is an architectural problem that I didn't think about at the time I created putAsync, to be honest, I used exactly the code you sent (I believe you've seen something like this on discord) for many years.
About a year ago I created a Wrapper that allows me to do this in a more elegant way, I believe I can insert it into the package.