Sposób budowania jest zaprojektowany w taki sposób, aby był czysty / bez skutków ubocznych . Dzieje się tak, ponieważ wiele czynników zewnętrznych może wywołać nową kompilację widżetu, na przykład:
- Trasa pop / push
- Zmiana rozmiaru ekranu, zwykle ze względu na wygląd klawiatury lub zmianę orientacji
- Widżet rodzica odtworzył swoje dziecko
- InheritedWidget widget zależy od
Class.of(context)
zmiany ( wzorca)
Oznacza to, że build
metoda nie powinna wyzwalać wywołania http ani modyfikować żadnego stanu .
Jak to się ma do pytania?
Problem, z którym się zmagasz, polega na tym, że metoda budowania ma skutki uboczne / nie jest czysta, co sprawia, że obce wywołania kompilacji są kłopotliwe.
Zamiast blokować wywołanie build, powinieneś uczynić swoją metodę budowania czystą, aby można ją było wywołać w dowolnym momencie bez wpływu.
W przypadku Twojego przykład, można by przekształcić swoje widgetu do StatefulWidget
następnie wyodrębnić tego połączenia HTTP do initState
twoich 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) {
},
);
}
}
Już to wiem. Przyszedłem tutaj, ponieważ naprawdę chcę zoptymalizować przebudowy
Możliwe jest również stworzenie widgetu zdolnego do odbudowy bez zmuszania jego elementów podrzędnych do budowania.
Gdy instancja widgetu pozostaje taka sama; Flutter celowo nie odbuduje dzieci. Oznacza to, że można buforować części drzewa widżetów, aby zapobiec niepotrzebnym przebudowom.
Najłatwiej jest użyć const
konstruktorów rzutek :
@override
Widget build(BuildContext context) {
return const DecoratedBox(
decoration: BoxDecoration(),
child: Text("Hello World"),
);
}
Dzięki temu const
słowu kluczowemu instancja DecoratedBox
pozostanie taka sama, nawet jeśli build był wywoływany setki razy.
Ale możesz osiągnąć ten sam wynik ręcznie:
@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,
],
);
},
);
}
W tym przykładzie, gdy StreamBuilder zostanie powiadomiony o nowych wartościach, subtree
nie zostanie odbudowany, nawet jeśli StreamBuilder / Column. Dzieje się tak, ponieważ dzięki zamknięciu instancja MyWidget
nie uległa zmianie.
Ten wzór jest często używany w animacjach. Typowe zastosowania AnimatedBuilder
i wszystkie przejścia, takie jak AlignTransition
.
Możesz również przechowywać subtree
w polu swojej klasy, chociaż jest to mniej zalecane, ponieważ łamie to funkcję hot-reload.