[Flutter] OverlayEntry로 Dropdown 직접 만들기

CHOI·2022년 3월 6일
10

[Flutter] 위젯

목록 보기
4/6
post-thumbnail

Dropdown 버튼은 활성화했을 때, 하위 목록들을 보여주는 버튼이다.
Flutter에서도 기본적으로 제공하는 Dropdown Button 위젯이 있다.

DropdownButton
항목 목록에서 선택하기 위한 머티리얼 디자인 버튼입니다.

드롭다운 버튼을 사용하면 사용자가 여러 항목 중에서 선택할 수 있습니다. 버튼은 현재 선택된 항목과 다른 항목을 선택하기 위한 메뉴를 여는 화살표를 보여줍니다.

유형 T는 각 드롭다운 항목이 나타내는 값의 유형입니다. 지정된 메뉴의 모든 항목은 일관된 유형의 값을 나타내야 합니다. 일반적으로 열거형이 사용됩니다. 항목의 각 DropdownMenuItem은 동일한 유형 인수로 특수화되어야 합니다.

⛔️ 하지만 Flutter에서 제공하는 DropdownButton은 몇가지 문제가 있다.
1️⃣ 디자인을 자유자재로 변경하지 못한다.
2️⃣ 하위 목록을 선택을 했을 때, 드롭다운 목록의 위치를 고정하지 못한다.

✅ 위의 같은 이유로 Flutter에서 제공하는 위젯이 아닌 직접 Dropdown을 만들기로 했다.


OverlayEntry

직접 Dropdown 버튼을 만들기 위해서는 OverlayEntry 위젯을 알아한다.
OverlayEntry는 Stack 일종으로 Stack처럼 화면에 떠있는 상태로 나타낼 수 있다.
또한 OverlayEntry의 child는 화면과 독립적인 상태를 가질 수 있다.

OverlayEntry
위젯을 포함할 수 있는 오버레이의 위치입니다.

오버레이 항목은 한 번에 최대 하나의 오버레이에 포함될 수 있습니다. 오버레이에서 항목을 제거하려면 오버레이 항목에서 remove 함수를 호출하십시오.

📌 OverlayEntry를 통해서 Dropdown Button을 만드는 방법은 다음과 같다.
1️⃣ OverlayEntry 위젯 생성.
2️⃣ DropdownButton이 필요로할 때 OverlayEntry 추가.
3️⃣ DropdownButton 목록 선택.
4️⃣ OverlayEntry 제거.


Custom Dropdown

OverlayEntry 위젯 생성

OverlayEntry을 이용하는 방법은 간단하다.
OverlayEntry를 통해서 원하는 위젯을 생성해 주면 된다.

Widget _customDropdown(){
  return OverlayEntry(
    builder: (context) => Positioned(
      width: _dropdownWidth,
      child: ...
    ),
  );
}

📌 Positioned을 감싸주는 이유는 OverlayEntry는 Stack 레이아웃이기 때문에 크기 및 위치를 지정해 주기 위해서 사용했다.

⛔️ 위의 코드로 OverlayEntry를 추가하게 되면 왼쪽 상단에 위치하게 된다!
그럼 원하는 위치를 Positioned으로 지정을 해줘야 할까? 전혀 그렇지 않다.
CompositedTransformFollower을 이용하면 좌표를 입력하지 않아도 원하는 위치에서 Dropdown을 보여줄 수 있다.

CompositedTransformFollower
CompositedTransformTarget을 따르는 위젯.

이 위젯이 합성 단계(WidgetsBinding.drawFrame에 설명된 대로 페인트 단계 이후에 옴) 동안 합성되면 연결된 CompositedTransformTarget의 targetAnchor와 이 위젯의 ​​followerAnchor를 함께 가져오는 변환을 적용합니다. offset이 Offset.zero가 아닌 경우 두 앵커 포인트는 동일한 전역 좌표를 갖게 됩니다.

링크로 사용되는 LayerLink 개체는 일치하는 CompositedTransformTarget에 제공된 것과 동일한 개체여야 합니다.

final LayerLink _layerLink = LayerLink();

// 드롭다운 버튼. 
CompositedTransformTarget(
  link: _layerLink,
  child: ...
);

// OverlayEntry.
OverlayEntry(
  builder: (context) => Positioned(
    width: _dropdownWidth,
    child: CompositedTransformTarget(
      link: _layerLink,
    ),
  ),
);

이렇게 드롭다운 버튼과, 드롭다운을 LayerLink로 연결을 해주면 원하는 위치에 동일한 위치에 출력이 가능하다.
CompositedTransformTarget의 매개변수 중에 offset: const Offset(0, 0) 을 지정해 주면 세부 좌표를 조절 가능하다.

OverlayEntry 추가

드롭다운 버튼을 클릭을 하면 위에서 만들었던 OverlayEntry를 Overlay.of(context)에 추가해 주면 화면에 나타난다.

// 드롭다운 생성.
void _createOverlay() {
  if (_overlayEntry == null) {
    _overlayEntry = _customDropdown();
    Overlay.of(context)?.insert(_overlayEntry!);
  }
}

OverlayEntry 제거

목록을 클릭을 하거나, 드롭다운 버튼 외의 화면을 클릭을 해주었을 때는 출력돼있는 드롭다운을 해제시켜서 나타나지 않게 해줄 수 있다.

// 드롭다운 해제.
void _removeOverlay() {
  _overlayEntry?.remove();
  _overlayEntry = null;
}

🔥 Flutter에서 기본으로 제공하는 Dropdown Button에서 나타났다 단점을 모두 해결했다. 디자인도 마음대로 설정 가능하고, 어떤 값을 선택을 하던 Dropdown 리스트가 동일한 위치로 고정돼있다.


입력창 자동 추천

🔥 Dropdown 위젯을 응용을 하면 Textfield에 입력 시 자동 추천 기능도 만들 수 있다.
이 외에도 Tooltip 등 다양한 곳에서 많은 기능들을 구현할 수 있다.


Github
https://github.com/cyb9701/custom-dropdown

profile
Mobile App Developer

2개의 댓글

comment-user-thumbnail
2022년 11월 14일

안녕하세요 dropdown 커스텀중 보고 가요 ㅎㅎ 본문에 CompositedTransformTarget의 매개변수 중에 offset: const Offset(0, 0) 라고 되어있는 부분 CompositedTransformFollower로 수정해주셔야 할 것 같아요. 또한 예시에서 Target위젯만 두개가 보여서 보시는분들이 헷갈릴 수 있을 것 같아요. 그래도 덕분에 dropdown 커스텀 편하게 하고갑니다~ 좋은 글 감사드립니다.

1개의 답글