วันอังคารที่ 9 มีนาคม พ.ศ. 2564

สรุปฟีเจอร์ใหม่ใน Flutter 2

 


          สวัสดีจ้าาาาา กลับมาพบกันอีกครั้งกับบทความที่นาน ๆ จะโพสที วันนี้ผมก็จะมาสรุปฟีเจอร์ใหม่ของ Flutter เวอร์ชั่น 2 ที่เพิ่งเปิดตัวในงาน Flutter Engage ไปในวันที่ 3 มีนาคม 2564 ที่ผ่านมา (ซึ่งตรงกับวันที่ 4 มีนาคม 2564 ของไทย) โดยในงานเปิดตัวครั้งนี้ได้มีการอัพเกรด Flutter ครั้งใหญ่ซึ่งจะช่วยให้การพัฒนาแอปพลิเคชันแบบ Multi-Platform ทำได้ง่ายยิ่งขึ้น

          Flutter รองรับการเขียน Native apps บน 5 ระบบปฏิบัติการ ได้แก่ iOS, Android, Windows, macOS และ Linux นอกจากระบบปฏิบัติการข้างต้นแล้ว Flutter ยังรองรับการพัฒนาแอปพลิเคชันที่ทำงานบนเว็บบราวเซอร์ รวมไปถึง Smart device ต่าง ๆ เช่น รถยนต์ หรือทีวีอีกด้วย เรียกได้ว่าเขียนโค้ดชุดเดียวแล้วสามารถแปลงไปเป็นแอปพลิเคชันที่ทำงานได้กับทุกอุปกรณ์เลยทีเดียว .....เอาล่ะ เกริ่นกันมานานแล้ว เราไปดูกันดีกว่าว่ามีอะไรใหม่ใน Flutter 2 กันบ้าง


Web

          เริ่มที่อัพเกรดแรกคือการประกาศรองรับการเขียนเว็บแอปพลิเคชันเต็มรูปแบบ ปรับเปลี่ยนจากเวอร์ชั่น beta เป็นเวอร์ชั่น stable และรองรับฟีเจอร์ต่าง ๆ ได้แก่

  • Progressive web apps (PWAs)
  • Single page apps (SPAs)
  • สามารถพอร์ต Mobile Application ไปเป็น Web Application โดยใช้โค้ดชุดเดียวกันได้เลย
โดยการอัพเกรดครั้งนี้ได้มีการปรับปรุง architecture ครั้งใหญ่ ซึ่งช่วยปรับปรุงประสิทธิภาพในด้านต่าง ๆ เช่น
  • Performance: เพิ่มตัวเลือกการ render เป็น 2 แบบ ได้แก่
    • HTML renderer ที่มีขนาดเล็ก เข้ากันได้ดีกับ HTML elements, CSS, Canvas elements, และ SVG elements
    • CanvasKit renderer ที่ใช้ WebAssembly และ WebGL ในการรันคำสั่ง Skia paint บนเว็บเบราว์เซอร์ เข้ากันได้ดีกับ Flutter mobile และ desktop แต่ก็แลกกับขนาดที่ใหญ่กว่า HTML renderer
  • Web-specific Features: เพิ่มฟีเจอร์ Custom URL สำหรับควบคุม URL บน address bar เพิ่ม Link Widget ในการจัดการลิงค์ไปยังที่ต่าง ๆ ทั้งภายในและภายนอกแอพ รวมไปถึงเพิ่มระบบ layout สำหรับจัดวางข้อความ
  • Desktop form factors: เพิ่มประสิทธิภาพของการ render เว็บบน desktop เนื่องจากบน desktop จะใช้เม้าส์ในการควบคุม ซึ่งจะแตกต่างจากการควบคุมด้วยนิ้วบน mobile
  • Plugins: รองรับ Plugin ecosystem ทำให้สามารถใช้งาน Flutter Application บนเว็บได้ สามารถเข้าถึง JavaScript Library ผ่านปลั๊กอิน


Sound Null Safety

          นอกจาก Flutter จะอัพเกรดเป็นเวอร์ชั่น 2 แล้ว ภาษา Dart ที่อยู่คู่กับ Flutter มานานก็มีการอัพเกรดเป็นเวอร์ชั่น 2.12 เช่นกัน มีการเพิ่มฟีเจอร์สำหรับแยกแยะประเภทตัวแปรออกเป็น nullable type และ non-nullable type ช่วยในการตรวจจับ null error ป้องกันไม่ให้แอปพลิเคชันเกิด Crash ได้


Desktop

          Flutter Desktop ถูกปรับสถานะเป็น Stable มีการปรับปรุงการทำงานของ Widget เช่น Scrollbar, ReorderableListView และตัวอื่น ๆ เพื่อให้รองรับการใช้งานด้วยเม้าส์บน Desktop


Google Mobile Ads

          นอกจาก Flutter Desktop แล้ว ในงานนี้ยังมีการเปิดตัว Google Mobile Ads SDK for Flutter เวอร์ชั่น beta ซึ่งเป็นปลั๊กอินตัวใหม่ที่ให้บริการแสดงโฆษณาในแอปพลิเคชัน โดยเจ้าปลั๊กอินตัวนี้ยังรองรับ Ad Manager และ Admob อีกด้วย


New iOS features

          ถึง Flutter จะเป็นของ Google แต่ก็ไม่ใช่จะมีแค่อัพเดตในส่วนของ Android เท่านั้นนะ ในเวอร์ชั่นนี้ Flutter ได้เพิ่ม widget ที่มีการแสดงผล UI แบบของใน iOS เช่น CupertinoSearchTextField ที่มีการแสดงผลเป็นแบบ iOS search bar และ CupertinoFormSection, CupertinoFormRow, CupertinoTextFormFieldRow ที่มีการแสดงผลเป็นแบบ validated form ที่แบ่งส่วนตามแบบของ iOS


New widgets

          Flutter เวอร์ชั่นนี้มาพร้อมกับ widget ตัวใหม่สองตัว ได้แก่ AutocompleteCore ที่ใช้สำหรับเติมข้อความอัตโนมัติลงใน Flutter Application และ ScaffoldMessenger ที่ใช้ควบคุมการแสดงผลของ  SnackBar


Add-to-App

          Add-to-App ฟีเจอร์ที่ช่วยให้นักพัฒนาอย่างเราสามารถนำ Flutter Application เข้าไปใส่ในลงในแอปพลิเคชัน iOS และ Android ที่มีอยู่แล้วในรูปแบบของ Library หรือ Module


Flutter Fix

          เมื่อ framework มีการอัพเกรดเวอร์ชั่นก็อาจส่งผลให้มีคำสั่งหรือ API บางตัวถูกยกเลิกการใช้งาน แต่ด้วยฟีเจอร์ใหม่ที่เรียกว่า Flutter Fix จะช่วยทำการค้นหาคำสั่งหรือ API ที่ถูกยกเลิกใช้งานไปแล้ว และแสดงวิธีอัปเดตคำสั่งหรือ API เหล่านั้น เพื่อให้ code ของเราสามารถรันได้ตามปกติ


Flutter Folio Sample

          ปัจจุบัน Flutter รองรับการทำงานบนแพลตฟอร์มอันหลากหลายทั้ง 6 แพลตฟอร์ม ได้แก่ Android, iOS Website, Windows, macOS และ Linux ซึ่งมีความแตกต่างกันทั้งขนาดหน้าจอแสดงผล ทั้งการรับอินพุต ซึ่งอาจจะสร้างความสับสนให้กับนักพัฒนาอย่างเราว่าจะต้องเขียนโค้ดจัดการอย่างไรให้รองรับกับทุกแพลตฟอร์ม ทางผู้พัฒนาจึงได้พัฒนาแอปพลิเคชันตัวอย่างที่ชื่อ Flutter Folio ออกมาให้เราดูเป็นแนวทางว่าแอปพลิเคชันในแต่ละแพลตฟอร์มจะมีรูปแบบการแสดงผลและการทำงานเป็นอย่างไร


          และนี่ก็คืออัพเดตใหญ่ ๆ ของ Flutter 2 ในงาน Flutter Engage ที่ผ่านมา หากเพื่อน ๆ สนใจรายละเอียดเพิ่มเติมก็สามารถเข้าไปอ่านได้ที่นี่ .....สุดท้ายนี้ผมก็หวังว่าจะได้กลับมาเขียนบทความบ่อยขึ้นครับ แล้วพบกันใหม่ในบทความถัดไป (เร็ว ๆ นี้?????) บ๊ายบาย



วันเสาร์ที่ 5 ธันวาคม พ.ศ. 2563

เคล็ด (ไม่) ลับการเตรียมตัวก่อนขึ้นไปเป็น Speaker บนเวที


        สวัสดีจ้าาาาา กลับมาพบกันอีกครั้งหลังจากที่หายไปปีกว่า ๆ ที่ไม่ได้อัพเดตบล็อกเลย คิดถึงเพื่อน ๆ ทุกคนจัง ช่วงนี้ผมเองก็พยายามจะหาเวลากลับมาเขียนบล็อกต่อแต่ก็โดนทั้งงาน ทั้งซีรี่ย์เกาหลีเรื่อง Start-up เข้ามารบกวนจนไม่ได้เริ่มเขียนซะที (ฮา) พูดถึงซีรี่ย์ Start-up ก็รู้สึกว่าบทละครทำออกมาดี มีการพูดถึง Start-up แม้ว่าในตอนท้าย ๆ จะเน้นเรื่องของความรักซะมากกว่า แต่ก็ถือว่าเปิดเผยวงการ Start-up ให้คนธรรมดาอย่างเรา ๆ เข้าใจได้ง่าย

        เอ๊ะ! ออกนอกเรื่องซะไกล กลับมากันก่อนดีกว่า วันนี้ตั้งใจจะเขียนบล็อกถ่ายทอดประสบการณ์การขึ้นไปเป็น Speaker บนเวทีในโรงหนัง ถึงจะได้พูดไม่นาน แต่ก็เผื่อว่าจะเป็นประโยชน์ในการเตรียมตัว หรือสร้างแรงบันดาลใจให้เพื่อน ๆ อยากขึ้นไปพูดบนเวทีบ้าง เพราะงั้นผมอาจจะไม่ได้ลงรายละเอียดเกี่ยวกับงานที่ผมไปพูดสักเท่าไหร่ แต่จะพูดถึงประสบการณ์การเตรียมตัวของผมเป็นหลัก

        เริ่มจากงานที่ไปพูดก่อนเนอะ ชื่องาน Coraline Big Data x Data Science Summit 2020 เป็นงานแรกงานเดียวที่พูดถึง Real Cases ความสำเร็จของโครงการ Big Data ที่เกิดขึ้นจริงในประเทศไทย จัดขึ้นที่เมเจอร์ซีนีเพล็กซ์ รัชโยธิน โรงภาพยนตร์ 13 ในวันเสาร์ที่ 28 พฤศจิกายน 2563 เวลา 13.00 - 17.00 น. 

        ภายในงานจะมีการพูดถึง ecosystem ในการทำโครงการ Big Data, ตำแหน่งงานที่เกี่ยวข้อง แล้วก็ยกตัวอย่าง Real Cases ที่ประสบความสำเร็จในการทำโครงการ Big Data โดยดร. อสมา กุลวานิชไชยนันท์ CEO และ Co-Founder บริษัท Coraline ส่วนผมได้รับโอกาสให้ไปพูดแนะนำเกี่ยวกับตำแหน่งหน้าที่ของ Full Stacker Developer และ QA Engineer

