Flutter & Dart | Мобильный трудоголик
404 subscribers
29 photos
1 video
82 links
Пишу простым языком про разработку на Flutter & Dart (iOS, Android, macOS, Windows) и мобильную разработку в целом.
Обо мне: https://t.me/hardworkerFlutter/2
Чат: @flutterDevChat
Другие мои каналы: @hardworkerIT и @itDenisov
Download Telegram
👣 Offstage: пререндеринг без боли.

Привет! Во Flutter, где каждое изменение состояния может запускать перестроение дерева виджетов, управление производительностью часто сводится к искусству скрытия. Не буквального, а архитектурного. Когда перед нами встает задача заранее подготовить сложный фрагмент интерфейса, но не показывать его немедленно, на помощь приходит неочевидный, но мощный виджет Offstage. В отличие от простого условного рендеринга, его работа гораздо тоньше и направлена на решение конкретных проблем с производительностью и состоянием.


Принцип работы:

Ключевое отличие Offstage от Visibility с флагом visible: false или условного оператора (if (condition) Widget()) - в его отношении к дереву. Когда вы оборачиваете виджет в Offstage(offstage: true), происходит следующее:

🔵Виджет физически исключается из процесса лейаута (layout). Система его не измеряет и не размещает, как если бы его не существовало.

🔵Виджет остается активной частью дерева виджетов. Его состояние (State), контроллеры анимаций (AnimationController), подписки (StreamSubscription, Listenable) продолжают жить и работать.

🔵Виджет не отрисовывается (не вызывает paint). Это экономит вычислительные ресурсы GPU.

Этот принцип «жить, но не мешать» создает уникальные возможности.


Ключевые сценарии применения:

🔹 Пребилдинг ресурсоемких экранов и вкладок. Представьте навигацию с табами, где каждый таб - это сложный экран с сетями, графиками или списками. Используя Offstage для неактивных вкладок, вы избегаете:

🔵Полного пересоздания состояния и загрузки данных при каждом переключении.

🔵Мерцания или задержек, пока виджет инициализируется с нуля.

🔵Это классический паттерн для TabBarView или кастомных навигационных решений, где нужно сохранять состояние экранов.

🔹 Сложные, готовые к показу модальные окна или меню. Если диалог или боковое меню должно появляться мгновенно по жесту или кнопке, его можно заранее построить и спрятать в Offstage. В момент показа (offstage: false) произойдет только включение в лейаут и отрисовка - без инициализации контроллеров, загрузки ассетов или вычисления сложной логики. Это критично для создания ощущения отзывчивости интерфейса.

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


Технические нюансы и ограничения:

🔵Размер. Хотя Offstage не участвует в лейауте, технически он может иметь размер, если ему переданы ограничения (constraints). Однако чаще его используют с size: Size.zero, чтобы он точно не занимал места.

🔵Не для всего. Не стоит оборачивать в Offstage бесконечно растущие списки (ListView.builder) с активными подписками на скролл - это может привести к утечке памяти. Инструмент эффективен для ограниченных по сложности виджетов, которые точно понадобятся.

🔵Альтернатива IndexedStack. IndexedStack - это, по сути, умный Stack, показывающий один из нескольких детей. Под капотом он использует похожий механизм для невидимых дочерних виджетов. Offstage дает более точечный, ручной контроль в случаях, когда логика показа/скрытия сложнее простого индекса.


Пример - быстрое переключение вкладок:

Сохраняем состояние обеих вкладок, а не пересоздаем их.


Stack(
children: [
Offstage(
offstage: _currentTab != 0,
child: SettingsScreen(), // Живое состояние
),
Offstage(
offstage: _currentTab != 1,
child: ProfileScreen(), // Контроллеры активны
),
],
)



💡 Вывод:

Offstage - это не просто скрытие виджета. Он сохраняет его состояние и ресурсы, предотвращая пересоздание. Используйте его для пребилдинга вкладок, сохранения анимаций и быстрых переходов. Так вы сделаете приложение отзывчивее без лишней нагрузки на процессор и память.


➡️ Flutter & Dart | Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥1
👣 Flutter: как FractionallySizedBox и FittedBox спасают адаптивную верстку.

Создание адаптивного интерфейса во Flutter часто сводится к использованию MediaQuery и расчетам на основе размеров экрана. Но есть два менее известных, но исключительно мощных виджета, которые решают специфичные задачи адаптивности на уровне композиции, без сложной математики. FractionallySizedBox и FittedBox - это не просто обертки, а точные инструменты контроля пропорций и масштабирования.


FractionallySizedBox - размер в процентах:

Этот виджет задает размер дочернего элемента как долю от родителя. Например, кнопка на 80% ширины контейнера:


FractionallySizedBox(
widthFactor: 0.8,
child: ElevatedButton(...),
)


Главное условие: родитель должен иметь конкретный размер. Не будет работать в Column без Expanded, где ширина не ограничена.


FittedBox - умное масштабирование:

Когда контент не помещается, FittedBox масштабирует его, сохраняя пропорции. Типичный случай - крупный заголовок в маленькой карточке:


FittedBox(
child: Text('Заголовок', style: TextStyle(fontSize: 40)),
)


Это предотвращает OverflowError и автоматически подбирает размер.


В чем разница между FractionallySizedBox и FittedBox:

🔵FractionallySizedBox управляет контейнером (задает его размер как процент от родителя).

🔵FittedBox управляет содержимым (масштабирует виджет внутри существующего контейнера).


Когда что использовать:

🔵Используйте FractionallySizedBox для: кнопок фиксированной ширины, прогресс-баров, колонок сетки.

🔵Используйте FittedBox для: текста в ограниченной области, иконок в CircleAvatar, изображений-превью.


💡 Вывод:

Изучение адаптивности во Flutter не должно начинаться и заканчиваться на MediaQuery.of(context).size.width. Такие виджеты, как FractionallySizedBox и FittedBox, предлагают декларативный и композиционный подход к решению распространенных проблем верстки. Они смещают фокус с реактивного программирования («посчитай размер экрана и обнови») на декларативное описание отношений между элементами интерфейса («этот элемент занимает половину родителя, а его содержимое подстраивается под доступное пространство»).

Внедрение этих виджетов в повседневную практику сокращает количество кастомных расчетов, делает код чище и предсказуемее, а главное - создает по-настоящему гибкий интерфейс, который корректно ведет себя не только на разных размерах экрана, но и в сложных, вложенных layout-структурах. Это следующий уровень мастерства после освоения Row, Column и Flex.


➡️ Flutter & Dart | Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3❤1🔥1
🔨 Как установить несколько Xcode с разными версиями?

При возникновении необходимости в установке нескольких, разных версий среды разработки Xcode на одной машине можно воспользоваться удобным приложением Xcodes, которое является удобным менеджером версий Xcode.


Зачем нужно:

🔵Тестирование приложений на разных версиях Xcode (включая бета-версии).

🔵Работа с проектами, которые требуют конкретной версии (например, legacy-код).

🔵Возможность не обновлять основной Xcode, если новая версия вызывает баги.


Особенности данной утилиты:

🔵Поддерживает процессоры Apple Silicon и Intel.

🔵Показывает релизные заметки для каждой версии.

🔵Упрощает установку и переключение между версиями Xcode.

🔵Автоматически скачивает нужные версии (включая старые и beta).

🔵Не требует ручного управления через xcode-select.


➡️ Flutter & Dart | Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤1
👣 Flutter+Rust: новый стек для системы HyperOS от Xiaomi

Компания Xiaomi анонсировала новую версию своей системы: HyperOS 4. Ключевое изменение: переход системных приложений на Flutter для UI и Rust для системной логики. Это не косметическое обновление, а замена технологического стека.


