Dark Theme - On/Off
Pulsate - On
Waves - On

Why Flutter?

Зачем вообще использовать Flutter?
Flutter используют, чтобы писать одно приложение сразу под несколько платформ из одного кода: iOS, Android, Web и Desktop.

Простыми словами: вместо двух отдельных приложений можно делать одно общее.


Преимущества Flutter
➤ Один код под несколько платформ.
➤ Быстрая разработка благодаря hot reload.
➤ Красивый и гибкий UI через виджеты.
➤ Хорошая производительность.
➤ Удобно для старта проекта, MVP и pet-project.


Недостатки Flutter
➤ Размер приложения обычно больше, чем у нативного.
➤ Иногда нужны плагины или нативный код для специфичных функций.
➤ Dart менее популярен, чем Swift, Kotlin или JavaScript.
➤ Не всегда лучший выбор для очень платформо-зависимых задач.

Коротко для собеса: Flutter нужен для быстрой кроссплатформенной разработки из одного кода. Плюсы — скорость, единый UI, hot reload и хороший перформанс. Минусы — больший размер приложения, зависимость от плагинов и иногда необходимость писать нативный код.

Главные типы виджетов

1. StatelessWidget (Неизменяемый / Плакат)
Слово "State-less" означает "без состояния" (без памяти). В нем есть только метод build, который вызывается один раз при появлении виджета на экране.

2. StatefulWidget (Изменяемый / Телевизор)
"State-ful" — с памятью. Состоит из двух классов: сам Виджет (внешняя коробка) и его State (умные мозги), который умеет обновлять экран через вызов setState().

3. InheritedWidget (Радиовышка / Proxy Widget)
Особый виджет, который умеет "раздавать" свои данные вниз по дереву всем желающим.
Суть: Чтобы передать переменную на 10 уровней вниз, не нужно прокидывать её через конструкторы каждого дочернего виджета. Дочерний виджет просто вызывает context.dependOnInheritedWidgetOfExactType() и подписывается на обновления. На нем под капотом построены Provider и Theme.

4. RenderObjectWidget (Художник)
Все обычные виджеты строятся из других виджетов. А этот виджет не имеет метода build. Он напрямую создает RenderObject, который занимается математикой и красит пиксели. К ним относятся Padding, Column, SizedBox.

5. AnimatedWidget (Самописец анимации)
Обертка над StatefulWidget, которая автоматически вызывает setState, когда меняется переданный ей контроллер анимации. Упрощает написание кастомных анимаций.

Тех. нюансы:
  • Widget build(...) — это фабрика. Flutter дергает этот метод каждый раз при перерисовке.
  • Scaffold — главный виджет ("скелет"), куда кладем AppBar, body и навигацию.
  • BuildContext context — место в дереве, context знает всё о том, где находится виджет.
  • Зачем const? — Для кэширования виджетов в памяти и пропуска их перерисовки.

Constraints (Ограничения)

Главное правило верстки: "Constraints go down. Sizes go up. Parent sets position."

Как это работает:

  1. Родитель говорит: "Твоя ширина может быть от 100 до 300 пикселей".
  2. Ребенок решает: "Мне нужно 150", и сообщает это родителю.
  3. Родитель размещает ребенка (например, по центру).
Ребенок не может нарушить ограничения родителя!

3 вида ограничений (BoxConstraints):
Tight (Жесткие): min = max. Ребенок обязан принять размер (SizedBox).
Loose (Свободные): min = 0, max = X. Любой размер до предела (Center).
Unbounded (Бесконечные): Когда одна из сторон не ограничена (ListView).

Виджеты верстки (Layout)

1. Flex-виджеты (Column и Row)
Выстраивают элементы по вертикали (Column) или горизонтали (Row).
Особенность: Не скроллятся. Грузят в память всё сразу. Если контент не влезет в экран, ты получишь ошибку RenderFlex Overflow (желто-черная полоса).
Как лечить: Использовать виджеты Expanded (заставляет ребенка занять всё свободное место) или Flexible.


2. Scaffold (Каркас экрана)
Scaffold — это базовый каркас Material-экрана во Flutter.
Что дает: готовую структуру страницы: appBar, body, drawer, bottomNavigationBar, floatingActionButton, snackBar.
Чем заменить: если нужен полностью кастомный экран, можно собрать layout вручную через Material, SafeArea, Column, Stack. Для iOS-стиля используют CupertinoPageScaffold.


