การกำหนดและใช้งาน PopupMenuButton ใน Flutter
เขียนเมื่อ 2 ปีก่อน โดย Ninenik Narkdeeenum type popupmenubutton flutter
คำสั่ง การ กำหนด รูปแบบ ตัวอย่าง เทคนิค ลูกเล่น การประยุกต์ การใช้งาน เกี่ยวกับ enum type popupmenubutton flutter
ไปที่
Copy



เนื้อหาตอนต่อไปนี้จะมาดู Widget เล็กๆ ที่มีรูปแบบการใช้งาน
ง่ายๆ ที่เรียกว่า PopupMenuButton เป็นปุ่มเมนูเพิ่มเติมที่แสดงมา
ให้เราเลือกใช้งาน หรือกำหนดการทำคำสั่งที่ต้องการทำงานเพิ่มเติม
จะใช้เนื้อหาจากตอนที่แล้ว จะจัดการเฉพาะในไฟล์ article.dart
ทบทวนตอนที่แล้วได้ที่บทความ
การใช้งาน WebView แสดงเว็บไซต์ ใน Flutter http://niik.in/1043
https://www.ninenik.com/content.php?arti_id=1043 via @ninenik
*เนื้อหานี้ใช้เนื้อหาต่อเนื่องจากบทความ http://niik.in/961
การใช้งาน PopupMenuButton
ตัว PopupMenuButton เมื่อกำหนดหรือเรียกใช้งาน จะแสดงเป็นปุ่มไอคอน จุด 3 จุดใน
แนวตั้งหรือชื่อไอคอนว่า Icons.more_vert เป็นการสื่อว่ามีเพิ่มเติม เมื่อเรากดที่ปุ่มนี้ก็จะแสดง
ลิสราายการปุ่มต่างๆ ให้เราเลือก ถ้าเราเลือกปุ่มรายการใดๆ ก็จะใช้ค่าปุ่มรายการนั้นๆ เป็นตัว
กำหนดเงื่อนไขการทำงานอีกที ถ้าเราไม่ต้องการเลือกรายการที่แสดง ก็สามารถกดไปที่พื้นที่ว่าง
นอกรายการเพื่อปิดปุ่มนั้นๆ ไป ในการสร้างปุ่ม PopupMenuButton จะต้องมีการกำหนด itemBuilder
เพื่อสร้างรายการของปุ่ม
รูปแบบการใช้งาน PopupMenuButton
PopupMenuButton<T>( onSelected: (T result) { }, itemBuilder: (BuildContext context) => <PopupMenuEntry<T>>[ const PopupMenuItem<T>( value: T.value, child: Text('Menu 1'), ), const PopupMenuItem<T>( value: T.value, child: Text('Menu 2'), ), ], )
สัญลักษณ์ T คือข้อมูลประเภท Type หรือก็คือ class ดูตัวอย่าง type ในภาษา Dart
// ข้อมูล type ColorOption enum ColorOption { red, green, blue } // ข้อมูล type Option class Option{ int a = 0; }
ทั้ง ColorOption และ Option เป็นรูปแบบหนึ่งของ class โดยตัว ColorOption จะใช้คำว่า enum เป็นคำ
keyword เป็น class พิเศษเฉพาะที่กำหนดจำนวนของค่าคงที่ ที่เรียกว่า enum type ข้อมูลที่มีการระบุแจกแจง
ค่าไว้อย่างชัดเจน ค่าของ Enum จะอ้างอิงผ่าน property ที่ชื่อ values
print(ColorOption.values); // แสดงข้อมูลของ enum type
ก็จะได้เป็น List<ColorOption> มีค่าเป็น
[ColorOption.red, ColorOption.green, ColorOption.blue] // ColorOption.values[0] = ColorOption.red // ColorOption.values[1] = ColorOption.green // ColorOption.values[2] = ColorOption.blue
เราจะใช้ช้อมูล Enum type สำหรับกำหนดรายการให้กับ PopupMenuButton ยกตัวอย่างเช่นข้อมูล
enum ColorOption { red, green, blue }
สามารถกำหนดใช้งานใน PopupMenuButton เป็นดังนี้
PopupMenuButton<ColorOption>( onSelected: (ColorOption result) { }, itemBuilder: (BuildContext context) => <PopupMenuEntry<ColorOption>>[ const PopupMenuItem<ColorOption>( value: ColorOption.red, child: Text('Menu 1 Red'), ), const PopupMenuItem<ColorOption>( value: ColorOption.green, child: Text('Menu 2 Green'), ), const PopupMenuItem<ColorOption>( value: ColorOption.blue, child: Text('Menu 3 Blue'), ), ], )
หรือกรณีเราใช้เป็นข้อมูล String type ก็จะเป็นประมาณนี้
PopupMenuButton<String>( onSelected: (String result) { }, itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[ const PopupMenuItem<String>( value: '1', child: Text('Menu 1 Red'), ), const PopupMenuItem<String>( value: '2', child: Text('Menu 2 Green'), ), const PopupMenuItem<String>( value: '3', child: Text('Menu 3 Blue'), ), ], )
หรือกรณีเราใช้เป็นข้อมูล boolean type ก็จะเป็นประมาณนี้
PopupMenuButton<bool>( onSelected: (bool result) { }, itemBuilder: (BuildContext context) => <PopupMenuEntry<bool>>[ const PopupMenuItem<bool>( value: true, child: Text('Menu 1 Red'), ), const PopupMenuItem<bool>( value: false, child: Text('Menu 2 Green'), ), ], )
เราสามารถสร้างลิสรายการจากข้อมูลอาเรย์หรือ List ได้ง่ายเพื่อลดขึ้นตอนการกำหนดแต่ละรายการ
// สร้างตัวแปร ลืสรายการเมนูที่เป็น String var myMenuItems = <String>[ 'Home', 'Profile', 'Setting', ];
จากนั้นทำการวนลูปแสดงใน PopupMenuItem ดังนี้
PopupMenuButton<String>( onSelected: (String result) { }, itemBuilder: (BuildContext context) { return myMenuItems.map((String choice) { return PopupMenuItem<String>( child: Text(choice), value: choice, ); }).toList(); } )
ผลลัพธ์ที่ได้

