1. ホーム
  2. ヒラメキ

[解決済み】不要なウィジェットビルドの対処方法は?

2022-04-02 17:32:44

質問

様々な理由により、時々 build メソッドが再度呼び出されます。

親が更新されたために起こることは分かっています。しかし、これは望ましくない効果を引き起こします。 この問題が発生する典型的な場面は FutureBuilder このように

@override
Widget build(BuildContext context) {
  return FutureBuilder(
    future: httpCall(),
    builder: (context, snapshot) {
      // create some layout here
    },
  );
}

この例では、もし ビルド メソッドが再び呼び出された場合、別の HTTP リクエストが発生します。これは望ましいことではありません。

このことを考えると、不要なビルドにどう対処すればいいのでしょうか?ビルドの呼び出しを防ぐ方法はないのでしょうか?

解決方法は?

その ビルド メソッドは、そのような設計になっています。 純粋/副作用なし . これは、次のような多くの外的要因が、新しいウィジェット構築の引き金となる可能性があるためです。

  • ルートpop/push
  • キーボードの表示や向きの変更に伴う画面サイズの変更。
  • 親ウィジェットが子ウィジェットを再作成した
  • ウィジェットが依存するInheritedWidget ( Class.of(context) パターン)変更

これはつまり build メソッドは ない http 呼び出しのトリガーとなったり、状態を変更したりします。 .


質問との関連は?

あなたが直面している問題は、あなたのビルドメソッドが副作用を持ち、純粋でないため、余計なビルドコールを厄介なものにしていることです。

ビルドコールを防ぐ代わりに、ビルドメソッドを純粋なものにし、いつでも影響を受けずに呼び出せるようにする必要があります。

この例の場合、ウィジェットを StatefulWidget への HTTP 呼び出しを抽出し、その HTTP 呼び出しを initStateState :

class Example extends StatefulWidget {
  @override
  _ExampleState createState() => _ExampleState();
}

class _ExampleState extends State<Example> {
  Future<int> future;

  @override
  void initState() {
    future = Future.value(42);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: future,
      builder: (context, snapshot) {
        // create some layout here
      },
    );
  }
}


もう分かっているんです。私がここに来たのは 本当に リビルドを最適化したい

また、子ウィジェットも強制的にビルドさせることなく、リビルドが可能なウィジェットを作成することも可能です。

ウィジェットのインスタンスが同じままだと、Flutterは意図的に子ウィジェットをリビルドしないようにします。これは、不要な再構築を防ぐために、Widget Treeの一部をキャッシュできることを意味します。

最も簡単な方法は、dartを使用することです。 const のコンストラクタを使用します。

@override
Widget build(BuildContext context) {
  return const DecoratedBox(
    decoration: BoxDecoration(),
    child: Text("Hello World"),
  );
}

おかげさまで const キーワードのインスタンスは DecoratedBox は、buildが何百回呼ばれても同じです。

しかし、同じ結果を手動で達成することができます。

@override
Widget build(BuildContext context) {
  final subtree = MyWidget(
    child: Text("Hello World")
  );

  return StreamBuilder<String>(
    stream: stream,
    initialData: "Foo",
    builder: (context, snapshot) {
      return Column(
        children: <Widget>[
          Text(snapshot.data),
          subtree,
        ],
      );
    },
  );
}

この例では、StreamBuilderが新しい値を通知されるとき。 subtree は、StreamBuilder/Columnが再構築されても、再構築されない。 これは、クロージャのおかげで MyWidget は変更されていない。

このパターンはアニメーションでよく使われる。典型的な使い方は AnimatedBuilder のようなすべてのトランジションと AlignTransition .

を格納することもできます。 subtree をクラスのフィールドに追加することをお勧めします。ただし、ホットリロード機能が壊れてしまうので、あまりお勧めできません。