본문 바로가기
프로그래밍/Flutter

[Flutter Layout] 15. PopupMenuButton(팝업메뉴버튼) 내비게이션 만들기

by 신나요 2022. 4. 21.

플러터의 앱 화면 간에 내비게이션을 할 때 주로 사용하는 위젯은 PopupMenuButton, Drawer, BottomNavigationBar, TabBar 등이 있습니다. 오늘은 내비게이션 레이아웃의 첫 번째 포스트로 PopupMenuButton을 이용해서 플러터 내비게이션을 만들어 보도록 하겠습니다. 플러터 앱을 만드는 데 있어서 필수적인 화면간 전환에 대해 꼭 알아보도록 합시다.

 

이번 포스트는 다음 내용을 다루고 있습니다.

  1. PopupMenuButton(팝업 메뉴 버튼) 개요 및 만들기
  2. onSelected 이벤트에서 내비게이션 설정
  3. 플러터 내비게이션 (Navigator, MaterialPageRoute)

기본 코드 만들기

PopupMenuButton을 이용해 내비게이션을 구현하기 앞서 바탕이 되는 기본코드를 작성하겠습니다. 먼저 VSCode에서 navigation_app이라는 새 프로젝트를 만들어 주었습니다.

첫 번째로 lib폴더에 dog_util.dart 파일을 만들고 아래와 같이 코딩하였습니다.

DogUtil 클래스는 유틸클래스로 화면에서 사용할 강아지 이미지의 URL을 가지고 있습니다.

 

두 번째로 lib 폴더에 dog_route.dart파일을 만들고 아래와 같이 코딩하였습니다.

DogRoute클래스는 StatelessWidget으로 build 메소드에서 Scaffold 위젯을 반환해주고 있습니다. DogRoute의 생성자를 보면 required this.dogImgUrl로 적어주고 있고 DogRoute를 생성할 때 필수 값으로 이미지 주소 문자열을 저장하고 있습니다. build에서 반환되는 Scaffold의 body는 Container를 설정하였고,BoxDecoration을 통해 이미지를 설정하고 있습니다. NetworkImage를 통해 url을 통해 이미지를 가져오고, fit 설정에 BoxFit.cover 설정을 하였으므로 화면에 꽉 차는 이미지를 디자인을 가지게 될 것입니다.

 

마지막으로 프로젝트에서 자동으로 생성해주는 main.dart파일을 아래와 같이 수정하였습니다.

MyApp 클래스는 StatelessWidget으로 build 메소드에서 MaterialApp을 반환하고 있습니다. home 설정에서 위에서 만든 DogRoute를 생성하고 있고 dogImgurl을 DogUtil을 통해 설정하고 있습니다.

 

기본 코드가 완성되었고 이상태에서 화면을 실행해보면 아래와 같이 나옵니다.

치와와 이미지가 잘 나오고 있습니다. 이제 팝업 메뉴 버튼을 사용할 준비가 모두 끝났습니다.


PopupMenuButton(팝업 메뉴 버튼) 개요

아래의 그림과 같이 PopupMenuButton을 클릭하면 PopupMenuItem의 위젯들이 표시됩니다. 그리고 PopupMenuItem을 클릭할 때 특정 화면으로 이동하는 로직을 넣어줄 수 있습니다. 물론 화면 이동 말고도 다양한 로직으로 활용할 수 있습니다.

 

PopupMenuButton(
  // icon: Icon(Icons.image),  // #1 icon 혹은 child
  child: Text('루트바꾸기'),    // #1 icon 혹은 child
  itemBuilder: (buildContext context) => [  // #2
    PopupMenuItem( value: 1, child: Text("first")),
    PopupMenuItem( value: 2, child: Text("second")),
  ]
  onSelected: (v)=> doSomething(context, v),  // #3
)

 

1. PopupMenuButton을 누르면 메뉴 목록이 표시됩니다. 팝업 메뉴 버튼은 아이콘으로 만들 수도 있고 텍스트로 만들수도 있습니다. 아이콘을 사용하려면 icon 속성이 있어야 합니다. icon 속성을 사용하지 않는다면 child 속성을 사용해야 합니다. 기억할 점은 둘 다 같이 쓸 수 없고 둘 중 하나를 선택해야 한다는 점입니다.

2. 메뉴 목록에 항목을 추가하려면 itemBuilder를 사용합니다. itembuilder에 설정하는 함수는 콘텍스트를 건네받고 일반적으로 텍스트 값을 가지는 자식이 있는 PopupMenuItem 위젯 세트를 반환하는 함수입니다.