Проблема:

Старая кодовая база MIUI - это смесь Java, Kotlin и кастомного кода. Результат: фрагментация интерфейсов, сложная поддержка, раздутая прошивка.


Решение:

🔵Flutter для всего UI. Единый фреймворк отрисовки для всех системных приложений (настройки, файлы, часы). Цель: идентичный интерфейс на всех устройствах и упрощение разработки.

🔵Rust для системного кода. Язык для драйверов, сервисов, работы с железом. Причины: безопасность памяти (нет GC), производительность на уровне C/C++.


Архитектурные выгоды:

🔵Модульность: команды могут разрабатывать приложения независимо.

🔵Упрощение поддержки: один стек вместо зоопарка технологий.

🔵Потенциал для быстрых обновлений: возможность обновлять системные приложения отдельно от прошивки.


Риски:

🔵Память: Runtime Flutter увеличит потребление RAM.

🔵Интеграция: сложные мосты между Flutter/Rust и обязательным слоем Android (Google Play Services).

🔵Квалификация: необходимы разработчики на Dart и Rust.

🔵Отладка: сложная диагностика проблем в цепочке Flutter - Rust - Android.


🔗 Ссылка на новость


💡 Вывод:

Xiaomi пытается заменить legacy-код единым современным стеком. Цель: получить управляемую, модульную и производительную систему. Успех даст им преимущество. Провал станет дорогим уроком о пределах кроссплатформенных решений на системном уровне. Это крупнейший технологический эксперимент среди Android-производителей.


➡️ Flutter & Dart | Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥1
👣 Запущен комплексный образовательный путь по Dart и Flutter

Команда Dart и Flutter представила масштабное обновление образовательных материалов - целостный обучающий путь «Getting Started Experience». Это не просто очередной туториал, а продуманная экосистема для входа в технологию, созданная с учетом современных принципов педагогического дизайна и пользовательского опыта.


Структурный подход к обучению - от любопытства к пониманию:

Новый подход кардинально отличается от традиционной документации. Вместо разрозненных статей и примеров кода предлагается последовательный путь, состоящий из взаимосвязанных модулей:

🔵Мгновенный старт без барьеров. Используя возможность горячей перезагрузки в вебе (hot reload), новички могут начать писать код на Flutter прямо в браузере, минуя сложную настройку локального окружения. Это снижает порог входа с первых секунд.

🔵Двухэтапное погружение: отдельные, но связанные курсы по Dart и Flutter. Это решает классическую дилемму: изучать язык и фреймворк вместе или раздельно. Теперь можно пройти основы Dart, а затем перейти к Flutter, либо начать сразу с фреймворка, если есть опыт в других ООП-языках.

🔵Практика через создание четырех законченных приложений. Ключевое решение: отказаться от одного монолитного учебного проекта в пользу нескольких небольших, но цельных приложений. Это минимизирует когнитивную нагрузку, снижает риск ошибок и позволяет фокусироваться на конкретных концепциях без отвлечения на вспомогательный код.

🔵Видеоролики с человеческим лицом. Обучение дополнено серией видео, где ключевые концепции объясняют не дикторы за кадром, а реальные инженеры, технические писатели и менеджеры продуктов из команд Dart и Flutter. Это создает эмоциональную связь и знакомит новичков с сообществом.

🔵Интерактивные элементы и финальный босс. После каждого модуля - простые неблокирующие квизы для самопроверки. А кульминацией пути становится рекомендованная серия видео «How Flutter Works», которая раскрывает внутреннее устройство фреймворка, переводя разработчика на новый уровень понимания.


Реорганизация сайтов - ясность и навигация:

Обновление затронуло и инфраструктуру сайтов Dart и Flutter. Они были переведены на Jaspr - статический генератор сайтов на Dart. Более заметное для пользователя изменение - четкое разделение контента:

🔵Раздел «Learn»: теперь дом для всех обучающих материалов, путей и туториалов.

🔵Раздел «User Guides»: содержит основную техническую документацию и справочные материалы.

Это разделение упрощает навигацию как для новичков, ищущих структурированное обучение, так и для опытных разработчиков, которым нужна конкретная справочная информация.


Что это значит для экосистемы? Тренд на качество онбординга:

Данный запуск - это сигнал всей индустрии. Команда Dart и Flutter демонстрирует, что инвестиции в developer experience (DX) начинаются не с продвинутых инструментов, а с момента, когда потенциальный разработчик только задумывается о выборе технологии. Такой подход:

🔵Снижает коэффициент оттока на старте. Меньше разработчиков бросают изучение из-за сложностей первоначальной настройки или непонимания, с чего начать.

🔵Формирует лояльное сообщество. Чувство поддержки и качественный входной опыт создают позитивное восприятие технологии в долгосрочной перспективе.

🔵Задает новый стандарт. Другим фреймворкам и языкам теперь придется равняться на столь же продуманный и человеко-ориентированный процесс обучения.


🔗 Ссылка на подробную статью


💡 Вывод:

Новый «Getting Started Experience» - это больше чем апдейт документации. Это стратегическая инвестиция в будущее экосистемы. Он превращает хаотичное первоначальное знакомство с Flutter и Dart в продуманное, поддерживающее и эффективное путешествие от первого интереса до глубокого понимания технологии. Для всех, кто присматривался к Flutter, но откладывал из-за «не знаю, с чего начать» - сейчас идеальный момент.


➡️ Flutter & Dart | Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥1👀1
👣 Flutter на Авроре получил апгрейд: что дает включение Impeller

В мире Flutter уже несколько лет говорят о Impeller - новом рендеринговом движке от Google, призванном решить главную боль разработчиков: фризы и лаги анимаций «на холодную». Пока для iOS он включен по умолчанию, а для Android только на новых версиях, команда разработки под ОС Aurora решила не оставаться в стороне и добавила экспериментальную поддержку этой технологии в свою экосистему. Разберем, что это дает на практике и стоит ли спешить с включением.


Чем Impeller отличается от Skia и почему это важно для Aurora:

Долгое время Flutter полагался на Skia - мощный, но универсальный графический движок. Его слабое место - компиляция шейдеров в рантайме, которая и вызывает те самые пресловутые фризы при первом отображении сложных интерфейсов. Решения существовали (прекомпиляция, прогрев приложения), но они усложняли разработку.

Impeller - это ответ Google на эту проблему. Его ключевая особенность - предсказуемое время отрисовки каждого кадра за счет отказа от компиляции шейдеров на лету. Для Аврора, которая позиционируется как стабильная и производительная отечественная платформа, такая оптимизация особенно актуальна, так как напрямую влияет на пользовательское восприятие приложений.


Особенности реализации - работа с OpenGL и текущие ограничения:

На Aurora графика работает через стек Wayland + OpenGL. Команда разработки интегрировала OpenGL-бэкенд Impeller, который в upstream-версии Flutter имеет статус экспериментального и не рекомендуется к использованию. Потребовалась доработка: например, реализация корректного преобразования матриц для поддержки альбомной ориентации.

Важный нюанс: включение Impeller на Аврора - это опция, а не замена по умолчанию. Активируется она флагом --enable-impeller при запуске. Это разумный подход, учитывая, что не все сценарии отрисовки в этом режиме стабильны, возможны артефакты в отдельных виджетах (как в примере с Checkbox).


Цифры и факты - что показывают тесты производительности:

Тестирование на планшете с Aurora 5.2.0 дает неоднозначную, но в целом обнадеживающую картину:

