Flutter

Flutter UI와 State의 분리 - @initState와 Callback 함수 사용

얼룩와와 2025. 3. 16. 11:16

페이지 이동에서 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)이 사용할 것이다~ 서로 약속하기 위해 사용한다.