Z różnych powodów czasami build
metoda moich widżetów jest wywoływana ponownie.
Wiem, że dzieje się tak, ponieważ rodzic się zaktualizował. Ale to powoduje niepożądane skutki. Typowa sytuacja, w której powoduje to problemy, ma miejsce podczas używania w FutureBuilder
ten sposób:
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: httpCall(),
builder: (context, snapshot) {
// create some layout here
},
);
}
W tym przykładzie, jeśli metoda budowania miałaby zostać wywołana ponownie, wyzwoliłoby to kolejne żądanie HTTP. Co jest niepożądane.
Biorąc to pod uwagę, jak poradzić sobie z niechcianą kompilacją? Czy istnieje sposób, aby zapobiec wywołaniu kompilacji?
flutter
flutter-widget
Rémi Rousselet
źródło
źródło
.value
a niechcianą kompilacją widżetu lubbuild
metodą, która musi być czysta.Odpowiedzi:
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:
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 doinitState
twoichState
: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 }, ); } }
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 instancjaDecoratedBox
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 instancjaMyWidget
nie uległa zmianie.Ten wzór jest często używany w animacjach. Typowe zastosowania
AnimatedBuilder
i wszystkie przejścia, takie jakAlignTransition
.Możesz również przechowywać
subtree
w polu swojej klasy, chociaż jest to mniej zalecane, ponieważ łamie to funkcję hot-reload.źródło
subtree
w polu klasy przerywa przeładowywanie na gorąco?StreamBuilder
tym, że kiedy pojawia się klawiatura, ekran się zmienia, więc trasy trzeba odbudować. WięcStreamBuilder
jest odbudowywany iStreamBuilder
tworzony jest nowy i subskrybujestream
. GdyStreamBuilder
subskrybuje elementstream
,snapshot.connectionState
staje się,ConnectionState.waiting
co powoduje, że mój kod zwraca aCircularProgressIndicator
, a następniesnapshot.connectionState
zmienia się, gdy są dane, a mój kod zwróci inny widżet, co powoduje migotanie ekranu przy różnych elementach.StatefulWidget
, zasubskrybowaćstream
oninitState()
i ustawićcurrentWidget
zsetState()
jakstream
wysyła nowe dane, przekazująccurrentWidget
dobuild()
metody. Czy jest lepsze rozwiązanie?FutureBuilder
.Możesz zapobiec niechcianym wywołaniom kompilacji, używając tych sposobów
1) Utwórz podrzędną klasę Statefull dla indywidualnej małej części interfejsu użytkownika
2) Użyj biblioteki Provider , dzięki czemu możesz zatrzymać niechciane wywołanie metody budowania
W poniższej sytuacji wywołanie metody budowania
źródło
Flutter też
ValueListenableBuilder<T> class
. Pozwala przebudować tylko niektóre widżety niezbędne do twojego celu i pominąć drogie widżety.możesz zobaczyć dokumenty tutaj ValueListenableBuilder flutter docs
lub tylko przykładowy kod poniżej:
return Scaffold( appBar: AppBar( title: Text(widget.title) ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text('You have pushed the button this many times:'), ValueListenableBuilder( builder: (BuildContext context, int value, Widget child) { // This builder will only get called when the _counter // is updated. return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ Text('$value'), child, ], ); }, valueListenable: _counter, // The child parameter is most helpful if the child is // expensive to build and does not depend on the value from // the notifier. child: goodJob, ) ], ), ), floatingActionButton: FloatingActionButton( child: Icon(Icons.plus_one), onPressed: () => _counter.value += 1, ), );
źródło