🔵Холодный запуск и навигация. В тестовом приложении переход на новый экран с анимацией при использовании Skia вызывал фризы как на UI-, так и на Raster-потоке. С Impeller фриз остался только на UI-потоке, в то время как GPU-рендеринг (Raster) выдавал стабильные 60 fps. Это прямое свидетельство того, что Impeller справляется со своей основной задачей - стабилизацией отрисовки.

🔵Сравнение под нагрузкой. В синтетическом тесте, нагружающем UI и Raster, результаты оказались неоднозначными. При выполнении стандартных операций, таких как построение списков или рисование на Canvas, разница в производительности между Skia и Impeller была практически незаметна. Однако ключевое преимущество нового рендерера проявилось в специализированном сценарии: отрисовке сложных визуальных эффектов. При работе с тенями, размытиями, градиентами и прозрачностью производительность Raster-потока с Impeller увеличилась примерно вдвое, что демонстрирует его существенное превосходство в данной нише.


🔗 Ссылка на подробную статью


💡 Вывод:

Поддержка Impeller в операционной системе Аврора - это важный, но экспериментальный шаг. Новый рендерер демонстрирует реальные преимущества в стабильности отрисовки и производительности сложных эффектов, приближая опыт разработки под Aurora к мировым стандартам Flutter. Однако из-за сырости OpenGL-реализации и возможных артефактов в интерфейсе переходить на него на проде пока рано. Для Аврора это возможность, а не обязательство - стратегический задел на будущее, когда Impeller окончательно созреет.


➡️ Flutter & Dart | Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
👣 Flutter 3.41: стабильность, модульность и подготовка к будущему

Google выпустил Flutter 3.41 - релиз, который выглядит как плановый апдейт, но на самом деле закладывает архитектурные изменения на годы вперед. 868 коммитов от 145 контрибьюторов, но главное не в количестве, а в направлении.


Прозрачность разработки:

Впервые Flutter вводит публичные release-окна на весь 2026 год. Теперь каждый знает точные даты заморозки веток: 3.44 выйдет в мае, 3.47 в августе, 3.50 в ноябре. Для команд, которые зависят от стабильности фреймворка, это снимает огромный пласт неопределенности. Больше не нужно гадать, попадет ли фича в ближайший релиз - календарь открыт.


Материалы и Cupertino уходят в отдельные пакеты:

Это ключевое изменение, которое многие недооценят. Material и Cupertino больше не будут привязаны к монолитному циклу релиза Flutter. Их обновления смогут выходить независимо, в любое время. Для разработчиков это означает две вещи: во-первых, вы сможете получать новые дизайн-системы (вроде Material 3 Expressive или Liquid Glass) не дожидаясь квартального обновления движка. Во-вторых, если вы застряли на старой версии Flutter из-за легаси, вы все равно сможете обновить визуальную часть отдельно. Фреймворк становится конструктором, а не монолитом.


iOS: UIScene по умолчанию и чистый blur:

Flutter окончательно прощается с наследием AppDelegate. Поддержка UIScene включена по умолчанию - это было требование Apple для будущих версий iOS, и теперь оно выполнено. Параллельно Impeller получил улучшенный рендеринг размытия: исчезли цветные ореолы по краям, которые раньше портили впечатление от BackdropFilter. CupertinoSheet обзавелся нативным drag-хендлом - мелочь, но именно из таких мелочей складывается ощущение «родного» интерфейса.


Android: подготовка к AGP 9 с осторожностью:

Важный нюанс: обновляться на Android Gradle Plugin 9 пока нельзя. Поддержка заморожена до аудита обратной совместимости. Но новые плагины уже по умолчанию генерируются на Kotlin DSL - индустрия движется, и Flutter движется с ней. Плюс появилась возможность точечно исключать ассеты для конкретных платформ: тяжелые десктопные текстуры больше не придется тащить в мобильную сборку.


Графика: синхронные текстуры и 128-битные float:

Для тех, кто работает с кастомными шейдерами, релиз принес две важные вещи. Первая - синхронное декодирование текстур. Раньше создание текстуры для шейдера могло «уронить» кадр, теперь это делается в том же фрейме через decodeImageFromPixelsSync. Вторая - поддержка 128-битных float-текстур. Это про LUT для цветокоррекции, про SDF-шейдеры и про фотофильтры на GPU. Технический потолок поднят.


🔗 Ссылка на подробное описание релиза


💡 Вывод:

Flutter 3.41 - это релиз, который не кричит о себе громкими заголовками, но меняет правила игры в долгую. Публичные окна релизов превращают разработку фреймворка из черного ящика в предсказуемый процесс, где каждый контрибьютор и каждая команда видят свой горизонт планирования.

Выделение Material и Cupertino в независимые пакеты ломает многолетнюю монолитность - теперь дизайн-системы могут эволюционировать со скоростью индустрии, а не со скоростью движка.

Flutter перестает быть просто инструментом для быстрого старта и становится платформой, на которой можно строить сложные, долгоживущие проекты - с предсказуемостью, гибкостью и уважением к обратной совместимости.


➡️ Flutter & Dart | Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥1
👣 Когда pubspec.yaml работает против вас: разбор типичных ошибок

Файл pubspec.yaml для многих остается просто местом, где перечислены зависимости. На деле это единственная точка, через которую Flutter общается с нативными мирами Android и iOS. Одна неверная строчка и приложение либо не собирается, либо падает на конкретных устройствах, либо молча теряет производительность. Разбираем ошибки, которые дорого обходятся на практике.


Версии, которые живут своей жизнью:

Самая частая проблема: использование кареток ^2.0.0 без понимания последствий. Запись ^2.0.0 означает «любая версия от 2.0.0 до 3.0.0, кроме ломающих изменений». Но что считать ломающим изменением для pub и что для нативного кода не всегда совпадает. Плагин может обновиться с 2.0.0 до 2.3.0, где для Android внезапно потребуются новые разрешения, а вы узнаете об этом только после релиза.

Выход: явные диапазоны версий '>=2.0.0 <3.0.0'. Да, строкой длиннее, зато контроль полный.


Платформы все же отличаются:

То, что отлично работает на iOS, может валиться на Android, и наоборот. Указывая зависимости глобально, вы часто тянете в сборку код, который не нужен на другой платформе. Или, что хуже, конфликтующие нативные библиотеки.

В pubspec.yaml можно (и нужно) указывать зависимости отдельно для каждой платформы с уточнением минимальных версий SDK, классов плагинов и прочих нативных параметров. Это не только решает конфликты, но и уменьшает размер итогового приложения.


Ресурсы, которые раздувают приложение:

🔵- assets/ - классическая запись, которая тащит в сборку вообще все, включая служебные файлы, исходники и мусор. На реальном проекте такой подход может добавить к размеру APK и IPA десятки мегабайт.

🔵Правильно - указывать точные пути и разделять ассеты по платформам. Картинки для ретина-экранов, шрифты, нативные ресурсы - все должно лежать в своих папках и подключаться адресно.


Окружение, о котором забывают:

Помимо версии Dart и Flutter, в environment стоит указывать ограничения для нативного мира: min_android_sdk, min_ios_version, kotlin_version, swift_version. Это страхует от ситуаций, когда разработчик с новым Kotlin пушит код, а у CI стоит старая версия, и сборка падает.


Плагины, которые конфликтуют:

Зависимости - это не только имена пакетов, но и их настройки. Разрешения, конфигурации, порядок инициализации - все это влияет на рантайм. Часто плагины тянут одни и те же нативные библиотеки, и без явного указания версий возникают конфликты, которые проявляются только на этапе выполнения.

В pubspec.yaml можно уточнять не только версию, но и конкретные параметры плагина для каждой платформы, включая разрешения и классы.


Сборка, которая не оптимизирована:

Флаг uses-material-design: true многие ставят не задумываясь, хотя приложению могут быть не нужны иконки Material. Но речь не только об этом. В секции flutter можно (и нужно) указывать параметры сборки для каждой платформы: proguard, multidex, bitcode, target platform. Это напрямую влияет на размер и скорость запуска.


Тесты, которые не включают:

dev_dependencies часто остаются минимальными: только flutter_test. Но для полноценной проверки нативной интеграции нужны инструменты: integration_test, flutter_driver, мок-генераторы. Их отсутствие не ломает сборку, но делает тестирование поверхностным, а баги уходят в прод.


💡 Вывод:

pubspec.yaml - не просто список зависимостей, а полноценный конфигурационный файл, управляющий нативной частью приложения. Отношение к нему как к формальности приводит к сбоям, раздутому размеру и потерянному времени на отладку. Аккуратное управление версиями, разделение по платформам и явные настройки сборки превращают этот файл из источника проблем в инструмент контроля качества. В Flutter «просто собралось» и «собралось правильно» - часто две большие разницы.


➡️ Flutter & Dart | Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2❤1🔥1
👣 Flutter: организация кода с помощью BLoC

Когда новичок приходит в разработку на Flutter, он быстро сталкивается с двумя вещами: виджетами и управлением состоянием. И если с виджетами все более-менее понятно: нарисовал кнопку, вывел текст, то управление состоянием вызывает вопросы. Особенно когда речь заходит о BLoC. Многие воспринимают его как очередную библиотеку, которую можно подключить и забыть. На деле BLoC - это способ перестать смешивать логику и интерфейс и начать думать потоками данных.


Состояние - это все, что видит пользователь:

Все, что отображается на экране - это состояние. Цвет кнопки, текст в заголовке, видимость лоадера - все это состояния. Даже пустой контейнер - это состояние, просто со значением «ничего не показывать».

Когда приложение маленькое, состояние можно хранить прямо в виджете. Вызвал setState и интерфейс обновился. Но как только логика усложняется, такой подход превращает код в кашу. Презентация мешается с бизнес-логикой, а разобраться, почему при нажатии на кнопку что-то пошло не так, становится сложно.

Выход - отделить логику от отрисовки. Вынести состояние наружу, сделать его независимым от виджетов. И вот здесь появляется BLoC.


Как BLoC меняет правила игры:

BLoC (Business Logic Component) работает по простой схеме: события -> обработка -> новые состояния. Пользователь нажал кнопку - это событие. BLoC его поймал, сходил в репозиторий, что-то посчитал и выдал новое состояние. Виджет это состояние увидел и перерисовался.

Важно, что BLoC ничего не знает о том, как именно рисуется интерфейс. Ему все равно, текст это, кнопка или анимация. Его задача: принять событие, выполнить логику и вернуть состояние. За это его и ценят: логика становится переиспользуемой и тестируемой независимо от UI.


Архитектура вокруг BLoC:

BLoC тянет за собой организацию кода. Чтобы он работал осмысленно, нужно выстроить слои:

🔵Data Layer: модели, репозитории, провайдеры данных (API, база).

🔵Business Logic Layer: сам BLoC, который обрабатывает события и общается с репозиториями.

🔵Presentation Layer: виджеты, которые подписаны на состояния и реагируют на них.

Такое разделение позволяет менять один слой, не трогая другие. Захотели заменить REST API на GraphQL - меняете только Data Layer. Интерфейс переехал на новую дизайн-систему - трогаете только Presentation Layer.


Улучшенное тестирование:

Когда логика вынесена из виджетов, ее становится легко тестировать. BLoC можно скормить события и проверить, какие состояния он выдал. Это не требует запуска приложения, эмулятора или взаимодействия с UI. Просто юнит-тесты, которые бегут за секунды.


🔗 Ссылка на подробную статью


💡 Вывод:

BLoC - это не просто способ управлять состоянием. Это способ думать о приложении как о потоке данных, где интерфейс - лишь отражение текущего состояния. Да, порог входа выше, чем у setState. Но когда проект вырастает, именно архитектура спасает от хаоса. BLoC заставляет разделять ответственность, писать тестируемый код и не смешивать логику с отрисовкой.


➡️ Flutter & Dart | Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1👀1🗿1
👣 Elementary: MVVM архитектура в Flutter без боли и лишнего кода

Когда проект вырастает, вопрос организации кода встает особенно остро. Разработчики начинают искать способы отделить логику от интерфейса, сделать код тестируемым и не потерять в производительности. Одно из решений: библиотека Elementary, которая предлагает свой взгляд на построение архитектуры Flutter-приложений, основанный на MVVM и вдохновленный лучшими практиками Clean Architecture.


Особенности Elementary:

В основе подхода лежит четкое разделение на три слоя:

🔵View (ElementaryWidget): только описание интерфейса. Здесь нет логики, только декларативное построение UI на основе данных, которые приходят из WidgetModel.

🔵ViewModel (WidgetModel): презентационная логика. Определяет, что показывать, как реагировать на действия пользователя, управляет состояниями виджетов.

🔵Model (ElementaryModel): бизнес-логика и работа с данными. Взаимодействует с сервисами, репозиториями, API. Не имеет доступа к Flutter-специфичным вещам.

Такое разделение решает несколько проблем, с которыми сталкиваются разработчики в больших проектах.


Почему это удобно:

🔵Четкие границы ответственности. Любой новый разработчик в команде сразу понимает, где искать UI, где презентационную логику, а где работу с данными. Это снижает порог входа и упрощает код-ревью.

🔵Тестируемость. Бизнес-логика изолирована от фреймворка, ее можно покрывать юнит-тестами без запуска виджетов. WidgetModel тестируется отдельно, подменяя зависимости. В результате тесты пишутся проще и охватывают больше кейсов.

🔵Производительность за счет умных обновлений. Elementary использует паттерн «наблюдатель» для свойств. Вместо тотального перестроения всего виджета обновляются только те части UI, которые подписаны на изменившиеся данные. Это особенно заметно на сложных экранах с большим количеством интерактивных элементов.

🔵Полностью декларативный UI. WidgetModel предоставляет всё необходимое для отрисовки, а ElementaryWidget просто описывает, как это должно выглядеть. Подход UI = f(state) реализуется естественно и без лишних усилий.


О гибкости Elementary:

Elementary не навязывает жестких правил, но задает удобные рамки. Внутри WidgetModel вы можете использовать любые привычные инструменты - ValueNotifier, Stream, ChangeNotifier. Главное, чтобы они выполняли свою задачу: уведомлять UI об изменениях точечно, а не целиком.

Библиотека продолжает развиваться, обрастать полезными виджетами и утилитами. Но уже сейчас она дает главное: понятную структуру, которая масштабируется вместе с проектом и не требует переписывать все, когда приложение вырастает из прототипа.


🔗 Ссылка на подробную статью


💡 Вывод:

Elementary - это попытка внедрить в Flutter-разработку проверенные архитектурные принципы, не усложняя процесс и не навязывая тонны шаблонного кода. Четкое разделение слоев, удобное тестирование и производительные обновления делают ее хорошим выбором для проектов, где важна поддерживаемость кода. А простота входа позволяет использовать ее даже новичкам, не пугая их сложными абстракциями.


➡️ Flutter & Dart | Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2❤1🔥1
👣 Как оживить интерфейс с помощью встроенных возможностей Flutter

Всем привет! Пользовательский опыт складывается из мелочей. То, как элемент реагирует на касание, насколько плавно происходит удаление, появляется ли подсказка в нужный момент - все это формирует ощущение качественного приложения. Flutter предоставляет готовые инструменты для таких сценариев и о них полезно знать не только новичкам.


Подсказки, которые не раздражают:

Tooltip - идеальный способ объяснить действие без захламления интерфейса. На мобильных устройствах подсказка появляется при долгом нажатии, на десктопе - при наведении.

Tooltip(
message: 'Очистить корзину',
child: IconButton(
icon: Icon(Icons.delete_sweep),
onPressed: _clearCart,
),
)


Это особенно актуально для иконок, значение которых не всегда очевидно. Но злоупотреблять не стоит: если каждая кнопка обзаведется подсказкой, пользователь перестанет их замечать.


Когда удаление становится действием:

Свайп для удаления давно стал стандартом мобильных интерфейсов. Вместо кнопок и лишних подтверждений - простое движение пальцем. Dismissible реализует это буквально в несколько строк.

Dismissible(
key: Key(todo.id),
background: Container(color: Colors.red),
onDismissed: (_) => _deleteTodo(todo.id),
child: ListTile(title: Text(todo.title)),
)


Важно помнить, что этот виджет не просто скрывает элемент, а требует явной обработки события onDismissed, иначе данные останутся в памяти, а пользователь увидит лишь визуальный трюк.


Перетаскивание как способ организации:

Draggable и DragTarget открывают возможности для создания интерфейсов, где пользователь сам управляет расположением элементов.

Draggable<int>(
data: itemIndex,
feedback: Material(child: Container(width: 50, height: 50, color: Colors.blue)),
child: Container(width: 50, height: 50, color: Colors.grey),
childWhenDragging: Container(width: 50, height: 50, color: Colors.grey[300]),
)


Классический пример: корзина в интернет-магазине. Важный нюанс: визуальная обратная связь должна быть мгновенной. Затемнение, прозрачность, изменение масштаба - все это дает понять, что элемент готов к перемещению.


Списки, которые можно перестраивать:

ReorderableListView решает задачу, которая раньше требовала танцев с бубном: изменение порядка элементов простым перетаскиванием.

ReorderableListView(
onReorder: (oldIndex, newIndex) {
setState(() {
if (newIndex > oldIndex) newIndex--;
final item = items.removeAt(oldIndex);
items.insert(newIndex, item);
});
},
children: [
for (int i = 0; i < items.length; i++)
ListTile(
key: ValueKey(items[i]),
title: Text(items[i]),
),
],
)


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


💡 Вывод:

Все перечисленные виджеты - это лишь верхушка айсберга. Flutter дает гораздо больше возможностей для тонкой настройки взаимодействия. Важно понимать, что UX - это не про количество анимаций, а про их уместность. Слишком активное использование жестов и подсказок может утомить пользователя быстрее, чем их отсутствие.

Хороший интерфейс не кричит о себе. Он просто работает так, как ожидает пользователь. Dismissible, Tooltip, Draggable и ReorderableListView помогают приблизиться к этому идеалу без изобретения велосипеда. Но любой инструмент требует меры и понимания контекста.


➡️ Flutter & Dart | Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
👣 Async/await и Isolate в Flutter: не путайте ожидание с работой

Когда приложение тормозит, первая мысль: «нужно вынести это в отдельный поток». В Flutter эта мысль часто выливается в async/await или Isolate. Но это не взаимозаменяемые вещи, а инструменты для разных задач. И если перепутать, можно получить либо бесполезный код, либо вечно зависающий UI.


Главное заблуждение:

Многие думают: «Раз async/await, значит, не блокирует UI». Но это не так. async/await сам по себе не переносит выполнение в другой поток. Он просто дает удобный способ работать с асинхронными операциями, которые уже неблокирующие по своей природе: запросы к сети, чтение с диска, ожидание таймера. Если внутрь async-функции положить тяжелые вычисления, они будут выполняться в основном потоке и тормозить интерфейс ровно так же, как если бы вы написали их без всяких await.


Когда async/await справляется сам:

Для операций ввода-вывода async/await достаточно. Сеть, базы данных, файловая система - все это уже асинхронно на уровне платформы. Достаточно дождаться результата, и UI останется отзывчивым.


Когда нужно подключать Isolate:

Как только появляются вычисления, которые грузят процессор - парсинг большого JSON, обработка изображений, сложные математические расчеты, - async/await перестает помогать. Здесь нужен настоящий параллелизм. Isolate запускает код в отдельном потоке (или даже ядре) и не трогает основной.

Самый простой способ - compute(). Он берет функцию и данные, запускает их в изоляте и возвращает результат. Идеально для разовых тяжелых задач.


Когда compute не хватает:

Если нужно постоянное взаимодействие с фоновым процессом, например, обработка потока данных или долгая работа с промежуточными результатами - придется использовать Raw Isolate. Там уже ручное управление портами и сообщениями, но зато полный контроль.


Что выбрать:

🔵Ждете ответ от сети или диска? async/await.

🔵Нужно один раз обработать большой кусок данных? compute().

🔵Есть долгий фоновый процесс с обменом сообщениями? Raw Isolate.

🔵Пытаетесь ускорить вычисления, просто добавив async? Бесполезно, почитайте заново.


💡 Вывод:

Async/await и Isolate не конкуренты, а партнеры. Первый отвечает за ожидание, второй - за параллельное выполнение. Смешивать их нужно осознанно, а не по принципу «чтоб не тормозило». Иначе вместо плавного интерфейса получите или вечно грузящийся процессор, или кучу мертвого кода, который ничего не ускоряет.


➡️ Flutter & Dart | Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥1
👣 Дорожная карта Flutter и Dart: что изменится в 2026 году

Всем привет! Команда Flutter опубликовала дорожную карту на 2026 год. Как обычно, без жестких гарантий, но с четкими намерениями. Если коротко: упор на производительность, интеграцию с ИИ и расширение экосистемы. Разбирем главное.


Графика без рывков: Impeller добивает Skia:

На Android завершится переход на Impeller. Для устройств с Android 10 и выше Skia уйдет в прошлое. Это значит конец фризам при первом рендеринге и более предсказуемая производительность. На iOS Impeller уже давно стал стандартом, теперь добираемся до второй платформы.

Для веба ставка делается на WebAssembly. Wasm должен стать стандартом для Flutter Web, приближая производительность к нативному уровню. Если нужно управлять DOM напрямую, предлагают присмотреться к Jaspr - фреймворку на Dart для серверного рендеринга.


GenUI и эфемерный код - Flutter становится ИИ-нативным:

Самая интересная часть - архитектура для агентных интерфейсов. Flutter GenUI SDK позволит создавать UI, который адаптируется к действиям пользователя в реальном времени, генерируясь динамически. Это уже не просто набор экранов, а интерфейсы, которые подстраиваются под контекст.

Чтобы это работало, в Dart планируют добавить поддержку интерпретируемого байт-кода. Это позволит подгружать части приложения по требованию, без публикации в сторах. Фактически - нативный code push, только официальный и встроенный в платформу. Если получится так же гладко, как у Shorebird, но из коробки - будет прорыв.


Full-stack Dart: бэкенд и облако:

Dart пытаются вытащить за пределы клиента. В планах - интеграция с Google Cloud SDK, поддержка Dart в Firebase Cloud Functions и работа с Genkit для ИИ-фич. Звучит амбициозно, но пока выглядит как попытка сделать Dart универсальным языком для всего стека.


ИИ в инструментах - Gemini CLI и Antigravity:

Разработчикам обещают лучшую поддержку Dart и Flutter в ИИ-инструментах от Google. Gemini CLI и новая IDE Antigravity (аналог Cursor) должны глубже понимать код на Dart, а MCP-серверы позволят агентам делать сложные рефакторинги, общаясь напрямую с анализатором.


Дизайн-библиотеки вынесут в независимые пакеты:

Material и Cupertino вынесут в отдельные пакеты. Это ускорит их обновление и снизит связанность с ядром Flutter. Flutter Engine и CLI получат механизмы для расширения, чтобы сторонние платформы (вроде ОС Аврора или Kaspersky OS) могли добавлять поддержку без изменений в основном коде.


Синтаксис и производительность Dart:

В языке появятся приватные именованные параметры, primary constructors и augmentations для генерации кода. build_runner будут улучшать, анализатор - рефакторить для скорости. Работу над компиляцией в Wasm продолжат.


Предсказуемые релизы и работа с сообществом:

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


🔗 Ссылка на подробную статью


💡 Вывод:

2026 год для Flutter сулит завершением больших миграций (Impeller, Wasm) и переходом в новую эру с ИИ-интерфейсами и эфемерным кодом. Dart пытаются сделать полноценным языком для бэкенда, а инструменты разработки более умными и интегрированными с ИИ. Если хотя бы половина этого выстрелит, экосистема ждет серьезный апгрейд.


➡️ Flutter & Dart | Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3❤1🔥1
👣 Настройка окружений во Flutter приложении с помощью Flavors

Когда приложение выходит за пределы пет-проекта, возникает необходимость держать окружения раздельно. Dev, Staging и Production должны жить своей жизнью: с разными API-ключами, бэкендом, а иногда даже иконками и названиями. В Flutter эта задача решается через Flavors. Рассказываю, как настроить и не запутаться.


Что такое Flavors и зачем они нужны:

Flavors (или схемы в iOS, продуктные варианты в Android) позволяют из одной кодовой базы собирать разные варианты приложения. У каждого варианта могут быть свои:

🔵URL бэкенда и ключи API.

🔵Имя приложения и bundle ID.

🔵Иконки и сплеш-скрины.

🔵Настройки сборки и зависимости.

Это дает возможность установить на устройство одновременно dev-версию и продовую, не боясь, что они перезатрут друг друга. И главное - исключает случайную отправку тестового кода в релиз.


Организация кода:

Самый простой способ - сделать отдельные точки входа для каждого окружения. В папке lib создаем файлы:

🔵main_dev.dart

🔵main_staging.dart

🔵main_prod.dart

В каждом передаем в приложение идентификатор среды, чтобы внутри можно было подставлять нужные конфиги.


void main() {
runApp(MyApp(environment: 'DEV'));
}



Android - настройка productFlavors:

В android/app/build.gradle добавляем секцию:


flavorDimensions "default"
productFlavors {
dev {
dimension "default"
applicationIdSuffix ".dev"
versionNameSuffix "-dev"
}
staging {
dimension "default"
applicationIdSuffix ".staging"
versionNameSuffix "-staging"
}
production {
dimension "default"
}
}


Это даст разные имена пакетов: .dev, .staging и основное. Приложения не будут конфликтовать при установке.


iOS - схемы и бандлы:

В Xcode нужно продублировать схему Runner для каждого окружения и задать разные идентификаторы бандла в настройках таргета. Например:

🔵com.example.app.dev

🔵com.example.app.staging

🔵com.example.app

В Info.plist можно выставить разные названия приложений, чтобы в меню было видно, какая версия запущена.


Запуск и сборка:

Для запуска нужного flavor используем флаги:


flutter run --flavor dev -t lib/main_dev.dart
flutter build apk --flavor prod -t lib/main_prod.dart
flutter build ios --flavor staging -t lib/main_staging.dart



Управление конфигурацией:

Внутри кода удобно сделать класс с константами для каждого окружения:


class AppConfig {
static const Map<String, String> apiUrls = {
'DEV': 'https://dev.api.example.com',
'STAGING': 'https://staging.api.example.com',
'PROD': 'https://api.example.com',
};
}


А в приложении просто обращаться по ключу, который пришел из main-файла.


Иконки для каждого flavor:

Пакет flutter_launcher_icons умеет генерировать иконки под разные flavors. В pubspec.yaml прописываем:


flutter_launcher_icons:
flavors:
dev:
image_path: "assets/icons/dev_icon.png"
staging:
image_path: "assets/icons/staging_icon.png"
production:
image_path: "assets/icons/prod_icon.png"


Запускаем и получаем разные иконки для каждого окружения.


Лучшие практики:

🔵Не храните ключи и секреты прямо в коде. Используйте .env файлы или безопасное хранилище.

🔵Ведите имена flavors одинаково на Android и iOS - меньше путаницы.

🔵Настройте CI/CD так, чтобы пайплайны собирали каждый flavor отдельно.

🔵Для тестировщиков dev-сборка должна визуально отличаться (иконка, цвет темы), чтобы случайно не перепутали с продакшеном.


🔗 Ссылка на подробную статью


💡 Вывод:

Flavors - это не про «сделать красиво», а про контроль и безопасность. Правильная настройка окружений убережет от случайных деплоев с тестовыми данными и позволит команде спокойно работать, не боясь что-то сломать в бою. Один раз настроив, вы сэкономите часы нервотрепки и багов на пустом месте.


➡️ Flutter & Dart | Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3❤1
👣 Почему Flutter-приложение греет смартфон и как это исправить

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


Слишком частые перестройки виджетов:

Flutter перерисовывает UI каждый раз, когда меняется состояние. Это нормально. Проблема начинается там, где перерисовывается все, хотя изменилась мелочь. Один лишний setState на родительском виджете и вся иерархия перестраивается заново. Сотни таких перестроек в секунду и процессор работает на износ.

Что делать: дробить UI на мелкие виджеты, использовать const где возможно, подключать умное управление состоянием (Riverpod, Bloc), чтобы обновлялись только те части экрана, которым это действительно нужно.


Анимации, которые не знают отдыха:

Красивая бесконечная анимация - гордость разработчика, но кошмар для батареи. Особенно если таких анимаций несколько и они продолжают работать даже тогда, когда пользователь ушёл на другой экран или свернул приложение. GPU не отдыхает, телефон греется.

Решение: всегда останавливать анимации в dispose, при возможности приостанавливать их, когда виджет не в фокусе, и не злоупотреблять тяжелыми Lottie-файлами там, где можно обойтись простыми переходами.


Сетевые запросы, которые грузят сервер без продыху:

Каждый запрос к сети - это пробуждение радио-модуля, парсинг JSON, обновление UI. Если приложение опрашивает сервер каждые пару секунд, телефон будет греться даже в фоне. Особенно больно, когда запросы делаются прямо в build или без кэширования.

Выход: выносить сеть из build, использовать кэширование, реже опрашивать сервер, а для real-time фич переходить на WebSockets, которые работают эффективнее частых REST-запросов.


Тяжелые изображения и видео:

Загрузить картинку в полном разрешении и отобразить ее как есть - легко. Но GPU придется каждый раз масштабировать этот гигантский файл, тратя на это ресурсы. Если таких картинок в списке много - телефон начнет нагреваться очень быстро.

Исправление: изменять размер изображений на лету (cacheWidth, cacheHeight), сжимать, использовать отложенную загрузку и не запускать видео автоматически в каждом элементе списка.


🔗 Ссылка на подробную статью


💡 Вывод:

Перегрев телефона - это не приговор и не баг платформы. Это следствие того, как написан код. Чаще всего проблемы решаются точечными правками: убрать лишние перестройки, остановить анимации, оптимизировать сеть и изображения. Один раз разобравшись с этими паттернами, вы не только спасете батарею пользователей, но и сделаете приложение быстрее и приятнее. А пользователи будут благодарны не только в отзывах, но и своим теплым, но не горячим телефоном.


➡️ Flutter & Dart | Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2❤1🔥1
👣 Обновлен плагин Flutter для VS Code: что нового?

