Flutter UI와 State의 분리 - @initState와 Callback 함수 사용
페이지 이동에서 UI와 State의 역할분담
UI : 어느 페이지로 이동해야 하는지 관리
State : 페이지 이동 시점
관심사 분리의 장점
1. 누가 무엇을 담당하는지 명확하면 유지보수가 쉬움
2. 서로 직접 의존하지 않음으로써 재사용이 가능함 (e.g. TestState가 Navigator를 직접 호출하면 TestState를 페이지 전환 없이 사용할 수 없어짐)
3. TestState 내부에서 Navigator를 직접 호출하면 UI 없이 단위테스트가 어려움
페이지 이동 시나리오 예시
퀴즈 기능을 구현하기 위한 UI는 `TestPage`이고, 퀴즈 관련 상태 관리는 `TestState`라는 클래스가 한다.
class TestState extends ChangeNotifier {
(...생략...)
void setOnTestEnd(VoidCallback callback) {
onTestEnd = callback; // 외부에서 종료 콜백 설정
}
void _loadNextQuestion() {
if (availableCreatures.isEmpty) {
if (onTestEnd != null) {
onTestEnd!(); // 모든 문제가 끝나면 UI에서 설정한 콜백 실행
}
return;
}
currentCorrectAnswer = availableCreatures.removeLast();
usedCorrectAnswers.add(currentCorrectAnswer!);
currentChoices = _generateChoices();
startPlayingPeriodically();
notifyListeners();
}
}
class _TestPageState extends State<TestPage> {
@override
void initState() {
super.initState();
Provider.of<TestState>(context, listen: false).setOnTestEnd(() {
if (context.mounted) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => const EndingPage()),
);
}
});
}
(...생략...)
void _loadNextQuestion() {
if (availableCreatures.isEmpty) {
if (onTestEnd != null) {
onTestEnd!(); // 모든 문제가 끝나면 UI에서 설정한 콜백 실행
}
return;
}
currentCorrectAnswer = availableCreatures.removeLast();
usedCorrectAnswers.add(currentCorrectAnswer!);
currentChoices = _generateChoices();
startPlayingPeriodically();
notifyListeners();
}
}
State의 역할 = 페이지 이동 시점에 이벤트 트리거
페이지 이동은 해야 하는 시점은 TestState가 알 수 있다. 지금 몇 번 퀴즈를 풀고 있는지, 퀴즈가 끝났는지 TestState가 관리하기 때문이다. 위 `TestState` 클래스에서 `_loadNextQuestion()` 함수 내에서 퀴즈 종료를 발견하면 UI에서 등록한 함수를 호출한다.
UI의 역할 = Navigator를 통해 어떤 Page로 이동해야 하는지 관리
어떤 페이지로 이동해야 하는지는 UI가 관리하도록 한다. 따라서 Navigator.push()를 호출하는 Callback 함수 하나를 초기화될 때 최초로 State에 등록시킨다. 그 코드가 바로 `initState`이다.
Callback 함수를 사용하는 이유
State 입장에서 어떤 함수를 호출하긴 할건데, 그게 어떻게 생긴 함수인지는 다른 사람(여기선 UI)이 알려줄 것이다~라고 하는 것이 바로 Callback 함수이다. UI도 마찬가지로 이런 함수를 `onTestEnd`라는 이름으로 등록해 놓겠다, 이 함수는 다른 사람(여기선 State)이 사용할 것이다~ 서로 약속하기 위해 사용한다.