fa
Feedback
Flutter Friendly

Flutter Friendly

رفتن به کانال در Telegram

Канал Friflex о разработке на Flutter. Обновления, плагины, полезные материалы — превращаем знания в реальный опыт, доступный каждому разработчику. 🔗 Наш канал для разработчиков: @friflex_dev 🔗 Канал о продуктовой разработке: @friflex_product

نمایش بیشتر
1 016
مشترکین
اطلاعاتی وجود ندارد24 ساعت
-37 روز
-630 روز
آرشیو پست ها
Какой принцип SOLID нарушен, если класс UserManager одновременно валидирует данные, сохраняет в БД и отправляет email?
Anonymous voting

😎Надеемся, вы отлично отдыхаете! За этот год во @flutterfriendly вышло 222 публикации, а вы поставили более 3800 реакций. Ре
😎Надеемся, вы отлично отдыхаете! За этот год во @flutterfriendly вышло 222 публикации, а вы поставили более 3800 реакций. Решили вспомнить посты, которые вы больше всего читали и которыми активно делились за 2025 год: Аудит безопасности мобильных приложений: виды и этапы Использование ARB-формата Обновление Android Studio без ошибок Как пользоваться режимом выбора виджетов Пакет meta Оптимизация списков: как сделать скролл плавным и эффективным Виджеты для управления размерами Работа с иконками Библиотека dartx FutureOr в Dart gRPC во Flutter: эффективная коммуникация между клиентом и сервером Модификаторы классов в Dart Тестирование кода: виды и для чего это нужно Режимы сборки: debug, profile и release Friflex Flutter Starter Паттерн Strategy Запись митапа: способы темизации и кастомизации мобильных приложений Создание виджетов поверх существующего интерфейса Определение местоположение пользователя: интеграция в приложение Как быстро развернуть сайт документации с помощью Docusaurus Адаптивная верстка: что это такое и как реализовать Утечки памяти во Flutter-приложениях Техническое собеседование: как подготовиться начинающим разработчикам Hot Restart и Hot Reload: в чем отличия и как работают

Какой Sliver позволяет вставить обычный виджет в CustomScrollView?
Anonymous voting

Какие модификаторы можно комбинировать?
Anonymous voting

Какой виджет позволяет дочернему элементу выходить за границы родителя?
Anonymous voting

Какие три основных подхода существуют для работы с иконками во Flutter?
Anonymous voting

💡Устроим небольшую проверку изученного за год? На новогодних праздниках вас будет ждать небольшая викторина от Розы, Кати и Анны

🙂Присоединяетесь, у нас тут Flutter-утренник А пока вы готовите костюмы, хотим поздравить с Новым годом! ✨Больше инновационн
+7
🙂Присоединяетесь, у нас тут Flutter-утренник А пока вы готовите костюмы, хотим поздравить с Новым годом! ✨Больше инновационных проектов и вдохновения для их реализации ✨Больше стабильных релизов и довольных пользователей ✨И конечно, больше времени на творчество, изучение нового и отдых