3. 유저가 PopupMenuItem 위젯 중 하나를 클릭하면 onSelected메소드가 호출됩니다. onSelected에 전달되는 값은 선택한 PopupMenuItem의 value입니다.


PopupMenuButton 만들기

실제로 만들어 보겠습니다. DogRoute 클래스의 build 메서드를 수정하였습니다.

1. appBar에서 배열을 취하는 actions 속성을 설정하고 배열에 PopupMenuButton을 포함시켰습니다.

2. icon과 child 중에 icon을 지정하고 있습니다.

3. 현재 콘테스트를 사용하는 함수인 itemBuilder를 설정하고 있습니다. 함수에서는 DogUtil 클래스의 menuItems 배열을 map으로 루프를 돌며 String타입의 PopupMenuItem 위젯 배열을 만든 후 List로 변환하여 리턴해주고 있습니다.

 

화면에서 확인해 보면 Appbar에 이미지 아이콘이 생긴 걸 볼 수 있습니다.

그리고 이미지 아이콘을 클릭하면 메뉴가 화면에 표시됩니다.

 

차이점을 보기 위해 Icon대신 Text로 바꿔보겠습니다.

텍스트로 바뀐 걸 볼 수 있습니다. 물론 이 텍스트에 스타일과 패딩을 설정해 디자인을 알맞게 맞출 수 있습니다.

 

만약 icon, child 둘 다 설정하지 않는다면 어떻게 보일까요?

icon과 child를 설정하지 않는다면 기본 아이콘이 표시되며 우리가 익숙한 아이콘 디자인입니다. 이대로 두고 진행하겠습니다. 항목을 클릭할 수도 있지만 아직 클릭에 대한 이벤트를 설정하지 않았으므로 아무 일도 일어나지 않는 상태입니다.


onSelected 이벤트에서 내비게이션 설정

PopupMenuItem 중 하나를 클릭하면 onSelected 이 번트가 호출됩니다. 먼저 화면을 변경할 메서드를 만들어 보도록 하겠습니다.

1. chageRoute메소드는 현재 컨텍스트와 클릭한 item의 문자열을 매개변수로 받습니다.

2. 화면에 표시하려는 이미지의 경로를 switch문을 통해서 판별 후 dogImage에 할당하였습니다.

그 다음 내비게이션을 하는 코딩이 나옵니다. 그전에 플러터의 내비게이션에 대해 알아보도록 합시다.

 

플러터 내비게이션 개념

플러터에서 내비게이션은 스택을 기반으로 합니다. 스택에는 앱이 실행한 이후 빌드된 모든 경로가 포함됩니다. 플러터의 화면이나 페이지를 경로라고 하는데, 경로를 변경해야 할 때 navigator 객체를 사용합니다. Navigator에는 스택을 처리하는 두 가지 방법 push와 pop이 있습니다. push 메서드는 스택의 맨 위에 새 경로를 넣습니다. pop 메서드는 스택의 맨 위에서 경로를 제거하여 스택의 이전 경로가 다시 표시되도록 합니다. push 메서드를 사용할 때는 로드할 경로를 지정해야 합니다. 이를 위해 푸시하는 경로의 이름을 지정하는 MaterialPageRoute클래스를 사용할 수 있습니다. push와 pop 모두 현재 컨텍스트가 필요합니다.

다시 코드로 돌아가겠습니다.

Navigator에서 push메서드를 호출하고 있습니다. 그리고 MaterialPageRoute의 빌더에서 DogRoute클래스를 호출합니다. DogRoute를 호출할 때 선택한 이미지 url을 전달합니다.

이제 마지막 단계로 PopupMenuButton에 onSelecgted를 추가하면 됩니다.

onSelected에서 컨텐스트와 선택된 값을 전달하여 chageRoute 메서드를 호출하고 있습니다.

최종 결과를 확인해 보겠습니다.

 

이제 메뉴의 항목을 클릭하면 경로가 변경됩니다. 항상 같은 클래스를 호출하더라도 메뉴를 클릭할 때마다 새 인스턴스이므로 새 경로가 스택에 푸시됩니다. 뒤로 가기 버튼을 누르면 상단의 경로가 제거되고 이전 화면이 보입니다.


여기까지 수고하셨습니다. 다음 포스트에서는 TabBar을 활용한 내비게이션에 대해 알아보도록 하겠습니다.

댓글