Метод сборки разработан таким образом, что он должен быть чистым / без побочных эффектов . Это связано с тем, что многие внешние факторы могут вызвать создание нового виджета, например:
- Маршрут поп / толчок
- Изменение размера экрана, обычно из-за изменения внешнего вида клавиатуры или ориентации
- Родительский виджет воссоздал своего дочернего элемента
- 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() {
future = Future.value(42);
super.initState();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: future,
builder: (context, snapshot) {
// create some layout here
},
);
}
}
Я это уже знаю. Я пришел сюда, потому что очень хочу оптимизировать перестройки
Также возможно сделать виджет способным к перестройке, не заставляя его дочерние элементы тоже строить.
Когда экземпляр виджета остается прежним; Flutter намеренно не восстанавливает детей. Это означает, что вы можете кэшировать части своего дерева виджетов, чтобы предотвратить ненужные перестроения.
Самый простой способ - использовать const
конструкторы дротиков :
@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 получает уведомление о новых значениях, subtree
он не будет перестраиваться, даже если StreamBuilder / Column это сделает. Это происходит потому, что благодаря закрытию экземпляр MyWidget
не изменился.
Этот паттерн часто используется в анимации. Типичное использование - AnimatedBuilder
и все переходы, такие как AlignTransition
.
Вы также можете сохранить subtree
в поле своего класса, хотя это менее рекомендуется, поскольку это нарушает функцию горячей перезагрузки.