🎅Всем привет! Год подходит к концу, и мы, Роза, Катя и Аня, хотим устроить небольшую ретроспективу и поделиться своими откры
🎅Всем привет! Год подходит к концу, и мы, Роза, Катя и Аня, хотим устроить небольшую ретроспективу и поделиться своими открытиями за 2025-ый 🎅Роза, Flutter Dev Friflex: Этот год был богат на задачи абсолютно разного характера. За это время я сделала пару парсеров, разработала расширение для DevTools, внедрила LLM во Flutter Web-приложение, поработала с Serverpod, узнала, что такое OTA, и в целом писала бэкенд на Dart. Главный инсайт: любую задачу можно сделать (почти). Какой бы сложной она ни казалась вначале, всегда находится решение. И в этом есть свой кайф — на второй-третий день ты всем сердцем ненавидишь ее, не понимая, с чего начать, а когда все-таки решаешь — это настоящее блаженство Раньше я часто бралась за задачу без особого анализа и обдумывания деталей. Но когда сталкиваешься с большой фичей, которая может повлиять на другие части проекта, начинаешь задумываться над каждым шагом. Моим ориентиром на год было улучшение кода и архитектуры. Справилась ли я? Сложно сказать. Но открывая свой старый код, я постоянно вижу, что его можно улучшить и оптимизировать — а то, что я это замечаю, наверное, о чем-то говорит. 🎅Катя, Flutter Dev Friflex: Мой главный инсайт: агенты и вайб-кодинг. В этом году у меня было открытие, которое изменило мой подход к разработке. Сейчас начинается эра vibe coding — когда ты не пишешь код построчно, а просто описываешь намерение, и агент превращает его в рабочий (не всегда) код. Раньше программирование было про «как»: как написать цикл, как обработать исключение, как настроить конфиг. С агентами это стало про «что»: что я хочу получить, какую проблему решаем. Что изменилось? ✔️Скорость: То, что занимало час, теперь — 15 минут ✔️Креативность: Освободилось время на архитектуру и эксперименты ✔️Рутина: Boilerplate, тесты, документация генерируются автоматически Но главное — важно понимать, что даже сейчас агенты работают неидельно и их нужно перепроверять. Но с базовыми задачками они справляются хорошо. Конкретно я использую copilot, и в первое время он выдавал чепуху, но тут главное хорошо описывать промпт и потом читать код. 🎅С наступающим Новым годом, коллеги! Пусть ваши билды всегда будут зелеными, а продакшен — стабильным. И помните: лучший коммит в конце декабря — это закрытый ноутбук. 🎅Анна, Flutter Team Lead Friflex Этот год был супер насыщенным на новые знания и открытия. Но мой самый главный инсайт — коммуникация команды с заказчиком. На первый взгляд здесь все максимально просто. В команде у каждого участника есть своя роль, а с ней — свои обязанности. Но, как показывает практика, иногда бизнес выдвигает свои требования, которые могут сильно повлиять на весь процесс коммуникации. В этом случае важно идти на компромисс. Да, заказчик может хотеть общаться напрямую с разработчиками и другими членами команды. Да, заказчик может просить встречи и обсуждения в нерабочее время. Здесь самое главное — уверенно отстоять границы команды, донести до бизнеса наши внутренние роли, предложить варианты, которые не будут негативно влиять на продуктивность команды. Самая идеальная схема коммуникации — через PM, как через некий буфер, который не пропускает лишней информации в обе стороны. В таком случае другие члены команды остаются максимально в фокусе своих задач и не тратят силы и время на дополнительные вопросы. И главное, что стоит помнить и заказчикам, и команде — вы не конкуренты, вы партнеры! У вас общая цель, вы идете к стабильному, рабочему продукту. А достичь этой цели возможно только совместными усилиями и обоюдными компромиссами.

🎅Привет! Это Анна, Friflex Flutter Team Lead Уже пару раз мы затрагивали вопрос in-app покупок в приложениях. Например, писа
🎅Привет! Это Анна, Friflex Flutter Team Lead Уже пару раз мы затрагивали вопрос in-app покупок в приложениях. Например, писали о том, как интегрировать плагин in_app_purchase в Flutter-проект. Сегодня же поговорим о том, как в целом по шагам работает процесс покупки и получения информации о них. Дисклеймер. Эта информация актуальна именно для покупок через App Store, так как они сейчас доступны в России, в отличие от покупок через Google Play. Для удобства представим — мы создаем Flutter-приложение, которое имеет определенный контент, доступный только пользователям с подпиской. Подписка оформляется через App Store и привязывается к конкретному аккаунту пользователя в базе данных на бэкенде. Здесь мы четко можем увидеть трех участников процесса — приложение, бэкенд и App Store. Теперь разберем, как же они будут друг с другом взаимодействовать. 1 шаг. Приложение. Пользователь в приложении знакомится с доступными продуктами для покупки. Затем через определенные элементы интерфейса осуществляет in-app покупку. Для этого в проект необходимо интегрировать in_app_purchase. Подробнее процесс интеграции я описывала в предыдущем посте на эту тему. Основная задача приложения — вывести информацию об актуальных продуктах и при покупке вызвать нативное окно оплаты App Store. 2 шаг. App Store. App Store самостоятельно выполняет процесс списания средств с привязанного счета. В случае успеха все данные о транзакции будут переданы дальше. И здесь два варианта развития событий: ✔️Данные о транзакции возвращаются колбэком в приложение. Дальше приложение может делать с ними все, что угодно, например, отправить на сервер ✔️Данные о транзакции получает сам сервер. За это ответственна технология Server-to-Server Notifications, когда App Store отправляет уведомление о покупке с данными на сервер. В этом случае требуется указать эндпоинт в App Store Connect в настройках приложения У первого варианта больше рисков, так как весь процесс покупки напрямую зависит от состояния устройства — включено ли, подключено ли к интернету, открыто ли приложение. Если какой-то из этих пунктов будет нарушен, процесс может не завершиться, и данные о покупке не дойдут на бэкенд. Второй вариант полностью абстрагирован от пользователя. Общение ведется только между сервером приложения и App Store. Здесь все выше перечисленные факторы не повлияют и не прервут процесс. В нашем примере пойдем именно по этому пути. 3 шаг. Бэкенд Бэкенд получает колбэк от App Store с данными о транзакции. Эти данные достаточно объемны, они содержат много полезной информации. Например, тут можно найти сведения о купленном продукте, о его цене и времени покупки. Также из приложения можно передавать данные о внутреннем аккаунте приложения через поле applicationUsername. Эта информация также придет с данными транзакции. Важно — перед выдачей прав пользователю покупку стоит сначала проверить. 4 шаг. Приложение. Дальше приложение стучится на бэкенд, просит данные для конкретного авторизованного пользователя. Сервер в свою очередь самостоятельно проверяет актуальность данных и наличие оплаченного периода и определяет, какой контент нужно отдать. Самое лучшее здесь то, что приложение остается «тонким клиентом». Оно не нагружено сложной логикой внутренних проверок. Его задача в том, чтобы просто отобразить полученные данные. Если остались вопросы, задавайте в комментариях⬇️

