guricode

[flutter]자취의 정석 -6 댓글 꾹~ 눌렀을때 메뉴 펼치기(신고하기,차단하기 등등) 본문

앱/Flutter&Dart

[flutter]자취의 정석 -6 댓글 꾹~ 눌렀을때 메뉴 펼치기(신고하기,차단하기 등등)

agentrakugaki 2025. 9. 22. 20:28

댓글 꾹 누른 위치에 팝업 띄우는 법 요약
onLongPressStart의 details.globalPosition으로 터치 좌표 확보
onLongPressStart: (details)의  details은onLongPressStart한 정보를 담고 있다.
onLongPressStart: (details) {
  print(details.globalPosition); // 화면 전체 기준 좌표 (Offset)
  print(details.localPosition);  // 해당 위젯 기준 좌표 (Offset)
  print(details.kind);           // 입력 종류 (터치, 마우스 등) PointerDeviceKind
}

 

일단 기능은 넣지 않았지만 메뉴가 나오게끔 작업을 미리 해뒀다

 

아이콘은 

https://fonts.google.com/icons?icon.query=share

 

GestureDetector(
          onLongPressStart: (details) async {
            //details = onLongPressStart했을떄 정보

            //현재화면의 최상단 레이어(Overlay)를 찾고 그 랜더박스 정보 제공, 목적: 화면전체 크기를 얻어 메뉴 위치계산에 사용
            final overlay =
                Overlay.of(context).context.findRenderObject() as RenderBox;

            //showMenu : 팝업 메뉴 표시
            final selected = await showMenu<String>(
              //꾹 눌렀을때 나오는 메뉴 모양 커스텀
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadiusGeometry.circular(15),
              ),
              context: context,

              //작은사각형이 큰 사각형의 어디있는지 상대좌표로 변환하여 메뉴 시작위치가 터치 지점으로 잡힘
              position: RelativeRect.fromRect(
                //사용자가 누른 지점을 0,0사이즈의 사각형으로 표현
                Rect.fromLTWH(
                  details.globalPosition.dx,
                  details.globalPosition.dy,
                  0,
                  0,
                ),

                //화면 전체를 덮는 사각형
                Offset.zero & overlay.size,
              ),
              color: Colors.white,
              items: [
                PopupMenuItem(
                  value: 'report',
                  child: Row(
                    children: [
                      Text('신고하기'),
                      SizedBox(
                        width: 50,
                      ),
                      Spacer(),
                      Icon(Icons.notifications_none),
                    ],
                  ),
                ),
                PopupMenuItem(
                  value: 'block',
                  child: Row(
                    children: [
                      Text('차단하기'),
                      Spacer(),
                      Icon(Icons.do_not_disturb_on_outlined),
                    ],
                  ),
                ),
                PopupMenuItem(
                  value: 'share',
                  child: Row(
                    children: [
                      Text('공유하기'),
                      Spacer(),
                      Icon(Icons.share),
                    ],
                  ),
                ),
              ],
            );

            //selected의 value에 따라 기능실행
            switch (selected) {
              case 'report':
                // 신고 처리
                ScaffoldMessenger.of(
                  context,
                ).showSnackBar(const SnackBar(content: Text('신고 완료')));
                break;
              case 'block':
                // 차단 처리
                ScaffoldMessenger.of(
                  context,
                ).showSnackBar(const SnackBar(content: Text('차단 완료')));
                break;
              case 'share':
                // 공유 처리
                // Share.share('공유할 내용'); // share_plus 사용 시
                break;
              case null:
                // 메뉴 밖을 눌러 닫힘. 아무것도 하지 않음.
                break;
            }
            ;
          },

1. Overlay.of(context)

  • context 로부터 가장 가까운 Overlay 위젯을 찾는다.
  • 반환 타입: OverlayState?
    → 여기서 ?.context 하면 OverlayState 가 들고 있는 자신의 BuildContext를 얻는다.

즉,

final overlay = Overlay.of(context);        // OverlayState
final overlayContext = overlay.context;     // Overlay의 BuildContext

context.findRenderObject()

  • BuildContext 로부터 실제 RenderObject를 가져온다.
  • RenderObject는 화면에 실제로 그려지는 객체. 레이아웃/페인트 계산을 담당.
  • UI 위치 계산할 때 꼭 필요하다.

 as RenderBox

  • RenderObject 는 추상 클래스.
  • 대부분의 위젯(특히 박스 기반)은 실제로 RenderBox를 쓴다.
  • 그래서 위치·크기 계산할 때는 RenderBox로 캐스팅해야 한다.

 

final overlay = Overlay.of(context).context.findRenderObject() as RenderBox;
  • Overlay.of(context) → OverlayState 찾기
  • .context → 그 Overlay의 BuildContext
  • .findRenderObject() → 실제 RenderObject 꺼내기
  • as RenderBox → 좌표·사이즈 계산 가능한 RenderBox로 캐스팅

팝업 메뉴나 다이얼로그 같은 걸 띄울 때,
화면 전체 기준 좌표(globalPosition)와 Overlay의 크기(overlay.size)가 필요하다.

RelativeRect.fromRect(
  Rect.fromLTWH(details.globalPosition.dx, details.globalPosition.dy, 0, 0),
  Offset.zero & overlay.size, // overlay 전체 크기
)

 

 

 

 

 

RelativeRect.fromRect 는 작은 직사각형(Rect)을 큰 직사각형(Rect) 안에서 상대적 위치로 바꿔주는 도우미다.

RelativeRect.fromRect(
  Rect rect,      // 기준이 되는 작은 사각형 (ex. 메뉴 뜰 위치)
  Rect container, // 전체 컨테이너 (ex. Overlay 전체 화면 크기)
)