iT邦幫忙

2021 iThome 鐵人賽

DAY 19
0

功能組件-InheritedWidget

InheritedWidget是一個具有特殊功能的組件,它提供可以將資料從 widget 從上到下傳遞的功能,達到共享數據的目的,其重要性與 StatelessWidgetStatefulWidget 相當。

我們在MaterialApp可以使用ThemeData定義App整體的風格,

Theme 使用案件

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Welcome to Flutter',
      theme: ThemeData(
        primaryColor: Colors.green,
        accentColor: Colors.yellow[300],
        textTheme: const TextTheme(
          headline6: TextStyle(fontSize: 24.0, fontStyle: FontStyle.italic),
        ),
      ),
      initialRoute: '/',
      routes: {
        '/': (context) => HomeScreen(),
        '/theme': (context) => ThemeScreen(),
      },
    );
  }
}

在底層的 widget 可以使用 Theme.of(context) 的方法取得上層的共享資料

class ThemeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Button")),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Container(
            color: Theme.of(context).accentColor,
            child: Text(
              '使用來自 MaterialApp 設定的 ThemeData',
              style: Theme.of(context).textTheme.headline6,
            ),
          ),
        ],
      ),
    );
  }
}

InheritedWidget 實作

Flutter 習慣使用名稱為 of 的靜態方法,定義 InheritedWidget 功能組件的使用,其應用 BuildContextdependOnInheritedWidgetOfExactType方法從 Element Tree 向父層尋找符合 MyThemeWidget 型態的資源。

讓我們使用 InheritedWidget 實作 MyThemeWidget,共享自己定義的 MyThemeData 樣式資料。

class MyThemeData {
  final Color? primaryColor;
  MyThemeData({this.primaryColor});
  MyThemeData.fallback() : this(primaryColor: Colors.blue);
}

class MyThemeWidget extends InheritedWidget {
  final MyThemeData? data;
  MyThemeWidget({required Widget child, this.data}) : super(child: child);

  @override
  bool updateShouldNotify(MyThemeWidget oldWidget) {
    return oldWidget.data != data;
  }

  static MyThemeData of(BuildContext context) {
    final MyThemeWidget? _inheritedTheme =
        context.dependOnInheritedWidgetOfExactType<MyThemeWidget>();
    MyThemeData theme = _inheritedTheme?.data ?? MyThemeData.fallback();
    return theme;
  }
}

範例中我們使用 Switch 模擬 MyThemeData 資料變更的狀況

class MyThemeBox extends StatefulWidget {
  _MyThemeBoxState createState() => _MyThemeBoxState();
}

class _MyThemeBoxState extends State<MyThemeBox> {
  bool toggle = false;
  Widget build(BuildContext context) {
    var color;
    if (toggle) {
      color = Colors.red;
    } else {
      color = Colors.blue;
    }
    return MyThemeWidget(
      data: MyThemeData(primaryColor: color),
      child: Container(
        child: Column(
          children: [
            Switch(
              value: toggle,
              onChanged: (bool value) {
                setState(() {
                  toggle = value;
                });
              },
            ),
            TextLabel(),
          ],
        ),
      ),
    );
  }
}

這邊是我們在 MyThemeWidget 底層的組件,可通過of取得 MyThemeData 資料。

還記得當初在談 State 生命週期的時候有提到 didChangeDependencies 的觸發與是否有使用 InheritedWidget 內的資料有關。

class TextLabel extends StatefulWidget {
  _TextLabelState createState() => _TextLabelState();
}

class _TextLabelState extends State<TextLabel> {
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print("didChangeDependencies()");
  }

  @override
  Widget build(BuildContext context) {
    var primaryColor = MyThemeWidget.of(context).primaryColor;

    return Text(
      "使用來自 MyThemeWidget 設定的 MyThemeData",
      style: TextStyle(color: primaryColor),
    );
  }
}

今日成果

程式碼

widget_theme


上一篇
Flutter體驗 Day 18-路由導覽v2
下一篇
Flutter體驗 Day 20-Provider
系列文
Flutter / Dart 跨平台App開發體驗30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言