페이지 이동에서 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)이 사용할 것이다~ 서로 약속하기 위해 사용한다.
'Flutter' 카테고리의 다른 글
setState() or markNeedsBuild() called during build 에러 원인과 해결 (1) | 2025.03.23 |
---|---|
Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active. 에러 원인과 해결 방법 (0) | 2025.03.20 |
Unbounded height에 대한 이해 (0) | 2025.02.02 |
새로운 flutter 프로젝트 생성하는 법 (1) | 2025.01.07 |
Dart, Flutter, Android Studio 개발 환경 구축 (3) | 2025.01.06 |