Зачем вообще использовать Flutter?
Flutter используют, чтобы писать одно приложение сразу под несколько платформ из одного кода:
iOS, Android, Web и Desktop.
Простыми словами: вместо двух отдельных приложений можно делать одно общее.
Преимущества Flutter
➤ Один код под несколько платформ.
➤ Быстрая разработка благодаря hot reload.
➤ Красивый и гибкий UI через виджеты.
➤ Хорошая производительность.
➤ Удобно для старта проекта, MVP и pet-project.
Недостатки Flutter
➤ Размер приложения обычно больше, чем у нативного.
➤ Иногда нужны плагины или нативный код для специфичных функций.
➤ Dart менее популярен, чем Swift, Kotlin или JavaScript.
➤ Не всегда лучший выбор для очень платформо-зависимых задач.
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 дергает этот метод каждый раз при перерисовке.
Главное правило верстки: "Constraints go down. Sizes go up. Parent sets position."
Как это работает:
3 вида ограничений (BoxConstraints):
— Tight (Жесткие): min = max. Ребенок обязан принять размер (SizedBox).
— Loose (Свободные): min = 0, max = X. Любой размер до предела (Center).
— Unbounded (Бесконечные): Когда одна из сторон не ограничена (ListView).
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). Сливеры позволяют объединить всё это в один общий скролл без лагов.
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
Что такое 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 виджетов ключи практически никогда не нужны, так как у них нет внутреннего состояния, которое нужно сохранять.
1. Дерево Виджетов (Widget Tree) — "Чертежи"
Это то, что мы пишем в коде.
➤ Суть: Это просто легковесные инструкции (Immutable объекты). Они не рисуют и не считают координаты.
➤ Особенность: Так как виджеты очень дешевые, Flutter пересоздает их целиком при каждом изменении (setState). Это не бьет по производительности.
2. Дерево Элементов (Element Tree) — "Менеджер"
Это связующее звено между чертежом и реальным объектом на экране.
➤ Суть: Элемент живет долго и не пересоздается при каждом билде. Именно он хранит в себе объект State (для StatefulWidget).
➤ Важно: Когда мы используем BuildContext — под капотом мы работаем именно с Элементом. Context знает, где виджет находится в дереве.
3. Дерево Рендера (RenderObject Tree) — "Художник"
Самые тяжелые объекты.
➤ Суть: Они занимаются реальной работой: считают Constraints (ограничения), определяют размеры виджета (Layout) и рисуют пиксели (Paint).
➤ Оптимизация: Flutter старается переиспользовать старые RenderObject как можно дольше, потому что создавать их с нуля очень дорого для процессора.
🔄 Как происходит магия обновления?
Когда ты вызываешь setState():
BuildContext — это и есть сам Element. Виджет "слеп", он не знает ничего о мире. Context позволяет ему "посмотреть" вверх по дереву и найти данные предков (Тему, Навигатор).
Использование:
— Navigator.of(context) — поиск навигатора.
— Theme.of(context) — поиск текущих цветов и стилей.
Зачем это нужно?
Главная цель — **отделить бизнес-логику (расчеты, запросы в сеть) от UI (виджетов)**.
Без стейт-менеджмента код превращается в "спагетти", где один виджет напрямую меняет данные другого, что невозможно тестировать и поддерживать.
1. setState (Локальный стейт)
➤ Суть: Базовый метод StatefulWidget. Сообщает Flutter, что данные внутри виджета изменились и нужно вызвать build.
➤ Когда использовать: Только для очень простых вещей в рамках одного экрана (например, открыть/закрыть чекбокс, переключить вкладку).
⚠️ Минус: Невозможно передать данные на другие экраны, а при вызове перерисовывается весь виджет целиком, что может тормозить UI.
2. Provider (Обертка над InheritedWidget)
➤ Суть: Самый популярный и простой инструмент. Позволяет хранить данные в объекте и "пробрасывать" их в любую точку дерева виджетов.
➤ Как работает: Использует класс ChangeNotifier. Когда данные меняются, мы вызываем notifyListeners(), и все виджеты, которые "слушают" этот провайдер, перерисовываются.
➤ Плюсы: Легко освоить, минимум кода.
3. BLoC / Cubit (Бизнес-логика на потоках)
➤ Суть: Самый мощный и строгий подход. Основан на Streams (потоках).
➤ BLoC (Event -> State): UI посылает Событие ➔ BLoC обрабатывает его ➔ Выдает новое Состояние. Это гарантирует, что состояние нельзя изменить случайно.
➤ Cubit: Упрощенная версия BLoC. Вместо событий используются обычные методы.
➤ Плюсы: Полное разделение логики и UI. Идеально для больших команд и написания тестов.
4. Riverpod (Современный стандарт)
➤ Суть: "Наследник" Провайдера от того же автора, но лишенный его минусов.
➤ Особенности: Не зависит от BuildContext (можно использовать вне виджетов), проверяет ошибки типов еще на этапе компиляции, позволяет легко создавать зависимости между разными состояниями.
➤ Жизненный цикл State: initState (1 раз), didChangeDependencies, didUpdateWidget, dispose (очистка).
➤ Async Gap: Нельзя использовать context после await без проверки if (!mounted).
➤ Оптимизация: Использование RepaintBoundary для отделения слоев отрисовки тяжелых виджетов.