Flutter中InheritedWidget使用的最佳實踐

轉載請聯繫: 微信號: michaelzhoujayredux

原文請訪問個人博客緩存


本文主要介紹InheritedWidget的設計目的、用法以及推薦的最佳實踐微信

InheritedWidget

Flutter的Widget層級能夠作得很是深,在Widget層級間傳遞數據會讓效率變得很低,也會多處不少bolierplate代碼。less

例以下面的accountIdscopeId,若是MyWidget肯本用不到它們,accountId, scopeId仍是要做爲MyWidget的final參數,由於它們要做爲構造函數的參數接受參數值並向MyOtherWidget傳遞,先得很是冗餘,由於MyWidget徹底沒有必要知道這兩個參數。ide

class MyPage extends StatelessWidget {
  final int accountId;
  final int scopeId;
  
  MyPage(this.accountId, this.scopeId);
  
  Widget build(BuildContext context) {
    return new MyWidget(accountId, scopeId);
  }
}

class MyWidget extends StatelessWidget {
  final int accountId;
  final int scopeId;
  
  MyWidget(this.accountId, this.scopeId);
  
  Widget build(BuildContext context) {
    // somewhere down the line
    new MyOtherWidget(accountId, scopeId);
    ...
  }
}

class MyOtherWidget extends StatelessWidget {
  final int accountId;
  final int scopeId;
  
  MyOtherWidget(this.accountId, this.scopeId);
  
  Widget build(BuildContext context) {
    // rinse and repeat
    ...
複製代碼

經過引進InheritedWidget,咱們就能解決這個困擾。函數

InheritedWidget的思想和redux的思想差很少,所謂accountIdscopeId其實是這些widget的狀態,用戶交互或其餘事件會觸發Action,Action產生新的State,Widget接受新的State生成新的RenderNode掛載到Widget Tree上,完成一次渲染。ui

那怎麼讓這些參數在不一樣層級之間傳遞呢?答案是將State往上放,也就是俗稱state up liftingthis

例以下面的代碼就是經過插入一個InheritedWidgetaccountIdscopeId存儲在InheritedWidget描述的這一層級中,全部這一層級和下面的層級都能訪問這兩個參數。spa

class MyInheritedWidget extends InheritedWidget {
  final int accountId;
  final int scopeId;

  MyInheritedWidget(accountId, scopeId, child): super(child);
  
  @override
  bool updateShouldNotify(MyInheritedWidget old) =>
    accountId != old.accountId || scopeId != old.scopeId;
}

class MyPage extends StatelessWidget {
  final int accountId;
  final int scopeId;
  
  MyPage(this.accountId, this.scopeId);
  
  Widget build(BuildContext context) {
    return new MyInheritedWidget(
      accountId,
      scopeId,
      const MyWidget(),
     );
  }
}

class MyWidget extends StatelessWidget {

  const MyWidget();
  
  Widget build(BuildContext context) {
    // somewhere down the line
    const MyOtherWidget();
    ...
  }
}

class MyOtherWidget extends StatelessWidget {

  const MyOtherWidget();
  
  Widget build(BuildContext context) {
    final myInheritedWidget = MyInheritedWidget.of(context);
    print(myInheritedWidget.scopeId);
    print(myInheritedWidget.accountId);
    ...
複製代碼

注意:設計

  1. InheritedWidgetchild是const修飾的構造函數,這樣作的目的是讓child能緩存
  2. accountId或者scopeId值更新的時候,MyInheritedWidget會被從新建立,可是它的child不必定會被建立,取決因而否用到了accountId或者scopeId,上面的這個例子中MyOtherWidget會被rebuild,可是MyWidget不會被rebuild
  3. 若是tree被其餘事件觸發rebuild,例如orientation changes,InheritedWidget會被rebuild,可是child一樣不必定被rebuild,由於accountIdscopeId沒變。

爲了讓InheritedWidget更加高效,推薦下面的最佳實踐:

Keep inherited widgets small

儘可能讓你的Context語義儘量單一,這樣Flutter渲染時才能更細粒度的判斷哪些Widget須要rebuild哪些須要複用,不然就失去了InheritedWidget的意義。

要這樣

class TeamContext {
  int teamId;
  String teamName;
}

class StudentContext {
  int studentId;
  String studentName;
}
 
class ClassContext {
  int classId;
  ...
}
複製代碼

不要這樣

class MyAppContext {
  int teamId;
  String teamName;
  
  int studentId;
  String studentName;
  
  int classId;
  ...
}
複製代碼

使用const方式來構造child

若是沒有const,InheritedWidget的整個child都不會緩存,選擇性rebuild整個子tree就不會生效。

管理好Context的Scope

由於Flutter的整個App是一個Tree,因此InheritedWidget能夠放在任意一個層級,這樣顯然不利於人腦來理解並管理,因此必須你們規約好,例如規定InheritedWidget只接受Scaffold做爲child,這樣全部的InheritedWidget最大粒度是Page,便於人腦理解及閱讀。

不要跨路由訪問context

Flutter眼裏沒有page不page的,全部東西就是widget,若是使用Navigation.push一個Widget請注意route的下一個widget沒有繼承上一個widget的InheritedWidget的Context,你須要明確將參數傳遞,而不是將InheriedWidget往上提。

相關文章
相關標籤/搜索