從零開始的Flutter之旅: InheritedWidget

往期回顧

從零開始的Flutter之旅: StatelessWidgetgit

從零開始的Flutter之旅: StatefulWidgetgithub

在以前的文章中,介紹了StatelessWidget與StatefulWidget的特性與它們的呈現原理。緩存

這期要聊的是它們的另外一個兄弟InheritedWidget。bash

特性

InheritedWidget是Flutter中的一個很是重要的功能組件,它可以提供數據在widget樹中從上到下進行傳遞。保證數據在不一樣子widget中進行共享。這對於一些須要使用共享數據的場景很是有效,例如,在Flutter SDK中就是經過InheritedWidget來共享應用的主題與語言信息。微信

可能你還有點模糊,別急,下面咱們經過一個簡單的示例來了解InheritedWidget。網絡

示例

相信開始學Flutter時都看過官方的計數器示例。咱們將官方提供的計數器示例使用InheritedWidget進行改造。架構

首先咱們須要一個CountInheritedWidget,它繼承於InheritedWidget。app

class CountInheritedWidget extends InheritedWidget {
  CountInheritedWidget({@required this.count, Widget child})
      : super(child: child);
 
  // 共享數據,計數的數量
  final int count;
 
  // 統一的獲取CountInheritedWidget實例, 方便樹中子widget的獲取共享數據
  // 必須在State中調用纔會有效
  static CountInheritedWidget of(BuildContext context) {
    // 調用共享數據的子widget將不會回調didChangeDependencies方法,即子widget將不會更新
    // return context.getElementForInheritedWidgetOfExactType<CountInheritedWidget>().widget;
    return context.dependOnInheritedWidgetOfExactType<CountInheritedWidget>();
  }
 
  // true -> 通知樹中依賴改共享數據的子widget
  @override
  bool updateShouldNotify(CountInheritedWidget oldWidget) {
    return oldWidget.count != count;
  }
}
複製代碼
  1. 在CountInheritedWidget中提供共享計數的數量count
  2. 同時爲外部提供統一的獲取CountInheritedWidget實例的of方法
  3. 最後再重寫updateShouldNotify方法,來通知依賴該共享count的子widget進行更新

如今已經有了共享數據count的提供,接下來是在具體的子widget中進行使用。框架

咱們抽離出一個CountText子widgetless

class CountText extends StatefulWidget {
  @override
  _CountTextState createState() {
    return _CountTextState();
  }
}
 
class _CountTextState extends State<CountText> {
  @override
  Widget build(BuildContext context) {
    return Text("count: ${CountInheritedWidget.of(context).count.toString()}");
  }
 
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print("didChangeDependencies");
  }
}
複製代碼
  1. 內部引用了CountInheritedWidget中的共享數據count,經過of方法獲取CountInheritedWidget實例
  2. didChangeDependencies能夠用來監聽子widget依賴是否反生改變

最後,咱們再將CountInheritedWidget與CountText結合起來,經過簡單的點擊自增事件來看下效果

class CountWidget extends StatefulWidget {
  @override
  _CountState createState() {
    return _CountState();
  }
}
 
class _CountState extends State<CountWidget> {
  int count = 0;
 
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Count App',
      theme: new ThemeData(primarySwatch: Colors.blue),
      home: Scaffold(
        appBar: AppBar(
          title: Text("Count"),
        ),
        body: Center(
          child: CountInheritedWidget(
            count: count,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                CountText(),
                RaisedButton(
                  onPressed: () => setState(() => count++),
                  child: Text("Increment"),
                )
              ],
            ),
          ),
        ),
      ),
    );
  }
}
複製代碼

上面的層級關係是CountText恰好是CountInheritedWidget的子widget。

如今咱們經過點擊事件直接改變外部的count值,若是InheritedWidget從上到下數據傳到的效果可以生效,那麼在CountText中引用的count將會與外部count同步,程序呈現的效果將會是自增的,同時因爲依賴的count發生改變CountText中的didChangeDependencies也會回調。

咱們直接運行一下

點擊後的輸出日誌

I/flutter: didChangeDependencies
複製代碼

說明InheritedWidget的效果已經生效,經過InheritedWidget的使用,咱們能夠很方便的在嵌套下層的子widget中拿到上層的數據,或者說整個widget的共享數據。

分析

在依賴方式改變時子widget的didChangeDependencies會回調,但因爲你可能會在該方法中作一些特殊的操做,例如網絡請求。只是須要一次就能夠。若是是套用咱們上面的示例,將會在count子增時反覆調用。

爲了防止didChangeDependencies的調用,咱們再來看CountInheritedWidget的of方法中註釋的那部分

static CountInheritedWidget of(BuildContext context) {
    // 調用共享數據的子widget將不會回調didChangeDependencies方法,即子widget將不會更新
    // return context.getElementForInheritedWidgetOfExactType<CountInheritedWidget>().widget;
    return context.dependOnInheritedWidgetOfExactType<CountInheritedWidget>();
  }
複製代碼

咱們使用的是dependOnInheritedWidgetOfExactType方法,依賴的共享數據發生改變時會回調子widget中的didChangeDependencies方法,若是咱們不想要子widget調用該方法,可使用註釋的代碼,經過getElementForInheritedWidgetOfExactType方法來獲取共享數據。

若是此時咱們再運行一下項目,點擊count自增,控制檯將不會再輸出日誌。這樣就能夠解決didChangeDependencies的反覆調用。

而這兩個方法的主要區別是在dependOnInheritedWidgetOfExactType調用的過程當中會進行註冊依賴關係

@override
  InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
    assert(ancestor != null);
    _dependencies ??= HashSet<InheritedElement>();
    _dependencies.add(ancestor);
    ancestor.updateDependencies(this, aspect);
    return ancestor.widget;
  }
複製代碼

因此dependOnInheritedWidgetOfExactType更新依賴的子widget中的didChangeDependencies方法。

思考下一個問題,雖然如今didChangeDependencies方法不會調用,可是CountText的build方法仍是會執行。緣由是在CountWidget中經過setState來改變count值,會從新build所用的子widget。但咱們真正想要的只是更新子widget中依賴的CountInheritedWidget的組件值。

那麼如何解決呢?這裏提供一個解決方案是爲子widget提供緩存。能夠經過封裝一個簡單的StatefulWidget,將子widget緩存起來。若是對這塊感興趣的,能夠期待個人後續文章。

推薦項目

下面介紹一個完整的Flutter項目,對於新手來講是個不錯的入門。

flutter_github,這是一個基於Flutter的Github客戶端同時支持Android與IOS,支持帳戶密碼與認證登錄。使用dart語言進行開發,項目架構是基於Model/State/ViewModel的MSVM;使用Navigator進行頁面的跳轉;網絡框架使用了dio。項目正在持續更新中,感興趣的能夠關注一下。

固然若是你想了解Android原生,相信flutter_github的純Android版本AwesomeGithub是一個不錯的選擇。

若是你喜歡個人文章模式,或者對我接下來的文章感興趣,建議您關注個人微信公衆號:【Android補給站】

或者掃描下方二維碼,與我創建有效的溝通,同時更快更準的收到個人更新推送。

相關文章
相關標籤/搜索