TL; DR
Verwenden Sie keine umfangreichen Berechnungen innerhalb der updateShouldNotify- Methode und verwenden Sie beim Erstellen eines Widgets const anstelle von new
Zunächst sollten wir verstehen, was ein Widget-, Element- und Render-Objekt ist.
- Renderobjekte sind das, was tatsächlich auf dem Bildschirm gerendert wird. Sie sind veränderlich , enthalten die Mal- und Layoutlogik. Der Renderbaum ist dem Document Object Model (DOM) im Web sehr ähnlich, und Sie können ein Renderobjekt in diesem Baum als DOM-Knoten betrachten
- Widget - beschreibt, was gerendert werden soll. Sie sind unveränderlich und billig. Wenn also ein Widget die Frage "Was?" (Deklarativer Ansatz) beantwortet, beantwortet ein Renderobjekt die Frage "Wie?" (Imperativer Ansatz). Eine Analogie aus dem Web ist ein "virtuelles DOM".
- Element / BuildContext - ist ein Proxy zwischen Widget- und Render- Objekten. Es enthält Informationen zur Position eines Widgets im Baum * und zum Aktualisieren des Renderobjekts, wenn ein entsprechendes Widget geändert wird.
Jetzt können wir uns mit InheritedWidget und der BuildContext-Methode inheritFromWidgetOfExactType befassen .
Als Beispiel empfehle ich, dieses Beispiel aus Flatters Dokumentation über InheritedWidget zu betrachten:
class FrogColor extends InheritedWidget {
const FrogColor({
Key key,
@required this.color,
@required Widget child,
}) : assert(color != null),
assert(child != null),
super(key: key, child: child);
final Color color;
static FrogColor of(BuildContext context) {
return context.inheritFromWidgetOfExactType(FrogColor);
}
@override
bool updateShouldNotify(FrogColor old) {
return color != old.color;
}
}
InheritedWidget - nur ein Widget, das in unserem Fall eine wichtige Methode implementiert - updateShouldNotify .
updateShouldNotify - eine Funktion, die einen Parameter oldWidget akzeptiert und einen booleschen Wert zurückgibt: true oder false.
Wie jedes Widget verfügt auch InheritedWidget über ein entsprechendes Element-Objekt. Es ist InheritedElement . InheritedElement ruft updateShouldNotify jedes Mal im Widget auf, wenn wir ein neues Widget erstellen (rufen Sie setState für einen Vorfahren auf). Wenn updateShouldNotify true zurückgibt , iteriert InheritedElement durch Abhängigkeiten (?) Und ruft die Methode didChangeDependencies auf.
Woher bekommt InheritedElement Abhängigkeiten ? Hier sollten wir uns die Methode inheritFromWidgetOfExactType ansehen .
inheritFromWidgetOfExactType - Diese in BuildContext definierte Methode und
jedes Element implementiert die BuildContext-Schnittstelle (Element == BuildContext). Jedes Element hat also diese Methode.
Schauen wir uns den Code von inheritFromWidgetOfExactType an:
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
if (ancestor != null) {
assert(ancestor is InheritedElement);
return inheritFromElement(ancestor, aspect: aspect);
}
Hier versuchen wir, einen Vorfahren in _inheritedWidgets zu finden, der nach Typ zugeordnet ist. Wenn der Vorfahr gefunden wird, rufen wir inheritFromElement auf .
Der Code für inheritFromElement :
InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
- Wir fügen Vorfahren als Abhängigkeit des aktuellen Elements hinzu (_dependencies.add (Vorfahren))
- Wir fügen den Abhängigkeiten der Vorfahren ein aktuelles Element hinzu (ancestor.updateDependencies (dies, Aspekt)).
- Wir geben das Widget des Vorfahren als Ergebnis von inheritFromWidgetOfExactType zurück (return ancestor.widget)
Jetzt wissen wir also, woher InheritedElement seine Abhängigkeiten bezieht.
Schauen wir uns nun die didChangeDependencies- Methode an. Jedes Element hat diese Methode:
void didChangeDependencies() {
assert(_active); // otherwise markNeedsBuild is a no-op
assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));
markNeedsBuild();
}
Wie wir sehen können, markiert diese Methode nur ein Element als fehlerhaft und dieses Element sollte im nächsten Frame neu erstellt werden. Neu erstellen bedeutet , dass die Aufrufmethode auf dem entsprechenden Widget-Element basiert.
Aber was ist mit "Ganze Teilbäume werden neu erstellt, wenn ich InheritedWidget neu erstelle?". Hier sollten wir uns daran erinnern, dass Widgets unveränderlich sind und wenn Sie ein neues Widget erstellen, wird Flutter den Unterbaum neu erstellen. Wie können wir das beheben?
- Cache-Widgets von Hand (manuell)
- Verwenden Sie const, weil const die einzige Instanz von value / class erstellt