Let’s take, for example, the State<StatefulWidget>. When you need to know if the widget is mounted, you just ask if(context.mounted).
If you check the signature of BuildContext, you’ll see that it is a property:
bool get mounted;
This (almost always) means that reading this value is fast to be read and there isn’t work being done to compute it.
Now, compare with MediaQuery.sizeOf(context):
First, it will call a InheritedModel (not an InheritedWidget):
static Size sizeOf(BuildContext context) => _of(context, _MediaQueryAspect.size).size;
Then the rabbit goes down the hole until it reaches this method (don’t worry in trying to read or understand it):
static T? inheritFrom<T extends InheritedModel<Object>>(BuildContext context, {Object? aspect}) {
if (aspect == null) {
return context.dependOnInheritedWidgetOfExactType<T>();
}
// Create a dependency on all of the type T ancestor models up until
// a model is found for which isSupportedAspect(aspect) is true.
final List<InheritedElement> models = <InheritedElement>[];
_findModels<T>(context, aspect, models);
if (models.isEmpty) {
return null;
}
final InheritedElement lastModel = models.last;
for (final InheritedElement model in models) {
final T value = context.dependOnInheritedElement(model, aspect: aspect) as T;
if (model == lastModel) {
return value;
}
}
assert(false);
return null;
}
}
Notice how complex this is. The important part here is that _findModels call:
static void _findModels<T extends InheritedModel<Object>>(
BuildContext context,
Object aspect,
List<InheritedElement> results,
) {
final InheritedElement? model = context.getElementForInheritedWidgetOfExactType<T>();
if (model == null) {
return;
}
results.add(model);
assert(model.widget is T);
final T modelWidget = model.widget as T;
if (modelWidget.isSupportedAspect(aspect)) {
return;
}
Element? modelParent;
model.visitAncestorElements((Element ancestor) {
modelParent = ancestor;
return false;
});
if (modelParent == null) {
return;
}
_findModels<T>(modelParent!, aspect, results);
}
Notice that this is a recursive function! It will go up the tree trying to find the first occurence of your InheritedModel (in this example, the MediaQuery.sizeOf).
That’s the main difference between properties and methods: usually, properties are safe to read and they don’t do almost any job, while methods can be very nasty, such as this one.
So, as a rule, whenever you need to get the value of something that you can use in two or more places (such as Theme.of(context)), it’s always wise to cache it: final theme = Theme.of(context).