TL; DR
Non utilizzare calcoli pesanti all'interno del metodo updateShouldNotify e utilizzare const invece di new quando si crea un widget
Prima di tutto, dobbiamo capire cosa sono oggetti Widget, Element e Render.
- Gli oggetti di rendering sono ciò che viene effettivamente visualizzato sullo schermo. Sono mutevoli , contengono la logica della pittura e del layout. L'albero di rendering è molto simile al DOM (Document Object Model) nel Web e puoi guardare un oggetto di rendering come un nodo DOM in questo albero
- Widget : è una descrizione di ciò che dovrebbe essere reso. Sono immutabili ed economici. Quindi, se un widget risponde alla domanda "Cosa?" (Approccio dichiarativo), un oggetto Render risponde alla domanda "Come?" (Approccio imperativo). Un'analogia dal web è un "DOM virtuale".
- Element / BuildContext - è un proxy tra oggetti Widget e Render . Contiene informazioni sulla posizione di un widget nell'albero * e su come aggiornare l'oggetto Render quando viene modificato un widget corrispondente.
Ora siamo pronti per immergerci nel metodo InheritedWidget e BuildContext inheritFromWidgetOfExactType .
Come esempio, consiglio di considerare questo esempio dalla documentazione di Flutter su InheritedWidget:
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 - solo un widget che implementa nel nostro caso un metodo importante - updateShouldNotify .
updateShouldNotify - una funzione che accetta un parametro oldWidget e restituisce un valore booleano: true o false.
Come ogni widget, InheritedWidget ha un oggetto Element corrispondente. È InheritedElement . InheritedElement chiama updateShouldNotify sul widget ogni volta che creiamo un nuovo widget (chiama setState su un antenato). Quando updateShouldNotify restituisce true, InheritedElement itera attraverso le dipendenze (?) E chiama il metodo didChangeDependencies su di esso.
Dove InheritedElement ottiene le dipendenze ? Qui dovremmo esaminare il metodo inheritFromWidgetOfExactType .
inheritFromWidgetOfExactType : questo metodo definito in BuildContext e
ogni elemento implementa l'interfaccia BuildContext (Element == BuildContext). Quindi ogni elemento ha questo metodo.
Diamo un'occhiata al codice di inheritFromWidgetOfExactType:
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
if (ancestor != null) {
assert(ancestor is InheritedElement);
return inheritFromElement(ancestor, aspect: aspect);
}
Qui proviamo a trovare un antenato in _inheritedWidgets mappato per tipo. Se viene trovato l'antenato, chiamiamo inheritFromElement .
Il codice per inheritFromElement :
InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
- Aggiungiamo ancestor come dipendenza dell'elemento corrente (_dependencies.add (ancestor))
- Aggiungiamo l'elemento corrente alle dipendenze dell'antenato (ancestor.updateDependencies (questo, aspetto))
- Restituiamo il widget dell'antenato come risultato di inheritFromWidgetOfExactType (return ancestor.widget)
Quindi ora sappiamo dove InheritedElement ottiene le sue dipendenze.
Ora diamo un'occhiata al metodo didChangeDependencies . Ogni elemento ha questo metodo:
void didChangeDependencies() {
assert(_active); // otherwise markNeedsBuild is a no-op
assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));
markNeedsBuild();
}
Come possiamo vedere, questo metodo contrassegna semplicemente un elemento come sporco e questo elemento dovrebbe essere ricostruito nel frame successivo. Ricostruire significa chiamare il metodo costruito sull'elemento widget corrispondente.
Ma che dire di "Ricostruzioni dell'intero sottoalbero quando ricostruisco InheritedWidget?". Qui dobbiamo ricordare che i widget sono immutabili e se crei un nuovo widget Flutter ricostruirà il sottoalbero. Come possiamo aggiustarlo?
- Cache widget manualmente (manualmente)
- Usa const perché const crea l'unica istanza di value / class