티스토리 뷰

Flutter에서 화면의 이동을 관리하는 위젯으로 Navigator가 있다.

Flutter는 모든 것이 위젯이라는 말처럼 화면 전환을 관리하는 것 역시 위젯으로 만들어져 있다. 이 말은 Navigator를 앱 전체 화면의 전환으로 사용할 수도 있고 화면의 일부 영역에 배치해서 그 부분에 대한 전환만 담당하게 할 수도 있다는 뜻이다.

앱 내에서 영역별 화면의 전환을 위해 다양하게 사용할 수 있다.

 

보통 MaterialApp을 가장 최상위 위젯으로 구성하게 되는데, 이때 MaterialApp은 내부적으로 하나의 전체 화면을 사용하는 Navigator를 하나 가지고 있다.  이를 통해 앱의 전체 화면의 전환을 관리할 수 있다.

보통 Navigator를 가져올 때 Navigator.of(context)로 가져와 사용하게 되는데, 이 정보는 위젯 트리에서 현재 context의 조상 중에서 가장 가까이에 있는 Navigator를 찾아서 반환하게 된다.

이때 Navigator.of(context, rootNavigator: true)로 지정하게 되면 가장 가까운 Navigator를 가져오는 것이 아닌 가장 최상의 Navigator를 가져오게 되는데, 보통은 MaterialApp에서 정의된 Navigator를 가져오게 된다. 이처럼 Navigator는 위젯으로 위젯 트리(정확히는 ElementTree)의 노드로써 계층 구조를 가진다.

 

Flutter의 위젯 트리에 Navigator가 여러 개 있을 수 있다는 점은 앱을 개발할 때 매우 중요한데, 개발자가 원하는 구조대로 유연하게 화면의 전환을 관리할 수 있다는 장점이 있기 때문이다.

또한, 자칫 잘못 개발하면 타겟이 되는 Navigator를 엉뚱한 것으로 골라 오류가 발생하기도 한다.

 

Flutter에서 rootNavigator의 개념은 매우 중요한데, Flutter에서 기본적으로 제공하는 많은 기본 함수에서 rootNavigator를 타겟할 것인지 파라미터로 받는 것들이 있다.

 

예를 들어, 직접 Navigator를 가져오는 함수에서 가장 가까운 Navigator를 가져올지 rootNavigator를 가져올지 선택할 수 있다. 다이얼로그를 표시하는 기본 함수에서는 useRootNavigator 파라미터를 이용해서 rootNavigator에 다이얼로그를 표시할지 가까운 Navigator에 다이얼로그를 표시할지 결정할 수 있다(기본값은 true이다).

 

Navigator를 잘못 타겟하게 될 때 발생할 수 있는 상황을 하나 예로 들어 보자.

위에서 보여준 Navigator.of showDialog 함수를 사용한 예이다.

 showDialog(
    context: context,
    builder: (context) {
      return AlertDialog(
        title: Text('Information'),
        content: Text('Content'),
        actions: [
          TextButton(
            onPressed: () => Navigator.of(context).pop(),
            child: Text('OK'),
          )
        ],
      );
    });

 

아마 'Flutter에서 dialog 표시하기'와 같은 것으로 검색을 하면 나올만한 코드일 것이다.

정상적인 코드처럼 보이지만, 위 코드는 현재 context의 부모로 Navigator가 몇 개 있느냐에 따라 정상적으로 동작할 수도 있고, 잘못 동작할 수도 있다.

 

Navigator가 rootNavigator 하나만 있는 경우에는 정상적으로 동작한다.

하지만 Navigator가 두 개 있는 상황이라면 잘못된 형태로 동작하게 된다. 그 이유는 showDialog에서 사용한 navigator는 rootNavigator이고,

OK 버튼에서 사용한 navigator은 rootNavigator가 아닌 가장 가까운 Navigator이기 때문이다.

이를 정상적으로 동작하기 위해서는 showDialog에서 useRootNavigator를 false로 주던지, Navigator.of에서 rootNavigator를 true로 주던지 해야 한다.

보통 다이얼로그를 표시할 rootNavigator 표시하는 경우가 많으므로, 아래와 같이 Navigator.of(context, rootNavigator: true).pop() 이렇게 수정하자.

 

showDialog(
    context: context,
    builder: (context) {
      return AlertDialog(
        title: Text('Information'),
        content: Text('Content'),
        actions: [
          TextButton(
            onPressed: () => Navigator.of(context, rootNavigator: true).pop(),
            child: Text('OK'),
          )
        ],
      );
    });

 

간단한 앱의 경우 rootNavigator만 가지고 사용할 수 있을지 모르지만, 앱이 복잡해질수록 Navigator를 여러 개 두고 사용하는 경우가 많다. 이럴 위와 같은 상황이 발생한다면 잘못된 동작을 하게 됨으로 유의해서 사용해야 한다.

 

Multiple Navigator

Navigator 위젯이다.

그래서 화면에서 유연하게 구성할 있는데, 다음과 같이 하나의 화면을 왼쪽과 오른쪽으로 나누어서 구성할 있다.

(테블릿 화면에서 왼쪽에서는 목록이 표시되고, 오른쪽에는 상세 정보가 표시되는 것을 상상해 보자.)

final leftNavigatorKey = GlobalKey<NavigatorState>();
final rightNavigatorKey = GlobalKey<NavigatorState>();

Row(children: [
  Expanded(child: Navigator(key: leftNavigatorKey)),
  Expanded(child: Navigator(key: rightNavigatorKey)),
]);

위와 같이 LeftNavigator와 RightNavigator로 나누어 화면을 구성하고 필요에 따라 아래와 같이 key 이용해서 Navigator 가져와 push / pop 한다.

 rightNavigatorKey.currentState?.push();

이런 방법을 적절히 사용하면 하나의 앱으로 스마트폰용과 테블릿용을 유연하게 구성할 있다.

 

Navigator Route

Navigator는 내부적으로 Stack으로 화면을 관리한다.

그래서 push / pop과 같은 단순한 함수를 제공하는 이것들을 이용해서 화면에 필요한 위젯을 표시한다.

이때 Navigator에 push / pop하는 정보는 Widget을 직접 넣는 것이 아니라 이동할 화면에 대한 설정 정보를 담는 Route이다.

    . push: route를 스택의 맨 위에 추가하고, route에 설정된 build로 widget을 가져와 화면에 표시한다.

   . pop: 스택의 맨 위에 있는 route를 제거하고, 이전 화면이 가장 최상위에 표시된다.

 

단순하게 정리하자면 다음과 같다.

  1. Route에는 화면에 대한 설정 정보(표시할 위젯 / 전환 효과)가 있다.

  2. Navigator에 route를 push / pop해서 표시할 화면 정보를 전달한다.

  3. Navigator는 stack의 가장 위에 있는 route 정보를 이용해 화면에 Widget를 표시한다.


Navigator에서 Route push하는 방법은 아래와 같다.

final route = MaterialPageRoute(builder: (context) => SecondScreen());
Navigator.of(context).push(route);


보통 처음 Flutter을 접하게 되면 Route의 존재를 잘 모르고 개발하는 경우를 보게 되는데,

이유는 Navigator에서 pushNamed 같이 route 직접 생성해서 전달하지 않고 문자열로 정의된 값을 사용하는 경우가 예제로 많이 보기 때문이다.

아래와 같이 MaterialApp routes 설정하고 이름을 지정한 원하는 위젯을 반환하도록 하면 쉽게 사용할 있다.

 MaterialApp(
  routes: {
    '/': (context) => HomeScreen(),
    '/second': (context) => SecondScreen(),
  })
…..
Navigator.of(context).pushNamed('/second');

간편하게 사용하는 방법으로는 위 방법도 좋다.

하지만 Route에는 우리가 생각하는 것보다 조금 더 많은 정보를 설정할 수 있고,

앱을 조금 다루기 위해서 필요한 이벤트들이 있음으로 route 대해서 조금 상세하게 알고 넘어 있으면 좋을 같다.

 

마치며

Navigator와 Route의 기본 개념과 사용법은 간단합니다.

그러나 실무에서는 화면 전환에 대한 다양한 요구 사항을 마주하게 되고, 때로는 단순한 push와 pop만으로 해결하기 어려운 상황에 직면하게 됩니다. 

Push와 pop을 통한 Route의 상태 변화를 알아야 하는 경우, 화면의 정보를 이전 화면으로 전달하거나 한 번에 여러 화면을 pop해야 하는 경우 등이 이에 해당합니다.

이러한 상황에서는 Navigator와 Route의 동작 원리를 좀 더 깊이 이해할 필요가 있습니다. 

또한, 앱에서 Navigator가 오직 하나만 존재하는 것이 아니라 개발자의 요구에 따라 여러 개를 구성하여 사용할 수 있는 유연한 Widget이라는 점을 명심하면 좋습니다. 

이를 통해 사용자 경험을 개선하고, 애플리케이션의 구조와 관리를 보다 효율적으로 있습니다.

 

 

“Navigator Route - 2 Route 상태 이벤트의 활용에서 계속 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
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
글 보관함