เหตุผลที่ควรใช้ Provider ใน Flutter
สำหรับใครที่กำลังต้องการพัฒนาแอปพลิเคชันมือถือด้วย Flutter ไม่ควรพลาดบทความนี้ เพราะจะพาไปรู้จักกับ pub ที่เป็นที่แนะนำและนิยมมาก ๆ ในกลุ่มของ Flutter developer อีกทั้งยังเป็น pub ที่ได้ Favorite จาก Flutter official อีกด้วย ซึ่งก็คือ Provider นั่นเอง
หลาย ๆ คนอาจจะยังไม่รู้จัก Provider ว่าทำหน้าที่อะไร ทำไมคนถึงนิยมใช้กันอย่างมากมาย หากมีโอกาสได้รู้จักกับ Provider แล้วนั้น จะทำให้ชีวิตมีความสุข ไม่หงุดหงิด และยังทำให้ App Logical ของเราดีขึ้นมากอีกด้วย
การ Passing Arguments to Widget แบบเดิม ๆ
ในการส่งข้อมูลระหว่าง widget รูปแบบแรก ๆ ที่ Flutter developer จะทำกัน คือ การส่งผ่าน arguement เพื่อให้ตัว class ที่เป็น child widget ทำการ construct ด้วย arguement ที่ส่งผ่านนั้น ๆ แต่ถ้าหากเราอยากส่งข้อมูลจาก parent widget ส่งไปหา child widget ในลำดับที่ 4 เราจะทำอย่างไงกันได้บ้าง ?
จากการส่งผ่านข้อมูลดังกล่าวอาจทำให้เกิดปัญหา ดังต่อไปนี้
1. Apropcalypse
โดยปกติแอปพลิเคชันที่สำเร็จแล้วจะมีการ communicate ระหว่าง widget ซึ่งไม่ใช่แค่ 4 - 5 widgets แต่อาจจะมีถึง 100 widgets ที่มีความสัมพันธ์ที่ซับซ้อน ทำให้การส่งผ่านข้อมูลแบบนี้ไม่น่าเป็นที่เหมาะสมเท่าไหร่นัก
2. Multiple communication level
หากเราส่ง widget จาก level 1 ไปหา level 4 โดยข้อมูลที่ส่งผ่าน level 2 และ 3 ไม่ได้ถูกนำไปใช้ หรือพูดง่าย ๆ คือ widget ที่ level 2 และ level 3 ทำหน้าที่เป็นแค่สะพานเชื่อมเพื่อให้ level 1 ส่งไปหา level 4 เท่านั้น ซึ่งสิ่งนี้เป็นสิ่งที่ไม่ควรจะเกิดขึ้นในการพัฒนาแอปพลิเคชันที่ดี
3. นำ InheritedWidget มาช่วย
InheritedWidget เกิดมาเพื่อแก้ปัญหาการส่งผ่านข้อมูลให้ลูก ๆ หลาน ๆ แบบไม่ต้องส่งต่อกันไปเรื่อย ๆ ถ้าหากใครเคยใช้ React ก็จะเหมือนกับ React Context แต่ถ้าหากเคยใช้ Vue ก็จะเหมือน Provider หรือ Inject ซึ่ง InheritedWidget ก็สามารถทำงานได้ไม่ต่างกับตัวที่เปรียบเทียบไปข้างต้น แต่ถ้าข้อมูลที่เราส่งผ่านนั้นเป็นข้อมูลที่มีการอัปเดต หรือ reactive ล่ะ ? ตัว InheritedWidget จะทำให้ widget ลูก ๆ ที่ใช้งานค่านั้นอยู่ rebuild หรือไม่
คำตอบคือ ได้ ถ้าหากนำ widgetStatful มาครอบไว้ข้างบน ซึ่งนำ state มาสร้าง instance ของ InheritedWidget แต่ข้อเสียคือ มันจะ update ทั้ง widget tree นั่นเอง ซึ่งหากเป็นแอปพลิเคชันที่เล็ก ๆ อาจจะทำได้ไม่มีปัญหาอะไร แต่ถ้าหากแอปพลิเคชันใหญ่นั้นจะกลายเป็นปัญหาใหญ่ทันที
แล้วคนอื่นเขาแก้ปัญหานี้กันอย่างไร ?
หากใครที่มาจากสาย Web developer คงจะรู้จัก state management หลาย ๆ ตัวอยู่แล้ว เช่น Redux, MobX, Recoild, Vuex เป็นต้น ซึ่งขั้นตอนในการทำงานอาจจะไม่เหมือนกันมากนัก แต่ผลลัพธ์ที่ได้จะเหมือนกับ Provider ทุกประการ นั่นก็คือ เมื่อ state ที่อยู่ใน store เปลี่ยน จะทำให้ state นั้นที่ถูกอัปเดต อยู่ใน widgets ไหน widget นั้นจะต้อง rebuild เพื่อให้ได้หน้า UI ใหม่ตาม state ที่เปลี่ยนด้วย
วิธีแก้ปัญหาด้วย Provider
จากที่กล่าวข้างต้น เราจะพาไปรู้จัก Redux ในโลกของ Flutter หรือที่เรียกว่า Provider ซึ่งสิ่งที่ต้องรู้จักในการทำ Provider จะมีดังต่อไปนี้
1. Provider
คือ เราจะทำการส่ง Data เข้าไปในขั้นนี้ โดยที่มันจะไป wrapped อยู่เหนือ widgets ที่เราจะเรียกใช้ข้อมูลชุดนั้น
2. Listener
คือ เป็นการเรียกใช้ข้อมูลของ provider ที่อยู่เหนือตัวเอง เมื่อมีการ update ข้อมูลชุดนั้น คนที่เรียก Listener นี้จะทำการ rebuild
3. ChangeNotifierModel
คือ เป็น class ข้อมูลที่รับคุณสมบัติมาจาก ChangeNotifier ซึ่งมี method ที่สามารถส่งการแจ้งเตือนไปหา widget ที่ทำการ subscribe ว่า state ได้ถูกการ update แล้วช่วย rebuild ใหม่ด้วยนั่นก็คือ NotifyListeners()
จากโค้ดข้างต้นจะเห็นว่า จะมีชุดข้อมูลหนึ่งที่ชื่อว่า ExampleData ที่เป็นคลาสข้อมูล สามารถเปลี่ยนแปลงทีหลังได้ ( ไม่ได้ใส่ final ) หากลองสังเกต จะเห็นว่า class นั้นมีการ mix ด้วย mixin ( with ) ที่ชื่อว่า ChangeNotifier และมีการเรียก method ที่ชื่อว่า notifyListeners เมื่อทำการ update ข้อมูลเรียบร้อยเพื่อเป็นการบอกให้ Listener ทุกตัวที่ Listening อยู่ทำการ rebuild ด้วย ข้อมูลชุดใหม่
ถัดมาในส่วนสุดท้ายจะเป็น Consumer ส่วนนี้จะเป็นส่วนที่เรียกอีกอย่างว่า Listener โดยที่มันจะ rebuild แค่สิ่งที่อยู่ใน Widget ที่ชื่อ Consumer เท่านั้น หากมี widget อื่นที่อยู่นอกเหนือจาก Consumer จะไม่มีการ rebuild ใด ๆ เกิดขึ้น ซึ่งนี่ถือเป็น ข้อดีมาก ๆ ในการทำเรื่อง performace
สำหรับบทความหน้าจะเป็นการใช้ Provider แบบ Nested ที่มีชุดข้อมูลหลายข้อมูล และจะทำยังไงให้ ชุดข้อมูลกลาง หรือ Provider Class เป็น Central และ reuseable ที่สุด ไว้เจอกันใหม่ครับ