Вышла новая версия плагина для VS Code, и в ней снова доработали то, что бесит больше всего - скорость, тесты и повседневные мелочи. Никаких революций, но десяток мелких улучшений, которые в сумме делают работу заметно комфортнее.


Тесты теперь работают адекватно:

В этом обновлении тестам уделили особое внимание. Исправили несколько раздражающих багов: тесты больше не выдают ошибку Cannot read properties of null в консоли, не пропадают из панели результатов и не помечаются ошибочно как пропущенные.

Главное - тесты стали обнаруживаться значительно быстрее в больших проектах. Особенно заметно, если на диске работает антивирус, который раньше мог тормозить процесс. Теперь это должно уйти.

Для тех, кто использует test_reflective_loader, тесты теперь группируются по классам - навигация по результатам становится чище.


Параллельные действия:

Команда Get Packages for All Projects теперь умеет запускать несколько процессов одновременно. Раньше пакеты подтягивались последовательно для каждого проекта, что могло затягиваться. Теперь все летает параллельно, используя до половины ядер процессора.


Редактор и навигация:

Несколько мелочей, которые важны в ежедневной работе:

🔵Переход к импорту больше не прыгает в другую вкладку, если файл уже открыт - остается там, где удобно.

🔵Исправления для зависимостей теперь корректно перемещают их из dev_dependencies в dependencies, если это нужно.

🔵Подсветка ключевых слов стала единообразной - разница между начальной и семантической подсветкой ушла.

🔵Обновили грамматику для грядущих Primary Constructors - готовимся к новым фичам Dart.

🔵Геттеры и поля теперь отображаются точнее, без путаницы.


Работа с эмуляторами:

Убрали лишние запросы к эмуляторам до того, как управление устройствами реально включено. Это должно уменьшить количество таймаутов, когда система только стартует.


Совместимость и старые версии:

Поддержка SDK ниже Dart 3.2 / Flutter 3.16 окончательно прекращена. Если вы все еще сидите на древних версиях, придется либо обновляться, либо откатывать плагин до старой версии - на сайте есть таблица совместимости.


Что будет в будущих SDK:

Некоторые фичи уже есть в бета-версиях, но в стабильных появятся позже:

🔵Стек-трейсы теперь показываются в всплывающих окнах, даже если ошибка проброшена через Completer.completeError().

🔵Фабричные конструкторы больше не отображаются как <unknown> в навигации.


🔗 Ссылка на плагин


💡 Вывод:

Очередное обновление, которое не кричит о себе, но делает работу с Flutter в VS Code чуть более предсказуемой и быстрой. Особенно радуют правки тестов и параллельные операции. Если вы активно используете VS Code для разработки - обновляйтесь, мелочи действительно имеют значение.


➡️ Flutter & Dart | Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤1
👣 Анонсирован Genkit для Dart: фреймворк для ИИ-приложений

Google анонсировала Genkit Dart - open-source фреймворк для создания полноценных ИИ-приложений на Dart и Flutter. Это не просто очередная обертка над API, а инструмент, который позволяет строить сложные ИИ-сценарии с типизацией, тестированием и удобным UI для отладки. Пока в предварительной версии, но уже выглядит многообещающе.


Что такое Genkit Dart и зачем он нужен:

Сейчас ИИ-функции проникают повсюду: кто-то строит вокруг них новые продукты, кто-то добавляет умные фичи в существующие. Genkit Dart закрывает сразу несколько потребностей:

🔵Единый API для работы с разными моделями (Google Gemini, OpenAI, Anthropic и другие совместимые).

🔵Строгая типизация ответов - никаких неожиданных форматов.

🔵Возможность писать логику один раз и запускать ее где угодно: на сервере, в облаке или прямо внутри Flutter-приложения.

🔵Удобный локальный UI для тестирования и отладки промптов.


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

Фреймворк предлагает несколько сценариев использования, в зависимости от того, где должна выполняться ИИ-логика и как вы хотите управлять ключами.


Все внутри Flutter (для прототипов):

Самый простой способ - писать всю логику прямо в приложении. Подходит для прототипов или случаев, когда пользователь сам вводит свой API-ключ. Но здесь есть важное предупреждение: публиковать в сторе приложение с зашитым ключом - плохая идея, его легко вытащат.


Бэкенд + Flutter с общей типизацией:

Когда логика сложная или ключи нужно прятать, весь ИИ-код уезжает на сервер. Flutter-приложение вызывает его как удаленный метод. Благодаря тому, что и фронт, и бэк на Dart, можно переиспользовать общие схемы данных и сохранить строгую типизацию от конца до конца.


Прокси-сервер для моделей:

Компромиссный вариант: на сервере поднимается тонкая прослойка, которая проксирует запросы к ИИ-моделям, добавляя авторизацию и валидацию. Ключи API хранятся на сервере, а Flutter-приложение общается с ним как с обычным Genkit-клиентом.


🔗 Читать подробнее


💡 Вывод:

Genkit Dart - это попытка внести порядок в хаос ИИ-разработки. Вместо того чтобы каждый раз городить свои обертки над моделями, можно взять готовый фреймворк с типизацией, тестируемостью и удобным UI. Пока это превью, но направление выглядит правильным. Если вы работаете с Dart/Flutter и думаете, как добавить в проект ИИ-фичи - присмотритесь.


➡️ Flutter & Dart | Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4👍1
This media is not supported in your browser
VIEW IN TELEGRAM
👣 Как добавить эффект Liquid Glass в Flutter-приложение

С выходом iOS 26 компания Apple представила дизайн Liquid Glass - ту самую жидкую стеклянную эстетику, которая делает интерфейсы объемными и тактильно приятными. Сообщество Flutter все это время искало способ повторить это на своей платформе. Попытки были, но с ограничениями: кто-то завязывался на Impeller и терял поддержку Windows и Web, кто-то просто делал обычное размытие, выдавая его за жидкое стекло. Теперь появилось работающее кроссплатформенное решение.


Что раньше:

Краткий обзор того, что предлагалось раньше:

🔵Пакеты, которые либо не работали, либо не имели примеров.

🔵Решения, завязанные на Impeller - красиво, но только для мобилок.

🔵Обычный стекломорфизм с размытием, который никакой не жидкий.

🔵Нативные обертки, которые работали только на iOS.

Все они либо не были кроссплатформенными, либо не давали нужного эффекта.


Что появилось сейчас:

Пакет liquid_glass_easy делает именно то, что заявлено: настоящие интерактивные линзы из жидкого стекла с физикой преломления, искажения и реакции на касания. Работает на Android, iOS, Web, macOS и Windows. В основе - GPU-шейдеры, так что производительность остается высокой даже при нескольких активных линзах.


Как это устроено:

Все строится вокруг двух виджетов:

🔵LiquidGlassView - контейнер, который захватывает содержимое фона в реальном времени и передает его в линзы.

🔵LiquidGlass - сама линза, которую можно настроить: форма, размер, степень искажения, размытие, цвет, направление света.

Линзу можно сделать перетаскиваемой, анимировать через контроллер, накладывать поверх любого контента. Все настраивается до мелочей.


🔗 Читать подробнее


💡 Вывод:

Жидкое стекло во Flutter наконец-то перестало быть чем-то, что можно только представить в воображении. liquid_glass_easy дает рабочий инструмент, который не требует жертвовать кроссплатформенностью и производительностью. Да, это не та фича, ради которой пользователь побежит скачивать приложение, но когда хочется добавить интерфейсу глубины и тактильности, теперь есть куда смотреть. Для дизайнерских экспериментов и вау-эффекта - вполне.


➡️ Flutter & Dart | Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4❤1👍1
👣 Keys во Flutter: как не потерять состояние при перестройке списков

