본문 바로가기

Flutter

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