1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/CarGuo-GSYFlutterBook

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
Flutter-N25.md 11 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 10.03.2025 00:06 5767d61

Небольшие хитрости Flutter 3.10: оптимизация MediaQuery и использование InheritedModel

Мы уже достаточно подробно говорили о 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`, даже если он недоступен для пользователя.![Логи](http://img.cdn.guoshuyu.cn/20230602_N25/image1.png)

Хотя ранее были предложены способы решения этой проблемы, начиная с версии 3.10 есть более элегантный подход, который также позволяет нам более точно контролировать связи внутри `InheritedWidget`. Этот подход заключается в использовании `InheritedModel`. # MediaQuery

С версии 3.10 в `MediaQuery` были добавлены методы `***of`, такие как `MediaQuery.platformBrightnessOf(context);`. Эти методы имеют соответствующие имперсональные типы в `_MediaQueryAspect`, а вызовы этих параметров в Flutter Framework были заменены новыми методами типа `***of`.

![image](http://img.cdn.guoshuyu.cn/20230602_N25/image2.png)

Например, начальный пример можно изменить так, чтобы вместо использования `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`, когда следует вызвать обновление данных**.

![image](http://img.cdn.guoshuyu.cn/20230602_N25/image3.png)
![image](http://img.cdn.guoshuyu.cn/20230602_N25/image4.png)

> Примечание: **в версии 3.10 теперь можно заменить получение параметров через `MediaQuery.of` на использование методов типа `MediaQuery.***Of(context);` для снижения количества ненужных rebuild**.# InheritedModel

Чтобы использовать `InheritedModel`, достаточно его наследовать. Основное внимание следует уделять методу `updateShouldNotifyDependent`, который используется для определения необходимости rebuild.

На следующем рисунке показана реализация `MediaQuery`. В методе `updateShouldNotifyDependent` мы можем различать типы через `dependencies`, например, если вызов происходит через `InheritedModel.inheritFrom<MediaQuery>(context, aspect: _MediaQueryAspect.size)`, то проверка будет выполнена для `_MediaQueryAspect.size`.

![Рисунок](http://img.cdn.guoshuyu.cn/20230602_N25/image5.png)

Если параметр `size` изменится, метод вернёт `true`, что приведёт к rebuild. В противном случае он вернёт `false`, что объясняет возможность `InheritedModel` обновлять страницу в зависимости от конкретных переменных.

> Конечно, здесь вы можете передавать не только перечисления, но и строки, в зависимости от ваших предпочтений. Почему метод `inheritFrom` в классе `InheritedModel` достигает такого эффекта?

Давайте рассмотрим реализацию метода `inheritFrom`, как показано ниже:

- Без использования аспекта (`aspect`) вызывается метод `dependOnInheritedWidgetOfExactType()`. Это аналогично использованию обычного `of(context)`.
- При наличии аспекта сначала вызывается метод `_findModels`, который находит соответствующий `InheritedElement`, после чего происходит привязка через метод `dependOnInheritedElement()`.

![Картинка](http://img.cdn.guoshuyu.cn/20230602_N25/image6.png)Может возникнуть вопрос, но давайте рассмотрим методы `dependOnInheritedWidgetOfExactType()` и `dependOnInheritedElement()`. Внутри первого метода используется второй для выполнения привязки. Какова же разница между ними?Если внимательно посмотреть, то различие заключается в том, что метод `dependOnInheritedElement()` использует аспект (`aspect`). В контексте `MediaQuery` это может быть, например, `_MediaQueryAspect.size`.

Это значит, что при последующем вызове `updateShouldNotifyDependent` будет использоваться этот аспект.

На следующей картинке представлен класс `InheritedModelElement` из `InheritedModel`. Можно заметить, что переданный аспект (`aspect`) становится зависимостью (`dependencies`).

![Картинка](http://img.cdn.guoshuyu.cn/20230602_N25/image7.png)

Наконец, в методе `notifyDependent` можно видеть, что `didChangeDependencies` вызывается только тогда, когда `updateShouldNotifyDependent` возвращает `true`.

Специфика метода `inheritFrom` состоит в том, что если существует аспект, он становится частью коллекции зависимостей (`dependencies`), и решение о необходимости обновления принимается путём вызова `updateShouldNotifyDependent`.

Что касается метода `_findModels`, вам не обязательно беспокоиться о его деталях. Хотя он принимает список, обычно вы получите только один элемент. Возможность получения нескольких элементов возникает, если вы переопределили метод `isSupportedAspect` класса `InheritedModel` и ваши условия позволяют несколько значений.

![Картинка](http://img.cdn.guoshuyu.cn/20230602_N25/image8.png)

Например, если вы наследуетесь от `InheritedModel`, метод `isSupportedAspect` позволяет определить, поддерживаются ли аспекты для данного экземпляра.

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

![image](http://img.cdn.guoshuyu.cn/20230602_N25/image9.png)

# В заключение

В заключении можно сказать, что сегодняшний совет очень простой: **обновите ваш `MediaQuery.of` до соответствующего `MediaQuery.of` с нужными параметрами, чтобы повысить производительность приложения**, и познакомьтесь с логикой реализации `InheritedModel` и возможностью его кастомизации, что поможет вам оптимизировать использование `InheritedWidget`.

Если у вас есть вопросы, просьба оставить свои комментарии ниже.

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/CarGuo-GSYFlutterBook.git
git@api.gitlife.ru:oschina-mirror/CarGuo-GSYFlutterBook.git
oschina-mirror
CarGuo-GSYFlutterBook
CarGuo-GSYFlutterBook
master