กรณีประยุกต์กับ Map Type เพิ่ม FontAwesome ไอคอนเข้าไป
// สร้างตัวแปร ลืสรายการเมนูที่เป็น Map<dynamic, dynamic> var myMenuItems = <Map>[ {'icon':FontAwesomeIcons.home,'value':'home','label':'Home'}, {'icon':FontAwesomeIcons.userAlt,'value':'profile','label':'Profile'}, {'icon':FontAwesomeIcons.cog,'value':'setting','label':'Setting'} ];
จากนั้นทำการวนลูปแสดงใน PopupMenuItem ดังนี้
PopupMenuButton<Map>( onSelected: (Map result) { }, itemBuilder: (BuildContext context) { return myMenuItems.map((Map choice) { return PopupMenuItem<Map>( child: ListTile( leading: Icon(choice['icon']), title: Text(choice['label'], style: Theme.of(context).textTheme.bodyText1), ), value: choice, ); }).toList(); } )
ผลลัพธ์ที่ได้

ตอนนี้เราได้รู้จักแนวทางการประยุกต์การสร้างลิสรายการในรูปแบบต่างๆ ให้สังเกตให้ค่า value ของ
PopupMenuItem คือเมื่อเราแตะเลือกที่รายการใด ค่า value นี้จะถูกส่งเข้าไปใน callback ฟังก์ชั่นของ
onSelected ดังนั้นในการกำหนดเงื่อนไขการทำงาน ก็จะไปกำหนดในค่าที่เลือกว่าเป็นค่าใด และให้ทำงาน
อย่างเรา ยกตัวอย่างรูปแบบกรณีล่าสุด ก็จะเป็น
PopupMenuButton<Map>( onSelected: (Map result) { result = Map<String, dynamic>.from(result); // แปลงค่ากลับ switch (result['value']) { // ตรวจสอบค่าที่จะใช้งานเป็นเงื่อนไข case 'home': print('Home clicked'); break; case 'profile': print('Profile clicked'); break; case 'setting': print('Setting clicked'); break; } }, itemBuilder: (BuildContext context) { return myMenuItems.map((Map choice) { return PopupMenuItem<Map>( child: ListTile( leading: Icon(choice['icon']), title: Text(choice['label'], style: Theme.of(context).textTheme.bodyText1), ), value: choice, ); }).toList(); } )
เนื่องจากค่าจาก Map type เป็นข้อมูลที่มีความซับซ้อนดังนั้น จึงมีการแปลงกลับมาในรูปแบบที่สามารถ
อ้างอิงการใช้งานได้ก่อน แต่ถ้าเป็นค่าอื่นๆ เช่น boolean Sring Int Enum เหล่านี้ สามารถนำค่าไปตรวจ
สอบเป็นเงื่อนไขได้เลย
ในตัวอย่างการทำคำสั่งเมื่อเข้าเงื่อนไข จะใช้เป็นการเรียกใช้ฟังก์ชั่นอีกที เพราะถ้าเขียนการทำงานในนี้
ก็จะยาวเกินไป ข้างต้นเราแค่ทดสอบแสดงข้อความเท่านั้น
ไฟล์ article.dart แบบเต็ม
import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; class Articles extends StatefulWidget { static const routeName = '/articles'; const Articles({Key? key}) : super(key: key); @override State<StatefulWidget> createState() { return _ArticlesState(); } } class _ArticlesState extends State<Articles> { // กำหนดตัวแปร controler สำหรับควบคุมการทำงาน final Completer<WebViewController> _controller = Completer<WebViewController>(); @override void initState() { super.initState(); // กำหนดการใช้งาน ที่รับการใช้งาน keyboard สำหรับ android if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView(); } @override Widget build(BuildContext context) { // รับค่า url ที่ส่งมาใน arguments final url = ModalRoute.of(context)!.settings.arguments as String; return Scaffold( appBar: AppBar( title: Text('Articles'), actions: <Widget>[ // สร้างอาเรย์หรือ List ของปุ่มใน action NavigationControls(_controller.future), SampleMenu(_controller.future), ], ), body: Builder(builder: (BuildContext context) { // ใช้งาน WebView กำหนดค่าเบื้องต้น return WebView( initialUrl: url, // ใช้ url จากหน้าที่ส่งมา javascriptMode: JavascriptMode.unrestricted, // ใช้งาน JavaScript ได้ onWebViewCreated: (WebViewController webViewController) { // เมื่อสร้าง webviewเสร็จ _controller.complete(webViewController); // การใช้งาน async เมื่อ controller พร้อมใช้งาน }, javascriptChannels: <JavascriptChannel>{ // กำหนดการใช้งาน javascriptChannels _toasterJavascriptChannel(context), }, gestureNavigationEnabled: true, // กำหนดให้รองรับ gusture ต่างๆ เช่นการปัด เพื่อเลื่อนไปหน้าต่างอื่น ); }), floatingActionButton: scrollTopButton(), ); } // สร้าง JavascriptChannel สำหรับรับค่าข้อมูลที่ส่งผ่านทาง JavaScript JavascriptChannel _toasterJavascriptChannel(BuildContext context) { return JavascriptChannel( name: 'Toaster', // กำหนดชื่อที่จะใช้ onMessageReceived: (JavascriptMessage message) { // ในที่นี้เมื่อได้ค่ามาแล้ว จะแสดงข้อความด้วย SnackBar ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(message.message)), ); }); } // สร้างฟังก์ชั่น คืนค่าเป็น widget Widget scrollTopButton() { return FutureBuilder<WebViewController>( future: _controller.future, // ใช้งาน controller future ได้เลย builder: (BuildContext context, AsyncSnapshot<WebViewController> controller) { if (controller.hasData) { // มีข้อมูล return FloatingActionButton( // คืนค่าเป็นปุ่มลูกศรเลื่อนบน onPressed: () async { // ถ้ากด // เรียกคำสั่ง javascript เลื่อน scroll ไปด้านบนสุด await controller.data!.evaluateJavascript('window.scrollTo(0, 0);'); }, child: const Icon(Icons.arrow_upward), ); } return Container(); // ยังไม่มีข้อมูล คืนค่า container ว่างไปแสดงที่ปุ่ม }); } } // กำหนด Enum Type สำหรับเป็นลิสรายการของ PopupMenuButton enum MenuOptions { showUserAgent, listCookies, clearCookies, addToCache, listCache, clearCache, } // สร้าง widget สำหรับทำปุ่มควบคุม เพิ่มเติมแบบ PopupMenuButton class SampleMenu extends StatelessWidget { SampleMenu(this.controller); final Future<WebViewController> controller; // ใช้งาน WebViewController final CookieManager cookieManager = CookieManager(); // ใช้งาน CookieManager @override Widget build(BuildContext context) { return FutureBuilder<WebViewController>( future: controller, builder: (BuildContext context, AsyncSnapshot<WebViewController> controller) { return PopupMenuButton<MenuOptions>( onSelected: (MenuOptions value) { switch (value) { // ใช้เงื่อนไขค่าที่เลือก ทำฟังก์ชั่นที่ต้องการ case MenuOptions.showUserAgent: _onShowUserAgent(controller.data!, context); break; case MenuOptions.listCookies: _onListCookies(controller.data!, context); break; case MenuOptions.clearCookies: _onClearCookies(context); break; case MenuOptions.addToCache: _onAddToCache(controller.data!, context); break; case MenuOptions.listCache: _onListCache(controller.data!, context); break; case MenuOptions.clearCache: _onClearCache(controller.data!, context); break; } }, itemBuilder: (BuildContext context) => <PopupMenuItem<MenuOptions>>[ PopupMenuItem<MenuOptions>( value: MenuOptions.showUserAgent, child: const Text('Show user agent'), enabled: controller.hasData, ), const PopupMenuItem<MenuOptions>( value: MenuOptions.listCookies, child: Text('List cookies'), ), const PopupMenuItem<MenuOptions>( value: MenuOptions.clearCookies, child: Text('Clear cookies'), ), const PopupMenuItem<MenuOptions>( value: MenuOptions.addToCache, child: Text('Add to cache'), ), const PopupMenuItem<MenuOptions>( value: MenuOptions.listCache, child: Text('List cache'), ), const PopupMenuItem<MenuOptions>( value: MenuOptions.clearCache, child: Text('Clear cache'), ), ], ); }, ); } // ส่วนของฟังก์ชั่นการทำงานต่างๆ // ฟังก์ชั่นแสดง UserAgent ของ WebView void _onShowUserAgent( WebViewController controller, BuildContext context) async { await controller.evaluateJavascript( 'Toaster.postMessage("User Agent: " + navigator.userAgent);'); } // ฟังก์ชั่นแสดงรายการ cookie void _onListCookies( WebViewController controller, BuildContext context) async { final String cookies = await controller.evaluateJavascript('document.cookie'); ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Column( mainAxisAlignment: MainAxisAlignment.end, mainAxisSize: MainAxisSize.min, children: <Widget>[ const Text('Cookies:'), _getCookieList(cookies), ], ), )); } // ฟังก์ชั่นเพิ่มรายการ cache void _onAddToCache(WebViewController controller, BuildContext context) async { await controller.evaluateJavascript( 'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";'); ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('Added a test entry to cache.'), )); } // ฟังก์ชั่นแสดงรายการ cache void _onListCache(WebViewController controller, BuildContext context) async { await controller.evaluateJavascript('caches.keys()' '.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))' '.then((caches) => Toaster.postMessage(caches))'); } // ฟังก์ชั่นล้างค่า cache void _onClearCache(WebViewController controller, BuildContext context) async { await controller.clearCache(); ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text("Cache cleared."), )); } // ฟังก์ชั่นล้างค่า cookie void _onClearCookies(BuildContext context) async { final bool hadCookies = await cookieManager.clearCookies(); String message = 'There were cookies. Now, they are gone!'; if (!hadCookies) { message = 'There are no cookies.'; } ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text(message), )); } // ฟังก์ชั่นแสดงรายการ cookie Widget _getCookieList(String cookies) { if (cookies == null || cookies == '""') { return Container(); } final List<String> cookieList = cookies.split(';'); final Iterable<Text> cookieWidgets = cookieList.map((String cookie) => Text(cookie)); return Column( mainAxisAlignment: MainAxisAlignment.end, mainAxisSize: MainAxisSize.min, children: cookieWidgets.toList(), ); } } // สร้าง widget สำหรับทำปุ่มควบคุม เช่น ก่อนหน้า ย้อนหลัง รีเฟรช class NavigationControls extends StatelessWidget { // รับค่าข้อมูล Future WebViewController ผ่าน parameter เข้ามาใช้งาน const NavigationControls(this._webViewControllerFuture); // กำหนดตัวแปรสำหรับรับค่าและเรียกใช้งาน WebViewController ใน widget นี้ final Future<WebViewController> _webViewControllerFuture; @override Widget build(BuildContext context) { return FutureBuilder<WebViewController>( // ดูเพิ่มเติมได้ที่ http://niik.in/1036 future: _webViewControllerFuture, builder: (BuildContext context, AsyncSnapshot<WebViewController> snapshot) { // กำหนดตัวแปรเงื่อนไข controller พร้อมทำงานหรือ webview พร้อมทำงาน final bool webViewReady = snapshot.connectionState == ConnectionState.done; // กำนหดตัวแปร controller เพื่อควบคุม webview final WebViewController? controller = snapshot.data; // สร้าง widget ปุมต่างๆ ไปใช้งาน return Row( children: <Widget>[ IconButton( icon: const Icon(Icons.arrow_back_ios), onPressed: !webViewReady // ไม่พร้อมทำงาน ? null // คืนค่า null : () async { // พร้อมทำงาน if (await controller!.canGoBack()) { //เช็คย้อนหลังได้ไหม await controller.goBack(); // ถ้าได้ ก็ย้อนหลัง } else { // ย้อนหลังไม่ได้ แสดงข้อความแจ้ง ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text("No back history item")), ); return; } }, ), IconButton( icon: const Icon(Icons.arrow_forward_ios), onPressed: !webViewReady // ไม่พร้อมทำงาน ? null // คืนค่า null : () async { // พร้อมทำงาน if (await controller!.canGoForward()) { // เช็คไปหน้าได้ไหม await controller.goForward(); // ถ้าได้ ก็ไปหน้าถัดไป } else { // ถ้าไปหน้าไม่ได้ แสดงข้อความแจ้ง ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("No forward history item")), ); return; } }, ), IconButton( icon: const Icon(Icons.replay), onPressed: !webViewReady // ไม่พร้อมทำงาน ? null // คืนค่า null : () { // พร้อมทำงาน controller!.reload(); // โหลดหน้าเว็บเพจใหม่อีกครั้ง }, ), ], ); }, ); } }
ผลลัพธ์ที่ได้

เนื้อหานี้จะเน้นไปที่การใช้งาน PopupMenuButton รายละเอียดโค้ดอื่นๆ ที่เสริมเข้ามามีรูปแบบ
การใช้งานเหมือนบทความตอนที่แล้ว คำอธิบายแสดงในโค้ด
หวังว่าเนื้อหานี้จะทำให้เราสามารถประยุกต์การใช้งาน PopupMenuButton เพื่อกำหนดคำสั่งเพิ่ม
เติมที่ต้องการได้ เนื้อหาตอนหน้าจะเป็นอะไร รอติดตาม
กด Like หรือ Share เป็นกำลังใจ ให้มีบทความใหม่ๆ เรื่อยๆ น่ะครับ

อ่านต่อที่บทความ
-
02 Nov2021การกำหนดและใช้งาน BottomSheet ใน Flutter อ่าน 4,201
เนื้อหาต่อไปนี้จะมาดูเกี่ยวกับการใช้งาน Bottom Sheet ส่วน ที่ใช้สำหรับแสด
เนื้อหาที่เกี่ยวข้อง
-
18 Dec2019การใช้งาน BottomNavigationBar ใน Flutter เบื้องต้น อ่าน 18,227
ในตอนที่แล้ว เราได้รู้จัก การใช้งาน Drawer หรือที่เรียกว่า SideMenu เบื้อ
-
21 Oct2021แสดงข้อความด้านล่างหน้าจอด้วย SnackBar ใน flutter อ่าน 2,743
SnackBar เป็น widget ที่เราสามารถใช้สำหรับแสดง popup ข้อความ แ
-
22 Oct2021การกำหนดเพิ่ม Fonts เพื่อใช้งานใน App ของ Flutter อ่าน 3,954
เนื้อหานี้จะมาแนะนำแนวทางการเพิ่มฟอนท์สำหรับ ใช้งานใน app กรณีเราอยากได้ร
-
23 Oct2021จัดการ Tab ด้วย TabController ใน Flutter อ่าน 7,596
เนื้อหานี้มาดูเกี่ยวกับการใช้งาน tab ใน flutter ซึ่งเป็นรูปแบบ การใช้งานท
-
24 Oct2021การใช้งาน FutureBuilder ที่เป็น Async widgets ใน Flutter อ่าน 5,457
เนื้อหาตอนต่อไปนี้ จะมาดูเกี่ยวกับการใช้งาน async widgets ที่ชื่อว่า Futu
-
25 Oct2021การใช้งาน StreamBuilder จัดการข้อมูล Stream ใน Flutter อ่าน 4,980
ในตอนที่แล้วเรารู้จักกับ FutureBuilder ซึ่งเป็น async widgets หนึ่งใน Flu
-
26 Oct2021การใช้งาน Http ดึงข้อมูลจาก Server มาแสดงใน Flutter อ่าน 9,696
เนื้อหาตอนต่อไปนี้ เราจะมาดูเกี่ยวกับวิธีการดึงข้อมูลจาก Server ที่เป็นข้
-
27 Oct2021การใช้งาน Card Widget ร่วมกับ ListView ใน Flutter อ่าน 6,590
เนื้อหาตอนต่อไปนี้ จะมาดูเกี่ยวกับ widget เล็กๆ ที่จะมาช่วย ให้การแสดงผลใ
-
28 Oct2021การใช้งาน RefreshIndicator ปัดเพื่อรีเฟรชข้อมูล ใน Flutter อ่าน 4,323
ต่อเนื่องจากเนื้อหาตอนที่แล้ว เนื้อหานี้เราจะมาดูเกี่ยวกับ widget ที่ให้เ
-
29 Oct2021จัดการข้อมูล Model และแนวทางการนำมาใช้งาน ใน Flutter อ่าน 5,932
เนื้อหาตอนต่อไปนี้จะประยุกต์ต่อเนื่องจากตอนที่แล้ว เราจะลองใช้ ข้อมูลทดสอ
-
30 Oct2021การใช้งาน GridView widget ใน Flutter อ่าน 9,718
เนื้อหาตอนต่อไปนี้ จะมาดูเกี่ยวกับการใช้งาน GridView เป็นเนื้อหาต่อ
-
31 Oct2021การใช้งาน WebView แสดงเว็บไซต์ ใน Flutter อ่าน 7,041
เนื้อหานี้จะมาดูเกี่ยวกับการใช้งาน WebView widget ซึ่งจะเป็น package ที่เ
-
กำลังอ่านเนื้อหานี้อยู่01 Nov2021การกำหนดและใช้งาน PopupMenuButton ใน Flutter อ่าน 3,216
เนื้อหาตอนต่อไปนี้จะมาดู Widget เล็กๆ ที่มีรูปแบบการใช้งาน ง่ายๆ ที่เรียก
-
02 Nov2021การกำหนดและใช้งาน BottomSheet ใน Flutter อ่าน 4,201
เนื้อหาต่อไปนี้จะมาดูเกี่ยวกับการใช้งาน Bottom Sheet ส่วน ที่ใช้สำหรับแสด
URL สำหรับอ้างอิง
Top
Copy
ขอบคุณทุกการสนับสนุน
![]()