Как устроена эта система на самом деле

Сам по себе InheritedWidget не умеет хранить или менять состояние. Он иммутабельный.

Чтобы превратить его в что-то похожее на state management, обычно используется связка из трёх частей:

1. StatefulWidget (мозг)

Хранит изменяемое состояние и обновляет его через setState.

2. InheritedWidget (курьер)

Живёт внутри build() у StatefulWidget. Его задача — взять актуальные данные и раздать их вниз по дереву.

3. BuildContext (навигатор)

Через него дочерние виджеты находят ближайший InheritedWidget вверх по дереву за константное время O(1).

StatefulWidget (Хранит данные + setState)

InheritedWidget (Берет данные и пробрасывает вниз)

┌─────┴─────┐

▼ ▼

Widget A Widget B (Вызывает .of(context) и подписывается)

Что происходит при изменении данных

1. Вызывается setState.

2. Пересоздаётся InheritedWidget с новыми данными.

3. Срабатывает метод updateShouldNotify.

4. Если он возвращает true ➡️ Flutter говорит всем зависимым виджетам (тем, кто вызвал .of(context)) принудительно перестроиться.

Где это используется во Flutter

Ты сталкиваешься с этим постоянно:

  • Theme.of(context) ➡️ используется InheritedTheme. Смена темы обновляет UI всего приложения.

  • MediaQuery.of(context) ➡️ данные о размере экрана, ориентации и плотности. Поворот экрана вызывает обновление UI.

  • Navigator.of(context) ➡️ доступ к навигации и стеку экранов через дерево.

  • FocusScope.of(context) ➡️ управление фокусом элементов ввода.

Итог

InheritedWidget — это не устаревшая штука, которую пора выкинуть. Это фундамент Flutter, на котором держится половина фреймворка.

Просто напрямую в коммерческих проектах его используют редко из-за монструозного количества шаблонного кода (бойлерплейта). Поверх него написаны более удобные абстракции, которые берут эту рутину на себя.

Если совсем просто: InheritedWidget — это встроенный механизм доставки данных по дереву, на рельсах которого построены Provider, Riverpod и сам Flutter.