This media is not supported in your browser
VIEW IN TELEGRAM
Представьте: вы пишите приложение с bloc, и у вас есть кнопка для загрузки данных. Пользователь может нажимать на эту кнопку бесконечное количество раз. К чему это приведет? Скорее всего — к множественным вызовам методов бэка и ухудшению работы UI.
Или, например, у меня была похожая ситуация с чипами, которые можно было выбирать. Запрос на бэк отправлялся при каждом выборе, не только при окончательном.
Как же это исправить? Можно подключить bloc_concurrency. Он позволяет управлять порядком и способом обработки событий в Bloc. Пакет идет вместе с bloc и предоставляет набор функций (EventTransformer’ов) для контроля того, как Bloc обрабатывает события.
По умолчанию в Bloc события обрабатываются строго одно за другим (то есть, sequential()).
Но мы можем изменить это поведение с помощью разных трансформеров:
✔️ concurrent() — события обрабатываются одновременно. Например, если добавить два события одновременно, оба будут выполняться параллельно
✔️ sequence() — события обрабатываются строго по очереди. Если добавить два события одновременно, сначала будет обработано первое, затем второе и так далее
✔️ droppable() — любые новые события, добавленные во время обработки текущего, будут проигнорированы
✔️ restartable() — обрабатывается только последнее событие, предыдущие отменяются, если они еще не завершены
За счет чего это достигается?
Каждый Bloc в методе on<Event> принимает необязательный параметр transformer, который определяет, как события этого типа обрабатываются.
on<LoadMoreKeysEvent>(
_loadMoreKeys,
transformer: droppable(),
);
Также можно создать свой кастомный трансформер. Например, добавить небольшую задержку для поиска:
on<SetSearchQueryEvent>(
_setSearchQuery,
transformer: debounce(const Duration(milliseconds: 300)),
);
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8🔥6👍4
Недавно вышла первая экспериментальная версия Flutter Extension для Gemini CLI — инструмента, который делает разработку Flutter-приложений с помощью ИИ-агентов проще и быстрее.
Что это такое
Flutter Extension for Gemini CLI объединяет возможности Dart и Flutter MCP-сервера с новыми командами и правилами, которые помогают писать чистый, тестируемый и производительный код.
ИИ-агенты теперь могут:
✔️Генерировать проект с нуля
✔️Создавать и реализовывать план разработки
✔️Анализировать, форматировать и тестировать код
✔️Делать коммиты в Git
Как установить
Вот тут не все просто, как минимум, у меня. Пока устанавливала, поймала 3 разные ошибки, связанные с авторизацией, дартом и переменными окружения. Если у кого-то будут проблемы со скачиванием, напишите в комментариях, помогу чем смогу. А так — вот дока по аутентификации.
Сам cli устанавливается одной командой:
gemini extensions install https://github.com/gemini-cli-extensions/flutter
Основные команды
▪️/create-app — создает новый Flutter-проект, добавляет линтеры и генерирует файлы DESIGN.md и IMPLEMENTATION.md
▪️/modify — cоздает подробный план изменений для существующего проекта
▪️/commit — форматирует код, запускает тесты, анализатор и делает коммит
Как работает /create-app
Команда /create-app — это как «умный» flutter create.
Она:
▫️спрашивает цель приложения
▫️создает файлы с дизайном и планом реализации
▫️делит процесс разработки на 3–5 фаз
▫️после каждой фазы анализирует код
▫️форматирует его и делает коммит
Файл DESIGN.md описывает задачу и архитектуру.
Файл IMPLEMENTATION.md — пошаговый план реализации
/modify — умные изменения
Если нужно доработать существующий код, команда /modify создаст:
▫️новый Git-бранч (по желанию)
▫️файл MODIFICATION_PLAN.md с описанием изменений
▫️план внедрения в несколько фаз
ИИ-агент предложит улучшения и выполнит их поэтапно
/commit — чистый коммит без хлопот
Команда /commit:
▫️запускает dart fix, dart format, dart analyze и тесты
▫️исправляет найденные проблемы
▫️генерирует понятное сообщение коммита
В этом посте описана малая часть возможностей. Подробнее читайте в официальном блоге Flutter — Flutter Blog.
Я сама только недавно поставила это расширение и пока начинаю разбираться. Так что если вы уже попробовали и нашли какие-то прикольные штуки —
обязательно поделитесь в комментариях
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11❤6👍4
Сегодня поговорим об утечках памяти во Flutter-приложениях. Рассмотрим, что может их вызывать и почему не все объекты очищаются самостоятельно.
В самом простом понимании утечка памяти — это ситуация, когда программа хранит в памяти объект, который уже не нужен. На первый взгляд она может показаться достаточно безобидной, но на самом деле способна привести к серьезным проблемам — медленной работе приложения, сбоям и даже крэшам.
Для снижения утечек памяти в Dart есть специальный механизм —Garbage Collector (GC). Его основная задача — выполнять автоматическую очистку неиспользуемых объектов в программе. GC самостоятельно отслеживает все объекты, которые создаются в процессе работы. Затем очищает те объекты, которые не имеют активные ссылки в программе.
Представим, что у нас есть некоторый класс
User. Метод createUser() внутри себя создаст его экземпляр и выполнит с ним некоторые манипуляции. После завершения работы метода createUser() объект user больше не будет иметь активных ссылок, и GC легко его очистит. void main() {
createUser(); // Вызываем функцию, которая внутри создает пользователя
print('Пользователь создан, но больше не используется');
// После этого GC может удалить объект User
}
void createUser() {
final user = User(name: 'Anna');
print('Привет, ${user.name}');
// Когда функция завершится, переменная user исчезнет из области видимости
}
Но если создать пользователя на уровень выше, в самом
main(), GC очистить его не сможет, так как активная ссылка будет существовать. void main() {
final user = User('Alice');
// Даже если мы больше не используем user, ссылка на него все еще существует.
// GC не удалит объект.
}
Больше подробностей о работе GC можете найти в этих статьях
▪️ Flutter: Don’t Fear the Garbage Collector
▪️ Garbage Collection in Dart and Its Implications in Flutter
▪️ How Dart’s Garbage Collector Works (And When It Fails You!)
Как мы выяснили, Garbage Collector не может очистить все объекты, поэтому многие источники утечки памяти разработчику требуется отслеживать самостоятельно.
Наиболее часто встречающиеся:
✔️ контроллеры — например, TextEditingController, PageController
✔️ подписки — StreamSubscription
✔️ таймеры — Timer
✔️ слушатели — при добавлении addListener()
✔️ глобальные и статические объекты
Здесь стоит соблюдать ряд простых правил:
▫️ если создаете контроллер или подписку, обязательно вызывайте методы
dispose() или cancel()▫️ таймеры также необходимо отменять с помощью
cancel()▫️ при добавлении слушателя через
addListener() не забывайте удалять его через removeListener()▫️ если в коде глобальные или статические объекты вам больше не нужны, можно явно обнулить их ссылки, присвоив
null▫️ систематически проверяйте свое приложение на утечки памяти
📎Отследить утечки памяти вам помогут специальные инструменты, например, Dart DevTools (вкладка Memory) и библиотека leak_tracker_flutter_testing.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍3🔥2😍1
Anonymous Poll
14%
Регулярно, при работе над каждой новой фичей
38%
Только перед релизом или при заметном падении производительности
48%
Редко или никогда, полагаюсь на Garbage Collector
Flutter Friendly
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6🤡2😁1
Сегодня сделаем с вами обзор библиотеки по локализации — slang. Многие из вас наверняка слышали про .arb и работали с intl, но про slang, возможно, вы слышите впервые.
Что же это такое?
Это библиотека для генерации локализаций. У нее есть ряд существенных преимуществ по сравнению с intl.
Давайте по порядку!
Генерация
Slang поддерживает больше форматов, чем intl: JSON, YAML, ARB и CSV.
Код генерируется командой:
dart run slang
Также можно использовать build_runner, если добавить дополнительный пакет slang_build_runner.
Перед генерацией необходимо создать конфигурационные файлы — slang.yaml или build.yaml. Они во многом похожи на l10n.yaml, но позволяют настроить больше параметров.
Поддержка placeholders
Здесь все стандартно: plural, gender. Но кроме placeholders так же есть возможность работы с links, richText.
{
"user": {
"messageCount": {
"one": "У тебя 1 сообщение",
"other": "У тебя {count} сообщений"
}
}
}Получаем
t.user.messageCount(count: 5);
Типобезопасность
Библиотека устойчива к ошибкам: опечатки или пропущенные аргументы невозможны,
так как все ключи и параметры проверяются на этапе компиляции.
Если ключа нет — компилятор не даст собрать проект.
Поддержка Flutter
Пакет slang_flutter предоставляет провайдер для управления локалями в проекте.
Можно легко переключать язык в рантайме, получать переводы через контекст
context.t и использовать ленивую инициализацию делегатов.CLI-команды
Библиотека предоставляет набор CLI-команд:
dart run slang # генерация файлов локализации
dart run slang analyze # поиск неиспользуемых и отсутствующих переводов
dart run slang normalize # сортировка переводов по базовой локали
dart run slang configure # автообновление CFBundleLocalizations
dart run slang edit move loginPage authPage # переименование или перенос ключей
dart run slang migrate arb src.arb dest.json # миграция из arb в json
Runtime overrides
Slang поддерживает runtime overrides — вы можете менять переводы на лету, без пересборки приложения.
Несколько замечаний:
✔️Переопределения могут быть частичными – обновляются только указанные ключи
✔️Повторное переопределение заменяет предыдущее
✔️Новые переводы анализируются, но не активируются
✔️Плейсхолдеры (
name) остаются без изменений❕Не стоит сразу бросаться внедрять slang во все проекты. Он может быть избыточен для небольших проектов, у него сложнее конфигурация, чем у intl. У вас
могут возникнуть сложности при работе с многомодульными проектами, где нужно объединять переводы из разных пакетов. Также типобезопасность делает невозможным использование динамических ключей, что в некоторых сценариях может ограничивать гибкость.Я лишь представила краткий обзор в этом посте, для подробной информации — посетите библиотеку.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤10🔥8👌2
Привет, это Катя, Flutter Dev Friflex. Сегодня расскажу о давней проблеме — ограничении метода Scrollable.ensureVisible, которое не работает для элементов, находящихся за пределами видимости в ListView.
Суть проблемы
Метод Scrollable.ensureVisible должен прокручивать список до нужного виджета, но работает он только для элементов, которые уже отрендерены на экране. Когда элемент находится далеко за пределами видимости, Flutter просто не создает для него контекст, и метод перестает работать.
Как проявляется
Допустим, у вас ListView с 11 элементами. При попытке прокрутить к элементу с индексом 1-7 все работает отлично. Но как только пытаетесь прокрутить к 8-му, 9-му или дальше — ничего не происходит.
// Не работает для элементов вне экрана
WidgetsBinding.instance.addPostFrameCallback((_) {
Scrollable.ensureVisible(selectedDataKey.currentContext);
// currentContext будет null для невидимых элементов!
});
Почему до сих пор не исправлено
Команда Flutter признала эту проблему еще в 2018 году. Причина — техническая сложность реализации. Чтобы прокрутить к элементу, Flutter должен знать его позицию и размер. Но для невидимых элементов эта информация недоступна, так как они еще не созданы.
Flutter не знает:
▫️в какую сторону прокручивать список
▫️какой offset использовать
▫️какой высоты будет целевой элемент
Попытка перебрать все элементы для поиска нужного была бы слишком дорогой операцией.
Workaround решения
Вариант 1: SingleChildScrollView для статичных списков
Если список небольшой и не меняется, замените ListView на SingleChildScrollView с Column:
SingleChildScrollView(
child: Column(
children: [
Container(key: firstKey, child: Text('Item 1')),
Container(key: secondKey, child: Text('Item 2')),
// Все элементы рендерятся сразу
],
),
)
// Теперь ensureVisible работает
Scrollable.ensureVisible(firstKey.currentContext);
Минус: не подходит для длинных списков — все элементы рендерятся сразу, что убивает производительность.
Вариант 2: ScrollablePositionedList
Самое популярное решение — использовать пакет scrollable_positioned_list, который добавляет нужную функциональность:
ItemScrollController _scrollController = ItemScrollController();
ScrollablePositionedList.builder(
itemScrollController: _scrollController,
itemCount: items.length,
itemBuilder: (context, index) => items[index],
)
// Прокрутка к любому индексу
_scrollController.scrollTo(
index: 150,
duration: Duration(seconds: 1),
curve: Curves.ease,
);
Это решение работает даже для элементов далеко за пределами экрана.
Вариант 3: Ручная прокрутка жестами
Хелпер для тестов, который эмулирует прокрутку до появления элемента:
Future<void> ensureVisibleByScrolling(
Finder finder, {
required Offset scrollFrom,
Offset scrollBy = const Offset(0, -50),
int maxScrolls = 100,
}) async {
final gesture = await widgetTester.startGesture(scrollFrom);
for (var i = 0; i < maxScrolls; i++) {
await gesture.moveBy(scrollBy);
await pump();
if (widgetTester.widgetList(finder).length == 1) {
break;
}
}
await gesture.cancel();
await widgetTester.ensureVisible(finder);
}
Этот метод постепенно прокручивает список, пока элемент не появится на экране.
Статус в 2025 году
К сожалению, официального решения в стандартной библиотеке Flutter до сих пор нет. Issue открыт с 2018 года, и команда Flutter признала, что не планирует в ближайшее время добавлять нативный scrollToIndex для ListView.
А какой вариант решения проблемы используете вы?
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤7🔥5
This media is not supported in your browser
VIEW IN TELEGRAM
Для тех, кто говорит на языке Flutter! Сегодня на CrossConf разыгрываем футболки и игру мемори.
Условия простые:
✅ Подписаться на @flutterfriendly
✅ Написать под этим постом: «Я сегодня на CrossConf»
Троих победителей определим с помощью генератора случайных чисел в 17:30. Забрать приз можно будет на стенде Friflex.
Условия простые:
Троих победителей определим с помощью генератора случайных чисел в 17:30. Забрать приз можно будет на стенде Friflex.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9👍5🥰3😢1
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8👍5👻2🏆1
Адель рассказал про то, как они пытаются продвинуть Аврору для разработчиков, сделал довольно прикольный uikit, аналогичный Cupertino и Fluent, с поддержкой нативных фич
В принципе прикольно🤔
Уже вижу как флаттер разработчики пишут псевдонативные прилки под Аврору😁
В принципе прикольно
Уже вижу как флаттер разработчики пишут псевдонативные прилки под Аврору
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4👍2
Flutter Friendly
Video message
Стас Чернышев жалуется (объективно) на Dart 🎉
Please open Telegram to view this post
VIEW IN TELEGRAM
Станислав Ильин рассказал что сложные вещи в начале лучше не делать, да и вообще пишите по простому, чтобы всем было понятно 🤙
Пришел в этот раз без костюма Марио, я расстроен 😭
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6🔥5
Юра показал, что флаттер может быть везде, даже можно под чайник собрать ui 🥳
Довольно прикольно, что это возможно, но сообщество довольно долго обновляет до актуальных версий сдк, исключение пожалуй ОМП и их Аврора
Хотя, как фишка, вдруг кому то пригодиться, недавно под Tizen и webos делал мини кинотеатр с стримингом, былобольно интересно 😁 (
Довольно прикольно, что это возможно, но сообщество довольно долго обновляет до актуальных версий сдк, исключение пожалуй ОМП и их Аврора
Хотя, как фишка, вдруг кому то пригодиться, недавно под Tizen и webos делал мини кинотеатр с стримингом, было
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11❤7👾4
На пятых докладах из двух потоков у меня начала плавиться голова 🫠
Андрей рассказал про то, как ПО для управления дрона писали на флухтере, много низкоуровневых штук, как будто оказался на парах в вузе по ассемблеру💀
Надо потыкать дрона, попробовать поуправлять🙈
Андрей рассказал про то, как ПО для управления дрона писали на флухтере, много низкоуровневых штук, как будто оказался на парах в вузе по ассемблеру
Надо потыкать дрона, попробовать поуправлять
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍3
Никита уже рассказывал про свой пакет для BDUi (Duit), как делал оптимизацию новой версию
Довольно долго рассматриваю все варианты, но никак не могу выбрать для себя наиболее правильный подход, все кажется каким то странным, хочется всего и сразу
Конструктор как у divkit от Яндекса, но и написание без jsonов🔔
Довольно долго рассматриваю все варианты, но никак не могу выбрать для себя наиболее правильный подход, все кажется каким то странным, хочется всего и сразу
Конструктор как у divkit от Яндекса, но и написание без jsonов
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6❤4👍3
This media is not supported in your browser
VIEW IN TELEGRAM
Ценный артефакт: книга «Основы Flutter» c афтографами авторов. Разыграем в канале?
👍22💯6
Подвели итоги конкурса на CrossConf. Поздравляем:
@malou1337
@Suneru
@swogf
Ждем победителей у стенда Friflex
@malou1337
@Suneru
@swogf
Ждем победителей у стенда Friflex
❤5👏4
@floral_whale (Роза) ❤️ , рассказывала про их сервис локализации с OTA обновлениями, прикольно
Из известных мне вроде знаю только о phrase
Ждем когда доработают до конца👍
Выглядит вроде как production-ready
Из известных мне вроде знаю только о phrase
Ждем когда доработают до конца
Выглядит вроде как production-ready
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9❤7
Всем привет, это Роза, Flutter dev Friflex!
Сегодня мы разберем, как быстро поднять и использовать собственное облачное хранилище MinIO S3.
MinIO — это полностью совместимый с S3-протоколом объектный сторедж для хранения файлов, резервных копий и больших датасетов.
MinIO поддерживает S3 API (операции GET, PUT, DELETE ), а также:
✔️Управление бакетами: Bucket policy, versioning и lifecycle rules
✔️Безопасность: шифрование на стороне сервера и клиента
✔️Поддержку Multi-user / Multi-tenant окружений
✔️Простую интеграцию с любыми S3-SDK и библиотеками
Он совместим с разными S3-инструментами, легко масштабируется благодаря архитектуре Shared-Nothing, обрабатывает запросы параллельно, использует Erasure Coding для надежности и восстановления данных, а целостность хранения обеспечивается криптографическими хэшами.
Для чего можно использовать MinIO:
▪️Приватное облако — хранение изображений и файлов пользователей
▪️AI/ML и Big Data — хранение больших наборов данных
▪️Бэкапы — резервное копирование баз данных и виртуальных машин
Использование MinIO во Flutter
Благодаря S3-совместимости, MinIO можно использовать как локальное S3 для быстрой разработки приложений на Flutter.
Для работы подходит пакет minio, совместимый с MinIO, AWS S3 и Firebase Storage.
Чтобы начать работу, достаточно создать клиент, передав данные S3-сервера:
Библиотека предоставляет удобные методы для работы с объектами. Рассмотрим некоторые из них:
◽️Загрузка объекта в S3
Метод putObject позволяет загружать объекты из потока байтов (Stream<Uint8List>).
◽️Загрузка с прогрессом
Для больших файлов можно отслеживать ход загрузки с помощью колбэка onProgress:
◽️Получение временно подписанной ссылки (Presigned URL)
Для безопасного предоставления доступа к файлу без публичного доступа к бакету используется временно подписанная ссылка.
📎С полным списком методов можно ознакомиться в документации библиотеки
Продолжение — в комментариях 👇
Сегодня мы разберем, как быстро поднять и использовать собственное облачное хранилище MinIO S3.
MinIO — это полностью совместимый с S3-протоколом объектный сторедж для хранения файлов, резервных копий и больших датасетов.
MinIO поддерживает S3 API (операции GET, PUT, DELETE ), а также:
✔️Управление бакетами: Bucket policy, versioning и lifecycle rules
✔️Безопасность: шифрование на стороне сервера и клиента
✔️Поддержку Multi-user / Multi-tenant окружений
✔️Простую интеграцию с любыми S3-SDK и библиотеками
Он совместим с разными S3-инструментами, легко масштабируется благодаря архитектуре Shared-Nothing, обрабатывает запросы параллельно, использует Erasure Coding для надежности и восстановления данных, а целостность хранения обеспечивается криптографическими хэшами.
Для чего можно использовать MinIO:
▪️Приватное облако — хранение изображений и файлов пользователей
▪️AI/ML и Big Data — хранение больших наборов данных
▪️Бэкапы — резервное копирование баз данных и виртуальных машин
Использование MinIO во Flutter
Благодаря S3-совместимости, MinIO можно использовать как локальное S3 для быстрой разработки приложений на Flutter.
Для работы подходит пакет minio, совместимый с MinIO, AWS S3 и Firebase Storage.
Чтобы начать работу, достаточно создать клиент, передав данные S3-сервера:
import 'package:minio/minio.dart';
MinioClient minio = MinioClient (
endPoint : 'your-s3-endpoint-url.com', // Замените на адрес вашего MinIO
accessKey : 'your-access-key', // Лучше передавать через переменные окружения (env)
secretKey : 'your-secret-key', // Лучше передавать через переменные окружения (env)
useSSL : false, // Установите true, если ваш сервер S3 использует HTTPS
);
Библиотека предоставляет удобные методы для работы с объектами. Рассмотрим некоторые из них:
◽️Загрузка объекта в S3
Метод putObject позволяет загружать объекты из потока байтов (Stream<Uint8List>).
Future<AppFileLink> uploadFile({
required String bucket,
required String object,
required Stream<Uint8List> data,
}) async {
// putObject выполняет загрузку
return minio
.putObject(
bucket,
object,
data,
)
// После успешной загрузки получаем публичную ссылку
.then((_) => getPublicUrl(bucket: bucket, object: object));
}◽️Загрузка с прогрессом
Для больших файлов можно отслеживать ход загрузки с помощью колбэка onProgress:
await minio.putObject(
'mybucket',
'myobject',
Stream<Uint8List>.value(Uint8List(1024)),
onProgress: (bytes) {
print('$bytes uploaded');
},
);
◽️Получение временно подписанной ссылки (Presigned URL)
Для безопасного предоставления доступа к файлу без публичного доступа к бакету используется временно подписанная ссылка.
Future<AppFileLink> getPublicUrl({
required String bucket,
required String object,
Duration expires = const Duration(days: 7), // Срок действия ссылки
}) async {
final link = await minio.presignedGetObject(
bucket,
object,
expires: expires.inSeconds, // Срок действия в секундах
);
// Возвращаем ссылку
// ...
}📎С полным списком методов можно ознакомиться в документации библиотеки
Продолжение — в комментариях 👇
🔥8👍5❤4