Какие ключевые абстракции использует Flutter?

Ответ

Основные абстракции Flutter образуют трёхуровневую архитектуру рендеринга:

  1. Widget — декларативное описание части UI. Все элементы, от кнопки до целого экрана, являются виджетами. Они бывают:

    • StatelessWidget — для статического контента.
    • StatefulWidget — для динамического контента с внутренним состоянием.
  2. Element — "живой" экземпляр виджета в дереве элементов. Связывает виджет с RenderObject и управляет жизненным циклом. При перестроении виджета (build()), элемент сравнивает новый виджет со старым и обновляет поддерево, если это необходимо.

  3. RenderObject — отвечает за вычисление layout (размеры и позиции) и непосредственную отрисовку (painting). Это низкоуровневая абстракция, с которой обычно не работают напрямую.

  4. BuildContext — это ссылка на Element. Используется для:

    • Навигации (Navigator.of(context)).
    • Доступа к данным темы или локализации через InheritedWidget.
    • Получения метрик экрана (MediaQuery.of(context)).
  5. InheritedWidget — специальный виджет для эффективной передачи данных вниз по дереву. Позволяет виджетам-потомкам подписываться на данные без явной передачи через конструкторы (prop drilling).

Пример InheritedWidget для темы:

class AppTheme extends InheritedWidget {
  const AppTheme({
    required this.primaryColor,
    required super.child,
  });

  final Color primaryColor;

  // Статический метод для удобного доступа из любого места дерева
  static AppTheme of(BuildContext context) {
    final result = context.dependOnInheritedWidgetOfExactType<AppTheme>();
    assert(result != null, 'No AppTheme found in context');
    return result!;
  }

  // Метод определяет, нужно ли уведомлять подписчиков при обновлении виджета
  @override
  bool updateShouldNotify(AppTheme oldWidget) {
    return primaryColor != oldWidget.primaryColor;
  }
}

// Использование в виджете-потомке:
Text(
  'Hello',
  style: TextStyle(color: AppTheme.of(context).primaryColor),
)

Ответ 18+ 🔞

А, ну это же про Флаттер, ёпта! Слушай, тут у них архитектура такая, блядь, трёхуровневая, как будто пирамида МММ, только работает. Сейчас разложу по полочкам, чтобы даже мартышлюшка поняла.

Первое — Виджеты. Это как инструкция по сборке шкафа из Икеи, только для интерфейса. Написал Container(color: Colors.red) — всё, декларация готова. Бывают двух видов: StatelessWidget — это как стул, который не двигается, и StatefulWidget — это как стул на колёсиках, который ещё и в жопу тебе может въехать, если состояние поменяется. Виджеты сами по себе — просто бумажки с описанием, они не рисуют нихуя.

Второе — Элементы (Element). Вот это уже реальные чуваки на стройке. Когда система видит твой виджет-инструкцию, она создаёт под него элемент — живую сущность в дереве. Этот элемент — тот самый менеджер-распиздяй, который связывает бумажку (виджет) с рабочим (рендер-объектом) и следит, чтобы при пересборке всё не разъехалось к хуям. Он сравнивает старую инструкцию с новой и решает, что обновлять, а что можно оставить в покое.

Третье — Рендер-объекты (RenderObject). Это уже суровые рабочие с отбойными молотками. Они берут расчёты от элементов и реально, блядь, рисуют. Вычисляют размеры, позиции — вся эта математика с constraints и layout происходит тут. С ними обычно не общаешься, если не хочешь, чтобы у тебя волосы поседели раньше времени.

Четвёртое — BuildContext. Это, по сути, твой пропуск в этот бардак. BuildContext — это и есть ссылка на тот самый элемент. Через него ты можешь орать на всю стройку: «Эй, навигатор, где тут?» (Navigator.of(context)), или спросить: «Какого хуя тут тема тёмная?» (Theme.of(context)), или померить экран (MediaQuery.of(context)). Без контекста ты как слепой кот — нихуя не сделаешь.

И наконец, звёздное шоу — InheritedWidget. Это гениальная хуйня, чтобы не таскать данные через всю вселенную. Представь: у тебя есть глобальная тема (цвета, шрифты). Без InheritedWidget тебе пришлось бы прокидывать её через конструкторы двадцати виджетов — пиздец просто, овердохуища кода. А так ты оборачиваешь всё в AppTheme, а любой потомок внизу может крикнуть AppTheme.of(context) и получить данные напрямую. Магия, сука! И работает быстро, потому что перестраиваются только те, кто реально подписался.

Вот тебе живой пример, как это выглядит в коде. Смотри, не зевай:

class AppTheme extends InheritedWidget {
  const AppTheme({
    required this.primaryColor,
    required super.child,
  });

  final Color primaryColor;

  // Статический метод для удобного доступа из любого места дерева
  static AppTheme of(BuildContext context) {
    final result = context.dependOnInheritedWidgetOfExactType<AppTheme>();
    assert(result != null, 'No AppTheme found in context');
    return result!;
  }

  // Метод определяет, нужно ли уведомлять подписчиков при обновлении виджета
  @override
  bool updateShouldNotify(AppTheme oldWidget) {
    return primaryColor != oldWidget.primaryColor;
  }
}

// Использование в виджете-потомке:
Text(
  'Hello',
  style: TextStyle(color: AppTheme.of(context).primaryColor),
)

Видишь? Вместо того чтобы прокидывать primaryColor через десять родителей, ты просто берёшь его из контекста. Если цвет поменяется — updateShouldNotify вернёт true, и все, кто от него зависит, перерисуются. А те, кому похуй — останутся как были. Эффективно, блядь!

Вот и вся архитектура. Виджеты болтают, элементы суетятся, рендер-объекты пашут, а InheritedWidget — как умная бабушка, которая всем печеньки раздаёт, не заставляя бегать по всему дому. Красота, ёпта!