รู้จัก Popup Menu ใน Flutter ตอนที่ 1
![](https://benzneststudios.com/blog/wp-content/uploads/2021/06/cover_ep1-1024x521.png)
สวัสดีผู้อ่านทุกท่านครับ หลังจากที่ Flutter ได้เปิดตัว Flutter Web และ Desktop ก็มีความสามารถใหม่ๆ widget ใหม่ๆเพียบเลยใช่ไหม ในบล็อกนี้ผมจะพามาลองเล่น Widget ที่ชื่อว่า Popup Menu มันคือ เมนูตัวเลือกต่างๆที่แสดงขึ้นมาตอนที่เรากดคลิกขวานั่นเอง หรือในเว็บไซต์ที่พบบ่อยๆ คือ menu ที่ใช้ใน NavBar ต่างๆ
ถ้านึกภาพไม่ออกก็เวลาเราคลิกขวาใน Android Studio หรือ VS code
![](https://benzneststudios.com/blog/wp-content/uploads/2021/06/a1-2.gif)
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"))
]);
},
...
)
ลองแสดงผล
![](https://benzneststudios.com/blog/wp-content/uploads/2021/06/a8-1.gif)
หากเราต้องการให้ 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 ของเราแล้ว
![](https://benzneststudios.com/blog/wp-content/uploads/2021/06/a9-1.gif)
Secondary Tap
ลองเปลี่ยนมาใช้ onSecondaryTap บ้าง หรือก็คือการคลิกขวาของ mouse จะได้เหมือนกับ popup menu ใน Desktop app
GestureDetector(onSecondaryTap: () {
...
หากใช้งานในเว็บจะพบว่าเมื่อเราคลิกขวา มันจะซ้อนกับกับ popup ของ browser
![](https://benzneststudios.com/blog/wp-content/uploads/2021/06/a10.gif)
ให้เรากำหนดคำสั่งเพิ่มลงไปใน 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,
),
]);
ตัวอย่างการแสดงผล
![](https://benzneststudios.com/blog/wp-content/uploads/2021/06/a11.gif)
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){
//
},)
![](https://benzneststudios.com/blog/wp-content/uploads/2021/06/a12.gif)
ตอนถัดไป
ในตอนถัดไป ผมจะพาไปลองเล่น Popup Menu แบบ Custom เองครับ เพราะในการทำงานจริงๆ PopupMenuItem หรือ CheckedPopupMenuItem อาจจะไม่ตอบโจทย์งานของเราได้ ทำให้เราจำเป็นต้อง custom Popup Menu สำหรับแอปของเรานั่นเอง จะเป็นอย่างไร มีข้อจำกัดอะไรบ้าง ติดตามอ่านในตอนถัดไปครับ
ขอบคุณที่ติดตามอ่านจนจบครับ (: