티스토리 뷰

화면의 설정 정보를 담는 Route는 여러 Callback methods를 가지고 있다.

이 메서드들은 자신이 Navigator에 push/pop 될 때 호출되고,  nextRoute가 push/pop될때도 호출된다.

 

앱을 개발할 때 현재 화면이  Natigator에서 가장 위에 표시되는 화면인지 알고 싶을 때가 있다.  특히 다음 화면으로 넘어갔다가 다시 이전화면으로 돌아올 때 화면의 데이터를 갱신하거나 기타 필요한 동작을 해야 할 때가 있기 때문이다. 

물론  await Nativiator.push()를 처럼 await구분을 이용해서 다음 화면이 pop될때 까지 기다리는 방법도 있지만 화면이 복잡해지고 연결된 다음화면이 여러 개 있을 경우 일괄적인 처리가 필요할 때도 있다.

 

Android에서는 Activity의 Lifecycle 이벤트를 이용해서 Resume/Pause 되는 이벤트를 수신할 수 있지만 Flutter에서는 그와 같은 이벤트를 발생키시지 않는다. 

 

Navigator는 내부적으로 _RouteEntry이라는 Route의 랩핑 클래스를 따로 만들어 Route를 관리하는데 _RouteEntry는 별도의 상태정보인 _RouteLifecycle를 사용한다. 이 값들은 Navigator에서 내부적으로 사용하기 위한 Inner class로  외부에서 직접 사용하기에는 문제가 있다.

 

이를 해결하기 위해서 우리가 할 수 있는 몇가지 방법이 있는데 그중 한 가지 방법을 소개하고자 한다. 

Route Callback Methods

아래 그림은 Navigator/_RouteEntiry /Route/Widget(화면)의 관계를 나타 내고 있다.

 

우리가 직접 다루는 Widget에서 _RouteLifecycle과 유사한 화면의 Lifecycle정보가 필요할때가 있다. 

개발자가 활용 할수 있는 방법은 Route의 Callback methods를 이용해서 Route를 Lifecycle정보를 직접 관리하고 이 값이 변경될 때 Widget에게 전달할 수 있도록 하면 된다.

 

그렇다면 우선 Route에서 활용 가능한 Callback Method들에 대해서 알아보자.

아래코드는 Route의 전체 코드 중 Callback Method만 따로 정리한 것이다. 

  TickerFuture didPush() { }

  void didAdd() { }

  void didReplace(Route<dynamic>? oldRoute) {  }

  bool didPop(T? result) {} 

  void didComplete(T? result) {}

  void didPopNext(Route<dynamic> nextRoute) { }

  void didChangeNext(Route<dynamic>? nextRoute) { }

  void didChangePrevious(Route<dynamic>? previousRoute) { }

  void dispose() {  }

함수 명에서 어느 정도 의미를 파악할 수 있겠지만 Route가 Navigator에 push / pop 되는 상태에 따라 호출되는 didPush/didPop 같은 함수도 있고 다음 Route(nextRoute)가 Navigator에 push/pop 되었을 때 알려주는 didChangeNext / didPopNext과 같은 함수도 있다.

 

Navigator가 Route의 Callback 함수를 호출하는 것을 이용해서 Route의 자체적인 상태를 정의해 볼 수 있다.

우선 안드로이드의 Activity의 상태를 참고해서 created/ resumed / paused의 상태가 있다고 가정해 보자

Enum RouteLifecycle { created, resumed, paused, destroryed }

created는 처음 Route의 상태이며, didPush가 발생하면 resumed로 didPop이 발생하면 destroryed로 변경된다.

그리고 nextRoute가 push 되는 상태를 알려주는 didChangeNext가 발생하면 paused로 didPopNext가 발생하면 resumed로 변경된다.

위 LifeCycle의 상태정의는 Flutter에서 제공하는 것이 아닌 개발자가 임의로 정해서 만든 것으로 Route의 Callback함수를 활용해서 정의한 것임에 유의하기 바란다. (필요에 따라 상태를 정의해서 사용하세요)

 

그런 이제 위 이벤트에 따라 Route의 상태를 변경하는 CustomPageRoute를 만들어 보자.
아래 Route는 몇 가지 Callback함수를 재정위 한 후는 ValueNotifier를 통해서 RouteLifecycle의 변경된 값이 외부로 전달될 수 있도록 구성하였다.

class CustomPageRoute<T> extends PageRoute<T> with MaterialRouteTransitionMixin<T> {

  final ValueNotifier<RouteLifecycleState> lifecycleState = ValueNotifier(RouteLifecycleState.created);

  Route? _nextRoute;
 
  CustomPageRoute({required super.builder});
 
  @override
  void didAdd() {
    lifecycleState.value = RouteLifecycleState.created;
    super.didAdd();
  }

  @override
  TickerFuture didPush() {
    lifecycleState.value = RouteLifecycleState.resumed;
    return super.didPush();
  }

  @override
  void didChangeNext(Route? nextRoute) {
    if (_nextRoute == null && nextRoute != null) {
      stateListener?.onRouteStateChanged(route: this, state: RouteState.paused);
    }
    _nextRoute = nextRoute;
    super.didChangeNext(nextRoute);
  }

  @override
  void didPopNext(Route nextRoute) {
    lifecycleState.value = RouteLifecycleState.resumed;
    _nextRoute = null
    super.didPopNext(nextRoute);
  }

  @override
  void dispose() {
    lifecycleState.dispose();
    _nextRoute = null
    super.dispose();
  }
}

위 코드는 lifecycleState의 값을 callback함수를 override 해 수정하는 코드만 추가된 상태이다.

 

Route 상태 전달하기

이제 이렇게 변경된 lifecycleState를 Widget에게 전달하는 방법을 찾아야 한다.

Route에서 생성하는 Widget의 build를 위임할 CustomPageBuilder 함수를 정의하자.

typedef CustomPageBuilder = Widget Function(
  BuildContext context,
  ValueNotifier<RouteLifecycleState> lifecycleState,
);

이제 CustomPageRoute를 통해 화면정보를 설정할 때에는 context와 lifecycleState를 모두 argument로 받아서 사용할 수 있다.

 

CustomPageRoute를 생성할 때는 CustomPageBuilder를 생성자 argument로 전달하도록 CustomPageRoute의 생성자를 추가해야 한다.  그리고 buildContent함수를 override 해서 builder에서 생성한 Widget을 전달하도록 변경하자.

class CustomPageRoute<T> extends PageRoute<T> with MaterialRouteTransitionMixin<T> {
  final ValueNotifier<RouteLifecycleState> lifecycleState = ValueNotifier(RouteLifecycleState.created);

 final CustomPageBuilder builder;

  CustomPageRoute(this.builder);

  ……..

  @override
  Widget buildContent(BuildContext context) => builder(context, lifecycleState);
}

이제 필요한 기본 준비는 완료되었다.


Navigator에 새로운 화면을 push 하기 위해 CustomPageRoute를 사용하게 되면 builder함수의 argument로 들어온 lifecycleState를 화면을 표시할 Widget으로 전달해서 route의 lifecycle상태를 구독할 수 있도록 하자.

final route = CustomPageRoute((context, routeLifecycleState) {
      return LoginPageWidget(routeLifecycleState: routeLifecycleState);
});

Navigator.push(context, route);


class LoginPageWidget extends StatefulWidget {
  final ValueNotifier<RouteLifecycleState>? routeLifecycleState;
  const LoginPageWidget({super.key, this.routeLifecycleState});

  @override
  State<LoginPageWidget> createState() => _LoginPageWidgetState();
}

class _LoginPageWidgetState extends State<LoginPageWidget> {
  ValueNotifier<RouteLifecycleState>? routeLifecycleState;

  @override
  void initState() {
    super.initState();
    routeLifecycleState = widget.routeLifecycleState;
    routeLifecycleState?.addListener(() {
      switch (routeLifecycleState?.value) {
        case RouteLifecycleState.resumed:
          // do something
          break;
        case RouteLifecycleState.paused:
          // do something
          break;
        default:
          break;
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    throw UnimplementedError();
  }
}

 

Widget은 위에서 처럼 StatefulWidget으로 만들어 사용할 수도 있고 별도의 상태관리 라이브러리를 사용할 수도 있다. 

하지만 중요한 것은 위젯을 생성자를 통해 ValueNotifier인  RouteLifecycleState를 수신할 수 있다는 것이며 이것을 활용해 원하는 작업을 해줄 수 있다.


Route의 상태 변경을 Widget로 전달할 수 있는 방법은 위에서 제시한 방법되에도 여러 가지 생각해 볼 수 있는 것 있다. 

예를 들면 상태정보를 InheritedWidget으로 만든 후 Route의 상태가 변경될 때 하위 위젯에 변경된 정보를 전달하도록 하는 방법도 사용할 수 있고, 그 외 Provider와 같은 라이브러리를 사용해서 InheritedWidget처럼 하위로 위젯으로 상태변경 이벤트를 전파할 수도 있을 것이다.

 

마치며 

Route의 상태변경 이벤트를 Widget에서 알 필요가 없다고 생각하실 수도 있습니다. 하지만 Route에 정의된 Callback 함수들을 활용하면 상태정보를 알 수 있다는 것 정도는 알고 있으면 좋지 않을까요?

Flutter는 개발자의 성향에 따라 아주 많이 유연하게 프로그램을 구성할 수 있습니다. 그래서 다른 것들에 비해서 해결책이 다양하게 나올 수 있습니다.  어쩌면 위에서 소개한 방법 외에도 Route의 상태 이벤트를 Widget으로 전파시키는 다양한 방법들이 있을 수 있으니 자신만의 방법을 찾아보시는 것도 좋을 것 같습니다. 

 

"3부 Navigator 1.0 vs Navigator 2.0" 에서 계속

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함