web analytics

รู้จัก Popup Menu ใน Flutter ตอนที่ 1

สวัสดีผู้อ่านทุกท่านครับ หลังจากที่ Flutter ได้เปิดตัว Flutter Web และ Desktop ก็มีความสามารถใหม่ๆ widget ใหม่ๆเพียบเลยใช่ไหม ในบล็อกนี้ผมจะพามาลองเล่น Widget ที่ชื่อว่า Popup Menu มันคือ เมนูตัวเลือกต่างๆที่แสดงขึ้นมาตอนที่เรากดคลิกขวานั่นเอง หรือในเว็บไซต์ที่พบบ่อยๆ คือ menu ที่ใช้ใน NavBar ต่างๆ

ถ้านึกภาพไม่ออกก็เวลาเราคลิกขวาใน Android Studio หรือ VS code

Popup Menu

การใช้งาน popup menu ทาง Flutter ได้เตรียม method ที่ชื่อว่า showMenu() มาให้แล้ว หลักการคล้ายกับ Dialog แต่จะมี parameter ที่ชื่อว่า position สำหรับกำหนดตำแหน่ง popup ที่แสดง โดย Position นี้ จะใช้ class ที่ชื่อว่า RelativeRect เปรียบเสมือนเป็นกรอบที่จะแสดงบนหน้าจอโดยอิงกับหน้าจอเดิม โดยมันจะเก็บค่าระยะห่างจาก ซ้าย, บน, ขวา, ล่าง ในตัวอย่างนี้ผมจะกำหนดที่ 0,0,0,0 นั่นหมายความว่า popup นี้จะมีขนาดที่สามารถแสดงผลทั้งหน้าจอ ดังนั้นหาก popup มีขนาดเล็กและอิงบนซ้ายบนหลัก มันจะไปปรากฏที่บนซ้ายนั่นเอง

parameter อีกตัวคือ items โดยจะเป็น list สำหรับแสดงรายการใน popup ซึ่งมี item ให้มาหลายแบบ ในตัวอย่างนี้เราจะลองใช้อันที่เป็นพื้นฐานที่สุด คือ PopupMenuItem

GestureDetector(
                  onTap: () async {
                    int? value = await showMenu<int>(
                        context: context,
                        position: RelativeRect.fromLTRB(0, 0, 0, 0),
                        items: [
                          PopupMenuItem(value: 1, child: Text("View")),
                          PopupMenuItem(value: 2, child: Text("Share"))
                        ]);
                  },
                  ...
                )

ลองแสดงผล

หากเราต้องการให้ popup menu แสดงในตำแหน่ง cursor ของเรา เราต้องรู้ตำแหน่ง cursor ขณะปัจจุบันของมันก่อน โดยใช้ Widget ที่ชื่อว่า MouseRegion โดยเราจะเก็บค่า PointerHoverEvent เอาไว้ ผ่าน onHover

class _HomeScreenState extends State<HomeScreen> {
  PointerHoverEvent? pointerEvent;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          actions: [],
        ),
        body: MouseRegion(
          onHover: (h) {
            pointerEvent = h;
          },
          child: ...

จากนั้นเมื่อเราต้องการให้แสดง popup menu เราสามารถใช้ position ของ PointerHoverEvent มากำหนด position ของ popup menu ได้

GestureDetector(onTap: () async{
      int? value = await showMenu<int>(
          context: context,
          position: RelativeRect.fromLTRB(
              pointerEvent?.position.dx ?? 0,
              pointerEvent?.position.dy ?? 0,
              pointerEvent?.position.dx ?? 0,
              pointerEvent?.position.dy ?? 0),
          items: [
            PopupMenuItem(value: 1, child: Text("View")),
            PopupMenuItem(value: 2, child: Text("Share"))
          ]);
    },
    ...

popup จะแสดงมาบริเวณ cursor ของเราแล้ว

Secondary Tap

ลองเปลี่ยนมาใช้ onSecondaryTap บ้าง หรือก็คือการคลิกขวาของ mouse จะได้เหมือนกับ popup menu ใน Desktop app

GestureDetector(onSecondaryTap: () {
  ...

หากใช้งานในเว็บจะพบว่าเมื่อเราคลิกขวา มันจะซ้อนกับกับ popup ของ browser

ให้เรากำหนดคำสั่งเพิ่มลงไปใน main ก่อน runApp()

import 'dart:html';

void main() {
  window.document.onContextMenu.listen((evt) => evt.preventDefault());
  ...
}

MenuItem อื่นๆ

Item รูปแบบอื่นๆ เช่น แบบที่มี check box ชื่อว่า CheckedPopupMenuItem การใช้งานเหมือนกับ PopupMenuItem แต่จะมี boolean checked เพิ่มเข้ามา และ item ที่ช่วยแสดงเส้นคั้นชื่อว่า PopupMenuDivider เอาไว้ช่วยแบ่ง section ของ menu

            int? value = await showMenu<int>(
                context: context,
                ...
                items: [
                  PopupMenuItem(value: 1, child: Text("View")),
                  PopupMenuItem(value: 2, child: Text("Share")),
                  PopupMenuDivider(),
                  CheckedPopupMenuItem(
                    value: 3,
                    child: Text("Available"),
                    checked: true,
                  ),
                ]);

ตัวอย่างการแสดงผล

PopupMenuButton

อีก widget ที่น่าสนใจคือ PopupMenuButton โดยเจ้า widget ตัวนี้จะช่วยให้เราสร้าง popup menu และเพิ่ม gesture onTap + cursor position ให้เลยในตัว มี callback function ที่ชื่อว่า onSelected ให้ด้วย แต่ติดตรงที่ไม่สามารถเปลี่ยน gesture เป็น gesture อื่นๆได้

        PopupMenuButton<int>(
            child: Text(
              widget.text,
              style: TextStyle(
                  fontSize: 28,)
            ),
            itemBuilder: (context) {
              return [
                PopupMenuItem<int>(value: 1, child: Text("View")),
                PopupMenuItem<int>(value: 2, child: Text("Share")),
              ];
            },
            onSelected: (value){
              //
            },)

ตอนถัดไป

ในตอนถัดไป ผมจะพาไปลองเล่น Popup Menu แบบ Custom เองครับ เพราะในการทำงานจริงๆ PopupMenuItem หรือ CheckedPopupMenuItem อาจจะไม่ตอบโจทย์งานของเราได้ ทำให้เราจำเป็นต้อง custom Popup Menu สำหรับแอปของเรานั่นเอง จะเป็นอย่างไร มีข้อจำกัดอะไรบ้าง ติดตามอ่านในตอนถัดไปครับ

ขอบคุณที่ติดตามอ่านจนจบครับ (: