Flutter Friendly – Telegram
Flutter Friendly
1.04K subscribers
187 photos
71 videos
1 file
149 links
Канал Friflex о разработке на Flutter. Обновления, плагины, полезные материалы — превращаем знания в реальный опыт, доступный каждому разработчику.

🔗 Наш канал для разработчиков: @friflex_dev
🔗 Канал о продуктовой разработке: @friflex_product
Download Telegram
Привет, я Катя, 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 для экспериментов. Делитесь опытом в комментариях!
Please open Telegram to view this post
VIEW IN TELEGRAM
6🔥2
Какой сценарий вы бы протестировали первым?
Anonymous Poll
41%
e-commerce
22%
бронирование
37%
что-то совсем другое
💠Привет! Это Анна, 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 нужно обязательно.

❤️— если было полезно
Please open Telegram to view this post
VIEW IN TELEGRAM
24🔥4❤‍🔥1🤩1
🎅Всем привет! С вами Роза, 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 оправдано, если у вас простой список элементов, строки сильно различаются по содержимому или нужен вертикальный скролл без необходимости выравнивания колонок

На этом все! ❤️ всем любителям табличек
Please open Telegram to view this post
VIEW IN TELEGRAM
14👍8🔥4😢2😁1
🤩Привет, это Катя, Flutter Dev Friflex

Сегодня хочу поговорить о самом волнующем вопросе конца декабря: «А давайте запушим в прод?»

Предновогодний синдром деплоя
Знаете, что объединяет разработчиков по всему миру в конце декабря? Нет, не корпоративы с оливье. Это священный ужас при виде коллеги, который бодро пишет в чатик: «Ребят, у меня тут маленький фикс, можно быстренько в прод?» Маленький фикс в конце декабря — это как «просто быстренько зайти в магазин с мамой». Такого не бывает.

Декабрь — это время, когда даже самые отчаянные разрабы начинают говорить страшным голосом про code freeze. И правильно делают! Потому что если что-то пойдет не так 30 декабря, вы будете чинить баги не под бой курантов, а вместо боя курантов.

Что такое code freeze и почему его вводить
Code freeze (или фриз деплоя) — это когда команда договаривается: «Все, ребята, стоп-машина! До Нового года только критичные баги трогаем, и то если они прям совсем критичные». Это как карантин, но для кода.

Обычно фриз начинается где-то с 20-25 декабря и длится до середины января. В это время:
Нельзя мерджить фичи (даже если они «точно работают»)
Нельзя рефакторить (это вообще никогда нельзя перед праздниками)
Нельзя обновлять зависимости (Flutter SDK может и подождать)
Можно только чинить то, что реально горит

Почему нельзя лить в прод в конце декабря
Представьте: вы залили фичу 29 декабря, все ушли праздновать, а 31-го приложение начинает крашиться у половины пользователей. Поздравляю, вы только что стали главным злодеем чужого Нового года! Теперь вместо шампанского вы будете пить кофе и смотреть в логи Crashlytics.

А еще есть нюанс: если что-то сломается, собрать команду для фикса будет невозможно. Кто-то уехал к бабушке в деревню без интернета, кто-то в Таиланде (и там другой часовой пояс), а кто-то вообще отключил рабочий телефон. И правильно сделал.

Исключения из правил
Единственный валидный повод для деплоя в конце декабря — это когда прод реально горит. И под горит я имею в виду не «кнопка немного криво стоит», а «пользователи не могут оплачивать заказы» или «приложение крашится при старте у всех».

Но даже в этом случае фикс должен быть:
▪️Минимальным (чиним только то, что сломалось)
▪️Протестированным (да, даже в предновогодней запаре)
▪️С откатом наготове (если что-то пойдет не так)

Как пережить фриз без стресса
Главный секрет спокойного Нового года — это подготовиться заранее. Залейте все важные фичи до 20 декабря, проведите тщательное тестирование, убедитесь, что мониторинг работает, и можете спокойно идти выбирать мандарины🍊
Please open Telegram to view this post
VIEW IN TELEGRAM
8🔥7👍4🏆1
🎅Привет! Это Анна, 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 шаг. Приложение.
Дальше приложение стучится на бэкенд, просит данные для конкретного авторизованного пользователя. Сервер в свою очередь самостоятельно проверяет актуальность данных и наличие оплаченного периода и определяет, какой контент нужно отдать.

Самое лучшее здесь то, что приложение остается «тонким клиентом». Оно не нагружено сложной логикой внутренних проверок. Его задача в том, чтобы просто отобразить полученные данные.

Если остались вопросы, задавайте в комментариях⬇️
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥861👍1🤩1🎄1
🎅Всем привет!

Год подходит к концу, и мы, Роза, Катя и Аня, хотим устроить небольшую ретроспективу и поделиться своими открытиями за 2025-ый

🎅Роза, Flutter Dev Friflex:

Этот год был богат на задачи абсолютно разного характера. За это время я сделала пару парсеров, разработала расширение для DevTools, внедрила LLM во Flutter Web-приложение, поработала с Serverpod, узнала, что такое OTA, и в целом писала бэкенд на Dart.

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

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

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


🎅Катя, Flutter Dev Friflex:

Мой главный инсайт: агенты и вайб-кодинг. В этом году у меня было открытие, которое изменило мой подход к разработке. Сейчас начинается эра vibe coding — когда ты не пишешь код построчно, а просто описываешь намерение, и агент превращает его в рабочий (не всегда) код.

Раньше программирование было про «как»: как написать цикл, как обработать исключение, как настроить конфиг. С агентами это стало про «что»: что я хочу получить, какую проблему решаем.

Что изменилось?
✔️Скорость: То, что занимало час, теперь — 15 минут
✔️Креативность: Освободилось время на архитектуру и эксперименты
✔️Рутина: Boilerplate, тесты, документация генерируются автоматически

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

🎅С наступающим Новым годом, коллеги! Пусть ваши билды всегда будут зелеными, а продакшен — стабильным. И помните: лучший коммит в конце декабря — это закрытый ноутбук.

🎅Анна, Flutter Team Lead Friflex

Этот год был супер насыщенным на новые знания и открытия. Но мой самый главный инсайт — коммуникация команды с заказчиком.

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

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

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

И главное, что стоит помнить и заказчикам, и команде — вы не конкуренты, вы партнеры! У вас общая цель, вы идете к стабильному, рабочему продукту. А достичь этой цели возможно только совместными усилиями и обоюдными компромиссами.
Please open Telegram to view this post
VIEW IN TELEGRAM
13👍6🎄6🔥31😁1🎅1
🙂Присоединяетесь, у нас тут Flutter-утренник

А пока вы готовите костюмы, хотим поздравить с Новым годом!

Больше инновационных проектов и вдохновения для их реализации
Больше стабильных релизов и довольных пользователей
И конечно, больше времени на творчество, изучение нового и отдых
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
14😁5🔥4👍1
💡Устроим небольшую проверку изученного за год? На новогодних праздниках вас будет ждать небольшая викторина от Розы, Кати и Анны
Please open Telegram to view this post
VIEW IN TELEGRAM
4👍2
Какие три основных подхода существуют для работы с иконками во Flutter?
Anonymous Quiz
4%
PNG, JPG, GIF
61%
Icon Fonts, SVG, Vector Graphics
33%
Material Icons, Cupertino Icons, Font Awesome
2%
Bitmap, Vector
Какой виджет позволяет дочернему элементу выходить за границы родителя?
Anonymous Quiz
85%
OverflowBox
3%
AspectRatio
11%
Expanded
2%
Align
Какие модификаторы можно комбинировать?
Anonymous Quiz
22%
base и final
26%
abstract и base
13%
sealed и base
39%
abstract и mixin
Какой Sliver позволяет вставить обычный виджет в CustomScrollView?
Anonymous Quiz
15%
SliverList
75%
SliverToBoxAdapter
5%
SliverAppBar
5%
SliverGrid