Как устроена эта система на самом деле
Сам по себе 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.