フラッターの継承ウィジェット

記事の翻訳は、Flutter MobileDeveloperコースの学生向けに用意されています。










Flutterウィジェットツリーのルーツは非常に深くなる可能性があります...







非常に深くなります。



Flutterウィジェットのコンポーネントの性質により、非常にエレガントでモジュール式の柔軟なアプリケーション設計が可能になります。ただし、これにより、コンテキストを渡すための多くの定型コードが生成される可能性もあります。accountIdとscopeIdをページから2レベル下のウィジェットに渡したいときに何が起こるかを確認してください。



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) {
    // -   
    new MyOtherWidget(accountId, scopeId);
    ...
  }
}

class MyOtherWidget extends StatelessWidget {
  final int accountId;
  final int scopeId;
  
  MyOtherWidget(this.accountId, this.scopeId);
  
  Widget build(BuildContext context) {
    //  
    ...


チェックを外したままにすると、このパターンはコードベース全体に非常に簡単に忍び寄る可能性があります。この方法で、30を超えるウィジェットを個人的にパラメーター化しました。MyWidget上記の例のように、作業時間のほぼ半分で、ウィジェットはパラメーターを受け取り、それらをさらに渡すだけでした



MyWidgetの状態はパラメーターに依存しませんが、パラメーターが変更されるたびに再構築されます。


もちろん、...より良い方法がなければならない



の紹介InheritedWidgetを



一言で言えば、これはサブツリーのルートでコンテキストを定義する特別な種類のウィジェットですこのコンテキストをそのサブツリー内のすべてのウィジェットに効率的に提供できますアクセスパターンは、Flutter開発者にとってなじみのあるものに見えるはずです。



final myInheritedWidget = MyInheritedWidget.of(context);


このコンテキストは単なるDartクラスです。したがって、そこに詰め込みたいものは何でも含めることができます。Styleまたはなどの一般的に使用されるFlutterコンテキストの多くは、MediaQueryレベルに存在するDependedWidgetにすぎませんMaterialApp



AliExpressWidgetを使用して上記の例を補足すると、次のようになります。



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) {
    // -   
    const MyOtherWidget();
    ...
  }
}

class MyOtherWidget extends StatelessWidget {

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


注意することが重要です:



  • コンストラクターはconst、これらのウィジェットをキャッシュ可能にするものです。生産性が向上します。
  • パラメータが更新されると、新しいパラメータが作成されMyInheritedWidgetます。ただし、最初の例とは異なり、サブツリーは再構築されません。代わりに、Flutterは、これInheritedWidgetアクセスしたウィジェットを追跡する内部レジストリを維持し、このコンテキストを使用するウィジェットのみを再構築します。この例では、ですMyOtherWidget
  • 向きの変更など、パラメータの変更以外の理由でツリーが再構築された場合でも、コードは新しいツリーを構築できますInheritedWidgetただし、パラメーターは同じままであるため、サブツリー内のウィジェットは通知されません。これが、updateShouldNotifyあなたが実装した関数の目的ですInheritedWidget


最後に、グッドプラクティスについて話しましょう。



着用ウィジェットは小さくする必要があります



Flutterはコンテキストのどの部分が更新され、どの部分がウィジェットによって使用されているかを判断できないため、多くのコンテキストでそれらをオーバーロードすると、上記の2番目と3番目の利点が失われます。代わりに:



class MyAppContext {
  int teamId;
  String teamName;
  
  int studentId;
  String studentName;
  
  int classId;
  ...
}


することを好む:



class TeamContext {
  int teamId;
  String teamName;
}

class StudentContext {
  int studentId;
  String studentName;
}
 
class ClassContext {
  int classId;
  ...
}


constを使用してウィジェットを作成します



constがないと、サブツリーを選択的に再構築することはできません。Flutterは、サブツリー内の各ウィジェットの新しいインスタンスを作成して呼び出しbuild()、特にビルドメソッドが十分に重い場合は、貴重なサイクルを浪費します。



あなたのInheritedWidget-sの範囲に目を離さないでください



InheritedWidget-yはウィジェットツリーのルートに配置されます。実際、これがその範囲を決定します。私たちのチームではウィジェットツリーのどこにでもコンテキストを宣言できるのはやり過ぎであることがわかりましたコンテキストウィジェットを制限してScaffold、子としてのみ(またはその派生物)を受け入れることにしましたこのようにして、最も詳細なコンテキストをページレベルにすることができ、2つのスコープを取得します。



  • などのアプリケーションレベルのウィジェットMediaQueryこれらは、アプリケーションのウィジェットツリーのルートにあるため、アプリケーションの任意のページの任意のウィジェットで使用できます
  • MyInheritedWidget上記の例のようなページレベルのウィジェット


コンテキストが適用される場所に応じて、どちらかを選択する必要があります。



ページレベルのウィジェットはルートの境界を越えることができません



当たり前のようです。ただし、ほとんどのアプリケーションには複数のレベルのナビゲーションがあるため、これには深刻な影響があります。アプリケーションは次のようになります。



> School App [App Context]
  > Student [Student Context]
    > Grades
    > Bio
  > Teacher [Teacher Context]
    > Courses
    > Bio


これはFlutterが見ているものです:



> School App [App Context]
  > Student [Student Context]
  > Student Grades
  > Student Bio
  > Teacher [Teacher Context]
  > Teacher Courses
  > Teacher Bio


Flutterの観点からは、ナビゲーション階層はありません。各ページ(またはスキャフォールド)は、アプリケーションウィジェットに関連付けられたウィジェットのツリーです。したがって、Navigator.pushこれらのページを使用して表示する場合、親コンテキストを保持するウィジェットを継承しません上記の例でStudentは、StudentページからStudentBioページにコンテキストを明示的に渡す必要があります。



コンテキストを渡す方法はいくつかありますが、ルートを昔ながらの方法でパラメーター化することをお勧めします(たとえば、名前付きルートを使用している場合はURLエンコード)。また、親ページのコンテキストを使用せずに、ルートに基づいてページを純粋に構築できるようにします。



ハッピーコーディング!



コースに間に合うように!



All Articles