อะไรคือ Widget, State, BuildContext และ InteritedWidget ใน Flutter

บทความนี้จะเล่าให้ฟังว่า อะไรคือ Widget, State, BuildContext และ InheritedWidget ใน Flutter หลักๆ เราจะพูดถึง InheritedWidget กันเพราะเป็นสิ่งที่สำคัญ แต่มีเอกสารเขียนถึงน้อยมาก

เกริ่นนำ

Widget, State และ BuildContext นั้นล้วนเป็นสิ่งที่สำคัญที่นักพัฒนา Flutter จำเป็นต้องเข้าใจ ถึงใน Doc มีการอธิบายอยู่ แต่ยังไม่ชัดเจนจึงนำมาอธิบายเพิ่มเติม

ซึ่งในบทความจะเขียนถึงหัวข้อต่อไปนี้

  • ความแตกต่างระหว่าง Stateful และ Stateless widgets
  • BuildContext คือ
  • State คืออะไรและใช้ยังไง
  • ความสัมพันธ์ระหว่าง BuildContext และ State
  • InheritedWidget และวิธีการเข้าถึงข้อมูลใน Widget tree
  • Rebuild คืออะไร

Concepts

Widget

ใน Flutter เกือบทุกอย่างเป็น Widget ให้คุณลองคิดว่า Widget คือ ส่วนต่างๆที่ประกอบกันเป็นส่วนที่แสดงผลบนหน้าจอ เช่น ปุ่ม ข้อความ เมื่อเราต้องการสร้างอะไรที่เกี่ยงข้องกับการแสดงผลบนหน้าจอ ถ้าใครเคยพัฒนา Android หรือ iOS มามันก็คือ View , UIView นั้นแหละจ้า

Widget tree

มันคือการเอา Widget มาซ้อนกันแบบแผนผังต้นไม้ (Tree structures)

Widget ที่บรรจุ Widget อื่นๆเรียกว่า parent Widget (Widget container)

Widget ที่อยู่ใน Widget อื่นๆเรียกว่า children Widgets

เราลองมาดูกันที่โค้ดตัวอย่าง ที่จะเห็นได้จากการสร้างโปรเจคใหม่

 

ส่วนที่อยู่ใน build method

จากโค้ดด้านบนนำมาแปลงเป็นโครงสร้างของ Widget ได้เป็น

BuildContext

ใช้สำหรับเก็บ Reference ถึง Widget นี้ที่อยู่ในโครงสร้าง Tree ตอนที่ Widget ถูกสร้างและจะผูกอยู่กับ Widget เดียวเท่านั้น

และเมื่อเราสร้าง Widget ที่ชื่อว่า A BuildContext ของ Widget A จะเป็นพ่อแม่ของ Widget ลูก พูดง่ายๆก็คือ Widget จะถูกเชื่อมต่อกันแบบโครงสร้างพ่อแม่ลูก

และถ้าเราลองเอาแผนผังด้านบนมา เปลี่ยนเป็นการจำลอง BuildContext
ซึ่งแบ่งได้เป็นแต่ละสี

BuildContext visibility
ซึ่งจากที่เราเห็นเราสามารถเข้าถึง Widget ที่เป็นพ่อแม่ได้

จากตัวอย่าง Scaffold > Center > Column > Text:
context.ancestorWidgetOfExactType(Scaffold) => เราจะได้ Scaffold ที่อยู่ด้านบนจาก context ของ Text ได้

และ BuildContext ของพ่อแม่ก็สามารถหาลูกได้เหมือนกันแต่ไม่แนะนะให้ทำแบบนั้น

ประเภทของ Widget

Widget มีอยู่สองประเภท

Stateless Widget

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

ตัวอย่างของ Widget เหล่านี้ เช่น Text, Row, Column, Container ดูง่ายๆคือเวลาเราใช้จะส่งข้อมูลไปให้มันผ่าน Constructor ซึ่งอาจจะเป็นได้ทั้ง decoration dimensions หรือ Widget อื่นๆ และมันจะไม่เปลี่ยนแปลงเลยจนกระทั้ง Build ครั้งถัดไป

