ข้ามไปยังเนื้อหา

InheritedWidget

การส่ง data ลง tree ลึก ๆ ด้วยมือ — ผ่านทุก constructor — เป็นเรื่องทรมาน นี่คือ “prop drilling” และผูก widget ตรงกลางทุกตัวเข้ากับ data ที่ไม่ได้ใช้

InheritedWidget คือคำตอบ built-in ของ Flutter คุณวางไว้สูงใน tree แล้ว descendant ตัวไหนก็อ่านค่าได้ ตรง ๆ ผ่าน context ไม่ว่าจะลึกแค่ไหน โดยไม่มี widget ตรงกลางสักตัวต้องรู้เรื่อง Theme, MediaQuery, Navigator และ Provider ล้วนเป็น InheritedWidget — คุณใช้กันมาตลอดอยู่แล้ว

descendant เรียก dependOnInheritedWidgetOfExactType ซึ่งมักถูกห่อไว้ใน static method of(context) ตามธรรมเนียม:

class ThemeConfig extends InheritedWidget {
const ThemeConfig({super.key, required this.color, required super.child});
final Color color;
// ธรรมเนียม: static `of` ที่ register ผู้เรียกเป็น dependent
static ThemeConfig of(BuildContext context) {
final result =
context.dependOnInheritedWidgetOfExactType<ThemeConfig>();
assert(result != null, 'No ThemeConfig found in context');
return result!;
}
// เรียกเมื่อ ThemeConfig ใหม่มาแทนตัวเก่า: dependent ควร rebuild ไหม?
@override
bool updateShouldNotify(ThemeConfig oldWidget) => color != oldWidget.color;
}
// ที่ไหนก็ได้ที่อยู่ต่ำกว่า:
final color = ThemeConfig.of(context).color;

มีสองกลไกที่สำคัญตรงนี้ dependOnInheritedWidgetOfExactType ทำสองอย่างพร้อมกัน: หา ancestor ที่ใกล้ที่สุดของ type นั้น (การ lookup ขึ้น element tree ที่เร็วและ cache ไว้) และ register element ที่เรียกให้เป็น dependent ส่วน updateShouldNotify ตัดสินว่า เมื่อ inherited widget ถูก rebuild ด้วย data ใหม่ dependent จำเป็นต้อง rebuild จริงไหม

นี่คือผลตอบแทน เมื่อ InheritedWidget update และ updateShouldNotify คืน true Flutter จะ rebuild เฉพาะ widget ที่เรียก of(context) — ไม่ใช่ทั้ง subtree widget ตรงกลางที่ไม่เคย depend ก็ไม่ถูกแตะ

flowchart TB
  iw["InheritedWidget
(color เปลี่ยน)"] --> a["Widget A
(ไม่ได้ depend)
ไม่ rebuild"]
  iw --> b["Widget B
เรียก of(context)
rebuild"]
  iw --> c["Widget C
(ไม่ได้ depend)
ไม่ rebuild"]
Only registered dependents rebuild on update

การ rebuild แบบเจาะจงนั้นคือเหตุผลที่ InheritedWidget scale ได้: การอ่านคือ lookup ขึ้นบนที่ราคาถูก และ update จะกระเพื่อมไปถึงแค่ dependent จริง ๆ ทุก state solution ระดับสูงกว่านี้ ล้วนเป็นกลไกนี้บวก ergonomics อยู่ข้างใต้

InheritedWidget แก้ปัญหาอะไร?
dependOnInheritedWidgetOfExactType ทำอะไร?
เมื่อ InheritedWidget update widget ตัวไหน rebuild?
บทบาทของ updateShouldNotify คืออะไร?