Мы уже достаточно подробно говорили о MediaQuery
, например в этой статье, где было отмечено, что необходимо аккуратно использовать MediaQuery.of(context)
вне компонента Scaffold
, так как метод MediaQuery.of
может привести к ненужному расходу производительности за счет связывания контекста BuildContext
. Например, при появлении клавиатуры могут возникнуть проблемы с перестроением страниц, связанных с вызовом MediaQuery.of(context)
.
Рассмотрим следующий пример, где мы используем MediaQuery.of(context).size
в классе MyHomePage
и выводим его значение:
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("######### MyHomePage ${MediaQuery.of(context).size}");
return Scaffold(
body: Container(
alignment: Alignment.center,
child: InkWell(
onTap: () {
Navigator.of(context).push(CupertinoPageRoute(builder: (context) {
return EditPage();
}));
},
child: new Text(
"Клик",
style: TextStyle(fontSize: 50),
),
),
),
);
}
}
class EditPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: new Text("EditPage"),
),
extendBody: true,
body: Column(
children: [
new Spacer(),
new Container(
margin: EdgeInsets.all(10),
child: new Center(
child: new TextField(),
),
),
new Spacer(),
],
),
);
}
}
```Как показывают логи ниже, когда клавиатура появляется, размер нижней части экрана изменяется, что приводит к изменениям данных `MediaQueryData`. Это, в свою очередь, приводит к постоянной перестройке виджета `MyHomePage`, даже если он недоступен для пользователя.
Хотя ранее были предложены способы решения этой проблемы, начиная с версии 3.10 есть более элегантный подход, который также позволяет нам более точно контролировать связи внутри `InheritedWidget`. Этот подход заключается в использовании `InheritedModel`. # MediaQuery
С версии 3.10 в `MediaQuery` были добавлены методы `***of`, такие как `MediaQuery.platformBrightnessOf(context);`. Эти методы имеют соответствующие имперсональные типы в `_MediaQueryAspect`, а вызовы этих параметров в Flutter Framework были заменены новыми методами типа `***of`.

Например, начальный пример можно изменить так, чтобы вместо использования `MediaQuery.of(context).size` использовать `MediaQuery.sizeOf(context)`. Это позволит избежать лишних rebuild при открытии страницы `EditPage` и появлении клавиатуры.
Причина такого поведения заключается в том, что эти методы `MediaQuery.*****Of(context)` используют внутренний вызов `InheritedModel.inheritFrom`.
Да, с версии 3.10 `MediaQuery` перешел от наследования `InheritedWidget` к `InheritedModel` ([см. коммит](https://github.com/flutter/flutter/commit/73cb7c2fc5ecab0476ef5d41d1227ed09f73db56)). **Метод `inheritFrom` класса `InheritedModel` позволяет разработчику определять через `aspect`, когда следует вызвать обновление данных**.


> Примечание: **в версии 3.10 теперь можно заменить получение параметров через `MediaQuery.of` на использование методов типа `MediaQuery.***Of(context);` для снижения количества ненужных rebuild**.# InheritedModel
Чтобы использовать `InheritedModel`, достаточно его наследовать. Основное внимание следует уделять методу `updateShouldNotifyDependent`, который используется для определения необходимости rebuild.
На следующем рисунке показана реализация `MediaQuery`. В методе `updateShouldNotifyDependent` мы можем различать типы через `dependencies`, например, если вызов происходит через `InheritedModel.inheritFrom<MediaQuery>(context, aspect: _MediaQueryAspect.size)`, то проверка будет выполнена для `_MediaQueryAspect.size`.

Если параметр `size` изменится, метод вернёт `true`, что приведёт к rebuild. В противном случае он вернёт `false`, что объясняет возможность `InheritedModel` обновлять страницу в зависимости от конкретных переменных.
> Конечно, здесь вы можете передавать не только перечисления, но и строки, в зависимости от ваших предпочтений. Почему метод `inheritFrom` в классе `InheritedModel` достигает такого эффекта?
Давайте рассмотрим реализацию метода `inheritFrom`, как показано ниже:
- Без использования аспекта (`aspect`) вызывается метод `dependOnInheritedWidgetOfExactType()`. Это аналогично использованию обычного `of(context)`.
- При наличии аспекта сначала вызывается метод `_findModels`, который находит соответствующий `InheritedElement`, после чего происходит привязка через метод `dependOnInheritedElement()`.
Может возникнуть вопрос, но давайте рассмотрим методы `dependOnInheritedWidgetOfExactType()` и `dependOnInheritedElement()`. Внутри первого метода используется второй для выполнения привязки. Какова же разница между ними?Если внимательно посмотреть, то различие заключается в том, что метод `dependOnInheritedElement()` использует аспект (`aspect`). В контексте `MediaQuery` это может быть, например, `_MediaQueryAspect.size`.
Это значит, что при последующем вызове `updateShouldNotifyDependent` будет использоваться этот аспект.
На следующей картинке представлен класс `InheritedModelElement` из `InheritedModel`. Можно заметить, что переданный аспект (`aspect`) становится зависимостью (`dependencies`).

Наконец, в методе `notifyDependent` можно видеть, что `didChangeDependencies` вызывается только тогда, когда `updateShouldNotifyDependent` возвращает `true`.
Специфика метода `inheritFrom` состоит в том, что если существует аспект, он становится частью коллекции зависимостей (`dependencies`), и решение о необходимости обновления принимается путём вызова `updateShouldNotifyDependent`.
Что касается метода `_findModels`, вам не обязательно беспокоиться о его деталях. Хотя он принимает список, обычно вы получите только один элемент. Возможность получения нескольких элементов возникает, если вы переопределили метод `isSupportedAspect` класса `InheritedModel` и ваши условия позволяют несколько значений.

Например, если вы наследуетесь от `InheritedModel`, метод `isSupportedAspect` позволяет определить, поддерживаются ли аспекты для данного экземпляра.
```dart
@override
bool isSupportedAspect(Object aspect) {
return aspects == null || aspects!.contains(aspect);
}
```Дополнительно в методе `_findModels` используется функция `getElementForInheritedWidgetOfExactType()`, которая отличается от `dependOnInheritedWidgetOfExactType()` тем, что первая регистрирует зависимость, а вторая — нет. Поэтому метод `_findModels` просто находит все подходящие `InheritedModel`.

# В заключение
В заключении можно сказать, что сегодняшний совет очень простой: **обновите ваш `MediaQuery.of` до соответствующего `MediaQuery.of` с нужными параметрами, чтобы повысить производительность приложения**, и познакомьтесь с логикой реализации `InheritedModel` и возможностью его кастомизации, что поможет вам оптимизировать использование `InheritedWidget`.
Если у вас есть вопросы, просьба оставить свои комментарии ниже.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )