Best way to use the MediaQuey

Hey everyone!

Right now I’m in doubt about the best way to use the MediaQuery, I’m searching for how to use it in the most optimised way.

I saw two ways to get the height of the screen:
Using MediaQuery.of(context).size.height and using MediaQuery.sizedOf(context).height, so far as I know, using sizedOf could be better because it uses the newest API to get the resources and is faster than the MediaQuery.of (fix me if wrong).

Another doubt that I have is where I should call it to avoid calling it multiple times without necessity.

Should I call it on the init like that:


on the didChangeDependencies method:

Or just call it inside the builder?

Fixing you:

Problem with MediaQuery.of is that it will trigger a rebuild for ANY of the properties it supports. For example: if you are interest in the screen size, but something else in the MediaQuery has changed, it will still trigger a rebuild. That’s why Google added some granular options for you to listen only to what you actually want.

TLDR: cache it on a variable.

And, yes, searching the tree for a match (that’s a thing ALL .of(context) does) it’s waste of time. You should cache it (I usually write final theme = Theme.of(context); as the first line of my build method when I require to use the current theme on that method.

BTW, methods (which are called using name()) must ALWAYS be cached because they often have side effects or heavy computations. Only properties (which are called using name) can be used this way (because, if they are written correctly, they are only exposing a private variable inside a class).

Hey @evaluator118, thank you so much for the input.
Can you explain that part better?

You don’t want pixel perfect. You want responsive. And Flutter has many amazing tools to make responsive layouts, like LayoutBuilder for breakpoints, and Flex (Row/Column) widgets for adaptive sizing. Figma and other mockup tools are generally very poor at representing this… the best tool to preview layout designs is Flutter itself (thanks to hot reload). Oh, and familiarize yourself with less-referenced layout widgets like FittedBox, FractionallySizedBox, AspectRatio, Spacer, Wrap, and learn when to use double.infinity for a width or height rather than querying with MediaQuery for the useless screen size. This is a great writeup from the author of Boxy on the fundamentals of Flutter layout including MediaQuery: MediaQuery - ping's notes.

3 Likes

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).

1 Like

BTW, you CANNOT use InheritedWidget or InheritedModel in the initState method (all those .of and .maybeOf).

Use those methods only on build. You want to re-execute those .of(context) methods when the thing you’re trying to access changes (for example: if you could get the current Theme in the initState and cache it and then the user changed the OS to dark mode, your build would run again, but the theme.brightness would not represent the current state because you cache it.

So, as a rule: whenever you need a XXX.of(context), put it in the first lines inside your build(BuildContext context) method:

Widget build(BuildContext context) {
  final theme = Theme.of(context);
  final viewPadding = MediaQuery.paddingOf(context);

  return WidgetThatUsesThoseVariableAbove();
}

This is also valid:

Widget build(BuildContext context) {
  return ListenableBuilder(
    builder: (context, value) {
      final theme = Theme.of(context);

      return SomeWidgetThatRespondsToThemeChanges();
    }
  );
}

So, use .of near where you’ll use it. They all will attach themselves to your widget and trigger a rebuild automatically when the thing they return changes.

1 Like

Thank you so much for the feedback and now it was more clear.
Thank you <3

According to State class - widgets library - Dart API, context is available and established by the time initState is running.

context means nothing (of course it is established, because it represents the element tree), but you CANNOT use inherited widgets that uses dependOnInheritedWidgetOfExactType:

Source: the source code:

/// You should not use [BuildContext.dependOnInheritedWidgetOfExactType] from this
/// method. However, [didChangeDependencies] will be called immediately
/// following this method, and [BuildContext.dependOnInheritedWidgetOfExactType] can
/// be used there.
///
/// Implementations of this method should start with a call to the inherited
/// method, as in `super.initState()`.