Как бороться с нежелательной сборкой виджетов? - PullRequest
0 голосов
/ 10 сентября 2018

По разным причинам иногда вызывается метод build моих виджетов.

Я знаю, что это происходит потому, что родитель обновился. Но это вызывает нежелательные последствия. Типичная ситуация, когда это вызывает проблемы, при использовании FutureBuilder таким образом:

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

В этом примере, если метод build будет вызван снова, он вызовет другой http-запрос. Что нежелательно.

Учитывая это, как бороться с нежелательной сборкой? Есть ли способ предотвратить вызов сборки?

1 Ответ

0 голосов
/ 10 сентября 2018

Метод build разработан таким образом, что он должен быть pure / без побочных эффектов . Это связано с тем, что многие внешние факторы могут инициировать создание нового виджета, например:

  • Route pop / push, для анимации входа / выхода
  • Изменение размера экрана, как правило, из-за появления клавиатуры или изменения ориентации
  • Родительский виджет воссоздал своего потомка
  • Унаследованный виджет, от которого зависит виджет (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() {
    future = Future.value(42);
    super.initState();
  }

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

  • Также возможно сделать виджет способным к восстановлению, не заставляя его потомков строить тоже.

Когда экземпляр виджета остается прежним; Флаттер целенаправленно не восстановит детей. Это означает, что вы можете кэшировать части своего дерева виджетов, чтобы предотвратить ненужные перестройки.

Самый простой способ - использовать конструкторы 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 и все * Transition, такие как AlignTransition.

Вы также можете хранить subtree в поле вашего класса, хотя это менее рекомендуется, так как оно не требует горячей перезагрузки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...