เคล็ด (ไม่) ลับการเตรียมตัวก่อนขึ้นไปพูดบนเวที

        ใคร ๆ ก็มักจะพูดว่า Developer เป็นพวกพูดไม่รู้เรื่อง ซึ่งก็ไม่ผิด และผมเองก็เป็นหนึ่งในพวกพูดไม่รู้เรื่องเหมือนกัน ตอนที่จะออกไปพูดหน้าชั้น หรือการนำเสนองาน ผมก็มักจะตื่นเต้น ภายในหัวก็จะโล่งคิดอะไรไม่ออก พอต้องออกไปพูดบนเวทีที่มีคนฟังเยอะมากขนาดนี้ ในใจก็คิดเลยว่า "แค่พูดนำเสนอยังตื่นเต้นเลย นี่ต้องออกไปพูดบนเวทีอีก จะรอดม๊ายแต่เมื่อได้รับโอกาสมาแล้วก็ต้องลองกันสักตั้งล่ะเนอะ

        การพูดครั้งนี้ใช้เวลาเตรียมตัวซ้อมพูดประมาณ 2-3 วัน เริ่มจากกำหนดเนื้อหาที่จะพูดก่อน ลองเขียนสคริปต์ขึ้นมา จากนั้นก็ลองซ้อมพูดดู ตอนแรกก็จะติด ๆ ขัด ๆ เพราะจำเนื้อหาที่จะพูดไม่ได้ แต่พอซ้อมซ้ำไปซ้ำมาก็เริ่มคล่องปาก ในการพูดแต่ละครั้งบทพูดก็ไม่ซ้ำกันเลย

        พอใกล้ถึงวันงาน ความตื่นเต้นก็เริ่มก่อตัว แต่จากที่ซ้อมมาแล้วก็ทำให้มั่นใจว่าจะพูดได้มากขึ้น พอมีเวลาว่างก็พยายามซ้อมไปเรื่อย ๆ ซ้อมในเวลางานก็มี จนงานการแทบไม่ได้ทำ (จุ๊ ๆ อย่าไปบอกใครนะว่าผมโดดงาน ฮะฮะ) ตอนอาบน้ำก็ซ้อมแบบไม่ออกเสียงทำให้เสียเวลาอาบนาน (เปลืองน้ำได้อี๊ก)

        ในที่สุดวันงานก็มาถึง ความตื่นเต้นยังคงมีอยู่ แต่ไม่ได้ตื่นเต้นมากเหมือนเมื่อก่อน ที่ตื่นเต้นอาจจะเพราะแค่ยังไม่เคยมีประสบการณ์ขึ้นไปพูดบนเวทีต่อหน้าผู้ฟังเยอะขนาดนี้มาก่อน พอถึงหน้างานก่อนเวลาก็ไปเดินซ้อมพูดในหัววนไปวนมาบนเวที พอได้ขึ้นเวทีจริง "อ้าว มุมมองจากบนเวทีไม่เห็นจะมีอะไรพิเศษเลยนี่นา" จากนั้นความตื่นเต้นก็ลดลง

        เมื่องานเริ่ม ก็แอบตื่นเต้นนิดนึง แต่พอใกล้ถึงเวลาขึ้นเวทีก็เริ่มสงบลง จนกระทั่งได้ขึ้นไปนั่งบนเวทีรอคิวพูดก็ยังคงสงบอยู่ จะมีตื่นเต้นบ้างก็ตอนใกล้ถึงคิวพูด "จะถึงแล้ว ๆ" แต่พอได้เริ่มพูดก็ไม่ตื่นเต้นเลย สภาพภายในใจเหมือนเป็นทะเลสีครามในวันที่ท้องฟ้าแจ่มใส (เว่อร์เนอะ) พูดออกไปได้ไหลลื่น อาจจะมีพูดผิดจังหวะอยู่บ้าง พลาดไปอีกเล็กน้อย แต่ก็รู้สึกว่าตัวเองทำได้ดีกว่าเมื่อก่อนมาก 

        หลังจบงานก็คิดว่า "เออ ถ้าจะทำจริงก็ทำได้นี่นา" แม้จะได้พูดไม่นาน แม้จะมีผิดพลาดไปบ้าง แม้จะไม่เพอร์เฟคที่สุด แต่ก็ถือเป็นประสบการณ์ที่ดี และรู้สึกว่าตัวเองได้เติบโตก้าวหน้าไปอีกก้าว เลยอยากจะมาแชร์เคล็ด (ไม่) ลับง่าย ๆ ที่ผมใช้ในครั้งนี้ ว่าแล้วก็มาดูกันเลยดีกว่า

  1. ซ้อมให้หนักเข้าไว้! ไม่ว่าจะเป็นนักวิ่งฝีเท้าไว หรือจะเป็นนักพูดชั้นยอด ก็ต้องผ่านการซ้อมมาก่อน การซ้อมบ่อย ๆ ของผมก็เพื่อให้ตัวเองจดจำเนื้อหาที่จะพูดบวกกับให้ปากเคยชินกับเนื้อหาจะได้ขยับคล่อง ๆ พูดไหลลื่น การซ้อมของผมส่วนใหญ่จะเป็นการซ้อมพูดอยู่คนเดียวก่อน พอจำเนื้อหาได้แล้วก็ไปซ้อมพูดให้คนอื่นฟังบ้าง ซ้อมหน้ากระจกบ้าง การซ้อมให้คนอื่นฟังจะช่วยให้เราไม่เขินและชินกับการถูกจับตามอง ส่วนการซ้อมหน้ากระจกจะทำให้เราเห็นท่าทางเวลาพูดของเรา ช่วยให้เราสามารถปรับบุคลิกท่าทางในการพูดได้ เช่น ท่ายืน การวางมือ การแสดงท่าทาง
  2. มั่นใจในตัวเองเข้าไว้ เราทำได้! ผมว่าหนึ่งเหตุผลที่ทำให้การออกไปพูดบนเวทีล้มเหลว ก็คือการขาดความมั่นใจ พอขาดความมั่นใจ เราก็จะตื่นเต้น พอตื่นเต้นก็จะเริ่มลน แล้วก็พูดวนไปวนมา เพราะงั้นความมั่นใจในตัวเองก็เป็นอีกจุดหนึ่งสำคัญที่ทำให้การพูดผ่านไปได้ด้วยดี ซึ่งความมั่นใจก็จะเกิดได้จาก 2 ทาง คือ การมั่นฝึกซ้อม และการบอกกับตัวเองว่าเราทำได้..... การที่เรามั่นฝึกซ้อมจะเพิ่มความมั่นใจให้กับตัวเองได้ในระดับหนึ่ง แต่ถ้าตัวเองยังคงคิดว่า "ยากจัง ทำไม่ได้หรอก" ก็จะเป็นการบั่นทอนความมั่นใจที่เพิ่มขึ้นมา เพราะงั้นเราต้องมาปรับทัศนคติกันนิดนึง บอกกับตัวเองว่า "เราทำได้ ไม่มีอะไรยากเกินความพยายามหรอก" แค่นี้บวกกับที่เราซ้อมมาก็จะทำให้เรามั่นใจได้มากขึ้น
  3. อย่าจำ script บทพูดทั้งหมด! ในขั้นตอนซ้อม แน่นอนว่าจะต้องมีการเตรียม script เนื้อหาที่จะพูด ซึ่งสิ่งที่อยู่ใน script ก็ควรจะเป็นหัวข้อที่จะพูด ไม่ใช่บทพูดทั้งหมด..... เมื่อก่อนผมเคยเขียน script เป็นบทพูดทั้งหมดแล้วพยายามท่องจำให้ได้ ผลลัพธ์ที่ออกมาคือพูดได้ตรงตามเนื้อหาเขียนไว้ แต่มันดูเป็นหุ่นยนต์มาก นอกจากนี้ถ้าเขียนบทพูดออกมายาว ๆ ก็จำไม่ค่อยได้อีก พอจำเนื้อหาที่จะพูดไม่ได้ก็ลน แล้วก็พังจ้า..... เพราะงั้นให้เราเขียนเฉพาะหัวข้อที่จะพูดออกมาแล้วก็จำแค่นั้น ส่วนเนื้อหาให้เราเล่าออกมา แม้ว่าจะเล่าออกมาไม่ซ้ำกันเลยในการพูดแต่ละครั้งก็ไม่เป็นไร ขอแค่พูดตามหัวข้อให้ครบก็พอ
  4. อย่ากลัวที่จะพูดผิด! ในการพูดแต่ละครั้งอาจจะมีบางคำที่เราพูดผิดไปบ้าง แต่ก็ไม่เป็นไร ขอแค่ให้มีความมั่นใจ กล้าที่จะพูดออกไป ถ้าพูดผิดไปแล้วก็ไม่ต้องลน ให้แก้ไขไปตามสถานะการณ์ เช่น ถ้าเราพูดชื่อผิด ไม่ต้องขอโทษ ให้เว้นช่วงนิดนึงแล้วพูดชื่อที่ถูกต้องใหม่ไปเลย เป็นต้น
  5. ทำความรู้จักผู้ฟัง! ก่อนจะออกไปพูดในงาน เราก็ควรจะต้องรู้จักกับพื้นฐานของผู้ฟังก่อน เพื่อที่เราจะได้กำหนดความละเอียดของเนื้อหาและเลือกคำที่จะใช้ เช่น ถ้าผู้ฟังไม่ได้อยู่ในวงการ IT แล้วเราดันใช้แต่ศัพท์เทคนิค ผู้ฟังก็จะไม่รู้เรื่อง ฉะนั้นเราจะต้องมีการอธิบายหรือยกตัวอย่างเพิ่ม เพื่อทำให้ผู้ฟังรู้เรื่อง .....อันนี้ถือเป็นเรื่องสำคัญ เพราะผู้ฟังยอมสละเวลามาฟังและให้ความสนใจในสิ่งที่เราพูดแล้ว เราก็ต้องให้เกียรติและพูดให้เขาเข้าใจให้ได้เป็นการตอบแทน
  6. สติ! อีกหนึ่งสิ่งสำคัญที่ต้องมีในเวลาพูดก็คือ "สติ" เพื่อให้ตัวเองรู้ตัวอยู่ว่าพูดอะไรอยู่ และกำลังจะพูดอะไรต่อ การจะมีสติได้นั้น เราจะต้องไม่ตื่นเต้น ซึ่งก่อนขึ้นเวทีผมก็พยายามหายใจลึก ๆ ทำใจเย็น ๆ อาจจะไม่ถึงกับทำสมาธิ แต่ก็ควบคุมให้ตัวเองหายใจเป็นปกติ 
        และนี่ก็เป็นเคล็ด (ไม่) ลับของผมที่ใช้ในการขึ้นไปพูดบนเวทีนะครับ ประสบการณ์การพูดในครั้งนี้ทำให้ผมค่อนข้างรู้สึกสนุกมาก แล้วก็สอนให้รู้ว่าการขึ้นไปบนเวที ถูกแสงไฟส่องหน้า มันก็ไม่ได้น่ากลัวอะไรเลย ถ้าเราผ่านการซ้อมและมีความมั่นใจในตัวเอง สุดท้ายผมก็หวังว่าบล็อกนี้จะช่วยให้เพื่อน ๆ ที่อยากจะขึ้นไปพูดบนเวทีได้รู้เทคนิคและวิธีเตรียมตัวก่อนจะออกไปพูดนะครับ แล้วพบกันใหม่ในบล็อกถัดไป บ๊ายบาย.....


วันพุธที่ 6 มีนาคม พ.ศ. 2562

(Back to Basic) ทำความรู้จักกับ DOM เรื่องที่นักพัฒนาเว็บควรรู้


          สวัสดีปีใหม่ย้อนหลังจ้า ถึงจะผ่านมาถึงเดือนมีนาคมแล้ว แต่สำหรับบล็อกที่หายไปนานก็ถือว่าเป็นการสวัสดีปีใหม่เป็นครั้งแรกล่ะนะ หลังจากวางมือไปนาน ก็จะไม่พูดพร่ำทำเพลงอะไรมาก มาเข้าเรื่องกันเลยดีกว่า..... สำหรับบทความแรกของปีนี้เกี่ยวข้องกับความรู้ที่นักพัฒนาเว็บควรรู้ ซึ่งเป็นพื้นฐานในการเริ่มต้นของการเขียนเว็บ นั่นก็คือ DOM

          DOM หรือ Document Object Model คือโครงสร้างของไฟล์ที่ทาง W3C (World Wide Web Consortium หรือองค์กรระหว่างประเทศที่ทำหน้าที่จัดระบบมาตรฐานที่ใช้งานบนเวิลด์ไวด์เว็บ) กำหนดขึ้นเป็นมาตราฐาน ซึ่งจะช่วยให้นักพัฒนาสามารถเขียนโค้ดหรือสคริปต์เพื่อเข้าถึง, เพิ่มเติม, เปลี่ยนแปลง รวมไปถึงลบเนื้อหาหรือโครงสร้างของไฟล์เอกสารประเภท HTML หรือ XML ได้

          เมื่อเว็บเบราว์เซอร์ทำการโหลดหน้าเว็บขึ้นมา เบราว์เซอร์ก็จะทำการสร้าง DOM ของหน้าเว็บนั้น แล้วมอง HTML Element ทั้งหมดเป็น object ที่มีโครงสร้างเป็นกิ่งก้านสาขาเหมือนต้นไม้ (ตามรูปด้านบนของบทความ) ถึงตรงนี้เพื่อน ๆ อาจจะยังงง ๆ อยู่ งั้นมาดูตัวอย่างกันเลยดีกว่า

<html>
 <head> 
  <title>Web Title</title>
 </head>
  
 <body>
  <a href="test.html">Google</a>
  <div>
   <span>Home</span>
  </div>
 </body>
  
</html>
          จากตัวอย่างโค้ดด้านบน เมื่อนำมาแปลงให้อยู่ในรูปแบบโครงสร้าง DOM ก็จะมีหน้าตาดังนี้


          จากรูปจะเห็นได้ว่า HTML DOM มีโครงสร้างเป็นลำดับชั้นเหมือนกับต้นไม้ที่แผ่กิ่งก้านสาขา โดยเริ่มต้นจาก <html> เป็นจุดเริ่มต้น ภายใน <html> ก็จะมี element ลูกเป็น <head> และ <body> ซึ่งเจ้า 2 Element นี้ก็จะมี Element ลูกไล่ระดับชั้นลึกเข้าไปเรื่อย ๆ โดยที่แต่ละ Element จะสามารถมี Element ลูกได้มากกว่า 1 Element (ตัวอย่างเช่น <html> ไง ที่มี Element ลูกมากกว่าหนึ่ง)


การจัดการ Object ภายใน DOM

          ตามมาตรฐานของ W3C อนุญาตให้เราสามารถเขียนสคริปต์เพื่อจัดการ Object ต่าง ๆ ที่อยู่ภายใน DOM ได้ ไม่ว่าจะเป็นการเพิ่มเติม การปรับเปลี่ยนแก้ไข หรือแม้แต่การลบ Element ก็สามารถทำได้แบบ Dynamic ซึ่งผลจากการแก้ไขก็จะแสดงให้เห็นบนเบราว์เซอร์ทันที

          เพื่อให้เห็นภาพ เรามาลองลงโค้ดจริงกันเลยดีกว่า โดยเราจะใช้ Javascript ในการเข้าถึงและจัดการ HTML DOM ซึ่งอันดับแรกให้เริ่มจากเขียนโค้ด HTML ธรรมดา ๆ เตรียมไว้ก่อน

<html>
 <head> 
  <title>Web Title</title>
 </head>
  
 <body id="app">
  <div id="myDiv"></div>
  
  <ul id="myList">
   <li class="item">1</li>
   <li class="item">2</li>
   <li class="item">3</li>
   <li class="item">4</li>
   <li class="item">5</li>
  </ul>
  
  <button id="deleteButton">Delete</button> | <button id="insertButton">Insert</button>
  
  
  <script>
   // พื้นที่สำหรับใส่โค้ด Javascript
  </script>
 </body>
  
</html>
          เมื่อเซฟโค้ด HTML ข้างบนเป็นไฟล์ index.html แล้วนำไปเปิดบนเบราว์เซอร์ เพื่อน ๆ ก็จะเห็นลิสต์ตัวเลข 1 ถึง 5 และมีปุ่มกดอยู่ข้างล่าง ซึ่งเมื่อกดปุ่มก็ไม่มีอะไรเกิดขึ้น ต่อไปให้เราจะทำการเพิ่มโค้ด Javascript ดังต่อไปนี้ลงในภายใน <script> แล้วทำการเซฟไฟล์

let myDiv = document.getElementById('myDiv');
myDiv.innerHTML = 'Hello my world!!';
          กด F5 เพื่อ refresh หน้าเว็บแล้วมาดูกันว่าเจ้าโค้ด 2 บรรทัดนี้ก่อให้เกิดอะไรบ้าง.....
ผลลัพธ์ที่ได้ก็คือ มีข้อความ "Hello my world!!" ปรากฏอยู่เหนือลิสต์ตัวเลขนั่นเอง

          เอาล่ะ เรามาดูคำสั่งของโค้ดที่ได้เพิ่มลงไปกัน คำสั่ง document.getElementById(id) ในบรรทัดแรกเป็นการอ้างถึง Element ที่มี attribute id เท่ากับ "myDiv" ในไฟล์ แล้วนำมาเก็บอยู่ในตัวแปรที่ชื่อว่า myDiv และในบรรทัดที่สองก็เป็นการเปลี่ยนแปลงเนื้อหาหรือส่วน text ของ Element ในตัวแปรที่เราได้เก็บไว้จากค่าว่างให้กลายเป็น "Hello my world!!" แทน แต่ข้อจำกัดของคำสั่ง document.getElementById(id) คือการอ้างอิงถึง Element ได้เพียงตัวเดียว (เนื่องจากใน HTML จะใช้ชื่อ id ซ้ำกันไม่ได้ แต่สามารถใช้ชื่อ class ซ้ำกันได้) ถัดไปให้เพื่อน ๆ ลองพิมพ์โค้ดดังต่อไปนี้

let listItem = document.getElementsByClassName('item');
listItem[0].innerHTML = listItem.length + ' items';
listItem[1].style['color'] = '#FF0000';
listItem[2].style['background-color'] = '#FF0000';
          เนื่องจากคำสั่ง document.getElementById(id) จะอ้างอิงถึง Element ได้เพียงตัวเดียว ถ้าเราต้องการอ้างถึง Element ครั้งละหลาย ๆ ตัวก็จำเป็นต้องเปลี่ยนไปใช้คำสั่ง document.getElementsByClassName(class name) หรือคำสั่ง document.getElementsByTagName(tag name) แทน ซึ่งจะคืนค่ากลับมาเป็นอาเรย์

          นอกจากเราจะเข้าถึง Element เพื่อแก้ไขแล้ว เราสามารถกำหนดให้มีการดักจับเหตุการณ์ให้กับ Element ได้อีกด้วย ตัวอย่างเช่น กำหนดให้มีการดักจับเหตุการณ์ "คลิก" เมื่อมีการคลิกปุ่มก็จะทำงานตามที่เราต้องการ

let deleteButton= document.getElementById('deleteButton');
deleteButton.addEventListener('click', function() {
 alert('Click delete button!!!');
 let app = document.getElementById('app');
 let myDiv = document.getElementById('myDiv');
 app.removeChild(myDiv);
});
         จากโค้ดข้างบน เป็นการกำหนดให้ปุ่มทำการแจ้งเตือนและลบ Element ออกเมื่อเกิดเหตุการณ์ "คลิก" ซึ่งคำสั่ง element.addEventListener('event', function) จะเป็นการกำหนดการดักจับเหตุการณ์ให้กับ Element โดย 'event' เป็นตัวกำหนดเหตุการณ์ที่จะดักจับ (ซึ่งในโค้ดตัวอย่างจะใช้เป็น 'click' ดักจับการคลิก) และ function เป็นการทำงานที่จะถูกเรียกใช้เมื่อเกิดเหตุการณ์ตามที่กำหนดไว้

          หลักการในการลบ Element นั้น เราจำเป็นต้องรู้ก่อนว่า Element นั้นอยู่ภายใต้ Element ตัวไหน ให้เราทำการอ้างอิงไปถึง Element ที่ครอบ Element ตัวนั้นก่อน จากนั้นจึงใช้คำสั่ง element.removeChild(element) เพื่อทำการลบ Element ตัวนั้น (อธิบายง่าย ๆ ก็คือ ถ้าต้องการลบ Element ตัวไหน ให้หา Element พ่อให้เจอ แล้วให้ Element พ่อสั่งลบ) จากโค้ดตัวอย่าง ขั้นตอนแรกที่ต้องทำคือการอ้างอิงถึง Element body (ซึ่งครอบ Element ที่ต้องการลบอยู่) ก่อน แล้วจึงใช้คำสั่งเพื่อลบ Element ที่มี id เท่ากับ "myDiv" ออก

let insertButton = document.getElementById('insertButton');
insertButton.addEventListener('click', function() {
 alert('Click insert button!!!');
 let newItem = document.createElement("li");
 let text = document.createTextNode("new li item");
 newItem.appendChild(text);

 let myList = document.getElementById("myList");
 myList.appendChild(newItem);
});
          ถัดจากการลบ Element แล้ว เราก็มาดูเหตุการณ์เพิ่ม Element กันบ้าง ซึ่งก็ใช้หลักการเดียวกันกับการลบ Element นั่นคือ เราจะต้องอ้างอิงไปที่ Element พ่อก่อน แล้วสั่งให้ Element พ่อทำการเพิ่ม Element ลูก โดยเราสามารถสร้าง Element ลูกด้วยคำสั่ง document.createElement(element tag) เพื่อสร้าง HTML Tag และคำสั่ง document.createTextNode(text) เพื่อสร้างข้อความที่อยู่ใน HTML Tag นั้น แล้วจึงค่อยใช้คำสั่ง element.appendChild(element) เพื่อเพิ่ม Element เข้าไปในอีก Element หนึ่ง

          ถึงตรงนี้ ผมหวังว่าเพื่อน ๆ น่าจะพอเห็นภาพและเข้าใจ DOM กันมากขึ้นแล้ว ซึ่งหลักการในบทความนี้จะเป็นพื้นฐานที่ใช้ต่อยอดในการใช้งาน Framework ตัวอื่น ๆ ที่ช่วยให้เราสามารถพัฒนาเว็บง่ายขึ้น และเพื่อน ๆ ก็จะสามารถเพิ่มลูกเล่นหรือการเคลื่อนไหวเพื่อเพิ่มความน่าสนใจให้กับเว็บไซต์ได้


ไฟล์โค้ด: AiNoTsubasa's Github


วันศุกร์ที่ 26 ตุลาคม พ.ศ. 2561

วิธีใช้ Message objects เพิ่มลูกเล่นในการตอบกลับของ Line บน Dialogflow ด้วย Custom payload


          ในบทความก่อนหน้านี้ เราได้ทำการ integrate Line เข้ากับ Dialogflow ไปแล้ว ส่งผลให้ความสะดวกสบายในการคัดกรองข้อความเพิ่มขึ้น แต่กระนั้นก็ยังมีข้อจำกัดอยู่ นั่นคือ Responses ของ Dialogflow ไม่สามารถส่งสติกเกอร์ตอบกลับได้..... "อ้าว! แล้วถ้าเราอยากส่งสติกเกอร์ตอบกลับล่ะ จะทำยังไงดี" .....ไม่เป็นไรทาง Dialogflow มีเปิดช่องทาง Custom payload ให้เราได้ customize ข้อความตอบกลับด้วย JSON เพื่อปรับเปลี่ยนข้อความตอบกลับให้ตรงตาม API ของแต่ละ platform นั่นเอง

          เรามาลองใช้งาน Custom payload กัน ให้เข้าไปที่ https://dialogflow.com/ แล้วเลือก Agent ที่ได้สร้างไว้แล้วในบทความก่อนหน้านี้ จากนั้นก็คลิกไปที่ "Default Welcome Intent" แล้วเลื่อนลงมาในส่วนของ Response


          เจ้า Custom payload จะอยู่ในส่วนของ Responses ให้คลิกปุ่ม ADD RESPONSE แล้วเลือก Custom payload ก็จะมีพื้นที่ว่าง ๆ ขึ้นมา และจาก docs ของ LINE Messaging API รูปแบบของ JSON ที่ใช้ตอบกลับในประเภทข้อความเป็นดังนี้

{     
    "type": "text",     
    "text": "Hello, world" 
}
โดย "type" คือประเภทของการตอบกลับ ใน docs ของ LINE Messaging API จะแบ่งประเภทการตอบกลับออกเป็นประเภทต่าง ๆ ซึ่งจะพูดถึงภายหลังในส่วนของ Message objects และ "text" คือข้อความที่จะตอบกลับ ให้เราใส่ JSON ดังต่อไปนี้ลงไป

{
  "line": {
    "type": "text",
    "text": "สวัสดี นี่คือคำทักทายจาก Dialogflow (Custom payload)"
  }
}
จากการสังเกต จะเห็นว่า JSON ที่เราเพิ่มลงไปในช่อง Custom payload จะอยู่ข้างใน key "line" อีกที เป็นการบอกให้ Dialogflow รู้ว่านี่เป็น response สำหรับ LINE เท่านั้น เมื่อพิมพ์ JSON เสร็จ ให้คลิกปุ่ม SAVE แล้วลองทดสอบด้วยการพิมพ์ว่า "สวัสดี"


ผลลัพธ์ที่ได้ก็จะเป็นข้อความ "สวัสดี นี่คือคำทักทายจาก Dialogflow" และ "สวัสดี นี่คือคำทักทายจาก Dialogflow (Custom payload)" โดยข้อความแรกมาจาก Text response และข้อความที่สองมาจาก Custom payload .....ว่าแต่เมื่อกี้เพื่อน ๆ สังเกตเห็นอะไรกันหรือเปล่าเอ่ย ข้าง ๆ แถบ DEFAULT มีแถบ LINE อยู่


          เมื่อคลิกไปที่แถบ LINE แล้ว ก็จะมีตัวเลือกรูปแบบข้อความตอบกลับให้ 5 ตัวเลือก ได้แก่
  • Text response: เป็นการตอบกลับด้วยข้อความ ซึ่งจะมีช่องให้กรอกข้อความที่จะใช้ในการตอบกลับผู้ใช้ เหมือนกับในแถบ DEFAULT
  • Image: เป็นการตอบกลับด้วยรูป โดยจะมีช่องให้กรอก URL ของไฟล์รูป
  • Card: เป็นการตอบกลับด้วยการ์ด ซึ่งจะมีช่องให้กรอก URL ของไฟล์รูปที่ใช้เป็นรูปของการ์ด, ช่องให้กรอก Card Title, ช่องให้กรอก Card Subtitle, ช่องให้กรอกข้อความของปุ่ม (รายละเอียดอยู่ในส่วนของ Message objects ข้างล่าง)
  • Quick replies: เป็นการตอบกลับด้วยข้อความพร้อมทั้งปุ่ม (รายละเอียดอยู่ในส่วนของ Message objects ข้างล่าง)
  • Custom payload
ซึ่งเป็นการเพิ่มความง่ายในการตอบกลับข้อความของ LINE (มีตัวเลือก Custom payload ให้ด้วย) เพราะมีเป็น Template ให้อยู่แล้ว ทำให้เราไม่ต้องสร้าง JSON ขึ้นมาใหม่


LINE Message objects


          อย่างที่กล่าวไว้เมื่อตอนต้นว่า LINE Message object แบ่งประเภทการตอบกลับออกเป็นประเภทต่าง ๆ แต่ละประเภทก็จะมีจุดประสงค์การใช้ที่แตกต่างกันไป เช่น สติกเกอร์ หรือพิกัดตำแหน่ง เป็นต้น ซึ่งเราสามารถสร้าง Message object ได้จาก JSON รูปแบบต่าง ๆ ตามที่ทาง LINE กำหนดไว้ จากนั้นก็เอาไปใส่ไว้ใน Custom payload ภายใต้ key "line" ตามที่ได้ลองทำเมื่อสักครู่ .....ว่าแล้วเราก็มาดู Message objects แต่ละประเภทกันเลยดีกว่า ซึ่งเราสามารถแบ่งประเภทการตอบกลับออกได้เป็นดังนี้


Text message


          เริ่มจากพื้นฐานสุด ๆ ที่ Text message หรือก็คือการตอบกลับประเภทข้อความ ซึ่งเป็นการตอบกลับด้วยข้อความธรรมดา ๆ โดยจะมีหน้าตา JSON ดังต่อไปนี้

{
  "line": {
    "type": "text",
    "text": "นี่คือข้อความตอบกลับของ Text message"
  }
}
  • type: เป็นตัวกำหนดประเภทของการตอบกลับ ในที่นี้คือ text
  • text: ข้อความที่แสดง สูงสุด 2,000 ตัวอักษร


Image message


          Image message เป็นการตอบกลับด้วยรูป โดยจะใช้ JSON ที่มีรูปแบบดังต่อไปนี้

{
  "line": {
    "type": "image",
    "originalContentUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjq9glxf1PI7p2RxnPHpL3oUiFgXW8Xhd1saDLw2xLsF3vZunZpGPW-UwPpEffmGhTRWoZ64HHY1VeljF0uDuGn6Lve2ncoTSZY4sl2L0ibbDlpojgUa0llhv7WnrVLkXX6tln7NLg-FkZI/s1600/sao-full.jpg",
    "previewImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvXWIsA2ka3azOhyeXwvizWjyz0sTiYhBuxFuQ451rXCKyX5wbIl47CIOKz8jk8pcKOC8kiriAIHTFo-zKkcO8Ied4vysq9Na00dlWfBksUMFxDdwSsYTrkjXyG-JGp5GcjgLJLjzmFerk/s1600/sao-preview.jpg"
  }
}
  • type: เป็นตัวกำหนดประเภทของการตอบกลับ ในที่นี้คือ image
  • originalContentUrl: Url ของไฟล์รูปภาพต้นฉบับที่มีจำนวนตัวอักษรไม่เกิน 1,000 ตัวอักษร เป็น HTTPS และมีขนาดไม่เกิน 1 MB
  • previewImageUrl: Url ของไฟล์รูปภาพที่ใช้แสดงซึ่งมีจำนวนตัวอักษรไม่เกิน 1,000 ตัวอักษร เป็น HTTPS และมีขนาดไม่เกิน 1 MB


Sticker message


          Sticker message เป็นการตอบกลับด้วยสติกเกอร์อันน่ารักของ LINE แต่มีข้อจำกัดตรงที่สติกเกอร์ที่ใช้ในการตอบกลับนั้นต้องเป็นสติกเกอร์ที่สร้างโดย LINE เท่านั้น ไม่สามารถใช้สติกเกอร์ของ Creator คนอื่น ๆ ได้ โดยมีรูปแบบ JSON ดังต่อไปนี้

{
  "line": {
    "type": "sticker",
    "packageId": "1",
    "stickerId": "1"
  }
}
  • type: เป็นตัวกำหนดประเภทของการตอบกลับ ในที่นี้คือ sticker
  • packageId: รหัสแพ็กเกจของสติกเกอร์ที่ต้องการส่ง ดูได้จากที่นี่
  • stickerId: รหัสของสติกเกอร์ที่ต้องการส่ง ดูได้จากที่นี่


Video message


          Video message เป็นการตอบกลับด้วยคลิปวิดีโอสั้น ๆ มีรูปแบบ JSON ดังต่อไปนี้

{
  "line": {
    "type": "video",
    "originalContentUrl": "https://s3-ap-southeast-1.amazonaws.com/dezpax/b_files/video_example.mp4",
    "previewImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvXWIsA2ka3azOhyeXwvizWjyz0sTiYhBuxFuQ451rXCKyX5wbIl47CIOKz8jk8pcKOC8kiriAIHTFo-zKkcO8Ied4vysq9Na00dlWfBksUMFxDdwSsYTrkjXyG-JGp5GcjgLJLjzmFerk/s1600/sao-preview.jpg"
  }
}
  • type: เป็นตัวกำหนดประเภทของการตอบกลับ ในที่นี้คือ video
  • originalContentUrl: Url ของไฟล์วิดีโอที่มีจำนวนตัวอักษรไม่เกิน 1,000 ตัวอักษร เป็น HTTPS และมีขนาดไม่เกิน 10 MB
  • previewImageUrl: Url ของไฟล์รูปภาพที่ใช้แสดงซึ่งมีจำนวนตัวอักษรไม่เกิน 1,000 ตัวอักษร เป็น HTTPS และมีขนาดไม่เกิน 1 MB


Audio message


          Audio message เป็นการตอบกลับด้วยเสียงสั้น ๆ มีรูปแบบ JSON ดังต่อไปนี้

{
  "line": {
    "type": "audio",
    "originalContentUrl": "https://s3-ap-southeast-1.amazonaws.com/dezpax/b_files/audio_example.m4a",
    "duration": 10000
  }
}
  • type: เป็นตัวกำหนดประเภทของการตอบกลับ ในที่นี้คือ audio
  • originalContentUrl: Url ของไฟล์เสียงที่มีจำนวนตัวอักษรไม่เกิน 1,000 ตัวอักษร เป็น HTTPS และมีขนาดไม่เกิน 10 MB
  • duration: ความยาวของไฟล์เสียง หน่วยเป็น milliseconds ( 1 วินาที = 1,000 milliseconds )


Location message


          Location message เป็นการตอบกลับด้วยพิกัดตำแหน่งที่อยู่ แสดงชื่อสถานที่และที่อยู่ของสถานที่นั้น ๆ เมื่อคลิกก็จะเป็นการเปิดแผนที่ใน LINE ขึ้นมา พร้อมทั้งปักหมุดพิกัดสถานที่นั้นให้โดยอัตโนมัติ มีรูปแบบ JSON ดังต่อไปนี้

{
  "line": {
    "type": "location",
    "title": "เซ็นทรัลพลาซา ลาดพร้าว",
    "address": "1693 ถนนพหลโยธิน แขวงจตุจักร เขตจตุจักร กรุงเทพมหานคร 10900",
    "latitude": 13.8164458,
    "longitude": 100.558962
  }
}
  • type: เป็นตัวกำหนดประเภทของการตอบกลับ ในที่นี้คือ location
  • title: ชื่อสถานที่
  • address: ที่อยู่ของสถานที่นั้น ๆ
  • latitude: ตำแหน่งละติจูดของสถานที่นั้น ๆ
  • longitude: ตำแหน่งลองจิจูดของสถานที่นั้น ๆ


Imagemap message


          Imagemap message เป็นการตอบกลับด้วยรูป ซึ่งเราสามารถฝัง action ลงไปในรูปได้ แต่มีข้อเสียนิดหน่อยตรงที่เราจะต้องออกแบบรูปโดยแบ่งพื้นที่ของแต่ละ action เอาเอง จากรูปตัวอย่างจะเป็นการแบ่งรูปออกเป็นสองฝั่ง เมื่อแตะฝั่งซ้ายจะเปิดหน้าเว็บ ส่วนฝั่งขวาจะเป็นการส่งข้อความว่า "Hello" เข้าไปในห้องพูดคุย โดย JSON ที่ใช้มีรูปแบบดังต่อไปนี้

{
  "line": {
    "type": "imagemap",
    "baseUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjq9glxf1PI7p2RxnPHpL3oUiFgXW8Xhd1saDLw2xLsF3vZunZpGPW-UwPpEffmGhTRWoZ64HHY1VeljF0uDuGn6Lve2ncoTSZY4sl2L0ibbDlpojgUa0llhv7WnrVLkXX6tln7NLg-FkZI/s1600/",
    "altText": "This is an imagemap",
    "baseSize": {
      "height": 1040,
      "width": 1040
    },
    "actions": [
      {
        "type": "uri",
        "linkUri": "https://www.google.com/",
        "area": {
          "x": 0,
          "y": 0,
          "width": 512,
          "height": 731
        }
      },
      {
        "type": "message",
        "text": "Hello",
        "area": {
          "x": 512,
          "y": 0,
          "width": 512,
          "height": 731
        }
      }
    ]
  }
}
  • type: เป็นตัวกำหนดประเภทของการตอบกลับ ในที่นี้คือ imagemap
  • baseUrl: Url ของไฟล์รูปภาพ ควรทำให้สามารถเข้าถึงภาพ 5 ขนาดได้ (240px, 300px, 460px, 700px, 1040px) โดยใช้รูปแบบ baseUrl/{image width} (เช่น https://1.bp.blogspot.com/-U90M8DyKu7Q/W9EtONMCf6I/AAAAAAAAW_4/7L_jB_Rg9oweu2HKhULNdu9WNefw9zf9wCLcBGAs/s1600/ เป็นต้น) ซึ่ง LINE จะเลือกโหลดขนาดภาพให้เหมาะสมกับอุปกรณ์เอง
  • altText: ข้อความของรูปภาพ จำนวนตัวอักษรสูงสุด 400 ตัวอักษร
  • baseSize: ขนาดของรูปภาพ
    • width: ความกว้างของรูปภาพ ให้เซ็ตเป็น 1040
    • height: ความสูงของรูปภาพ ให้เซ็ตเป็น 1040
  • actions: อาร์เรย์ actions ของ Imagemap โดยเราสามารถใส่ action ได้มากสุด 50 actions
    • type: ประเภทของ action ได้แก่ uri หรือ message ถ้ากำหนดเป็น uri นั่นหมายถึง เมื่อกดที่ action นี้แล้วก็จะทำการเปิดเว็บขึ้นมา และถ้ากำหนดเป็น message นั่นหมายถึง เมื่อกดที่ action นี้แล้วจะทำการส่งข้อความกลับมาให้ bot
    • label: ป้ายกำกับของ action
    • linkUri: Url ของเว็บ ใช้ในกรณีที่กำหนด type = uri
    • text: ข้อความที่จะทำการส่งกลับมาให้ bot ใช้ในกรณีที่กำหนด type = message
    • area: พื้นที่สำหรับ action
      • x: ตำแหน่งแนวนอนนับจากมุมบนซ้ายของพื้นที่
      • y: ตำแหน่งแนวตั้งนับจากมุมบนซ้ายของพื้นที่
      • width: ความกว้างของพื้นที่ action
      • height: ความสูงของพื้นที่ action


Template messages

          Template messages คือ ข้อความที่มีรูปแบบตามที่ LINE กำหนดเอาไว้แล้ว ซึ่งเราสามารถปรับแต่งเพื่อให้ผู้ใช้สามารถโต้ตอบกับ bot ได้ง่ายขึ้น ซึ่ง LINE แบ่งประเภทของเทมเพลตออกเป็นดังนี้


          Buttons template เทมเพลตที่ประกอบไปด้วยรูปภาพ ชื่อ ข้อความ และปุ่มการดำเนินการหลายปุ่ม โดยใช้ JSON ที่มีรูปแบบดังต่อไปนี้

{
  "line": {
    "type": "template",
    "altText": "This is a buttons template",
    "template": {
        "type": "buttons",
        "thumbnailImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjq9glxf1PI7p2RxnPHpL3oUiFgXW8Xhd1saDLw2xLsF3vZunZpGPW-UwPpEffmGhTRWoZ64HHY1VeljF0uDuGn6Lve2ncoTSZY4sl2L0ibbDlpojgUa0llhv7WnrVLkXX6tln7NLg-FkZI/s1600/sao-full.jpg",
        "imageAspectRatio": "rectangle",
        "imageSize": "cover",
        "imageBackgroundColor": "#FFFFFF",
        "title": "แผ่นเกม Sword Art Online",
        "text": "กรุณาเลือก",
        "defaultAction": {
            "type": "uri",
            "label": "View detail",
            "uri": "https://www.google.com"
        },
        "actions": [
            {
              "type": "postback",
              "label": "สั่งซื้อ",
              "data": "action=buy&itemid=123"
            },
            {
              "type": "postback",
              "label": "เพิ่มลงรถเข็น",
              "data": "action=add&itemid=123"
            },
            {
              "type": "uri",
              "label": "อ่านรายละเอียด",
              "uri": "https://www.google.com"
            }
        ]
    }
  }
}
  • type: เป็นตัวกำหนดประเภทของการตอบกลับ ในที่นี้คือ template
  • altText: ข้อความกำกับของ template จำนวนตัวอักษรสูงสุด 400 ตัวอักษร
  • template:
    • type: ประเภทของ template ในที่นี้คือ buttons
    • thumbnailImageUrl: Url ของไฟล์รูปภาพที่มีจำนวนตัวอักษรไม่เกิน 1,000 ตัวอักษร เป็น HTTPS และมีขนาดไม่เกิน 1 MB
    • imageAspectRatioอัตราส่วนของภาพ มีค่าเป็น rectangle (สี่เหลี่ยมผืนผ้า) หรือ square (สี่เหลี่ยมจตุรัส)
    • imageSize: ขนาดของรูปภาพ มีค่าเป็น cover หรือ contain
    • imageBackgroundColor: สีพื้นหลังของรูปภาพ
    • title: หัวเรื่อง จำนวนตัวอักษรสูงสุด 40 ตัวอักษร
    • text: ข้อความ กรณีที่ไม่มีหัวเรื่องหรือรูปภาพ สามารถใส่ได้สูงสุด 160 ตัวอักษร แต่ถ้ามีหัวเรื่องหรือรูปภาพจะใส่ได้สูงสุด 60 ตัวอักษร
    • defaultAction: action ที่จะทำงานเมื่อรูปภาพถูกกด (อ่านรายละเอียดของ action เพิ่มเติมในส่วนของ Action objects)
    • actions: อาร์เรย์ของ action ที่จะทำงานเมื่อถูกกด ใส่ได้มากสุด 4 actions  (อ่านรายละเอียดของ action เพิ่มเติมในส่วนของ Action objects)
....................


          Confirm template เทมเพลตที่ประกอบไปปุ่มการดำเนินการสองปุ่ม โดยใช้ JSON ที่มีรูปแบบดังต่อไปนี้

{
  "line": {
    "type": "template",
    "altText": "this is a confirm template",
    "template": {
      "type": "confirm",
      "text": "ต้องการที่จะลบข้อมูลหรือไม่?",
      "actions": [
        {
          "type": "message",
          "label": "ใช่",
          "text": "ใช่"
        },
        {
          "type": "message",
          "label": "ไม่",
          "text": "ไม่"
        }
      ]
    }
  }
}
  • type: เป็นตัวกำหนดประเภทของการตอบกลับ ในที่นี้คือ template
  • altText: ข้อความกำกับของ template จำนวนตัวอักษรสูงสุด 400 ตัวอักษร
  • template:
    • type: ประเภทของ template ในที่นี้คือ confirm
    • text: ข้อความ จำนวนตัวอักษรสูงสุด 240 ตัวอักษร
    • actions: อาร์เรย์ของ action ที่จะทำงานเมื่อถูกกด ใส่ได้มากสุด 2 actions (อ่านรายละเอียดของ action เพิ่มเติมในส่วนของ Action objects)
....................


          Carousel template เทมเพลตที่มีหลายคอลัมน์ ซึ่งแต่ละคอลัมน์จะประกอบไปด้วยรูปภาพ ชื่อ ข้อความ และปุ่มการดำเนินการหลายปุ่ม หรือพูดง่าย ๆ ก็คือเทมเพลตที่มี Buttons template หลาย ๆ ตัว มีการแสดงผลตามลำดับโดยการเลื่อนไปตามแนวนอน โดยใช้ JSON ที่มีรูปแบบดังต่อไปนี้

{
  "line": {
    "type": "template",
    "altText": "this is a carousel template",
    "template": {
      "type": "carousel",
      "imageAspectRatio": "rectangle",
      "imageSize": "cover",
      "columns": [
        {
          "thumbnailImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjq9glxf1PI7p2RxnPHpL3oUiFgXW8Xhd1saDLw2xLsF3vZunZpGPW-UwPpEffmGhTRWoZ64HHY1VeljF0uDuGn6Lve2ncoTSZY4sl2L0ibbDlpojgUa0llhv7WnrVLkXX6tln7NLg-FkZI/s1600/sao-full.jpg",
          "imageBackgroundColor": "#FFFFFF",
          "title": "แผ่นเกม Sword Art Online",
          "text": "แผ่นเกม Sword Art Online",
          "defaultAction": {
            "type": "uri",
            "label": "รายละเอียด",
            "uri": "https://www.google.com/"
          },
          "actions": [
            {
              "type": "postback",
              "label": "สั่งซื้อ",
              "data": "action=buy&itemid=111"
            },
            {
              "type": "postback",
              "label": "เพิ่มลงรถเข็น",
              "data": "action=add&itemid=111"
            },
            {
              "type": "uri",
              "label": "รายละเอียด",
              "uri": "https://www.google.com/"
            }
          ]
        },
        {
          "thumbnailImageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibVSKKr0uN4_y_UPxK7jk3cP6xB9ERt1OZ_efvW7t40lhiV8xsCakvoYcqy1_3aQMNQHy3f1AX7p9KqBMiWHEVK60QJc3nDFe1FkJP8qPSvImtP7Vq0JSAEt_6HZ3a2jVC1ZRLdYUufulM/s1600/sao-os.jpg",
          "imageBackgroundColor": "#FFFFFF",
          "title": "Sword Art Online Ordinal Scale",
          "text": "Sword Art Online Ordinal Scale",
          "defaultAction": {
            "type": "uri",
            "label": "รายละเอียด",
            "uri": "https://www.google.com/"
          },
          "actions": [
            {
              "type": "postback",
              "label": "สั่งซื้อ",
              "data": "action=buy&itemid=111"
            },
            {
              "type": "postback",
              "label": "เพิ่มลงรถเข็น",
              "data": "action=add&itemid=111"
            },
            {
              "type": "uri",
              "label": "รายละเอียด",
              "uri": "https://www.google.com/"
            }
          ]
        }
      ]
    }
  }
}
  • type: เป็นตัวกำหนดประเภทของการตอบกลับ ในที่นี้คือ template
  • altText: ข้อความกำกับของ template จำนวนตัวอักษรสูงสุด 400 ตัวอักษร
  • template:
    • type: ประเภทของ template ในที่นี้คือ carousel
    • imageAspectRatioอัตราส่วนของภาพ มีค่าเป็น rectangle (สี่เหลี่ยมผืนผ้า) หรือ square (สี่เหลี่ยมจตุรัส)
    • imageSize: ขนาดของรูปภาพ มีค่าเป็น cover หรือ contain
    • columns: อาร์เรย์ของ column ใส่ได้มากสุด 10 column
      • thumbnailImageUrl: Url ของไฟล์รูปภาพที่มีจำนวนตัวอักษรไม่เกิน 1,000 ตัวอักษร เป็น HTTPS และมีขนาดไม่เกิน 1 MB
      • imageBackgroundColor: สีพื้นหลังของรูปภาพ
      • title: หัวเรื่อง จำนวนตัวอักษรสูงสุด 40 ตัวอักษร
      • text: ข้อความ กรณีที่ไม่มีหัวเรื่องหรือรูปภาพ สามารถใส่ได้สูงสุด 160 ตัวอักษร แต่ถ้ามีหัวเรื่องหรือรูปภาพจะใส่ได้สูงสุด 60 ตัวอักษร
      • defaultAction: action ที่จะทำงานเมื่อรูปภาพถูกกด (อ่านรายละเอียดของ action เพิ่มเติมในส่วนของ Action objects)
      • actions: อาร์เรย์ของ action ที่จะทำงานเมื่อถูกกด ใส่ได้มากสุด 4 actions (อ่านรายละเอียดของ action เพิ่มเติมในส่วนของ Action objects)
....................


          Image carousel template เทมเพลตที่มีรูปภาพหลายรูป มีการแสดงผลตามลำดับโดยการเลื่อนไปตามแนวนอน โดยใช้ JSON ที่มีรูปแบบดังต่อไปนี้

{
  "line": {
    "type": "template",
    "altText": "this is a image carousel template",
    "template": {
      "type": "image_carousel",
      "columns": [
        {
          "imageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjq9glxf1PI7p2RxnPHpL3oUiFgXW8Xhd1saDLw2xLsF3vZunZpGPW-UwPpEffmGhTRWoZ64HHY1VeljF0uDuGn6Lve2ncoTSZY4sl2L0ibbDlpojgUa0llhv7WnrVLkXX6tln7NLg-FkZI/s1600/sao-full.jpg",
          "action": {
            "type": "postback",
            "label": "สั่งซื้อ",
            "data": "action=buy&itemid=111"
          }
        },
        {
          "imageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibVSKKr0uN4_y_UPxK7jk3cP6xB9ERt1OZ_efvW7t40lhiV8xsCakvoYcqy1_3aQMNQHy3f1AX7p9KqBMiWHEVK60QJc3nDFe1FkJP8qPSvImtP7Vq0JSAEt_6HZ3a2jVC1ZRLdYUufulM/s1600/sao-os.jpg",
          "action": {
            "type": "message",
            "label": "ตกลง",
            "text": "ตกลง"
          }
        },
        {
          "imageUrl": "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDnf6GMcW7s9i9uBie9tQZBaepFr6hTOVozf97hmq1w-XC3gEfsZn-EILsLKjpBihd-scR3kdXGJY6iXa2LnTFtXmz5ajoQP_9YtxC1au4aFGmyYGir8b0ZV4B1laG1ZBndVd74BOoDrcf/s1600/saohr-cover-image.jpg",
          "action": {
            "type": "uri",
            "label": "อ่านรายละเอียด",
            "uri": "https://www.google.com/"
          }
        }
      ]
    }
  }
}
  • type: เป็นตัวกำหนดประเภทของการตอบกลับ ในที่นี้คือ template
  • altText: ข้อความกำกับของ template จำนวนตัวอักษรสูงสุด 400 ตัวอักษร
  • template:
    • type: ประเภทของ template ในที่นี้คือ image_carousel
    • columns: อาร์เรย์ของ column ใส่ได้มากสุด 10 column
      • imageUrl: Url ของไฟล์รูปภาพที่มีจำนวนตัวอักษรไม่เกิน 1,000 ตัวอักษร เป็น HTTPS และมีขนาดไม่เกิน 1 MB
      • actions: action ที่จะทำงานเมื่อรูปภาพถูกกด  (อ่านรายละเอียดของ action เพิ่มเติมในส่วนของ Action objects)


Flex message


          Flex Message คือ ข้อความที่เปิดให้นักพัฒนาสามารถออกแบบหน้าตาของข้อความได้อย่างอิสระ แตกต่างจาก Template messages ตรงที่ Template messages จะมีรูปแบบตายตัว ถึงจะมีส่วนที่เราสามารถปรับแต่งได้ แต่โดยรวมก็ยังอยู่ในรูปแบบตามที่ LINE กำหนด เช่น ตำแหน่งของรูปภาพจะต้องอยู่ข้างบนสุด ตามด้วยข้อความ จากนั้นจึงจะเป็นปุ่มที่อยู่อันดับล่างสุด เป็นต้น ซึ่งเราไม่สามารถแก้ไขตำแหน่งเหล่านั้นได้ แต่ Flex Message เปิดให้เราสามารถออกแบบข้อความได้ตามใจชอบ สามารถกำหนดให้ตำแหน่งของรูปอยู่ข้างล่างข้อความได้

          เนื่องจากความอิสระของ Flex Message ทำให้ JSON ค่อนข้างมีความซับซ้อน ซึ่งผมขอแนะนำให้เพื่อน ๆ ไปอ่านรายละเอียด รวมถึงวิธีการสร้าง JSON ของ Flex Message เพิ่มเติมที่บทความ "ฉีกกฎการแสดงผลข้อความแบบเดิมๆใน LINE Messaging API ด้วย Flex Messag" ของคุณ Jirawatee ครับ


Action objects

          ประเภทของ action ที่ bot สามารถใช้งานได้เมื่อผู้ใช้แตะปุ่มหรือรูปภาพในข้อความ เช่น เปิดเว็บ เปิดกล้อง หรือแสดงตำแหน่งบนแผนที่ เป็นต้น โดยแบ่งประเภทของ action ออกได้เป็น


          Postback action เมื่อตัวควบคุมที่เกี่ยวข้องกับ Postback action ถูกแตะก็จะทำการส่งค่ากลับไปยัง webhook ด้วยค่าที่ระบุใน data โดย JSON ของการกระทำนี้จะมีหน้าตาประมาณนี้

{  
   "type":"postback",
   "label":"สั่งซื้อ",
   "data":"action=buy&itemid=123",
   "displayText":"สั่งซื้อ"
}
  • type: เป็นตัวกำหนดประเภทของการตอบกลับ ในที่นี้คือ postback
  • label: ข้อความกำกับของ action
  • data: ค่าที่จะส่งไปยัง webhook จำนวนตัวอักษรไม่เกิน 300 ตัวอักษร
  • displayText: ข้อความที่จะส่งเข้าไปในห้องพูดคุยในฐานะข้อความจากผู้ใช้ จำนวนตัวอักษรไม่เกิน 300 ตัวอักษร
....................

          Message action เมื่อตัวควบคุมที่เกี่ยวข้องกับ Message action ถูกแตะ ข้อความที่ระบุอยู่ใน text จะถูกส่งเข้าไปในห้องพูดคุยในฐานะข้อความจากผู้ใช้ โดย JSON ของการกระทำนี้จะมีหน้าตาเป็นแบบนี้

{  
   "type":"message",
   "label":"สั่งซื้อ",
   "text":"สั่งซื้อ"
}
  • type: เป็นตัวกำหนดประเภทของการตอบกลับ ในที่นี้คือ message
  • label: ข้อความกำกับของ action
  • text: ข้อความที่จะส่งเข้าไปในห้องพูดคุยในฐานะข้อความจากผู้ใช้ จำนวนตัวอักษรไม่เกิน 300 ตัวอักษร
....................

          URI action เมื่อตัวควบคุมที่เกี่ยวข้องกับ URI action ถูกแตะ URI ที่ระบุอยู่ใน uri จะถูกเรียกใช้งาน โดย JSON ของการกระทำนี้จะมีหน้าตาเป็นแบบนี้

{  
   "type":"uri",
   "label":"Google",
   "uri":"https://www.google.com/"
}
  • type: เป็นตัวกำหนดประเภทของการตอบกลับ ในที่นี้คือ uri
  • label: ข้อความกำกับของ action
  • uri: URI ที่จะทำงาน รองรับ http, https, line, และ tel จำนวนตัวอักษรไม่เกิน 1,000 ตัวอักษร
....................

          Datetime picker action เมื่อตัวควบคุมที่เกี่ยวข้องกับ Datetime picker action ถูกแตะ ก็จะแสดงตัวเลือกวัน-เวลาขึ้นมาให้ผู้ใช้เลือก เมื่อเลือกเสร็จแล้วก็จะส่งค่าไปที่ webhook โดย JSON ของการกระทำนี้จะมีหน้าตาเป็นแบบนี้

{  
   "type":"datetimepicker",
   "label":"เลือกวันและเวลา",
   "data":"value=12345",
   "mode":"datetime",
   "initial":"2018-10-26t00:00",
   "max":"2019-12-31t23:59",
   "min":"2017-01-01t00:00"
}
  • type: เป็นตัวกำหนดประเภทของการตอบกลับ ในที่นี้คือ datetimepicker
  • label: ข้อความกำกับของ action
  • data: ค่าที่จะส่งไปยัง webhook จำนวนตัวอักษรไม่เกิน 300 ตัวอักษร
  • mode: โหมดสำหรับตัวเลือก มีทั้งหมด 3 โหมด ได้แก่ date สำหรับเลือกวันที่, time สำหรับเลือกเวลา และ datetime สำหรับเลือกทั้งวันที่และเวลา
  • initial: ค่าเริ่มต้น
  • max: ค่ามากสุดที่สามารถเลือกได้
  • min: ค่าน้อยสุดที่สามารถเลือกได้
ค่าของวันที่จะอยู่ในรูปแบบ "ปี-เดือน-วัน" เช่น 2018-10-26 เป็นต้น
ค่าของเวลาจะอยู่ในรูปแบบ "ชั่วโมง:นาที" เช่น 13:00 เป็นต้น
ค่าของวันเวลาจะอยู่ในรูปแบบ "ปี-เดือน-วันTชั่วโมง:นาที" หรือ "ปี-เดือน-วันtชั่วโมง:นาที" เช่น 2018-10-26T13:00 หรือ 2018-10-26t13:00 เป็นต้น

....................

          Camera action เมื่อตัวควบคุมที่เกี่ยวข้องกับ Camera action ถูกแตะก็จะทำการเปิดหน้าจอกล้องถ่ายรูปของ LINE ขึ้นมา โดย JSON ของการกระทำนี้จะมีหน้าตาประมาณนี้

{  
   "type":"camera",
   "label":"กล้องถ่ายรูป",
}
  • type: เป็นตัวกำหนดประเภทของการตอบกลับ ในที่นี้คือ camera
  • label: ข้อความกำกับของ action จำนวนตัวอักษรไม่เกิน 20 ตัวอักษร
....................

          Camera roll action เมื่อตัวควบคุมที่เกี่ยวข้องกับ Camera roll action ถูกแตะก็จะทำการเปิดหน้าจอเลือกรูปภาพของ LINE ขึ้นมา โดย JSON ของการกระทำนี้จะมีหน้าตาประมาณนี้

{  
   "type":"cameraRoll",
   "label":"เลือกรูป",
}
  • type: เป็นตัวกำหนดประเภทของการตอบกลับ ในที่นี้คือ cameraRoll
  • label: ข้อความกำกับของ action จำนวนตัวอักษรไม่เกิน 20 ตัวอักษร
....................

          Location action เมื่อตัวควบคุมที่เกี่ยวข้องกับ Location action ถูกแตะก็จะทำการเปิดหน้าจอเลือกรูปภาพของ LINE ขึ้นมา โดย JSON ของการกระทำนี้จะมีหน้าตาประมาณนี้

{  
   "type":"location",
   "label":"แผนที่",
}
  • type: เป็นตัวกำหนดประเภทของการตอบกลับ ในที่นี้คือ location
  • label: ข้อความกำกับของ action จำนวนตัวอักษรไม่เกิน 20 ตัวอักษร


          บทความนี้เป็นการแนะนำวิธีใช้ความสามารถในการตอบกลับอันหลากหลายของ LINE bot บน Dialogflow โดยการใช้ Custom payload ช่วยในการดัดแปลงข้อความตอบกลับ ซึ่งจะทำให้ bot ของเรามีความน่าสนใจมากยิ่งขึ้น และถ้าอยากทราบรายละเอียดเชิงลึก เพื่อน ๆ สามารถเข้าไปอ่านเพิ่มเติมได้ที่ docs ของ LINE Messaging API .....ถึงตรงนี้ ผมก็คงต้องขอตัวลาไปก่อน ไว้พบกันใหม่ในบทความถัดไป บ๊ายบาย


วันอาทิตย์ที่ 14 ตุลาคม พ.ศ. 2561

ก้าวแรกกับการพัฒนาแอปพลิเคชันบนสมาร์ทโฟนด้วย Flutter


          Flutter คือ SDK ที่ใช้ภาษา Dart ในการพัฒนาแอปพลิเคชันบนสมาร์ตโฟนที่สามารถทำงานข้ามแพลตฟอร์มบนระบบปฏิบัติการ Android และระบบปฏิบัติการ iOS ถูกพัฒนาโดย Google และแน่นอนว่าเป็น open source ที่สามารถนำไปใช้งานได้โดยไม่เสียค่าใช้จ่าย นอกจากนี้ Flutter จะช่วยทำให้ UI และประสิทธิภาพของแอปพลิเคชันที่ถูกสร้างขึ้นมามีความคล้ายกับ Native แอปพลิเคชัน แถม Google ก็ยังโฆษณาอีกว่า Flutter สามารถทำงานร่วมกับภาษา Native (ภาษา Java หรือ Kotlin ของ Android และภาษา ObjectiveC หรือ Swift ของ iOS) ได้อีกด้วย .....จะจริงหรือมั่ว ชัวร์หรือไม่ เราก็มาลองเล่นกันเลยดีกว่า

ติดตั้ง Flutter SDK


          เริ่มติดตั้ง Flutter SDK โดยการเข้าไปที่ Flutter Install


          ให้เลือกติดตั้งตามระบบปฏิบัติการของเพื่อน ๆ ในที่นี้ผมขอติดตั้งบน Windows ละกัน ซึ่งการจะเรียกใช้งาน Flutter นั้น ในเครื่องจำเป็นต้องมี PowerShell เวอร์ชัน 5.0ขึ้นไปและ Git for Window ก่อน ถ้าเป็นคอมพิวเตอร์ที่มีการอัปเดต Window อยู่เสมอ ๆ ก็คงไม่ต้องติดตั้ง PowerShell เพิ่ม ส่วนเพื่อน ๆ ที่ยังไม่ได้ติดตั้ง Git ก็สามารถย้อนกลับไปอ่านบทความ "วิธีติดตั้ง Git ก่อนเริ่มใช้งาน" ก่อนได้เลย


          อันดับถัดไปให้ดาวน์โหลด Flutter SDK จาก Flutter SDK โดยเลือกเวอร์ชันล่าสุด (ณ ปัจจุบันที่เขียนบทความนี้คือ Beta v0.6.0)

          หลังจากโหลดเสร็จ ก็จะได้ไฟล์ .zip มาตัวนึง ให้ทำการ Extract ไฟล์ออกมา แล้ว copy โฟล์เดอร์ flutter ไปเก็บไว้ในพาทที่ต้องการ (ในที่นี้ผมเอาไปไว้ใน E:\ ก็จะกลายเป็น E:\flutter\ ) ยกเว้นพาท C:\Program Files\ (เพราะในพาทนี้จะต้องการสิทธิ์ในการเขียนข้อมูล) จากนั้นเข้าไปยังโฟล์เดอร์ flutter และดับเบิ้ลคลิกไฟล์ flutter_console.bat ก็จะได้ Flutter Console ขึ้นมา เพียงแค่นี้เราก็พร้อมใช้งานคำสั่งของ Flutter แล้ว

          ขั้นตอนถัดไป ให้ติดตั้ง Android Studio โดยเพื่อน ๆ สามารถดูขั้นตอนติดตั้งได้ที่ "เตรียมความพร้อมก่อนจะพัฒนาโปรแกรมบน Android" จากนั้นเพื่อน ๆ จะต้องอัปเดต Android SDK (วิธีอัปเดตก็อยู่ล่าง ๆ ของบทความติดตั้งแล้ว) ต่อไปเราก็จะมาติดตั้ง Flutter Plugin ใน Android Studio เพื่อเพิ่มความสามารถในการพัฒนาแอปพลิเคชัน


          เปิด Android Studio ขึ้นมาแล้วคลิกที่ Configure > Plugins


          คลิก Browse repositoriess... จากนั้นกรอกคำว่า flutter ในช่องค้นหา แล้วคลิก Install ระบบจะถามโดยอัตโนมัติว่า "จะติดตั้ง plugin ของภาษา Dart ด้วยหรือไม่?" ก็ให้คลิก Yes ติดตั้งไปด้วยเลย รอจนติดตั้ง plugin เสร็จ Android Studio ก็จะ restart ตัวเอง


          กลับมาที่ Flutter Console ให้พิมพ์คำสั่ง  flutter doctor  เพื่อให้คุณหมอตรวจสอบว่าคอมพิวเตอร์ของเราพร้อมจะใช้งาน Flutter หรือยัง (ถ้าต้องการรายละเอียดเพิ่มเติม ให้พิมพ์  flutter doctor -v  แทน) โดยสิ่งที่คุณหมอจะตรวจสอบ ได้แก่
  • Flutter SDK และ Dart SDK
  • Android Toolchain เครื่องมือที่จำเป็นในการพัฒนา Android แอปพลิเคชัน
  • iOS Toolchain เครื่องมือที่จำเป็นในการพัฒนา iPhone แอปพลิเคชัน
  • โปรแกรมสำหรับเขียนโค้ดรวมถึง plugin ที่จะต้องใช้ (เช่น Android Studio IDE, IntelliJ IDEA, VS Code)
  • การเชื่อมต่อกับสมาร์ตโฟนหรือ Emulator

          ผลจากการรันในเครื่องของผมจะเห็นได้ว่าไม่มี iOS Toolchain อยู่ (ซึ่งก็แน่อยู่แล้วเพราะผมไม่ได้ใช้ Macbook ฮา) และมี [!] อยู่ในบรรทัดสุดท้าย เนื่องจากยังไม่ได้เชื่อมต่อสมาร์ตโฟนเข้ากับคอมพิวเตอร์ (หรือเปิด Emulator) อ่านวิธีเชื่อมต่อสมาร์ตโฟน หรือวิธีเปิด Emulator


          หลังจากเชื่อมต่อสมาร์ตโฟนเข้ากับคอมพิวเตอร์หรือเปิด Emulator แล้ว ให้พิมพ์คำสั่งคุณหมอ  flutter doctor -v  เหมือนเดิม ก็จะพบว่าเครื่องหมาย [!] หายไปแล้ว ฟู่ กว่าจะมาถึงขั้นนี้ได้ก็ลำบากพอสมควรเหมือนกันนะเนี่ย แต่ไม่เป็นไร สู้ต่อไป! ทาเคชิ! .....เปิดโปรแกรม Android Studio มาต่อกันเลย

สร้างโปรเจค Flutter



          หลังจากติดตั้ง Flutter plugin แล้วจะมีเมนูใหม่โผล่ขึ้นมา ให้คลิก Start a new Flutter project ได้เลย


          เลือกที่ Flutter Application แล้วคลิกปุ่ม Next


          ให้กรอกข้อมูลของแอปพลิเคชัน จากนั้นคลิกปุ่ม Next
  • Project Name: ชื่อโปรเจค ใช้เป็นอักษรตัวเล็กหรือ _ คั่นระหว่างคำ ไม่สามารถใช้อักษรพิเศษหรือช่องว่างได้
  • Fultter SDK path: ตำแหน่งที่เก็บ SDK ของ Flutter ตามที่ได้ติดตั้งในบทความนี้ (ของผมเป็น E:\flutter)
  • Project location: ตำแหน่งที่เก็บไฟล์โปรเจค
  • Description: คำอธิบายโปรเจค

          ถัดไปให้กรอก Company domain หรือ URL ของเว็บไซต์ เพื่อจะใช้ในการสร้าง Package name *ชื่อ Package name คือชื่อรหัสประจำตัวของแอปฯ ซึ่งจะต้องห้ามซ้ำกับของแอปพลิเคชันอื่น จากนั้นก็คลิก Finish เป็นอันเสร็จสิ้นการสร้างโปรเจค


          คลิกปุ่มเล่นตรงแถบข้างบนโค้ด (หรือกด Shift + F10) เพื่อสั่งรันโปรเจค


          เท่านี้ก็ได้โปรแกรมง่าย ๆ ของ Flutter แล้ว สำหรับก้าวแรกในการพัฒนาแอปพลิเคชันแบบ Cross Platform ก็ขอจบลงเพียงเท่านี้ เอาไว้พบกันใหม่กับบทความถัดไป บ๊ายบาย ✋


ของแถม!!!

วิธีใช้งาน Flutter บน CMD

          ปกติคำสั่งของ Flutter จะถูกใช้งานบน Flutter Console เท่านั้น แต่ถ้าเพื่อน ๆ ต้องการใช้งานคำสั่งของ Flutter ใน Windows command prompt ก็ให้เพิ่ม Flutter ลงใน PATH environment variable ของคอมพิวเตอร์ก่อน


          คลิกขวาที่ My Computer แล้วเลือก Properties


          เลือก Advanced system settings


          คลิก Environment Variables…


          เลือกที่ Path ในส่วนของ System variable แล้วคลิก Edit...


          จะปรากฏหน้าต่างแบบนี้ขึ้นมา ให้คลิกที่ปุ่ม New


          ให้พิมพ์ตำแหน่งที่เก็บ bin ของ Flutter ในที่นี้ผมใส่เป็น E:\flutter\bin แล้วคลิกปุ่ม OK


          คลิก OK


          คลิก OK เท่านี้เราก็สามารถใช้คำสั่งของ Flutter ใน Windows command prompt ได้แล้ว ลองพิมพ์คำสั่ง  flutter doctor  ก็จะได้ผลลัพธ์เหมือนกับที่พิมพ์ใน Flutter Console


          เรียบร้อยจ้า!!

วิธีอัพเกรด Flutter

          ปัจจุบันนี้ Flutter อยู่ในช่วงตั้งไข่ ยังไม่ใช่เวอร์ชันเต็ม ซึ่งทีมพัฒนา Flutter ของ Google ก็พยายามพัฒนาความสามารถของ Flutter ให้สูงขึ้นเรื่อย ๆ โดยเราสามารถเช็คเวอร์ชันของ Flutter ที่เราใช้งานอยู่ได้ด้วยคำสั่ง

flutter --version
และเมื่อทีมพัฒนา Flutter ปล่อยเวอร์ชันใหม่ของ Flutter ออกมา เราก็จำเป็นต้องอัพเกรด Flutter ให้เป็นเวอร์ชันล่าสุดเพื่อเพิ่มความสามารถให้สูงขึ้นด้วยคำสั่ง

flutter upgrade
อีกหนึ่งเรื่องสำคัญที่เราจำเป็นต้องอัพเกรด Flutter ให้เป็นเวอร์ชันล่าสุด นั่นคือ โค้ดคำสั่งบางโค้ดอาจจะถูกยกเลิกออกไป ซึ่งส่งผลให้แอปพลิเคชันที่เราพัฒนาขึ้นมาเกิด Error จนไม่สามารถใช้ได้ เพราะงั้นเพื่อน ๆ ก็อย่าลืมอัพเกรด Flutter กันด้วยนะ