3. Stack (Слои по Z-оси)
Позволяет накладывать виджеты друг на друга. Для точного позиционирования детей внутри Stack используется виджет Positioned (например, чтобы повесить красный кружок уведомлений поверх иконки колокольчика).


4. Списки (ListView)
Обычный ListView: Создает сразу все виджеты. Использовать только для коротких статических меню (до 20 элементов).
ListView.builder: Работает с ленивой загрузкой (lazy loading). Рендерит только те элементы, которые сейчас видны на экране. Скроллишь ниже — старые удаляются из памяти, новые создаются. Идеально для бесконечных списков.


5. Slivers (CustomScrollView)
Слово "Sliver" означает "кусочек". Это особые виджеты, которые общаются напрямую с движком скролла.
Для чего: Создание сложного UI. Например: исчезающая при скролле шапка (SliverAppBar), под ней карусель (SliverToBoxAdapter), а ниже бесконечная сетка товаров (SliverGrid). Сливеры позволяют объединить всё это в один общий скролл без лагов.

Widget и отрисовка во Flutter

1. Что такое Widget?
Widget — это описание UI.
➤ Он не хранит реальную кнопку, текст или картинку на экране.
➤ Он только говорит Flutter, что нужно показать.


2. Как он отрисовывается?
Widget сам не отрисовывается.
Он только описывает, что нужно показать.

Дальше процесс такой:
➤ Flutter берет Widget
➤ создает для него Element
➤ если нужно, создает RenderObject
RenderObject считает размер, позицию и ограничения от родителя
➤ потом Flutter через rendering engine рисует это уже в пиксели на экране


3. Цепочка отрисовки
Widget → Element → RenderObject → pixels on screen


4. Простыми словами
Widget = описание
Element = связка
RenderObject = реальная отрисовка и layout

Ключи (Keys)

Что такое Key?
Key — это идентификатор виджета. Ключи во Flutter нужны, чтобы framework мог правильно отличать виджеты друг от друга при перестроении дерева. Это особенно важно для списков, форм, анимаций и сохранения state. Он помогает Flutter правильно: 1.сохранять состояние 2.обновлять нужный элемент 3.не путать виджеты местами


Зачем они нужны?
По умолчанию Flutter различает виджеты только по их Типу. Если у тебя в списке 10 одинаковых Stateful виджетов и ты удалишь первый, Flutter может запутаться и привязать состояние (State) удаленного виджета к следующему.


Где используются?
1. в списках ListView 2.при удалении / перестановке элементов 3.в формах 4.при анимациях 5.когда нужно сохранить state у конкретного виджета


1. LocalKey (Для списков)
Используются внутри одного родителя (например, в ListView).
ValueKey: Самый частый. Привязывается к уникальному ID из базы (например, ValueKey(user.id)).
ObjectKey: Если у данных нет уникального ID, ключом становится весь объект данных.
UniqueKey: Генерирует новый ID при каждом билде. Используется, чтобы принудительно "убить" старый виджет и создать его с нуля (например, чтобы сбросить анимацию).


2. GlobalKey (Тяжелая артиллерия)
Уникален для всего приложения. Позволяет достучаться до виджета из любой точки кода.
Для чего: Валидация форм (formKey.currentState.validate()), получение размеров или координат виджета на экране, сохранение стейта виджета при его перемещении между разными экранами.
⚠️ Важно: GlobalKey — дорогой объект, не используй его там, где можно обойтись обычным ValueKey.


💡 Правило использования
Ключи нужно ставить на самый верхний виджет в элементе списка. Для Stateless виджетов ключи практически никогда не нужны, так как у них нет внутреннего состояния, которое нужно сохранять.

Три Дерева в Flutter

1. Дерево Виджетов (Widget Tree) — "Чертежи"
Это то, что мы пишем в коде.
Суть: Это просто легковесные инструкции (Immutable объекты). Они не рисуют и не считают координаты.
Особенность: Так как виджеты очень дешевые, Flutter пересоздает их целиком при каждом изменении (setState). Это не бьет по производительности.


