รู้จัก 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 สำหรับแอปของเรานั่นเอง จะเป็นอย่างไร มีข้อจำกัดอะไรบ้าง ติดตามอ่านในตอนถัดไปครับ
ขอบคุณที่ติดตามอ่านจนจบครับ (: