Flutter Friendly
810 subscribers
132 photos
57 videos
1 file
120 links
Канал Friflex о разработке на Flutter. Обновления, плагины, полезные материалы — превращаем знания в реальный опыт, доступный каждому разработчику.

🔗 Наш канал для разработчиков: @friflex_dev
🔗 Канал о продуктовой разработке: @friflex_product
Download Telegram
This media is not supported in your browser
VIEW IN TELEGRAM
Google Play 2025: что нового для разработчиков и пользователей

Привет, это Катя, Flutter Dev Friflex. Недавно Google Play выпустил большую обнову, которая затронула разработчиков, игроков и обычных пользователей. Сегодня коротко расскажу про самое главное, а подробнее можно почитать тут.

👨‍💻 Для разработчиков

В Play Console появились новые метрики (предрегистрации, открытия страниц), библиотека для хранения креативов и даже кнопка, чтобы остановить проблемный релиз

Engage SDK теперь работает и в Play Store, а значит — больше персональных подборок и контента прямо на главной

В Play Games Services достижения стали заметнее — их видно в поиске и на карточках игр

🎮 Для игроков

В программе Play Points запустили Diamond District в Roblox — с эксклюзивным VIP-доступом и мерчем для Gold+

Google Play Games for PC добавил мультиаккаунты и мультиокна. Теперь можно играть сразу в несколько игр (или в одну, но с разных аккаунтов 😏)

На Google TV появились рейтинги и отзывы, а на часах (Wear OS) — персональные рекомендации циферблатов

🔒 Безопасность

Кампания Download Apps, Not Traps — теперь каждое приложение проходит больше 10 000 проверок безопасности

В Play Integrity API добавили новые защиты от взломов и злоупотреблений

💳 Платежи

Больше способов оплаты: PayPal в Сингапуре, Napas во Вьетнаме, Airtel в Кении и других странах

В Индонезии появился QRIS — новый стандарт для бесконтактных платежей

Подписки стали удобнее: можно покупать несколько сразу, есть напоминания о бенефитах и увеличенные grace-периоды. Для разработчиков — возможность ставить цены до $5000

🌍 Глобальные проекты

Google продолжает поддерживать стартапы по всему миру:
программы для студий в Корее, Индонезии, Индии и Латинской Америке

#WeArePlay — серия историй про разработчиков, например, про игру, которая помогла высадить 360 000 деревьев

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

💬А что думаете вы по поводу этих обновлений?
Please open Telegram to view this post
VIEW IN TELEGRAM
4🔥4👍3
🗣️Привет! С вами Анна, Flutter Team Lead Friflex.

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

cached_network_image — это плагин, который позволяет не только загружать и отображать изображения из сети, но кэшировать их на устройстве. Эта функция является его главным преимуществом перед стандартным Image.network.

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

CachedNetworkImage(
imageUrl: url,
),


Как мы все знаем, загрузка изображений из сети может занимать некоторое время, поэтому рекомендую добавлять индикатор загрузки. Самый простой вариант — использовать placeholder. Сюда вы можете добавить любой виджет для отображения загрузки, например, кастомный скелетон.

CachedNetworkImage(
imageUrl: url,
placeholder: (context, url) => const AppSkeleton(),
),


Если вам необходимо отображать прогресс загрузки, идеальным решением вместо placeholder станет progressIndicatorBuilder. Он с помощью данных класса DownloadProgress даст вам полный доступ к информации о размере изображения и текущему загруженному объему

CachedNetworkImage(
imageUrl: url,
progressIndicatorBuilder: (context, url, progress) {
return AppProgressIndicator(progressValue: progress.progress);
},
),


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

CachedNetworkImage(
imageUrl: url,
errorWidget: (context, url, error) => AppErrorView(),
errorListener: (error) => print(error),
),


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

Для этого вам потребуется дополнительно подключить в проект flutter_cache_manager. Библиотека дает доступ к объекту DefaultCacheManager, который в свою очередь необходимо передать в CachedNetworkImage.

final cacheManager = DefaultCacheManager();
...
CachedNetworkImage(
imageUrl: url,
cacheManager: cacheManager,
),


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

await cacheManager.emptyCache(); // полная очистка кэша
await cacheManager.removeFile(key); // удаление конкретного файла из кэша
await cacheManager.getFileFromCache(key); // извлечение конкретного файла из кэша


А если функционала дефолтного менеджера вам недостаточно, можно создать свой кастомный. Он позволит настроить дополнительные параметры по кэшированию, например, срок жизни кэша или максимальное количество объектов в нем.

final customCacheManager = CacheManager(
Config(
'customCacheKey',
stalePeriod: const Duration(days: 7), // срок жизни кэша
maxNrOfCacheObjects: 100, // максимальное количество объектов в кэше
)
);
...
CachedNetworkImage(
imageUrl: url,
cacheManager: customCacheManager,
),


❤️ — если было полезно
Please open Telegram to view this post
VIEW IN TELEGRAM
18👍3🔥3😍1
Всем привет! Это Роза, Flutter Dev Friflex 👋

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

🐸 Как начать
Чтобы начать работу с dart_frog, сначала необходимо установить зависимость. Далее можно работать с командами пакета

Для создания проекта используйте:

dart_frog build

В результате у вас создастся структура проекта, которая включает в себя DockerFile.

Основная логика обработки запросов в Dart Frog строится на маршрутах (routes). Каждый маршрут соответствует определенному пути и методу HTTP, и чтобы его добавить необходимо создать файл-обработчик в директории routes/.

Например, чтобы добавить POST /api/localization_key/export, создайте файл вручную:

lib/routes/api/localization_key/export.dart


Или используйте команду:

dart_frog new route "/api/localization_key/export"


Далее внутри созданного роута реализуйте функцию:

Future<Response> onRequest(RequestContext context) async {
// Логика обработки запроса
return Response.json(body: {'message': 'Экспорт данных'});
}


RequestContext позволяет получить доступ к входящему запросу, а также к зависимостям, представленным контексту запроса.

🐸 Middleware
В Dart Frog middleware реализуется как функция, которая оборачивает обработчик маршрута и возвращает новый обработчик с дополнительной логикой.

Middleware добавляется в проект в виде файла с названием _middleware.dart.

Создать middleware можно также с помощью команды:

dart_frog new middleware 'NAME_OF_FOLDER'


После добавления или изменения middleware необходимо перезапустить генерацию кода для применения изменений.

Пару важных моментов:
◽️Если файл _middleware.dart находится в папке маршрута без вложенности, middleware применяется ко всем маршрутам на этом уровне

◽️Чтобы middleware применялся только к определенным маршрутам, необходимо создать _middleware.dart внутри соответствующей папки маршрутов

◽️ Если middleware создан в произвольной, не связанной с маршрутом папке, он применен не будет

Структура middleware выглядит таким образом:

Handler middleware(Handler handler) {
return handler.use(requestLogger());
}


Кроме маршрутов и middleware, часто нужно работать с заголовками запросов. В Dart Frog это реализуется так:

Response onRequest(RequestContext context) {
final request = context.request;
final headers = request.headers; // Map<String, dynamic>
...
}


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

Для этого используйте:

dart_frog build


Это создает сгенерированный код маршрутов в директории build/, который используется сервером (bin/server.dart).

❗️ При работе с dart_frog важно поддерживать правильную вложенность папок, чтобы маршруты сгенерировались корректно. Более подробно можно прочитать в документации Dart Frog.
👍54💩2
Привет, это Катя, Friflex Flutter Dev. Сегодня разберем TDD (Test-Driven Development) — подход к разработке, при котором сначала пишут тесты, а потом уже код.

Как это работает
▪️Пишем маленький тест, описывающий нужное поведение
▪️Запускаем его — тест падает, ведь кода еще нет
▪️Реализуем минимальный код, чтобы тест прошел
▪️Рефакторим код, при этом тесты должны оставаться зелеными

Пример для понимания
Допустим, нам нужна функция sum, которая складывает два числа.

🔴 Сначала пишем тест:

import 'package:flutter_test/flutter_test.dart';

void main() {
test('sum should return correct result', () {
expect(sum(2, 3), 5); // ожидаем 2 + 3 = 5
});
}

Он упадет, так как функции еще нет.

🟢Пишем минимальный код, чтобы тест прошел:

int sum(int a, int b) {
return a + b;
}


🔁 После того как тест прошел, рефакторим код

Плюсы
◽️Код становится надежнее и полностью покрыт тестами
◽️Рефакторить проще — тесты страхуют
◽️Требования становятся понятнее (каждый тест фиксирует ожидание)

Минусы
◽️Разработка стартует медленнее
◽️Нужно уметь писать хорошие тесты
◽️Сложнее применять к UI или к большим интеграциям

А как у вас с TDD?
🔥54👍2
Media is too big
VIEW IN TELEGRAM
🌠 Если вы ждали знак, это он!

Разыгрываем 2 билета на CrossConf — главную конференцию по кроссплатформенным технологиям в России и СНГ.

В программе — двойной поток по Flutter от спикеров из Магнит Маркета, Лаборатории Касперского, BetBoom, Яндекс Про, Friflex и других технологичных компаний, доклады по Crossplatform и Al, нетворкинг, встреча с самой активной частью Flutter-сообщества и веселая вечеринка 🪩

Условия конкурса:
☑️Подписаться на @crossconf и @flutterfriendly
☑️Нажать «участвую» под постом по ссылке.

Итоги подведем 13 октября. Удачи 💙
Please open Telegram to view this post
VIEW IN TELEGRAM
❤‍🔥33👍1
Всем привет! Это Анна, Friflex Flutter Team Lead.

Сегодня поговорим о Pull to Refresh — популярном механизме в интерфейсах мобильных приложений, который позволяет одним жестом обновить информацию на экране.

Для пользователя это простое и понятное движение — содержимое экрана нужно потянуть сверху вниз и отпустить. В момент, когда Pull to Refresh срабатывает, пользователю показывается индикатор загрузки. Как только новые данные получены, содержимое экрана меняется, а индикатор загрузки исчезает.

Этот механизм считается неким «золотым стандартом», почти каждое коммерческое приложение не обходится без него. Почему?

▫️ Жест понятен. Пользователи уже настолько привыкли к такому поведению, что их не нужно дополнительно обучать

▫️ Процесс обновления прозрачен и нагляден. Красивые анимации, индикатор загрузки, плавные жесты — все это дает пользователю ощущение полного контроля и не вызывает вопросов

▫️ Механизм дает пользователю обновлять данные, когда он сам захочет. Это повышает его доверие и лояльность к продукту

▫️ Механизм стал настолько распространенным, что его реализация не вызывает проблем на всех платформах

Как реализовать Pull to Refresh во Flutter-приложении?

▪️Базовый виджет RefreshIndicator
Это самый простой способ. Для реализации достаточно прокручиваемый виджет на экране обернуть в RefreshIndicator, а затем добавить вызов onRefresh.
RefreshIndicator(
onRefresh: () async {
// обновить данные тут
},
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) => ChildWidget(),
),
)


Готово! Такая реализация легко поддерживается и является стабильной.

Но тут важно учесть, что RefreshIndicator не позволит вам особенным образом кастомизировать индикатор загрузки — это всегда будет CircularProgressIndicator, который можно лишь немного видоизменить.

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

▪️Библиотека pull_to_refresh
Если возможностей RefreshIndicator окажется недостачно, на помощь придет SmartRefresher из библиотеки pull_to_refresh.

Этот виджет позволит не только обработать жест перезагрузки (сверху вниз), но и жест дозагрузки (снизу вверх). Можно реализовать механизм по горизонтали с помощью scrollDirection.
SmartRefresher(
enablePullDown: true, // включает жест сверзу вниз
enablePullUp: true, // включает жест снизу вверх
scrollDirection: Axis.vertica l// указывает направление жестов
onRefresh: _onRefresh, // обработка обновления
controller: _refreshController,
child: ListView(...),
);


SmartRefresher удобен тем, что его индикатор загрузки можно кастомизировать. Пакет уже дает готовые решения, например, WaterDropHeader. С помощью CustomHeader и CustomFooter вы можете настроить вид под свои нужды.

▪️Полноценная кастомная реализация.
Если оба решения вам не подходят, и вам требуется совершенно нестандартный вид — поможет самописное решение.

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

Делитесь своим опытом в комментариях😘
Please open Telegram to view this post
VIEW IN TELEGRAM
8👍5🔥2😍1😐1