不要なウィジェットビルドに対処するにはどうすればいいですか? 質問する

不要なウィジェットビルドに対処するにはどうすればいいですか? 質問する

さまざまな理由により、buildウィジェットのメソッドが再度呼び出されることがあります。

親がアップデートされたためにこれが発生することはわかっています。しかし、これは望ましくない影響を引き起こします。問題が発生する典型的な状況は、FutureBuilder次の方法を使用する場合です。

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

この例では、ビルドメソッドが再度呼び出されると、別の HTTP 要求がトリガーされます。これは望ましくありません。

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

ベストアンサー1

ビルドメソッドは純粋で副作用のないように設計されています。これは、次のような多くの外部要因によって新しいウィジェットのビルドがトリガーされる可能性があるためです。

  • ルートポップ/プッシュ
  • 画面のサイズ変更(通常はキーボードの外観や向きの変更による)
  • 親ウィジェットが子ウィジェットを再作成した
  • ウィジェットが依存するInheritedWidget(Class.of(context)パターン)の変更

つまり、buildメソッドはhttp 呼び出しをトリガーしたり、状態を変更したりしてはならないということです。


これは質問とどう関係するのでしょうか?

あなたが直面している問題は、ビルド方法に副作用があり、純粋ではないため、余分なビルド呼び出しが面倒になることです。

ビルド呼び出しを防止する代わりに、ビルド メソッドを純粋にして、影響を与えることなくいつでも呼び出せるようにする必要があります。

あなたの例の場合、ウィジェットを に変換し、StatefulWidgetその HTTP 呼び出しを の に抽出しinitStateますState

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

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

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

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

これはもうわかっています。再構築を最適化したいのでここに来ました

子ウィジェットのビルドを強制せずにウィジェットを再構築できるようにすることも可能です。

ウィジェットのインスタンスが同じままの場合、Flutter は意図的に子を再構築しません。これは、ウィジェット ツリーの一部をキャッシュして、不要な再構築を防ぐことができることを意味します。

最も簡単な方法は、Dartconstコンストラクターを使用することです。

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

このキーワードのおかげで、ビルドが何百回呼び出されても のconstインスタンスは同じままになります。DecoratedBox

ただし、手動でも同じ結果を得ることができます。

@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 に新しい値が通知されると、 StreamBuilder/Column が再構築されても、は再構築されません。これは、クロージャのおかげで、のインスタンスが変更されなかったsubtreeために発生します。MyWidget

このパターンはアニメーションでよく使用されます。典型的な使用法はAnimatedBuilder、 や などのすべてのトランジションですAlignTransition

クラスのフィールドに保存することもできますsubtreeが、ホットリロード機能が壊れるためあまりお勧めできません。

おすすめ記事