А как у вас в команде?
Anonymous voting

🤩Привет, это Катя, Flutter Dev Friflex Сегодня хочу поговорить о самом волнующем вопросе конца декабря: «А давайте запушим в
🤩Привет, это Катя, Flutter Dev Friflex Сегодня хочу поговорить о самом волнующем вопросе конца декабря: «А давайте запушим в прод?» Предновогодний синдром деплоя Знаете, что объединяет разработчиков по всему миру в конце декабря? Нет, не корпоративы с оливье. Это священный ужас при виде коллеги, который бодро пишет в чатик: «Ребят, у меня тут маленький фикс, можно быстренько в прод?» Маленький фикс в конце декабря — это как «просто быстренько зайти в магазин с мамой». Такого не бывает. Декабрь — это время, когда даже самые отчаянные разрабы начинают говорить страшным голосом про code freeze. И правильно делают! Потому что если что-то пойдет не так 30 декабря, вы будете чинить баги не под бой курантов, а вместо боя курантов. Что такое code freeze и почему его вводить Code freeze (или фриз деплоя) — это когда команда договаривается: «Все, ребята, стоп-машина! До Нового года только критичные баги трогаем, и то если они прям совсем критичные». Это как карантин, но для кода. Обычно фриз начинается где-то с 20-25 декабря и длится до середины января. В это время: ❌Нельзя мерджить фичи (даже если они «точно работают») ❌Нельзя рефакторить (это вообще никогда нельзя перед праздниками) ❌Нельзя обновлять зависимости (Flutter SDK может и подождать) ❕Можно только чинить то, что реально горит Почему нельзя лить в прод в конце декабря Представьте: вы залили фичу 29 декабря, все ушли праздновать, а 31-го приложение начинает крашиться у половины пользователей. Поздравляю, вы только что стали главным злодеем чужого Нового года! Теперь вместо шампанского вы будете пить кофе и смотреть в логи Crashlytics. А еще есть нюанс: если что-то сломается, собрать команду для фикса будет невозможно. Кто-то уехал к бабушке в деревню без интернета, кто-то в Таиланде (и там другой часовой пояс), а кто-то вообще отключил рабочий телефон. И правильно сделал. Исключения из правил Единственный валидный повод для деплоя в конце декабря — это когда прод реально горит. И под горит я имею в виду не «кнопка немного криво стоит», а «пользователи не могут оплачивать заказы» или «приложение крашится при старте у всех». Но даже в этом случае фикс должен быть: ▪️Минимальным (чиним только то, что сломалось) ▪️Протестированным (да, даже в предновогодней запаре) ▪️С откатом наготове (если что-то пойдет не так) Как пережить фриз без стресса Главный секрет спокойного Нового года — это подготовиться заранее. Залейте все важные фичи до 20 декабря, проведите тщательное тестирование, убедитесь, что мониторинг работает, и можете спокойно идти выбирать мандарины🍊

