tags: Flutter、InheritedWidget
[Note] Inherited Widget
基本概念
Inherited Widget 是一個可以向下傳遞資料的 Widget,在特定父 widget 通過 Inherited Widget 共享了一份資料,就可以在其任意子 widget 中來獲取該共享的資料。
下面會用簡單的範例來實作 Inherited Widget。
需求
- 
點擊
change color按鈕(OutlinedButton)會改變底下兩個GO PageX按鈕(ElevatedButton)的背景色

 - 
Widget Tree(同顏色為同一層),右半部灰底,有另外抽成一個 widget(
RedirectButtons)

 
實作
範例原始碼:Repo 網址
Step1. 創建一個 Inherited Widget 的 Class
新增 TextColor 檔案:
TextColor繼承InheritedWidget- 定義 
Color? color屬性,需要在子 widget 共享的資料 - 定義 
of方法,可以在子 widget 中取得共享的資料 - 當 color 改變時候,
updateShouldNotify會通知子 widget tree 中,有依賴到 color 的 widget 重新 build 
// text_color.dart
// extend InheritedWidget
class TextColor extends InheritedWidget {
  TextColor({
    super.key,
    required super.child,
    required this.color,
  });
  // shared data
  Color? color;
  // return text color to widget
  static Color of(BuildContext context) {
    final Color? textColor =
        context.dependOnInheritedWidgetOfExactType<TextColor>()!.color;
    return textColor!;
  }
// this will notify all child widgets when there is a changes
  @override
  bool updateShouldNotify(TextColor oldWidget) {
    return color != oldWidget.color;
  }
}
Step2. 使用 InheritedWidgets 實例,包覆父 Widget
- 使用 
TextColor包覆 Homepage 的 body - 傳入參數
color:要共享的資料child:會使用到共享資料的子 widget tree
 
// page_home.dart
class _HomePageState extends State<HomePage> {
  int _index = 0;
  List<Color> _colorList = [Colors.green, Colors.red, Colors.blue];
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("InheritedWidget"),
      ),
      // create InheritedWidgets
      // when color in TextColor change, all child Widget changed
      // now let's create a button for multiple color changing
      body: TextColor(
        color: _colorList[_index % 3],
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            OutlinedButton.icon(
              onPressed: () {
                // press button to change color in list
                setState(() {
                  _index++;
                });
              },
              icon: const Icon(Icons.loop),
              label: const Text('change color'),
            ),
            const RedirectButtons(),
          ],
        ),
      ),
    );
  }
}
Step2. 子 Widget 取得共享資料
- 透過 
of方法(TextColor.of(context)) 取得共享資料 
// redirect_buttons.dart
class _RedirectButtonsState extends State<RedirectButtons> {
  @override
  Widget build(BuildContext context) {
    // receive data from TextColor
    final _color = TextColor.of(context);
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        ElevatedButton(
            onPressed: () {
              Navigator.of(context).push(
                MaterialPageRoute(
                  builder: (context) => PageA(color: _color),
                ),
              );
            },
            style: ButtonStyle(
              backgroundColor: MaterialStateProperty.all(_color),
            ),
            child: const Text('Go Page A')),
        ...
      ],
    );
  }
}
解決了什麼問題?
主要解決跨 widget 共享資料問題,以上面例子來看,一般做法會在 page 層定義變數 color,當最底下的 ElevatedButton 也需要使用到 color,color 會被當作 prop 一路往目標子 widget 傳遞:

經過改寫過後,RedirectButtons 內部可以透過 Inherited Widget 直接拿到 page 層的 color 資料:

看似只節省一層傳遞(RedirectButtons 不需要再接收父層的 color),但如果現實 page 層和 RedirectButtons 中間隔了很多 widgets,且這些 widgets 也用不到變數 color,這功能就變得非常方便,因為大大解決 prop 傳遞過深問題。

注意事項
Inherited Widget只有在資料發生變化時才會進行更新,如果需要強制更新,可以使用 setState 或者其他方法來實現- 需要共享不同的資料,可以定義不同的 
Inherited Widget,從而達到不同的共享資料的目的 (e.g. dialog 顯示狀態、toast 顯示狀態) 
參考資料
 FlutterInherited Widget
 Published on 15 Dec 2024
 Updated on 15 Dec 2024