web analytics

Flutter : การใช้ Provider กับ State Management

สวัสดีผู้อ่านครับ บล็อกนี้ผมจะพามาลองเล่น Provider ซึ่งเป็นคลาสสำหรับช่วยจัดการเรื่อง State Management ที่มาก็มาจาก ในงาน Google I/O 2019 ที่ผ่านมา ใน session ของ Flutter ทีมงาน Flutter ได้อธิบายเกี่ยวเรื่อง State management ว่าทำอย่างไร โดยได้หยิบยกตัวอย่างการใช้ Provider ซึ่งจริงๆแล้วมันก็ใช้งานเหมือนคล้ายกับ
InheritedWidget + ScopeModel + BLoC รวมกัน ทำให้การใช้งานง่ายมากขึ้น

เริ่มต้น

เริ่มต้น ตัวอย่าง จากโปรเจคต้นแบบของ Flutter คือแอป counter สำหรับนับเลข

จากปัญหาของเรื่อง State ของแอปนี้ คือ เมื่อเรากดปุ่มบวก แอปจะเรียก setState แล้ว build Widget ใหม่ทั้งหมด ทำให้ประสิทธิภาพของแอปไม่ดีอย่างที่ควรจะเป็น แถมตัวโค้ดยังผูกกับ widget มากเกินไป

แนวทางคือการใช้ State management แบบต่างๆมาช่วย ซึ่งมีหลายตัว เช่น Redux , BLoC โดยสิ่งที่เพิ่มเข้ามาจะช่วยแยก State ให้จัดการง่ายขึ้น แยก business logic และช่วยเพิ่มประสิทธิภาพของแอป เราสามารถเขียนให้ build เฉพาะในสิ่งที่จำเป็น

เพิ่ม Dependencies

เพิ่ม dependencies ของ provider ใน pubspec.yaml

Model Provider

สร้างคลาส provider ของเรา ซึ่งในที่นี้ คือ Counter โดยมันจะเก็บข้อมูลต่างๆ รวมทั้ง method การทำงานเอาไว้ และจะสามารถอัพเดท Widget ได้ ซึ่งความสามารถในการอัพเดท widget เมื่อข้อมูลเปลี่ยนแปลง จะใช้คลาสที่ชื่อว่า notifier โดยหลักๆมีอยู่ 2 แบบ

ChangeNotifier

แบบแรก คือให้คลาส provider ของเราสืบทอด ChangeNotifier จะ extends หรือ with ก็ได้ โดยข้อดีคือสามารถกำหนดตัวแปรกี่ตัวให้ provider ก็ได้ แต่จะต้องเรียก notifyListener() เมื่อต้องการอัพเดท UI

ValueNotifier

อีกรูปแบบคือ ValueNotifier ข้อแตกแตกต่างจาก ChangeNotifier คือ มันจะมีข้อมูลในตัวเองได้ตัวเดียว โดยใช้ชื่อว่า value และเมื่อเราเปลี่ยนแปลงค่า value มันจะอัพเดท UI ให้อัตโนมัตินั่นเอง

สรุป ChangeNotifier กับ ValueNotifier มีข้อแตกต่างเรื่องการใช้งานนิดหน่อย ต้องเลือกใช้กับ provider ของเราให้เหมาะสม

Provider

ลองใช้งาน provider ในตัว widget กันบ้าง ก่อนอื่น import provider เข้ามาก่อน

ที่ root ของแอป เราจะต้องกำหนด provider ให้กับแอป โดยใช้คลาส Provider แล้วกำหนด counter provider ของเราลงไปใน builder ทำให้ตอนนี้เราสามารถเข้าถึง
counter provider ได้จากทุกที่แล้ว

ChangeNotifierProvider + Consumer

จากนั้นเมื่อไหร่ที่เราต้องใช้ counter provider ก็เรียกด้วยคำสั่ง Provider.of<CounterProvider>(context);

ทีนี้จุดที่เราต้องการให้ build Widget เฉพาะกับข้อมูลเที่เราอัพเดท เราจะใช้ ChangeNotifierProvider แล้วใส่ provider เข้าไปให้มัน แล้วกำหนด child เป็น Consomer โดย Consumer ก็จะมีหน้าที่นำข้อมูลจาก provider มา build เป็น widget ส่วนที่ปุ่ม action ก็แค่เรียก counterProvider.increment(); เพื่ออัพเดทค่าใน provider แล้วมันจะจัดการ build Widget ใน ChangeNotifierProvider ใหม่ให้เอง

ตอนนี้แอปของเราก็จะ build เฉพาะตรงจุดที่เรากำหนดแล้ว ตาม provider ของเรา

ซึ่งลองเข้ามาดู คลาส Consumer ว่าภายในมันทำงานอย่างไร ก็จะพบว่าจริงๆแล้วมันก็คือๆไปเรียก Provider.of เพื่อให้ได้ provider มาเท่านั้น ไม่มีอะไรเลยจริงๆ

Multi-Provider

เมื่อแอปของเรามี provider มากขึ้น เราสามารถเขียน provider นึงเป็น child ของอีกตัวนึง แล้วซ้อนๆกันไปเรื่อยๆก็ได้อยู่ แต่มันไม่สวย จึงเป็นที่มาของ MultiProvider ที่เราสามารถกำหนด provider เป็น list ได้เลย จากนั้นมันก็เอาไปวนลูปมาสร้างเป็น tree ให้

Multi-Consumer

ส่วนที่ Consumer หาก widget ที่เราต้องการ build ร่วมกับ provider หลายๆตัว Flutter ก็ได้เตรียม Consumer ที่รองรับ provider มากทึ่สุดถึง 6 ตัว เช่น Consumer3 จะสามารถใช้ provider ได้ 3 ตัว วิธีการใช้ก็เหมือนกับ Consumer ปกติ

ลองดูการทำงานภายในของคลาส Consumer3 ไม่ได้ต่างกับ Consumer เลย แค่เพิ่ม provider.of มาสำหรับตัวที่ 2 และ 3 เท่านั้น ดังนั้นจะมีกี่ provider เราก็ทำได้

สรุป

พอจะเห็นภาพแล้วใช่มัยครับ ว่า Provider ช่วยจัดการกับปัญหา State ได้อย่างไร ส่วนตัวผมคิดว่า Provider เหมือนเป็น BLoC แบบปรับปรุงให้ง่ายขึ้นนั่นเอง เพราะคอนเซ้ปการใช้มันคล้ายกันมาก แค่ไม่ต้องไปยุ่งกับ Stream เขียนแค่กับที่เราสนใจก็พอ

หากยังไม่ได้อ่านเรื่อง BLoC Pattern อ่านได้ที่ลิงค์ด้านล่าง

หากยังไม่ได้อ่านเรื่อง Redux Pattern อ่านได้ที่ลิงค์ด้านล่าง