2. Дерево Элементов (Element Tree) — "Менеджер"
Это связующее звено между чертежом и реальным объектом на экране.
Суть: Элемент живет долго и не пересоздается при каждом билде. Именно он хранит в себе объект State (для StatefulWidget).
Важно: Когда мы используем BuildContext — под капотом мы работаем именно с Элементом. Context знает, где виджет находится в дереве.


3. Дерево Рендера (RenderObject Tree) — "Художник"
Самые тяжелые объекты.
Суть: Они занимаются реальной работой: считают Constraints (ограничения), определяют размеры виджета (Layout) и рисуют пиксели (Paint).
Оптимизация: Flutter старается переиспользовать старые RenderObject как можно дольше, потому что создавать их с нуля очень дорого для процессора.


🔄 Как происходит магия обновления?
Когда ты вызываешь setState():

  1. Flutter создает новое Дерево Виджетов (новые чертежи).
  2. Element сравнивает старый виджет с новым по двум параметрам: runtimeType (тип) и Key (ключ).
  3. Если они совпали, Элемент говорит: "О, это та же самая кнопка, только цвет другой! Я не буду пересоздавать тяжелый RenderObject, я просто обновлю в нем цвет".
  4. Это называется Reconciliation (согласование). Именно благодаря этому Flutter выдает 60-120 FPS.

Зачем это знать? Чтобы понимать: виджеты — это просто данные. Если мы меняем данные (вызываем build), Flutter не перерисовывает весь мир с нуля, а точечно обновляет только нужные параметры в тяжелом RenderObject дереве.

BuildContext

BuildContext — это и есть сам Element. Виджет "слеп", он не знает ничего о мире. Context позволяет ему "посмотреть" вверх по дереву и найти данные предков (Тему, Навигатор).

Использование:
Navigator.of(context) — поиск навигатора.
Theme.of(context) — поиск текущих цветов и стилей.

State Management (Управление состоянием)

Зачем это нужно?
Главная цель — **отделить бизнес-логику (расчеты, запросы в сеть) от UI (виджетов)**. Без стейт-менеджмента код превращается в "спагетти", где один виджет напрямую меняет данные другого, что невозможно тестировать и поддерживать.


1. setState (Локальный стейт)
Суть: Базовый метод StatefulWidget. Сообщает Flutter, что данные внутри виджета изменились и нужно вызвать build.
Когда использовать: Только для очень простых вещей в рамках одного экрана (например, открыть/закрыть чекбокс, переключить вкладку).
⚠️ Минус: Невозможно передать данные на другие экраны, а при вызове перерисовывается весь виджет целиком, что может тормозить UI.


2. Provider (Обертка над InheritedWidget)
Суть: Самый популярный и простой инструмент. Позволяет хранить данные в объекте и "пробрасывать" их в любую точку дерева виджетов.
Как работает: Использует класс ChangeNotifier. Когда данные меняются, мы вызываем notifyListeners(), и все виджеты, которые "слушают" этот провайдер, перерисовываются.
Плюсы: Легко освоить, минимум кода.


3. BLoC / Cubit (Бизнес-логика на потоках)
Суть: Самый мощный и строгий подход. Основан на Streams (потоках).
BLoC (Event -> State): UI посылает Событие ➔ BLoC обрабатывает его ➔ Выдает новое Состояние. Это гарантирует, что состояние нельзя изменить случайно.
Cubit: Упрощенная версия BLoC. Вместо событий используются обычные методы.
Плюсы: Полное разделение логики и UI. Идеально для больших команд и написания тестов.

BLoC architecture diagram

4. Riverpod (Современный стандарт)
Суть: "Наследник" Провайдера от того же автора, но лишенный его минусов.
Особенности: Не зависит от BuildContext (можно использовать вне виджетов), проверяет ошибки типов еще на этапе компиляции, позволяет легко создавать зависимости между разными состояниями.

Что выбрать? Если проект маленький или средний — Provider/Riverpod. Если это крупный банковский или Enterprise проект, где важна строгая архитектура и 100% покрытие тестами — BLoC.

Что еще важно знать (Middle+)

Жизненный цикл State: initState (1 раз), didChangeDependencies, didUpdateWidget, dispose (очистка).

Async Gap: Нельзя использовать context после await без проверки if (!mounted).

Оптимизация: Использование RepaintBoundary для отделения слоев отрисовки тяжелых виджетов.