🎅Всем привет! С вами Роза, Flutter dev Friflex Все мы привыкли использовать Column, Row, ListView и различные layout-виджеты
🎅Всем привет! С вами Роза, Flutter dev Friflex Все мы привыкли использовать Column, Row, ListView и различные layout-виджеты. Но как часто в проектах вы сталкивались с таблицами и использовали для этого виджет Table? Табличную структуру можно реализовать через комбинацию Column + Row + ListView. Иногда это оправдано, но не всегда — иногда лучше использовать специально предназначенные для этого инструменты. Во Flutter для работы с таблицами есть несколько виджетов: ✔️ PaginatedDataTable — для работы с большими таблицами с поддержкой постраничной навигации. Используется для больших таблиц, где можно  управлять сортировкой и переходами между страницами данных
PaginatedDataTable(
  header: Text('Список пользователей'),
  columns: const [
    DataColumn(label: Text('ID')),
    DataColumn(label: Text('Имя')),
    DataColumn(label: Text('Email')),
  ],
  source: myDataSource, // DataTableSource
  rowsPerPage: 10,
)
Для сортировки таблицы используются параметры sortColumnIndex (номер колонки) и sortAscending (направление сортировки), а обработка самой сортировки выполняется через onSort. ✔️ DataTable – для небольших или заранее известных наборов данных. Строки добавляются динамически из моделей, что упрощает масштабирование Пример с моделью User:
DataTable(
  columns: const [
    DataColumn(label: Text('ID')),
    DataColumn(label: Text('Имя')),
    DataColumn(label: Text('Email')),
  ],
  rows: users.map((user) {
    return DataRow(cells: [
      DataCell(Text(user.id.toString())),
      DataCell(Text(user.name)),
      DataCell(Text(user.email)),
    ]);
  }).toList(),
)
✔️ Table — дает полный контроль над структурой строк и столбцов. Каждая строка создается черезRow, ListVie а каждая ячейка может содержать любой виджет или комбинированный UI.
Table(
  border: TableBorder.all(color: Colors.black26), // границы таблицы
  columnWidths: { // ширина столбцов
    0: FlexColumnWidth(2),
    1: FlexColumnWidth(3),
  },
  children: [ // список строк
    TableRow(children: [ // каждая строка с набором виджетов
      Text('ID', textAlign: TextAlign.center),
      Text('Имя', textAlign: TextAlign.center),
    ]),
    TableRow(children: [
      Text('1', textAlign: TextAlign.center),
      Row(children: [Icon(Icons.person), Text('Алексей')]),
    ]),
    TableRow(children: [
      Text('2', textAlign: TextAlign.center),
      Row(children: [Icon(Icons.person), Text('Мария')]),
    ]),
  ],
)
Через columnWidths можно управлять шириной столбцов: ▫️ FixedColumnWidth — фиксированная ширина ▫️ FlexColumnWidth – пропорциональная ▫️ IntrinsicColumnWidth – по содержимому      ⚠️ Внутри ячеек Table нельзя использовать Expanded, так как отрисовка и размеры управляются самим виджетом. Table удобно использовать для кастомных макетов, где важны выравнивание ячеек, ширина столбцов и границы. Каждая строка состоит из набора виджетов, что облегчает управление макетом и сохраняет структуру данных. При работе с таблицами есть несколько нюансов: ✔️IntrinsicColumnWidth заставляет Flutter делать два прохода при компоновке: сначала измерить все элементы, затем отрисовать. Если строк сотни — интерфейс может начать тормозить. По возможности лучше использовать FlexColumnWidth ✔️Адаптивность на маленьких экранах. Таблица подстраивается под ширину экрана, но на небольших устройствах может быть трудночитаема. В таких случаях стоит использовать горизонтальный скролл или адаптивный макет ✔️ Динамическая высота строк. Если содержимое ячеек часто меняется (например, при загрузке данных, анимациях или использовании ExpansionTile`), `Table может стать узким местом. Для таких случаев иногда удобнее использовать комбинацию ListView + Row/Column ✔️Использование Row/Column + ListView оправдано, если у вас простой список элементов, строки сильно различаются по содержимому или нужен вертикальный скролл без необходимости выравнивания колонок На этом все! ❤️ всем любителям табличек

💠Привет! Это Анна, Flutter Team Lead. Сегодня поговорим про версионирование Futter-приложений. Тема простая, но часто вызыва
💠Привет! Это Анна, Flutter Team Lead. Сегодня поговорим про версионирование Futter-приложений. Тема простая, но часто вызывает вопросы у начинающих разработчиков. Наверняка вы знаете, что во Flutter-приложениях версия указывается в pubspec.yaml с ключом vesion. Выглядит это так:
version: 1.2.3+4
Для удобства восприятия возьмем буквенное представление:
version: A.B.C+D
Сочетание A.B.C отвечает за версию приложения. Значение под буквой D — за версию сборки. Когда повышать каждый уровень версии приложения? A — отвечает за мажорные изменения. Повышать этот уровень точно нужно, если ваше приложение сильно менялось. Например, если вы: ✔️Полностью изменили дизайн ✔️Переработали пользовательские пути ✔️Радикально изменили или удалили ключевые функции ✔️Загрузили полностью новое приложение Если приложение уже не похоже на себя и может восприниматься пользователем как другой продукт, смело повышайте уровень А. B — минорные изменения. Сюда обычно относятся: ✔️Новый функционал и масштабирование старого ✔️Новые пользовательские пути без критичного изменения старых Если приложение выросло, получило новые фичи, но при этом сохранило свой предыдущий вид — нужно увеличивать уровень B. C — мелкие фиксы и доработки. Стоит повысить уровень С, когда вы: ✔️Вносите правки по багам или верстке ✔️Повышаете производительность приложения ✔️Выполняете рефакторинг или обновление зависимостей без влияния на ранее реализованные функции При повышении версии важно помнить: оно выполняется по уровню наиболее значимых изменений. Например, сейчас версия вашего приложения — 3.9.14. В новом релизе вы исправили баги, добавили новую фичу и полностью изменили весь UX/UI приложения. В этом случае самым критичным изменением считается последнее. Версия — 4.0.0. А что с версией сборки? D — версия конкретного билда. Стандартно это значение инкрементируется в каждый новый билд вашего приложения. Независимо от изменений внутри. Без повышения этой версии сторы не смогут принять вашу новую сборку. Возможно, в коде вообще не было никаких изменений — поднять D нужно обязательно. ❤️— если было полезно

Какой сценарий вы бы протестировали первым?
Anonymous voting

Привет, я Катя, Friflex Flutter Dev. Сегодня расскажу о GenUI SDK для Flutter — инструменте, который помогает собирать динами
Привет, я Катя, Friflex Flutter Dev. Сегодня расскажу о GenUI SDK для Flutter — инструменте, который помогает собирать динамические пользовательские интерфейсы с помощью генеративного ИИ. Что такое GenUI SDK GenUI SDK для Flutter — это альфа-версия SDK на pub.dev. Она предлагает другой подход к взаимодействию с пользователем: вместо текстового ответа чат-бота вы можете показывать адаптивные визуальные компоненты. Например, выпадающие списки, слайдеры, карусели товаров и формы с выбором даты. Компоненты меняются в реальном времени, в зависимости от намерений пользователя. ⭐️ Вопрос к размышлению: как часто вы сталкивались с тем, что чат-бот перечисляет товары текстом, хотя удобнее было бы сразу увидеть карточки с кнопками? Как работает GenUI Процесс устроен как интерактивный цикл. Пользователь вводит запрос (например: «помоги спланировать поездку в Токио»), а приложение отправляет его ИИ-агенту вместе с описанием доступных виджетов. Дальше ИИ-агент генерирует не только текст, но и описание интерфейса с помощью инструментов GenUI SDK. Обычно это структура в формате JSON (JavaScript Object Notation), которую приложение десериализует и превращает в соответствующие Flutter-виджеты. SDK может отрисовывать стандартные компоненты постепенно, по мере генерации ответа большой языковой моделью (LLM). Так интерфейс появляется быстрее, и пользователю не нужно ждать, пока придет ответ целиком. Что GenUI SDK умеет 🔴Генерировать UI из структурированных данных 🔴Работать с настраиваемым каталогом виджетов 🔴Обрабатывать события и поддерживать интерактивный диалог Каталог виджетов задает словарь Flutter-компонентов, которые ИИ может использовать. Каждый CatalogItem содержит имя виджета, JSON-схему его свойств и builder-функцию для рендеринга. Реактивная система автоматически перестраивает виджеты при изменении данных в клиентской модели. А обработка событий позволяет фиксировать действия пользователя (клики, ввод текста) и отправлять обновленное состояние обратно ИИ для следующего шага. ⭐️Попробуйте представить: какие компоненты вашего приложения могли бы генерироваться на основе запросов пользователей? Практическое применение GenUI SDK подходит для AI-нативных приложений. Например, вместо текстового списка продуктов можно показать кликабельную карусель, а при планировании поездки — сгенерировать форму с полями ввода и слайдерами. Если продукт уже сделан на Flutter, интеграция может быть проще: вы используете существующие компоненты и систему стилей. SDK позволяет собирать макеты экранов и управлять навигацией на основе пользовательских намерений, чтобы создавать персонализированные интерфейсы в рамках вашего бренда. Подробнее — в блоге Flutter: Rich and dynamic user interfaces with Flutter and generative UI. P.S. SDK пока в альфа-версии, но уже доступен на pub.dev для экспериментов. Делитесь опытом в комментариях!

Подойдут и для Android, и для iOS Сделали новогодние заставки для телефонов с вайбом разработки на Flutter. Можно поставить с
+5
Подойдут и для Android, и для iOS Сделали новогодние заставки для телефонов с вайбом разработки на Flutter. Можно поставить себе, закинуть коллеге или в рабочий чат 🎄 Скачать можно в комментариях ⬇️

Обновление дизайна от Apple с активным использованием glass effect вызвало у многих споры: кому-то понравилось, а кто-то был
Обновление дизайна от Apple с активным использованием glass effect вызвало у многих споры: кому-то понравилось, а кто-то был просто в ужасе. Чуть позже к этому тренду подтянулись и другие продукты, и в итоге мы внезапно оказались в мире стеклянных интерфейсов🐈 А теперь давайте представим, что мы ТОЖЕ ХОТИМ ТАК ЖЕ! Как же быть? Есть 2 решения: воспользоваться готовыми библиотеками или реализовать все самостоятельно. Начнем со второго варианта. Для этого нам понадобится виджет BackdropFilter. В отличие от обычных эффектов, он работает не с самим виджетом, а с тем, что находится под ним. BackdropFilter берет уже отрисованный фон и применяет к нему фильтр, в нашем случае — размытие. Именно поэтому BackdropFilter почти всегда используют вместе с ClipRRect или ClipPath. Без него фильтр применится ко всей области экрана, а не только к нужному участку. Также важно помнить, что сам BackdropFilter ничего не рисует и не имеет размера, он лишь изменяет фон. Поэтому поверх него всегда добавляют полупрозрачный контейнер с цветом, границей и тенью. Без этого слоя эффект будет почти незаметен. Простейшая реализация может выглядеть так:

class GlassContainer extends StatelessWidget {
  final double width;
  final double height;
  final Widget child;

  const GlassContainer({
    Key? key,
    required this.width,
    required this.height,
    required this.child,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ClipRRect(
      borderRadius: BorderRadius.circular(25),
      child: BackdropFilter(
        filter: ImageFilter.blur(sigmaX: 18, sigmaY: 18),
        child: Container(
          width: width,
          height: height,
          decoration: BoxDecoration(
            color: Colors.white.withOpacity(0.15),
            borderRadius: BorderRadius.circular(25),
            border: Border.all(
              color: Colors.white.withOpacity(0.2),
              width: 1.2,
            ),
          ),
          child: child,
        ),
      ),
    );
  }
}

Если хочется воспользоваться готовыми решениями, можно присмотреться, например, к пакету glass_kit — внутри он более глубоко работает с BackdropFilter и помогает добиться аккуратного эффекта размытия. При работе с glassmorphic-дизайном важно помнить о нескольких нюансах. Размытие стоит использовать умеренно – его избыток быстро утомляет, а мы этого не хотим! Кроме того, BackdropFilter не самый дешевый в отрисовке виджет, поэтому лучше избегать большого количества перекрывающихся размытых элементов. В следующей статье можем разобрать liquid glass эффект. Его часто считают разновидностью glassmorphism, но это не совсем так. В отличие от классического glass effect с размытием фона, liquid glass добавляет ощущение движения, текучести и визуальных искажений. А какие библиотеки для работы с glassmorphism или готовые реализации вы используете?

⏰Привет! Это Анна, Flutter Team Lead Friflex Сегодня поговорим о той самой лотерее, которой опасаются все разработчики — отправке приложения на проверку в AppStore. Очень часто этот процесс вызывает трудности и надолго оттягивает момент публикации приложения в стор. Обсудим, на какие моменты точно стоит обратить внимание, чтобы увеличить вероятность успеха. Первая проверка нового приложения — процесс небыстрый. Официально никакого временного регламента нет, но как показывает практика, лучше закладывать на это около 7 дней. Перед отправкой оцените обстановку — если впереди праздники, например, как сейчас близится Новый Год и Рождество, будьте готовы к увеличению срока. Обычно об этом в App Store Connect заранее появляется сообщение. Повторные проверки и проверки обновлений уже опубликованного приложения проходят значительно быстрее — обычно в течение 1-2 суток. Но опять же на праздники срок может растянуться. Теперь поговорим о том, на что стоит обратить внимание при подготовке: Совместимость Приложение обязательно должно быть совместимо с последними версиями iOS. Если на новых версиях продукт падает или не запускается совсем, это весомый повод получить отказ. Стабильность Приложение не должно вылетать — это важно не только для положительного пользовательского опыта, но и для проверки. Если какие-то сценарии вызовут краш вашего приложения при проверке, сборка точно будет отклонена. Отсутствие вывода отладочной информации Перед публикацией стоит почистить все вызовы методов вывода отладочной информации — print, debugPrint. Разрешения Особое внимание уделяется всем разрешениям, которые запрашивает приложение. Во-первых, приложение не должно запрашивать неиспользуемые разрешения. А во-вторых, каждое разрешение обязательно должно иметь четкое обоснование, прописанное в Info.plist. Это описание будет отображено пользователю в нативном окне. Если App Store посчитает формулировку недостаточно информативной, сборка может быть отклонена. Нативные интеграции Если вы добавляете в свой проект платформенные функции, требующие специфичной нативной настройки, обязательно обратите особое внимание на соблюдение всех этапов подключения, а также досконально проверьте работоспособность. Это относится, например, к push-уведомлениям и in-app покупкам. Интерактивность При проверке всегда обращается внимание на реагирование приложения на действия пользователя. Все активные кнопки должны отдавать какой-то отклик, выполнять понятное и видимое действие. Сам интерфейс должен быть понятным и не вызывать вопросов. Если у вас по каким-то причинам есть пустые кнопки, перед проверкой их стоит скрыть. Описание и оформление Особое внимание стоит уделить заполнению карточки приложения. Описание обязательно должно быть достоверным и не должно содержать оценивающих суждений, например, «самый лучший калькулятор в мире» точно не подойдет. Преувеличения лучше оставить при себе. Пусть описание будет продающее, завлекающее, но без фанатизма. Сюда же стоит отнести и скриншоты. Они должны быть в высоком качестве, определенного размера (нормы можно найти во вкладке загрузки медиаданных). На изображениях должен отражаться только реальный функционал и интерфейс. Кроме этого, картинки не могут содержать альфа-каналы или прозрачные области. Сбор информации Перед публикацией вам требуется заполнить несколько форм с описанием того, какие данные собирает/передает ваше приложение, какие алгоритмы шифрования используются, какой функционал предусмотрен и какой контент содержит. Если ваши ответы не совпадут с реальностью, сборку отклонят. Тестовые данные и контактная информация Если ваше приложение содержит какое-то ограничение в доступе, вам обязательно необходимо приложить инструкцию, контактные данные и тестовый аккаунт, чтобы проверяющие могли полноценно проверить все функции. Важно, что тестовая учетная запись должна быть доступна всегда. Если при проверке что-то случится с доступом, вы получите отказ. 🐱А вы сталкивались с проблемами при прохождении проверки в AppStore? Делитесь своим опытом в комментариях.

❄️Привет, с вами Катя, Flutter Dev Friflex В предыдущем посте я рассказывала о принципе разделения интерфейса (I). Сегодня ра
❄️Привет, с вами Катя, Flutter Dev Friflex В предыдущем посте я рассказывала о принципе разделения интерфейса (I). Сегодня разбираем последнюю букву — D (Dependency Inversion Principle), принцип инверсии зависимостей. Что такое Dependency Inversion Principle? Принцип инверсии зависимостей говорит о двух вещах: ✔️высокоуровневые модули не должны зависеть от низкоуровневых — и те, и другие должны зависеть от абстракций ✔️абстракции не должны зависеть от деталей, детали должны зависеть от абстракций. По-простому: «верх» приложения (экран, бизнес-логика) не должен быть привязан к конкретным реализациям «низа» (HTTP‑клиент, база данных, SharedPreferences и другим), он должен зависеть только от интерфейсов. Почему это важно? Когда высокоуровневый код напрямую знает о конкретных классах нижнего уровня, это приводит к проблемам: ◾️Любое изменение реализации «внизу» (REST → gRPC, другая БД, кэш) требует правок в бизнес-логике ◾️Код сложно тестировать — приходится тянуть реальные репозитории/сети вместо заглушек ◾️Система становится хрупкой: одна деталь «внизу» ломает много кода «наверху» ◾️Нарушаются другие принципы SOLID — растет связанность, падает переиспользуемость. Инверсия зависимостей как раз про то, чтобы «перевернуть» направление зависимости: не высокоуровневый модуль зависит от деталей, а детали зависят от контракта, который описывает высокоуровневый модуль. ❌Нарушение принципа Рассмотрим типичный пример с авторизацией:
class AuthRepository {
  Future<void> login(String email, String password) async {
    // Здесь конкретная реализация:
    // HTTP-запрос, парсинг ответа, сохранение токена и т.д.
  }
}

class LoginViewModel {
  Future<void> login(String email, String password) async {
    final repo = AuthRepository(); // Жёсткая зависимость
    await repo.login(email, password);
  }
}
Проблемы: ✖️LoginViewModel сам создает AuthRepository и жестко на него завязан ✖️Нельзя легко подменить репозиторий в тестах (например, на фейковый, который не ходит в сеть) ✖️Любое изменение механизма авторизации требует лезть в LoginViewModel ✖️Высокоуровневый модуль (view model) зависит от конкретной детали (репозитория), а не от абстракции ✅Правильное применение принципа Введем абстракцию и будем передавать зависимость извне (через конструктор):
// Абстракция (контракт), от которой зависит верхний уровень
abstract class IAuthRepository {
  Future<void> login(String email, String password);
}

// Низкоуровневая реализация для реального API
class NetworkAuthRepository implements IAuthRepository {
  @override
  Future<void> login(String email, String password) async {
    // HTTP, обработка ошибок, сохранение токена и т.д.
  }
}

// Другая реализация, например, фейковая для тестов
class FakeAuthRepository implements IAuthRepository {
  @override
  Future<void> login(String email, String password) async {
    // Ничего не делает или имитирует успешный логин
  }
}

// Высокоуровневый модуль зависит только от интерфейса
class LoginViewModel {
  final IAuthRepository _authRepository;

  LoginViewModel(this._authRepository);

  Future<void> login(String email, String password) async {
    await _authRepository.login(email, password);
  }
}
Теперь: ✔️LoginViewModel не знает, как именно реализован репозиторий — он видит только интерфейс ✔️В проде можно передать NetworkAuthRepository, в тестах — FakeAuthRepository ✔️Изменения в реализации репозитория не требуют правок в бизнес-логике ✔️Мы перевернули зависимость: конкретные реализации зависят от абстракции IAuthRepository, а не наоборот Если вы видите в коде new/() внутри бизнес‑логики или ViewModel, создающий конкретные репозитории, сервисы и клиенты, — это хороший сигнал задуматься: не пора ли ввести интерфейс и развернуть зависимость? ✍️Повторим все принципы SOLID: Single Responsibility Principle Open/Closed Principle Liskov Substitution Principle Interface Segregation Principle Dependency Inversion Principle