Один из самых частых источников багов во Flutter - потеря состояния при перестройке списков или перестановке элементов. Кажется, все работает, но при добавлении новой карточки счетчик перескакивает на другую. Или при изменении порядка чекбоксы остаются отмеченными не там. Чаще всего проблема решается при помощи Keys. Но использовать их бездумно - тоже плохая идея.


Для чего используются Keys:

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

Keys дают Flutter дополнительную информацию: вместо «третий элемент в списке» он ищет «элемент с таким-то идентификатором». Благодаря этому состояние прикрепляется к конкретному объекту, а не к индексу.


Типы ключей и когда их выбирать:

🔵ValueKey - самый частый выбор. Берет какое-то стабильное значение (id, уникальный заголовок) и использует его как идентификатор. Идеально для списков, где у каждого элемента есть уникальный ключ из данных.

🔵ObjectKey - использует сам объект в качестве идентификатора, сравнивая его через стандартный оператор ==. Пригождается, когда у элемента нет уникального поля (например id), но сам объект достаточно стабилен и не меняется в процессе работы.

🔵UniqueKey - каждый раз создает новый идентификатор. Это гарантирует, что Flutter не будет пытаться сопоставить виджет с предыдущим, а создаст новый элемент с нуля. Удобно, когда нужно принудительно сбросить состояние (например пересоздать анимацию), но использовать на каждый чих не стоит - производительность пострадает.

🔵GlobalKey - тяжелая артиллерия. Позволяет получить доступ к состоянию виджета из любой точки приложения. Нужен для сложных сценариев: работа с формами, программная навигация, тестирование. Но каждый такой ключ хранится глобально и не очищается автоматически, поэтому их количество должно быть минимальным.


Где без ключей не обойтись:

🔵Списки, где элементы можно переставлять, добавлять или удалять.

🔵Виджеты, которые меняют порядок в зависимости от условий.

🔵Любые места, где важно сохранить состояние за конкретным экземпляром данных.


Где ключи не нужны:

🔵Статичные списки, которые не меняются.

🔵Виджеты без внутреннего состояния (статусные иконки, просто текст).

🔵Случаи, где состояние вообще не важно.


Чего делать не стоит:

Не надо оборачивать каждый виджет в Key просто потому, что так можно. Лишние ключи усложняют алгоритмы сравнения и могут замедлить рендеринг. Особенно это касается GlobalKey - его наличие в каждом втором виджете быстро приведет к утечкам и падению производительности.


💡 Вывод:

Keys - это инструмент для точечного решения проблем с идентификацией виджетов. Если при перестройке интерфейса состояние прыгает или теряется - скорее всего, нужен ValueKey. Если нужно сбросить внутреннее состояние - поможет UniqueKey. А если без доступа к виджету из другого места не обойтись - придется использовать GlobalKey. Во всех остальных случаях лучше обойтись без них.


➡️ Flutter & Dart | Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3❤1
👣 Flutter и нативный код: как работают MethodChannel и EventChannel

Flutter хорош тем, что один код работает на двух платформах. Но рано или поздно наступает момент, когда нужно сделать что-то нативное: получить модель устройства, подключиться к Bluetooth, считать данные с датчиков. Здесь на помощь приходят платформенные каналы - механизм, который позволяет Dart и нативному коду обмениваться сообщениями.


Как устроен этот мост:

Все взаимодействие строится вокруг каналов. Это не магия, а просто способ передавать сообщения туда и обратно. Dart-код отправляет запрос, нативная сторона его обрабатывает и возвращает ответ. Все данные при этом сериализуются (превращаются в понятный для обеих сторон формат) и десериализуются обратно.

Самый частый сценарий - метод-канал (MethodChannel). Это как вызов функции, но на удаленной стороне. Вызываете метод с именем, передаете параметры, ждете ответа. Все асинхронно, то есть приложение не зависает, пока нативная часть думает.


Когда нужен MethodChannel:

Если нужно разово что-то запросить у системы - модель телефона, версию ОС, разрешение - это к MethodChannel. Работает просто: на стороне Dart создается канал с уникальным именем (обычно используют обратную доменную нотацию, чтобы не пересекаться с другими плагинами). Потом вызывается метод, и в ответ приходит результат или ошибка.

В нативной части (Android на Kotlin, iOS на Swift) этот же канал регистрируется, и для каждого вызова прописывается обработчик. Если метод известен - возвращается результат, если нет - вызывается notImplemented.


Когда нужен EventChannel:

Бывают ситуации, когда данные приходят не по запросу, а сами. Например, акселерометр или датчик движения - они отправляют показатели постоянно, пока подписка активна. Здесь пригождается EventChannel.

Flutter подписывается на поток, нативная сторона начинает слать события через специальный объект (eventSink). Как только подписка отменяется, поток закрывается. Это идеально для датчиков, геолокации, состояния батареи - всего, что меняется во времени.


🔗 Читать подробнее


💡 Вывод:

Платформенные каналы - это не страшно. MethodChannel закрывает задачи «спросили - получили», EventChannel - «подписались - получаем поток». Главное - помнить про асинхронность, типы данных и не перегружать главный поток. Если нужна сложная логика, лучше вынести ее в нативную часть, а в Dart только вызывать и получать результат. А еще - всегда оборачивать вызовы в try-catch, потому что нативная сторона может вернуть ошибку.


➡️ Flutter & Dart | Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥2❤1
🏋️ Установка APK на Android усложняется: придется ждать сутки

С августа 2026 года установка APK-файлов от неизвестных разработчиков на Android станет сложнее. Google вводит продвинутый процесс, который должен защитить пользователей от мошенников, но заодно превращает установку любого приложения не из Google Play в квест.

Это касается не только нативных разработчиков, но и разработчиков на Flutter, которые собирают приложения под Android и которые распространяют свои приложения в обход Google Play (например через сайты, соц. сети или телеграм). Это означает, что привычный способ «включил неизвестные источники -> установил» больше не сработает. Вместо этого - сутки ожидания, биометрия и подтверждение, что вас никто не принуждает.


Что изменится:

Раньше достаточно было включить разрешение на установку из неизвестных источников и можно было ставить любой APK. Теперь нужно будет пройти целую процедуру:

🔵Включить режим разработчика.

🔵Подтвердить, что вас никто не принуждает.

🔵Перезагрузить телефон.

🔵Подождать сутки.

🔵Пройти биометрическую проверку.

Только после этого можно будет устанавливать приложения от неизвестных разработчиков. И то - каждые семь дней процедуру, возможно, придется повторять.


Почему ввели данный процесс:

Google объясняет это борьбой с мошенниками. По статистике Global Anti-Scam Alliance, 57% взрослых сталкивались с мошенничеством за последний год, а ущерб составил 442 миллиарда долларов. Мошенники часто давят на жертв по телефону, заставляя прямо сейчас отключить защиту и установить вредоносное приложение. Принудительная пауза в сутки должна сломать эту схему.

Для тех, кто просто хочет установить приложение от независимого разработчика, это кажется излишним и выглядит как наказание.


🔗 Читать подробнее


💡 Вывод:

Android теряет одну из главных своих особенностей - свободу установки любого приложения без ограничений. Теперь это будет возможно только после процедуры с суточным ожиданием. Google говорит о безопасности и это действительно важная тема. Для нативных разработчиков и Flutter-разработчиков, которые тестируют сборки на реальных устройствах, раздают бета-версии клиентам через телеграм или просто устанавливают собственные APK в процессе разработки, это ощущается как шаг назад. В погоне за защитой от мошенников страдают в том числе разработчики, которым нужна возможность быстро ставить приложения не из магазина.


➡️ Flutter & Dart | Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👀4🙏2👍1🗿1