StatefulWidget 을 사용해서 만들었던 위젯을 GetX의 Dependency Injection을 이용해서 View - Controller 패턴으로 수정하는 과정에서 GetX와 FutureBuilder를 잘못 사용했던 케이스
// main.dart
// App Main class
class MyApp extends StatelessWidget {
MyApp({Key? key}) : super(key: key);
final AppController c = Get.put(AppController());
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: AppController.to.initialize(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
return Center(child: Text('Initialization Success.');
} else if (snapshot.hasError) {
return Center(child: Text('Initialization Failed'); // 초기화 실패
} else {
return Center(child: Text('Initializing...'); // 초기화중. Splash 화면
}
},
);
}
}
// app_controller.dart
// Main Controller class
class AppController extnds GetxService {
static AppController get to => Get.find();
@override
void onInit() {
super.onInit();
}
Future<void> initialize() async {
await initSettings(); // 이하 임의의 초기화 루틴들
await checkVersions();
await initFirebase();
....
return;
}
}
이런 패턴으로 작성하는 경우 별 문제 없이 동작하는 것처럼 보이지만, MyApp 의 build() 가 다시 호출될 때마다 AppController의 initialize() 함수가 다시 실행되어서 초기화 과정이 반복 실행되는 문제가 생긴다.
그래서 FutureBuilder의 future 에서는 function을 직접 호출하지 말고, 그 전에 호출된 Future 를 기다리는 형태로 처리해야 한다.
StatefulWidget의 경우에는 아래와 같은 패턴으로 먼저 initState() 에서 초기화 함수를 호출하고, 그 결과를 FutureBuilder의 future에 지정해주는 방식으로 처리해주면 깔끔하다.
class MyApp extends StatefulWidget {
MyApp({Key? key}) : super(key: key);
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Future<InitResult> _initResult;
@override
void initState() {
_initResult = _initializeApp();
super.initState();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _initResult,
builder: (context, snapshot) {
.......
}
);
}
Future<InitResult> _initializeApp() async {
await .......
await ....... // 초기화
return ret;
}
GetX 를 사용하는 경우에는 대략 아래와 같이 FutureBuilder 대신 Obs, Obx를 이용하면 되겠다.
// main.dart
// App Main class
class MyApp extends StatelessWidget {
MyApp({Key? key}) : super(key: key);
final AppController c = Get.put(AppController());
@override
Widget build(BuildContext context) {
return Obx(() {
if (c.isInitialized.value) {
return Center(child: Text('Initialization Success.');
} else {
return Center(child: Text('Initializing...'); // 초기화중. Splash 화면
}
}
);
}
}
// app_controller.dart
// Main Controller class
class AppController extnds GetxService {
static AppController get to => Get.find();
final isInitialized = false.obs;
@override
void onInit() {
initialize();
super.onInit();
}
@override
void onClose() {
isInitialized.valie = false;
super.onClose();
}
Future<void> initialize() async {
await initSettings(); // 이하 임의의 초기화 루틴들
await checkVersions();
await initFirebase();
....
isInitialized.value = true;
return;
}
}