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 buildmetoda 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 StatefulWidgetnastępnie wyodrębnić tego połączenia HTTP do initStatetwoich 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ć constkonstruktorów rzutek :
@override
Widget build(BuildContext context) {
return const DecoratedBox(
decoration: BoxDecoration(),
child: Text("Hello World"),
);
}
Dzięki temu constsłowu kluczowemu instancja DecoratedBoxpozostanie 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, subtreenie zostanie odbudowany, nawet jeśli StreamBuilder / Column. Dzieje się tak, ponieważ dzięki zamknięciu instancja MyWidgetnie uległa zmianie.
Ten wzór jest często używany w animacjach. Typowe zastosowania AnimatedBuilderi wszystkie przejścia, takie jak AlignTransition.
Możesz również przechowywać subtreew polu swojej klasy, chociaż jest to mniej zalecane, ponieważ łamie to funkcję hot-reload.