Stateless Widget สามารถวาดได้แค่ครั้งเดียวตอนที่ Widget ถูกสร้าง หมายความว่า Widget จะไม่เปลี่ยนแปลงเวลาที่มีการกดหรือมีเหตุการณ์อะไรเกิดขึ้น

Lifecycle ของ Stateless Widget

ด้านล่างเป็นตัวอย่างโค้ดของ Stateless Widget

ซึ่งจะเห็นว่าเราส่งข้อมูลไปผ่านทาง constructor อย่างไรก็ตามข้อมูลนี้จะไม่ถูกเปลี่ยนหลังจากที่ Widget มีการนำไปใช้สร้าง

 

จะเห็นว่าเราเพียงแค่ Override method ที่ชื่อว่า build ก็สามารถใช้ได้เลย ถึงแม้เราจะสามารถ Override method อื่นๆได้ เช่น createElement แต่มันก็ไม่จำเป็น

ดังนั้น Stateless lifecycle ค่อนข้างจะตรงตัว

  • สร้าง
  • แสดงผลผ่าน Build

Stateful Widget
เป็น Widget ที่จะสามารถจัดการข้อมูลที่มีการเปลี่ยนแปลงได้ด้วยตัวมันเอง ซึ่งเซตของข้อมูลทั้งหมดที่ใช้ใน Widget และสามารถเปลี่ยนแปลงได้เรียกว่า State

ตัวอย่างของ Widget เหล่านี้ อาจจะเป็นรายการของ Checkbox หลายตัวที่ผู้ใช้สามารถเลือกได้ หรือจะเป็นปุ่มที่ถูกปิดตามเงื่อนไขที่เรากำหนดไว้

State

State เป็นตัวที่จะกำหนดว่า Widget เราทำอะไรได้บ้าง ซึ่งเป็นส่วนหนึ่งของ Stateful Widget

มันจะเก็บข้อมูลที่เกี่ยวข้องกับ Widget

  • Behavior
  • Layout

เวลาที่ State เปลี่ยนแปลงจะทำให้ Widget นั้นถูกสร้างใหม่

ความสัมพันธ์ระหว่าง State และ BuildContext

State ในหรับ Stateful widget จะถูกจัดการโดย BuildContext และ State จะไม่สามารถเปลี่ยน BuildContext ได้

แม้ว่า Widget BuildContext สามารถย้ายไปยังตำแหน่งอื่นๆในโครงสร้างได้ แต่ State จะยังคงถูกจัดการโดย BuildContext ตัวเดิม

เมื่อ State ถูกจัดการด้วย BuildContext State จะมีสถานะที่เรียกว่า mounted

Stateful Widget lifecycle
หลังจากที่เราได้พูดถึงคอนเซปคร่าวๆแล้ว เรามาลองเจาะลึกลงไปดีกว่า

ด้านล่างเป็นตัวอย่างโค้ดของ Stateful Widget

ด้วยเป้าหมายถึงผู้เขียนบทความต้องการที่จะอธิบายถึง ความหมายของ State ว่าเป็นเซตของข้อมูลอันนึง ที่เกี่ยวข้องกับ Widget ในที่นี้เลยจะไม่พูดถึง method ที่ไม่เกี่ยวข้อง

ซึ่ง method อื่นๆที่สามารถ override ได้มีดังนี้
didUpdateWidget, deactivate, reassemble. แต่จะถูกเขียนในบทความอื่นๆ

 

ด้านล่างเป็นแผนผัง ตัวอย่างของการทำงานที่เกี่ยวข้องกับการสร้าง Stateful Widget

ที่ด้านขวาของแผนผัง คุณจะสังเกตเห็นสถานะของ State ในแต่ล่ะช่วงเวลา

และคุณจะเห็นว่า context จะถูกผูกกับ State และพร้อมใช้ตอน mounted

จากด้านบนเรามาดูไปทีละส่วน

initState()

เป็น Method แรกที่จะถูกเรียกหลังจาก constructor ซึ่งจะถูกเรียกครั้งเดียวตอนที่ State สร้างเรียบร้อยแล้ว

ส่วนใหญ่ใช้ในการเขียน Initial อะไรบ้างอย่าง เช่น animations, controllers

และต้องอย่าลืมเรียก super.initState() ด้วย

ใน method นี้ จะมี context มาแต่คุณไม่ควรใช้มันเพราะว่าตัว Framework ยังจัดการการผูกกับ state ไม่เรียบร้อย

หลังจากที่ initState() เสร็จ State ถูกสร้างและเราถึงจะสามารถใช้ context ได้

didChangeDependencies()

เป็น method ที่จะถูกเรียกถัดมา ในส่วนนี้เราสามารถใช้ context ได้แล้ว

และเราจะใช้ method นี้ในการเชื่อมกับ InheritedWidget หรือต้องการ Initial อะไรบ้างอย่างที่ต้องใช้ BuildContext

build()

มาถึง method ที่สำคัญที่สุด build(BuildContext context) จะถูกเรียกหลังจาก didChangeDependencies() และ didUpdateWidget

ใช้ในการสร้าง widget ของเรา ถ้าเราต้องการให้ Widget ของเรามีหน้าตาแบบไหนก็จะกำหนดจากในนี้

จะถูกเรียก ในแต่ล่ะครั้งที่ข้อมูลใน State ถูกเปลี่ยน หรือ InheritedWidget แจ้งไปยัง widget ที่ลงทะเบียนไว้

หรือถ้าเราต้องการจะให้มัน build ใหม่ก็สามารถทำได้ด้วยการเรียก method setState((){…})

dispose()

จะถูกเรียกเวลาที่ widget กำลังจะหายไปจากหน้าจอ

ในการหยุดการทำงานบ้างอย่างเช่น listenners, controllers

และจึงเรียก super.dispose()

มีสองอย่างแบบนี้จะเลือกยังไงดี Stateless หรือ Stateful Widget?

คำถามนี้ เป็นคำถามที่ถูกถามบ่อยมาก แต่คุณต้องหาคำตอบเอง มันขึ้นอยู่กับความต้องการว่าเราจะทำอะไร

เราต้องดูว่า ตอนที่เราใช้ Widget นี้ เราจะมีการเปลี่ยนแปลงข้อมูลไหม ถ้าเปลี่ยน จำเป็นไหมที่ Widget จะต้องสร้างใหม่ ถ้าใช่ให้คุณเลือก Stateful ถ้าไม่ก็ใช้ Stateless มาลองดูตัวอย่างกัน

  • widget ที่ต้องการแสดงรายการของ Checkbox เพื่อที่แสดงผลอย่างถูกต้อง เราต้องเก็บข้อมูลในรายการ ซึ่งแต่ล่ะอันเก็บ หัวข้อ และสถานะ ถ้าเรากดที่ Checkbox สถานจะถูกเปลี่ยน ในกรณีนี้เราจะใช้ Stateful เพื่อเก็บสถานะของแต่ล่ะอันไว้ ถ้าหน้าจอมีการวาดใหม่เราจะสามารถ แสดงผล Checkbox ใหม่ให้ถูกต้องตามข้อมูลได้
  • ในหน้าจอมี Form อันหนึ่งเอาไว้ในผู้ใช้กรอกข้อมูลและข้อมูลจะถูกส่งไปยัง Server ในกรณนี้เราอาจจะใช้แค่ Stateless ยกเว้นจะมีการ validate form หรือมีการทำอะไรบางอย่างก่อนส่งข้อมูลถึงจะใช้ Stateful

อันนี้เป็นแค่ส่วนแรกนะครับ ถ้ามีเวลาผู้เขียนจะมาแปลในส่วนที่เหลือต่อไปครับ

ที่มา Satjanut , medium.com




Comments

ขายบ้านรายได้ดี ทำงานที่ไหนก็ได้ รับเพียง 10 ท่านเท่านั้น | ระเบียงขาว

สายช้อปปิ้ง แหล่งช้อปปิ้งสำหรับสายช้อป