Привет, с вами Катя, Flutter Dev Friflex.
Сегодня я расскажу о новом расширении в Visual Studio Code для Swift.
🔴 Цель обновления
Сделать возможной разработку Swift-приложений на всех платформах. Это позволит разработчикам создавать приложения на Swift без ограничений по операционной системе.
🔴 О переходе
Для тех, кто уже использует VS Code, переход будет плавным. Все существующие функции и инструменты останутся доступными, что делает адаптацию к новым возможностям легкой и удобной. Старый релиз должен автоматически установить новое расширение и отключить себя, а в дальнейшем все языковые функции будут предоставляться новым расширением.
📎 Ссылка на установку расширения.
Сегодня я расскажу о новом расширении в Visual Studio Code для Swift.
Сделать возможной разработку Swift-приложений на всех платформах. Это позволит разработчикам создавать приложения на Swift без ограничений по операционной системе.
Для тех, кто уже использует VS Code, переход будет плавным. Все существующие функции и инструменты останутся доступными, что делает адаптацию к новым возможностям легкой и удобной. Старый релиз должен автоматически установить новое расширение и отключить себя, а в дальнейшем все языковые функции будут предоставляться новым расширением.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8❤4👌2
Сегодня #Friflex_team заряжает знаниями на FlutterConf:
🔸 12:40 — Юрий (руководитель отдела разработки) и Анна (Flutter TeamLead) в докладе «Router во Flutter. Когда думал, что все легко» расскажут про концепцию декларативного роутера.
📍 Зал 1
🔸 15:50 — Flutter-разработчики Роза и Екатерина в докладе «Как не наступать на одни и те же грабли: систематизация ошибок в разработке на Flutter» поделятся, как систематизировать ошибки, с которыми часто сталкиваются разработчики и предложат подходы по их минимизации.
📍 Зал 2
Если вы здесь, то приходите послушать наши доклады и пообщаться. Присоединиться можно и онлайн.
Если вы здесь, то приходите послушать наши доклады и пообщаться. Присоединиться можно и онлайн.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10👍5❤4
Media is too big
VIEW IN TELEGRAM
Flutter или …
На FlutterConf пообщались с Анной, Flutter TeamLead в Friflex, и поставили ее перед непростым выбором.
Что ответила Анна🖱
На FlutterConf пообщались с Анной, Flutter TeamLead в Friflex, и поставили ее перед непростым выбором.
Что ответила Анна
Please open Telegram to view this post
VIEW IN TELEGRAM
❤15🔥11👍5🍓1
Привет! Это Роза, Flutter Dev Friflex! 👋
Открывая любое приложение, скорее всего вы там увидите одну или 2 иконки. Но откуда они, и как добавить свои кастомные иконки? В Flutter вы можете работать с иконками, используя три основных подхода:
1️⃣ Иконки из шрифтов (Icon Fonts) — встроенные иконки, такие как Material Icons.
2️⃣ SVG-иконки — обработка с помощью пакета flutter_svg
3️⃣ Векторная графика — использование vector_graphics для более быстрого рендеринга.
Предлагаю сравнить эти варианты и рассмотреть, когда и где использовать каждый из них.
🔸 Иконки из шрифтов (Icon Fonts)
Иконки из шрифтов — это символы в формате шрифтов, например, Material Icons:
Можно использовать кастомные шрифты с иконками. Например, с помощью IcoMoon:
1. Генерируем файл
2. Подключаем в
3. Используем:
🔸 SVG-иконки с flutter_svg:
Для кастомных иконок в векторном формате используем пакет
🔸 Векторная графика (Vector Graphics)
Для повышения производительности конвертируем SVG в формат vector_graphics. Он рендерится быстрее.
Что выбрать?
✅ Icon Fonts — для стандартных иконок с высокой производительностью.
✅ Flutter SVG — для кастомных и анимированных иконок.
✅ Vector Graphics — для максимальной скорости рендеринга.
Какой метод используете вы? Делитесь в комментариях! 🚀
Открывая любое приложение, скорее всего вы там увидите одну или 2 иконки. Но откуда они, и как добавить свои кастомные иконки? В Flutter вы можете работать с иконками, используя три основных подхода:
Предлагаю сравнить эти варианты и рассмотреть, когда и где использовать каждый из них.
Иконки из шрифтов — это символы в формате шрифтов, например, Material Icons:
Icon(Icons.home, size: 32, color: Colors.blue)
Можно использовать кастомные шрифты с иконками. Например, с помощью IcoMoon:
1. Генерируем файл
.ttf
с иконками.2. Подключаем в
pubspec.yaml:
flutter:
fonts:
- family: CustomIcons
fonts:
- asset: assets/fonts/CustomIcons.ttf
3. Используем:
const Text(
'\uE900',
style: TextStyle(fontFamily: 'CustomIcons', fontSize: 32),
),
Для кастомных иконок в векторном формате используем пакет
flutter_svg:
import 'package:flutter_svg/flutter_svg.dart';
SvgPicture.asset(
'assets/icons/home.svg',
width: 32,
height: 32,
colorFilter: ColorFilter.mode(Colors.blue, BlendMode.srcIn),
)
Для повышения производительности конвертируем SVG в формат vector_graphics. Он рендерится быстрее.
import 'package:vector_graphics/vector_graphics.dart';
VectorGraphic(
assetName: 'assets/icons/home.vec',
width: 32,
height: 32,
)
Что выбрать?
Какой метод используете вы? Делитесь в комментариях! 🚀
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10👍8❤5🤩1
Привет! С вами вновь Анна, Friflex Flutter Team Lead.
🛒 Что делать, если в Flutter-приложение нужно добавить возможность покупок виртуальных товаров и услуг? Сегодня подробно разберемся с нюансами в трех самых популярных сторах — Google Play, AppStore и RuStore.
Довольно часто в коммерческих проектах появляется необходимость настраивать внутренние оплаты. Они могут потребоваться как для разовой покупки набора объектов (например, внутренняя валюта приложения), так и для получения постоянного доступа к закрытому контенту в течение некоторого времени (подписки).
Приобретение подобного виртуального товара называется цифровой покупкой. При реализации таких покупок важно учитывать, что Google Play разрешает проведение оплаты только через свою внутреннюю систему платежей. Это правило четко указано в их документации, и пренебрежение им может стать причиной отклонения вашей сборки.
Но в оплате через Google Play есть нюанс — даже при корректной интеграции в ваше приложение и настройке в кабинете разработчика проведение платежей в России, к сожалению, недоступно.
В AppStore подход немного лояльнее. Клиенты имеют возможность оплачивать покупки. Для этого в аккаунте Apple достаточно подключить как источник оплаты мобильный телефон. Важно: в России Apple поддерживает только номера операторов МТС и Билайн.
📎 Подробнее о доступных способах оплаты тут.
Кроме этого для аккаунтов разработчиков, зарегистрированных в РФ, есть возможность провести цифровые покупки через внешние ссылки. Для этого необходимо подать в App Store Connect специальную заявку, после одобрения которой появится возможность настроить StoreKit External Purchase Link Entitlement в приложении. Комиссия за такие платежи — 27%.
📎 Делюсь полезным туториалом по настройке и заполнению заявки.
Меньше всего проблем сейчас встречается в оплатах через RuStore. Здесь нет никаких ограничений на источники платежей и регион, откуда производится оплата. RuStore позволяет оплатить продукт различными способами, например, картой онлайн, через сервисы T-Pay, SberPay и даже СБП, что сейчас очень удобно для пользователей.
👍 В следующем посте разберем на практике, как настроить в приложении in-app покупки c вызовом нативных окон от сторов.
Делитесь своим опытом в комментариях.
Довольно часто в коммерческих проектах появляется необходимость настраивать внутренние оплаты. Они могут потребоваться как для разовой покупки набора объектов (например, внутренняя валюта приложения), так и для получения постоянного доступа к закрытому контенту в течение некоторого времени (подписки).
Приобретение подобного виртуального товара называется цифровой покупкой. При реализации таких покупок важно учитывать, что Google Play разрешает проведение оплаты только через свою внутреннюю систему платежей. Это правило четко указано в их документации, и пренебрежение им может стать причиной отклонения вашей сборки.
Но в оплате через Google Play есть нюанс — даже при корректной интеграции в ваше приложение и настройке в кабинете разработчика проведение платежей в России, к сожалению, недоступно.
В AppStore подход немного лояльнее. Клиенты имеют возможность оплачивать покупки. Для этого в аккаунте Apple достаточно подключить как источник оплаты мобильный телефон. Важно: в России Apple поддерживает только номера операторов МТС и Билайн.
Кроме этого для аккаунтов разработчиков, зарегистрированных в РФ, есть возможность провести цифровые покупки через внешние ссылки. Для этого необходимо подать в App Store Connect специальную заявку, после одобрения которой появится возможность настроить StoreKit External Purchase Link Entitlement в приложении. Комиссия за такие платежи — 27%.
Меньше всего проблем сейчас встречается в оплатах через RuStore. Здесь нет никаких ограничений на источники платежей и регион, откуда производится оплата. RuStore позволяет оплатить продукт различными способами, например, картой онлайн, через сервисы T-Pay, SberPay и даже СБП, что сейчас очень удобно для пользователей.
Делитесь своим опытом в комментариях.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12❤8❤🔥4👍3👌2🆒1
Недавно вышел новый релиз Flutter 3.29, который включает в себя множество улучшений, обновлений и новых возможностей. Предлагаю еще раз по ним пройтись.
Основные изменения в Flutter 3.29
Обновленная страница предоставляет более детальное представление о внутренней архитектуре Flutter, что поможет разработчикам лучше понять, как работает фреймворк.
Здесь все подробности о новом Flutter.
Поддержка Dart 3.7
Вместе с обновлением Flutter выпущена новая версия Dart — Dart 3.7. Она включает в себя улучшения производительности и новые функции языка.
Осторожность при обновлении до Flutter 3.29
Как и в каждом новом релизе, Flutter 3.29 включает в себя ряд разрушающих изменений. Ознакомиться можно тут.
Хотя этот релиз принесет множество новых возможностей и улучшений, важно помнить, что не стоит спешить с обновлением, особенно если вы активно используете сторонние библиотеки из pub.dev. Многие библиотеки на pub.dev могли еще не обновиться под новую версию Flutter. Это может привести к несовместимости и ошибкам в вашем проекте.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11❤7⚡4👍4
This media is not supported in your browser
VIEW IN TELEGRAM
Всем привет! С вами Анна, Friflex Flutter Team Lead.
В прошлом посте мы разобрались, что такое внутренние цифровые покупки в приложении и какие есть нюансы их проведения в разных сторах. Сегодня на практике посмотрим, как можно интегрировать процесс in-app оплат в Flutter-приложение.
Для интеграции in-app покупок в приложение на Android и iOS существует плагин in_app_purchase. Он разработан командой Flutter и имеет отличную репутацию (2,24 тыс лайков и более 123 тыс скачиваний). При правильной интеграции его можно смело использовать в продакшн приложениях.
Какой же функционал дает эта библиотека? Разберемся!
1️⃣ Проверка доступности покупок
Покупки на устройстве доступны только тогда, когда платежная платформа активна и не нарушено подкючение устройства к стору, через который будет осуществленна оплата. Метод вернет true или false в зависимости от результатов проверки.
2️⃣ Получение данных о конкретных продуктах
Вы можете запросить в магазине основные данные определенных продуктов по их идентификаторам. В случае, если продукт с таким id был найден, сервис вернет его основную информацию — заголовок, описание, цену, валюту. Если какие-то идентификаторы не были обнаружены, библиотека вернет их общим списком.
3️⃣ Осуществление покупки
Продукты бывают двух типов — consumable (расходуемые продукты, например, жизни в игре) и non consumable (одноразовые покупки доступа, такие как подписки).
Для оплаты каждого из типов существуют отдельные методы, которые вызывают нативные окна стров для оплаты. Они требуют данных о продукте для покупки. Опционально можно передать некоторый идентификатор пользователя, чтобы покупку в будущем можно было восстановить на другом устройстве.
4️⃣ Восстановление покупки
Нередко бывает, что пользователь делает покупку на одном устройстве, а через какое-то время меняет его на другое. В таких случаях необходимо давать возможность восстановить доступ к ранее оплаченным покупкам. Для этого библиотека дает доступ к методу восстановления.
5️⃣ Получение обновлений о покупках
С помощью потока событий purchaseStream плагин дает возможность отслеживать обновления о текущих покупках. По завершении метода осуществления или восстановления покупки, данные о ней попадают в этот стрим.
С помощью этих 5 основных функций вы легко можете интегрировать процесс покупки в ваше приложение.
❗️ Если вы планируете публиковать свое приложение не только в Google Play и AppStore, важно понимать, что для других сторов необходима другая имплементация и, соответственно, другие библиотеки. Например, для RuStore подойдет flutter_rustore_billing, а для AppGallery — huawei_iap.
Делитесь в комментариях своим опытом интеграции in-app покупок.
В прошлом посте мы разобрались, что такое внутренние цифровые покупки в приложении и какие есть нюансы их проведения в разных сторах. Сегодня на практике посмотрим, как можно интегрировать процесс in-app оплат в Flutter-приложение.
Для интеграции in-app покупок в приложение на Android и iOS существует плагин in_app_purchase. Он разработан командой Flutter и имеет отличную репутацию (2,24 тыс лайков и более 123 тыс скачиваний). При правильной интеграции его можно смело использовать в продакшн приложениях.
Какой же функционал дает эта библиотека? Разберемся!
Покупки на устройстве доступны только тогда, когда платежная платформа активна и не нарушено подкючение устройства к стору, через который будет осуществленна оплата. Метод вернет true или false в зависимости от результатов проверки.
final isAvailable = await InAppPurchase.instance.isAvailable();
Вы можете запросить в магазине основные данные определенных продуктов по их идентификаторам. В случае, если продукт с таким id был найден, сервис вернет его основную информацию — заголовок, описание, цену, валюту. Если какие-то идентификаторы не были обнаружены, библиотека вернет их общим списком.
final data =
await InAppPurchase.instance.queryProductDetails({'id1', 'id2', 'id3'});
final products = data.productDetails;
final notFoundIds = data.notFoundIDs;
Продукты бывают двух типов — consumable (расходуемые продукты, например, жизни в игре) и non consumable (одноразовые покупки доступа, такие как подписки).
Для оплаты каждого из типов существуют отдельные методы, которые вызывают нативные окна стров для оплаты. Они требуют данных о продукте для покупки. Опционально можно передать некоторый идентификатор пользователя, чтобы покупку в будущем можно было восстановить на другом устройстве.
await InAppPurchase.instance.buyNonConsumable(
purchaseParam: PurchaseParam(
productDetails: product,
applicationUserName: userId,
),
);
await InAppPurchase.instance.buyConsumable(
purchaseParam: PurchaseParam(
productDetails: product,
applicationUserName: userId,
),
);
Нередко бывает, что пользователь делает покупку на одном устройстве, а через какое-то время меняет его на другое. В таких случаях необходимо давать возможность восстановить доступ к ранее оплаченным покупкам. Для этого библиотека дает доступ к методу восстановления.
await
InAppPurchase.instance.restorePurchases(applicationUserName: userId);
С помощью потока событий purchaseStream плагин дает возможность отслеживать обновления о текущих покупках. По завершении метода осуществления или восстановления покупки, данные о ней попадают в этот стрим.
InAppPurchase.instance.purchaseStream.listen((purchaseDetails) {
// обработка данных о покупках в МП
});
С помощью этих 5 основных функций вы легко можете интегрировать процесс покупки в ваше приложение.
Делитесь в комментариях своим опытом интеграции in-app покупок.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥14❤8👍4👌2🐳1
This media is not supported in your browser
VIEW IN TELEGRAM
Привет, это Роза, Flutter Dev Friflex! 👋
Представь: ты создаешь Flutter-приложение и хочешь сделать переход между экранами эффектным. Что же выбрать?
💡 Конечно, Hero
Как и положено настоящему герою, Hero анимирует элементы при смене экранов, плавно изменяя их размер и положение.
Что умеет Hero?
✅ Улучшать восприятие интерфейса
✅ Сохранять контекст перехода
✅ Создавать красивые анимации
Например, можно сделать так, чтобы изображение плавно увеличивалось при переходе на новый экран или кнопка перемещалась между экранами.
Как это работает?
Hero связывает элементы с помощью уникального тега и автоматически анимирует их переход.
Давайте разберемся на примере:
📌 Первый экран
Оборачиваем изображение в Hero и задаем тег:
📌 Второй экран
Используем тот же тег:
Hero автоматически выполняет анимацию — никаких сложных настроек.
Параметры Hero:
Где еще можно использовать Hero?
🔴 В анимации текста
🔴 Для перемещения кнопок
🔴 Чтобы создавать эффектные переходы между карточками
Полезные советы:
➡️ Используйте уникальные теги — каждый Hero должен иметь уникальный тег
➡️ Комбинируйте с анимациями — FadeTransition, AnimatedContainer и другими
➡️ Роутинг: без правильного маршрута даже Hero не взлетит — выбирайте переходы с анимацией
➡️ Не перегружайте интерфейс — анимируйте только ключевые элементы
👍 Лайфхак: можно настроить анимацию вручную через
Теперь Hero не только в твоем коде, но и в тебе — ты создал крутой переход!
Представь: ты создаешь Flutter-приложение и хочешь сделать переход между экранами эффектным. Что же выбрать?
Как и положено настоящему герою, Hero анимирует элементы при смене экранов, плавно изменяя их размер и положение.
Что умеет Hero?
Например, можно сделать так, чтобы изображение плавно увеличивалось при переходе на новый экран или кнопка перемещалась между экранами.
Как это работает?
Hero связывает элементы с помощью уникального тега и автоматически анимирует их переход.
Давайте разберемся на примере:
Оборачиваем изображение в Hero и задаем тег:
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(imageUrl: ‘some_image_url’),
),
);
},
child: Hero(
tag: 'image',
child: Image.network(image, height: 200),
),
);
Используем тот же тег:
Hero(
tag: 'image',
Child: Image.network(imageUrl, width: 300),
);
Hero автоматически выполняет анимацию — никаких сложных настроек.
Параметры Hero:
tag
— уникальный идентификатор элемента
child
— сам анимируемый виджет (изображение, кнопка и другие элементы)
flightShuttleBuilder
— позволяет кастомизировать анимацию
placeholderBuilder
— задает виджет-заполнитель до завершения анимации
transitionOnUserGestures
— разрешает запуск анимации по жесту пользователяГде еще можно использовать Hero?
Полезные советы:
heroFlightShuttleBuilder
или управлять временем анимации с помощью HeroController.
Теперь Hero не только в твоем коде, но и в тебе — ты создал крутой переход!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤8🔥6🍾3
Привет, с вами вновь Катя, Flutter Dev Friflex.
Сегодня я расскажу, как работать с Generics.
🔖 Что это?
Generics позволяют создавать классы, методы и интерфейсы, которые могут работать с различными типами данных. Они позволяют определять параметры типа, которые можно заменить конкретными типами во время выполнения. Это особенно полезно для создания универсальных структур данных или методов, которые могут работать с различными типами, сохраняя при этом строгую типизацию.
🔖 Как это работает?
Рассмотрим пример, где мы создаем класс, который будет работать с моделью Model. Мы определим класс Repository, который будет использовать дженерики для работы с различными типами моделей.
Создадим базовую модельку с параметром name.
Определим класс Repository, который принимает параметр типа T. Условие extends Model гарантирует, что T будет подтипом Model. Создадим два метода: один — для добавления элементов в список, а другой — для их получения.
Теперь добавим модели User и Product, которые будут наследоваться от базовой модели. У каждой модели есть свои свойства id и price.
Теперь перейдем к реализации и посмотрим, как с этим работать. В примере я создала два репозитория: один — для управления пользователями, а другой — для управления продуктами.
После запуска приложения выводятся следующие данные:
Благодаря тому, что репозиторий принимает дженерики, мы можем подставлять туда любую модель, которая наследуется от Model.
🔖 Преимущества использования
🔴 Типобезопасность: Generics обеспечивают строгую типизацию, что позволяет избежать ошибок времени выполнения
🔴 Повторное использование кода: вы можете создавать универсальные классы и методы, которые можно использовать с различными типами.
🔴 Читаемость кода: код становится более понятным и структурированным.
Теперь ты освоил суперсилу гибкости! Главное — применять ее, иначе это будет как шпагат, который уже не такой уж и поперечный🤪
Сегодня я расскажу, как работать с Generics.
Generics позволяют создавать классы, методы и интерфейсы, которые могут работать с различными типами данных. Они позволяют определять параметры типа, которые можно заменить конкретными типами во время выполнения. Это особенно полезно для создания универсальных структур данных или методов, которые могут работать с различными типами, сохраняя при этом строгую типизацию.
Рассмотрим пример, где мы создаем класс, который будет работать с моделью Model. Мы определим класс Repository, который будет использовать дженерики для работы с различными типами моделей.
Создадим базовую модельку с параметром name.
class Model {
Model(this.name);
final String name;
}
Определим класс Repository, который принимает параметр типа T. Условие extends Model гарантирует, что T будет подтипом Model. Создадим два метода: один — для добавления элементов в список, а другой — для их получения.
class Repository<T extends Model> {
final List<T> items = [];
void addItem(T item) => items.add(item);
List<T> getItems() => items;
}
Теперь добавим модели User и Product, которые будут наследоваться от базовой модели. У каждой модели есть свои свойства id и price.
class User extends Model {
User(this.id, String name) : super(name);
final int id;
}
class Product extends Model {
Product(String name, this.price) : super(name);
final double price;
}
Теперь перейдем к реализации и посмотрим, как с этим работать. В примере я создала два репозитория: один — для управления пользователями, а другой — для управления продуктами.
void main() {
// Репозиторий для пользователей
final userRepository = Repository<User>();
userRepository.addItem(User(1, 'Alice'));
userRepository.addItem(User(2, 'Bob'));
// Выводим информацию о пользователях
for (var user in userRepository.getItems()) {
print('User: ${user.name}, ID: ${user.id}');
}
// Репозиторий для продуктов
final productRepository = Repository<Product>();
productRepository.addItem(Product('Laptop', 999.99));
productRepository.addItem(Product('Smartphone', 499.99));
// Выводим информацию о продуктах
for (var product in productRepository.getItems()) {
print('Product: ${product.name}, Price: \$${product.price}');
}
}
После запуска приложения выводятся следующие данные:
консоль
User: Alice, ID: 1
User: Bob, ID: 2
Product: Laptop, Price: $999.99
Product: Smartphone, Price: $499.99
Благодаря тому, что репозиторий принимает дженерики, мы можем подставлять туда любую модель, которая наследуется от Model.
Теперь ты освоил суперсилу гибкости! Главное — применять ее, иначе это будет как шпагат, который уже не такой уж и поперечный
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9🔥7👍6⚡2
Привет, это Анна, Friflex Flutter Team Lead.
Признавайтесь, кому приходилось хотя бы раз писать расширения для строк, чисел и даже списков, чтобы выполнить какие-то простые преобразования или проверки, которых нет среди стандартных функций? Уверена, почти всем! Сегодня я расскажу, как перестать это делать.
Вам нужна всего одна библиотека — dartx.
Пакет дает доступ к дополнительным расширениям классов String, int, num, Iterable, DateTime, File и других, которые используются буквально в каждом проекте. Разберем самые интересные функции.
🔸 String
1️⃣
2️⃣
3️⃣
🔸 Iterable
1️⃣
2️⃣
🔸 DateTime/Duration (эти функции можно подключить отдельно через пакет time)
1️⃣
2️⃣
3️⃣
Это только малая часть того, что умеет dartx!
❤️ — если уже пользуетесь пакетом
🔥 — если только сейчас открыли для себя его чудесные функции
Признавайтесь, кому приходилось хотя бы раз писать расширения для строк, чисел и даже списков, чтобы выполнить какие-то простые преобразования или проверки, которых нет среди стандартных функций? Уверена, почти всем! Сегодня я расскажу, как перестать это делать.
Вам нужна всего одна библиотека — dartx.
Пакет дает доступ к дополнительным расширениям классов String, int, num, Iterable, DateTime, File и других, которые используются буквально в каждом проекте. Разберем самые интересные функции.
capitalize()
и decapitalize()
— изменят регистр только первой буквы предложенияisNotNullOrBlank
и isNullOrBlank
— проверят, есть ли читаемое содержимое, при этом не учитывая специальные символы по типу \n
и пробеловurlEncode
и urlDecode
— в строке преобразуют ссылку в формат application/x-www-form-urlencoded или обратно
final capitalizedText = 'пример заглавной буквы'.capitalize(); // Пример заглавной буквы
final isBlank = ' \n'.isNullOrBlank; // true
final decodedText = 'Пример%20декодирования'.urlDecode; // Пример декодирования
sortedBy()
и thenBy()
— позволят вам выполнить сортировку по нескольким признакамchunkWhile()
и splitWhen()
— объединит в подсписки при выполнении или невыполнении условия
final dogs = [
Dog(name: "Charlie", age: 1),
Dog(name: "Bark", age: 3),
Dog(name: "Charlie", age: 6),
];
final sorted = dogs.sortedBy((dog) => dog.name).thenByDescending((dog) => dog.age); // Bark, Charlie (6), Charlie (3)
final chunckedList =[1, 2, 4, 9, 10, 11].chunkWhile((a, b) => a + 1 == b); // [[1, 2], [4], [9, 10, 11]]
isAtSameYearAs(date)
— проверит, находится ли текущая дата в рамках одного и того же года даты date в параметре (есть аналогичные проверки по месяцу и дню)minutes.fromNow
и minutes.ago
— высчитает DateTime по указанной длительности в будущем и прошломhours
— создаст Duration объект из целого числа
final tenMinutes = 10.minutes; // Duration(minutes: 10)
final isAtSameYear = DateTime(2025, 01, 01).isAtSameYearAs(DateTime(2020, 10, 05)); // false
final timeInFuture = 5.minutes.fromNow; // DatiTime.now() + 5 минут
Это только малая часть того, что умеет dartx!
❤️ — если уже пользуетесь пакетом
🔥 — если только сейчас открыли для себя его чудесные функции
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥15⚡6❤6🥰1
This media is not supported in your browser
VIEW IN TELEGRAM
Всем привет! Это Роза, Flutter Dev в Friflex! 👋
Недавно я столкнулась с проблемой: во время поиска в моем приложении происходило что-то странное — оно начинало тормозить, и это раздражало. Каждое нажатие или ввод символа моментально триггерили действие, создавая нагрузку и снижая производительность.
И я нашла решение!
Дело в том, что во время поиска каждое мое нажатие инициировало отправку события на бэкенд и изменение состояния виджетов, тем самым перегружая UI. Чтобы этого избежать, я использовала Debounce.
Что такое Debounce?
Представьте, что вы разговариваете с человеком, а он перебивает после каждого слова. Debounce, в отличие от этого человека, отличный собеседник — он ждет, пока ты закончишь, и только потом реагирует.
В коде это означает, что частые вызовы функции игнорируются, пока не пройдет заданное время без нового события.
Как это работает?
✅ Debounce ставит таймер при каждом вызове функции
✅ Если в течение этого времени приходит новое событие, таймер сбрасывается
✅ Действие выполняется только тогда, когда пауза между событиями превышает заданное время
Как использовать Debounce в коде?
Достаточно создать объект Debouncer и указать время задержки:
А еще лучше — создать отдельный класс для управления Debounce:
Каждый раз при вызове
Где использовать Debounce?
▶️ В поиске, чтобы не отправлять запрос на сервер при каждом символе
▶️ В кнопках, чтобы избежать дублирующих API-запросов
▶️ В валидации форм, чтобы не проверять ввод на каждом символе
▶️ В анимации и UI, чтобы сглаживать обновления интерфейса
Почему это важно?
🔴 Меньше запросов — API загружается только тогда, когда это действительно нужно
🔴 Более плавный UI — интерфейс не дергается при быстром вводе
🔴 Оптимизированная работа приложения — снижается нагрузка на процессор
🔴 Экономия трафика — меньше ненужных запросов к серверу
📌 Важно: не забывай освобождать ресурсы и вызывать
Согласитесь, иногда Debounce не хватает и в жизни💜
Недавно я столкнулась с проблемой: во время поиска в моем приложении происходило что-то странное — оно начинало тормозить, и это раздражало. Каждое нажатие или ввод символа моментально триггерили действие, создавая нагрузку и снижая производительность.
И я нашла решение!
Дело в том, что во время поиска каждое мое нажатие инициировало отправку события на бэкенд и изменение состояния виджетов, тем самым перегружая UI. Чтобы этого избежать, я использовала Debounce.
Что такое Debounce?
Представьте, что вы разговариваете с человеком, а он перебивает после каждого слова. Debounce, в отличие от этого человека, отличный собеседник — он ждет, пока ты закончишь, и только потом реагирует.
В коде это означает, что частые вызовы функции игнорируются, пока не пройдет заданное время без нового события.
Как это работает?
Как использовать Debounce в коде?
Достаточно создать объект Debouncer и указать время задержки:
final _debouncer = Debouncer(milliseconds: 500);
А еще лучше — создать отдельный класс для управления Debounce:
import 'dart:async';
class Debouncer {
final int milliseconds;
Timer? _timer;
Debouncer({required this.milliseconds});
void run(VoidCallback action) {
_timer?.cancel(); // Отменяем предыдущий таймер
_timer = Timer(Duration(milliseconds: milliseconds), action); // Запускаем новый
}
void dispose() {
_timer?.cancel(); // Очищаем ресурсы
}
}
Каждый раз при вызове
run()
действие выполняется не сразу, а только спустя указанное время.Где использовать Debounce?
Почему это важно?
dispose()
при уничтожении Debounce.Согласитесь, иногда Debounce не хватает и в жизни
Please open Telegram to view this post
VIEW IN TELEGRAM
👍19🔥9❤4
Рисуем как Пикассо, только на Flutter
Привет, это Катя, Flutter Dev Friflex. Flutter предоставляет мощные инструменты для работы с графикой, один из которых — CustomPainter.
Этот класс позволяет рисовать кастомные фигуры, линии, градиенты и другие элементы, которые невозможно создать стандартными виджетами. В этом посте рассмотрим, как использовать CustomPainter, разберем основные методы и попробуем нарисовать кастомную фигуру.
Основные принципы работы CustomPainter
CustomPainter работает в связке с CustomPaint, который отвечает за рендеринг на экране.
CustomPainter переопределяет два метода:
🔴 paint(Canvas canvas, Size size): содержит код отрисовки на canvas.
🔴 shouldRepaint(CustomPainter oldDelegate): указывает, нужно ли перерисовывать объект при изменении состояния
Создание простого CustomPainter
Рассмотрим, как нарисовать круг с градиентной заливкой:
1️⃣ Наследуемся от CustomPainter, что позволяет переопределить метод paint, в котором выполняется отрисовка
2️⃣ Внутри метода paint создаем Paint — кисть для рисования
3️⃣ Используем shader для градиентной заливки — это задает радиальный градиент (от центра к краям), который переходит от синего к фиолетовому цвету
4️⃣ С canvasdrawCircle рисуем круг в центре с радиусом, равным половине ширины
Теперь используем CustomPaint, чтобы отобразить рисунок:
Улучшение производительности
Чтобы избежать ненужных перерисовок, важно:
🔸 Указывать shouldRepaint как false, если рисование не меняется
🔸 Использовать RepaintBoundary, чтобы ограничить область перерисовки
CustomPainter открывает широкие возможности для создания сложных графических элементов в Flutter. Он полезен для кастомных UI-решений, диаграмм, анимаций и визуализаций. Используйте его, когда стандартные виджеты не дают нужного результата.
Привет, это Катя, Flutter Dev Friflex. Flutter предоставляет мощные инструменты для работы с графикой, один из которых — CustomPainter.
Этот класс позволяет рисовать кастомные фигуры, линии, градиенты и другие элементы, которые невозможно создать стандартными виджетами. В этом посте рассмотрим, как использовать CustomPainter, разберем основные методы и попробуем нарисовать кастомную фигуру.
Основные принципы работы CustomPainter
CustomPainter работает в связке с CustomPaint, который отвечает за рендеринг на экране.
CustomPainter переопределяет два метода:
Создание простого CustomPainter
Рассмотрим, как нарисовать круг с градиентной заливкой:
import 'package:flutter/material.dart';
class GradientCirclePainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..shader = RadialGradient(
colors: [Colors.blue, Colors.purple],
).createShader(Rect.fromCircle(
center: Offset(size.width / 2, size.height / 2),
radius: size.width / 2,
));
canvas.drawCircle(
Offset(size.width / 2, size.height / 2),
size.width / 2,
paint,
);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
Теперь используем CustomPaint, чтобы отобразить рисунок:
class GradientCircleWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CustomPaint(
size: Size(200, 200),
painter: GradientCirclePainter(),
);
}
}
Улучшение производительности
Чтобы избежать ненужных перерисовок, важно:
RepaintBoundary(
child: CustomPaint(
size: Size(200, 200),
painter: GradientCirclePainter(),
),
)
CustomPainter открывает широкие возможности для создания сложных графических элементов в Flutter. Он полезен для кастомных UI-решений, диаграмм, анимаций и визуализаций. Используйте его, когда стандартные виджеты не дают нужного результата.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12✍6❤3👌2🏆1
Привет! С вами снова Анна, Friflex Flutter Team Lead.
На собеседованиях Flutter-разработчиков очень часто звучит вопрос — чем отличаются императивная и декларативная навигация. И как показывает опыт, многие кандидаты не понимают различий в этих двух подходах. Сегодня постараемся разобраться.
Императивная навигация работает по принципу управления набором маршрутов, которые существуют в проекте относительно друг друга. Все роуты организуются в приложении по принципу LIFO.
Проще говоря, в императивном подходе маршруты собираются в единый стек. Для примера их можно представить стопкой тарелок. Когда роут добавляется, он складывается сверху (push). Когда вызывается возврат назад — самый верхний удаляется (pop).
Декларативный подход подразумевает работу с состоянием навигации приложения как частью состояния всего приложения.
Он позволяет более абстрактно управлять навигацией, так как не требуется ручное создание каждого конкретного маршрута и есть добавление/удаление. Достаточно передать приложению информацию о том, какое требуется конечное состояние навигации.
Для лучшего понимания различий можно выделить вопросы для каждого из подходов:
🔴 императивный — как выполнить переход и какие методы нужно вызвать для этого?
🔴 декларативный — что нужно показать и какое текущее состояние навигации?
А вы какой подход чаще всего используете в своих проектах?
На собеседованиях Flutter-разработчиков очень часто звучит вопрос — чем отличаются императивная и декларативная навигация. И как показывает опыт, многие кандидаты не понимают различий в этих двух подходах. Сегодня постараемся разобраться.
Императивная навигация работает по принципу управления набором маршрутов, которые существуют в проекте относительно друг друга. Все роуты организуются в приложении по принципу LIFO.
Проще говоря, в императивном подходе маршруты собираются в единый стек. Для примера их можно представить стопкой тарелок. Когда роут добавляется, он складывается сверху (push). Когда вызывается возврат назад — самый верхний удаляется (pop).
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondPage()),
);
Navigator.pop(context);
Декларативный подход подразумевает работу с состоянием навигации приложения как частью состояния всего приложения.
Он позволяет более абстрактно управлять навигацией, так как не требуется ручное создание каждого конкретного маршрута и есть добавление/удаление. Достаточно передать приложению информацию о том, какое требуется конечное состояние навигации.
Для лучшего понимания различий можно выделить вопросы для каждого из подходов:
А вы какой подход чаще всего используете в своих проектах?
Please open Telegram to view this post
VIEW IN TELEGRAM
❤10🔥6👍3❤🔥1
Привет, с вами Роза, Flutter Dev Friflex👋 И сегодня мы немного погрузимся в магию FutureOr!
Представьте, вы создаете некий абстрактный класс с различными методами, но точно не знаете, будет ли реализация асинхронной или синхронной. Конечно, вы можете создать два метода или изощряться с разными подходами, но действительно ли это хорошее решение?
Лучше, если вы объявите метод, как FutureOr.
Звучит пока не очень понятно? Давайте разберемся на примерах.
Предположим, мы разрабатываем сервис, который получает данные. Одна реализация будет синхронной, другая — асинхронной:
Aбстрактный класс
⚙️ Когда же использовать FutureOr?
FutureOr — ваш спаситель, когда вам нужно абстрагироваться от того, является ли результат операции асинхронным или синхронным.
🔧 Как обрабатывать FutureOr?
Самый простой способ — использовать проверку типа с помощью is Future.
Да, такой вариант решения может показаться не самым элегантным. Ведь чрезмерное использование is Future может запутать логику и сделать код менее читаемым. Но в некоторых случаях, особенно при работе с абстракциями, это вполне рабочий и понятный подход.
У меня с работой так же. Иногда мне нужен await, чтобы подумать, а иногда все складывается супер. А у вас?
Представьте, вы создаете некий абстрактный класс с различными методами, но точно не знаете, будет ли реализация асинхронной или синхронной. Конечно, вы можете создать два метода или изощряться с разными подходами, но действительно ли это хорошее решение?
Лучше, если вы объявите метод, как FutureOr.
FutureOr<T>
— это такой хитрый тип в Dart, который говорит: «Эй, результат моего метода может быть либо обычным значением типа T,
либо Future<T>,
если вдруг придется подождать».Звучит пока не очень понятно? Давайте разберемся на примерах.
Предположим, мы разрабатываем сервис, который получает данные. Одна реализация будет синхронной, другая — асинхронной:
import 'dart:async';
abstract class SomeService {
FutureOr<String> fetch();
}
class FirstImplService extends SomeService {
@override
Future<String> fetch() async {
await Future.delayed(Duration(seconds: 2));
return 'Данные из Future';
}
}
class SecondImplService extends SomeService {
@override
String fetch() {
return 'Простые данные';
}
}
Aбстрактный класс
SomeService
объявляет метод fetch() с типом возвращаемого значения FutureOr<String>. Это значит, что fetch() может вернуть либо String, либо Future<String>.FutureOr — ваш спаситель, когда вам нужно абстрагироваться от того, является ли результат операции асинхронным или синхронным.
Самый простой способ — использовать проверку типа с помощью is Future.
Да, такой вариант решения может показаться не самым элегантным. Ведь чрезмерное использование is Future может запутать логику и сделать код менее читаемым. Но в некоторых случаях, особенно при работе с абстракциями, это вполне рабочий и понятный подход.
У меня с работой так же. Иногда мне нужен await, чтобы подумать, а иногда все складывается супер. А у вас?
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13🔥12❤4✍1👎1🙏1
Привет, с вами вновь Катя, Flutter Dev Friflex. Сегодня поговорим об extension.
Extension — это инструмент, который позволяет добавлять новые методы, геттеры, сеттеры или операторы в существующие классы, не изменяя их исходный код. Это полезно для улучшения читаемости кода, инкапсуляции логики и повышения переиспользуемости.
Синатксис: основные правила
🔴 Имя Extension — имя расширения (необязательно, но рекомендуется для читаемости)
🔴 Тип — существующий тип, который расширяется (String, List, int и другие)
✅ Пример без Extension
В этом случае мы создаем отдельную функцию для изменения строки:
Минусы:
🔴 Неинтуитивный вызов (capitalize(text)), несвойственный String
🔴 Нужно передавать строку в функцию, что делает код менее читаемым
🔴 Усложнение автокомплита в IDE, так как методы не привязаны к типу
✅ С использованием Extension
Здесь метод capitalize становится частью String:
Преимущества:
🔴 Код становится лаконичным: text.capitalize() вместо capitalize(text)
🔴 Лучшая читаемость и автодополнение
🔴 Логика метода инкапсулирована в extension, а не в отдельной функции
Когда использовать Extension?
➡️ Для расширения стандартных типов — когда нужно добавить удобные методы к String, List, DateTime и другим встроенным классам.
➡️ Для инкапсуляции вспомогательной логики. Если часто используемая функция относится к конкретному типу, лучше оформить ее как метод через extension.
➡️ Для упрощения работы с объектами — позволяет обращаться к данным через удобные геттеры или методы, избегая лишнего кода.
Ограничения Extension
🔸 Нельзя добавлять новые поля в класс
🔸 Нельзя переопределить существующие методы
🔸 Расширения не наследуются, то есть нельзя создать extends для другого extension
🔸 Конфликты: если два расширения имеют одинаковый метод, нужно явно указывать, какое расширение использовать
📎 Если кратко: используйте расширения для инкапсуляции часто используемых методов и упрощения работы с базовыми типами в вашем проекте.
Extension — это инструмент, который позволяет добавлять новые методы, геттеры, сеттеры или операторы в существующие классы, не изменяя их исходный код. Это полезно для улучшения читаемости кода, инкапсуляции логики и повышения переиспользуемости.
Синатксис: основные правила
extension ИмяExtension on Тип {
// Методы, геттеры, сеттеры
}
В этом случае мы создаем отдельную функцию для изменения строки:
String capitalize(String text) {
if (text.isEmpty) return text;
return text[0].toUpperCase() + text.substring(1);
}
void main() {
print(capitalize('flutter')); // Flutter
}
Минусы:
Здесь метод capitalize становится частью String:
extension StringExtension on String {
String capitalize() {
if (isEmpty) return this;
return this[0].toUpperCase() + substring(1);
}
}
void main() {
print('flutter'.capitalize()); // Flutter
}
Преимущества:
Когда использовать Extension?
Ограничения Extension
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍8🍓4❤3👻1
Всем привет! Это Анна, Friflex Flutter Team Lead.
Мобильные приложения, в том числе написанные на Flutter, дают возможность настроить обработку различных ссылок.
🔗 Глубокие ссылки (deeplinks) — это ссылки с кастомной схемой, которые позволяют не только открыть именно ваше приложение на устройстве, но и осуществить переход на вложенные маршруты внутри него. Например, ссылка
🔗 Universal links — это универсальные ссылки iOS приложений, которые имеют формат стандартной веб-ссылки, например,
🔗 App Links — это ссылки для Android, которые работают по принципу, идентичному Universial links на iOS.
После настройки ссылок на проектах часто возникает вопрос — как же выполнить их обработку внутри приложения? Здесь на помощь нам придет библиотека app_links.
Для получения ссылок используется экземпляр класса AppLinks:
С помощью этого объекта можно отследить, с какой конкретной ссылки запустили приложение.
Кроме этого, в момент работы приложения в него из платформы также могут поступать различные ссылки. В таком случае плагин дает возможность получать эти ссылки потоком строк или объектов Uri.
У библиотеки хорошая репутация: почти 1 тысяча лайков и более 800 тысяч скачиваний.
Делитесь в комментариях своим опытом работы с app_links и с ссылками приложения в целом.
Мобильные приложения, в том числе написанные на Flutter, дают возможность настроить обработку различных ссылок.
app://product/id123
позволит открыть ваше приложение сразу на странице продукта с идентификатором id123. https://www.example.com.
В случае, если приложение установлено, ссылки открывают его. Если нет — в браузере открывается веб-сайт, который связан с этой же ссылкой. После настройки ссылок на проектах часто возникает вопрос — как же выполнить их обработку внутри приложения? Здесь на помощь нам придет библиотека app_links.
Для получения ссылок используется экземпляр класса AppLinks:
final _instance = AppLinks();
С помощью этого объекта можно отследить, с какой конкретной ссылки запустили приложение.
Future<void> handleInitialLink() async {
final initialLink = await _instance.getInitialLink();
// обработка начальной ссылки
}
Кроме этого, в момент работы приложения в него из платформы также могут поступать различные ссылки. В таком случае плагин дает возможность получать эти ссылки потоком строк или объектов Uri.
final uriSubscription = _instance.uriLinkStream.listen((uri) {
// обработка ссылки в Uri формате
});
final srtringLinksSubscription = _instance.stringLinkStream.listen((stringLink) {
// обработка ссылки в String формате
});
У библиотеки хорошая репутация: почти 1 тысяча лайков и более 800 тысяч скачиваний.
Делитесь в комментариях своим опытом работы с app_links и с ссылками приложения в целом.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤7🔥5🍓3
This media is not supported in your browser
VIEW IN TELEGRAM
Привет, с вами Роза, Flutter Dev Friflex👋
Рано или поздно разработчики сталкиваются с ситуацией, когда приложение начинает вести себя странно: что-то перестает работать или же появляются непонятные ошибки. В такие моменты логи становятся главной подсказкой.
Они помогают быстро выяснить, что пошло не так, понять причину сбоя и найти решение. Кроме того, логирование полезно для:
🔴 анализа поведения пользователей
🔴 поиска узких мест в производительности
🔴 обеспечения стабильной работы приложения
Чтобы логи действительно приносили пользу, важно правильно их структурировать.
Логи условно можно разделить на пять уровней важности. Они могут отличаться в разных библиотеках, но суть остается такой же.
Чтобы лучше понять, как работают уровни логирования, представьте их как пирамиду, где каждый уровень отвечает за определенную степень важности:
🔸 DEBUG — детализированные данные: переменные, выполнение функций, отладочные сообщения.
🔸 INFO — ключевые этапы работы приложения.
🔸 WARNING — потенциальные проблемы, которые пока не приводят к сбоям, но требуют внимания.
🔸 ERROR — ошибки, влияющие на работу приложения.
🔸 CRITICAL (или FATAL) — критические сбои, после которых приложение не может продолжать работу.
Чем ниже уровень, тем больше информации он содержит.
Чем выше, тем критичнее события.
Каждый уровень включает в себя логи всех предыдущих. Такое разделение позволяет:
✅ фильтровать логи по уровню важности
✅ структурировать данные для быстрого анализа
✅ мгновенно реагировать на серьезные проблемы
Как логировать в Dart/Flutter?
Вы можете воспользоваться инструментами, представленными в самом Dart, написать собственный логгер или же использовать готовые решения.
1️⃣ Простейший способ —
2️⃣ Использование dart:developer
3️⃣ Готовые библиотеки:
Logging — официальная библиотека от Dart. Простая, но мощная
Logger — удобный форматированный вывод, цветовые индикации, фильтрация
Talker — гибкая настройка логов, отличная поддержка Flutter
➡️ Если вам нужно просто быстро вывести сообщение —
➡️ Если хотите базовый контроль уровней логов —
➡️ Для продвинутого логирования лучше использовать
Оптимизация логирования: ленивые вычисления
Логирование может быть дорогостоящей операцией, особенно если записывать сложные данные. Чтобы избежать лишних вычислений, можно использовать ленивую инициализацию (closure).
Так выражение выполнится, только если лог действительно нужен.
Еще немного об инструментах логирования — в табличке в комментариях.
❤️ — обсудим в следующем посте удаленные хранилища логов, безопасное логирование и другие аспекты.
Рано или поздно разработчики сталкиваются с ситуацией, когда приложение начинает вести себя странно: что-то перестает работать или же появляются непонятные ошибки. В такие моменты логи становятся главной подсказкой.
Они помогают быстро выяснить, что пошло не так, понять причину сбоя и найти решение. Кроме того, логирование полезно для:
Чтобы логи действительно приносили пользу, важно правильно их структурировать.
Логи условно можно разделить на пять уровней важности. Они могут отличаться в разных библиотеках, но суть остается такой же.
Чтобы лучше понять, как работают уровни логирования, представьте их как пирамиду, где каждый уровень отвечает за определенную степень важности:
Чем ниже уровень, тем больше информации он содержит.
Чем выше, тем критичнее события.
Каждый уровень включает в себя логи всех предыдущих. Такое разделение позволяет:
Как логировать в Dart/Flutter?
Вы можете воспользоваться инструментами, представленными в самом Dart, написать собственный логгер или же использовать готовые решения.
print()
или debugPrint()
Logging — официальная библиотека от Dart. Простая, но мощная
Logger — удобный форматированный вывод, цветовые индикации, фильтрация
Talker — гибкая настройка логов, отличная поддержка Flutter
print
.dart:developer.log
logging, logger
или talker
Оптимизация логирования: ленивые вычисления
Логирование может быть дорогостоящей операцией, особенно если записывать сложные данные. Чтобы избежать лишних вычислений, можно использовать ленивую инициализацию (closure).
logger.i(() => 'State: \$state');
Так выражение выполнится, только если лог действительно нужен.
Еще немного об инструментах логирования — в табличке в комментариях.
❤️ — обсудим в следующем посте удаленные хранилища логов, безопасное логирование и другие аспекты.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤10🔥7❤🔥5👍1
Привет, это Катя, Flutter Dev Friflex. Сегодня поговорим о фреймворке gRPC и его реализации.
Что это?
gRPC — это фреймворк для удаленного вызова процедур (RPC), разработанный Google. Он использует HTTP/2 для транспорта и Protocol Buffers (protobuf) в качестве языка описания интерфейсов и формата сериализации данных.
Настройка и использование gRPC
1️⃣ Добавляем в pubspec.yaml:
2️⃣ Создаем файл .proto
Сделаем на примере создания чата. Определяем сервис в файле .proto. Например, lib/protos/chat.proto:
3️⃣ Генерируем код
Запускаем команду для генерации Dart-кода:
4️⃣ Создаем клиента
5️⃣ Используем клиента в приложении
На что следует обратить внимание:
🔸 gRPC клиент:
➡️ инициализируется один раз при создании состояния
➡️ обеспечивает двустороннюю коммуникацию
➡️ управляет подпиской на поток сообщений
🔸 Потоковая передача:
➡️ receiveMessages() возвращает Stream<Message>
➡️ listen() подписывается на новые сообщения
🔸 Управление ресурсами:
➡️ gRPC соединение должно закрываться
➡️ Отмена подписок происходит автоматически при dispose()
Продолжение — в комментариях📌
Что это?
gRPC — это фреймворк для удаленного вызова процедур (RPC), разработанный Google. Он использует HTTP/2 для транспорта и Protocol Buffers (protobuf) в качестве языка описания интерфейсов и формата сериализации данных.
Настройка и использование gRPC
dependencies:
grpc: ^4.1.0
protobuf: ^3.1.0
dev_dependencies:
protoc_plugin: ^21.1.2
Сделаем на примере создания чата. Определяем сервис в файле .proto. Например, lib/protos/chat.proto:
syntax = "proto3";
package chat;
service ChatService {
rpc SendMessage (Message) returns (MessageResponse);
rpc ReceiveMessages (Empty) returns (stream Message);
}
message Message {
string text = 1;
string sender = 2;
int64 timestamp = 3;
}
message MessageResponse {
bool success = 1;
string error = 2;
}
Запускаем команду для генерации Dart-кода:
bash
protoc --dart_out=grpc:lib/generated -Ilib/protos lib/protos/chat.proto
class GrpcClient {
late ChatServiceClient client;
/// Инициализация канала соединения
GrpcClient() {
final channel = ClientChannel(
'https://localhost',
port: 50051,
options: const ChannelOptions(
credentials: ChannelCredentials.insecure(),
),
);
client = ChatServiceClient(channel);
}
/// Отправка сообщения
Future<MessageResponse> sendMessage(String text, String sender) async {
final message = Message()
..text = text
..sender = sender
..timestamp = DateTime.now().millisecondsSinceEpoch;
try {
return await client.sendMessage(message);
} catch (e) {
print('Error sending message: $e');
return MessageResponse()..success = false..error = e.toString();
}
}
/// Получение сообщений
Stream<Message> receiveMessages() {
return client.receiveMessages(Empty());
}
}
На что следует обратить внимание:
Продолжение — в комментариях
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10⚡7❤5💯1
This media is not supported in your browser
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10❤5👌2
This media is not supported in your browser
VIEW IN TELEGRAM
Привет, с вами Роза, Flutter Dev Friflex!
Когда только начинаешь разрабатывать на Flutter, многие возможности языка остаются незамеченными. А с опытом начинаешь глубже разбираться в деталях и повышать свою экспертность. С модификаторами классов у меня было так же: изначально в моем арсенале был лишь abstract... и все, наверное. А со временем я узнала и про sealed, и про base. Делюсь этим и с вами!
Зачем нужны модификаторы?
Модификаторы в Dart помогают управлять наследованием и доступностью классов. Они задают ограничения, предотвращают нежелательное расширение или, наоборот, определяют четкие правила для будущих реализаций.
Основные модификаторы:
🔸 abstract
Если вам не нужна реализация всех методов, а вы хотите создать класс-шаблон, используйте abstract.
🔴 Запрещает создавать экземпляры этого класса напрямую (new AbstractClass() не сработает).
🔴 Часто используется как базовый класс, определяющий интерфейс и частичное поведение для наследников.
🔸 base
Разрешает наследование (extends) и реализацию (implements), но только в пределах текущего пакета.
🔴 За пределами пакета base-класс нельзя реализовать (implements).
🔴 Полезно, если нужно предотвратить реализацию, но оставить возможность наследования.
🔸 interface
Принуждает использовать класс только через implements, запрещая наследование (extends).
Полезно, если хотите создать чистый контракт, без возможности переиспользовать реализацию.
🔸 final
Запрещает любое наследование (extends) или реализацию (implements) класса за пределами текущей библиотеки.
Гарантирует, что класс — конечная точка в иерархии. Его нельзя расширить или изменить поведение через подклассы вне вашего контроля.
🔸 mixin
Позволяет переиспользовать код без наследования.
🔴 Класс с mixin можно добавлять к другим классам через with.
🔴 Миксины не могут иметь конструкторов и не могут быть инстанцированы напрямую.
🔸 sealed
Позволяет создавать закрытый набор подтипов.
🔴 Все подклассы должны быть в той же библиотеке, что и sealed-класс.
🔴 Полезно для switch, так как компилятор проверяет, что все случаи учтены (exhaustiveness).
🔴 Отлично подходит для описания состояний (Loading, Success, Error), событий и других строго определенных иерархий.
Как использовать модификаторы?
Добавьте перед классом нужное ключевое слово. Например:
🔖 Важно! Модификаторы можно комбинировать (abstract base class), создавая тонкие правила для классов. Подробнее — в таблице в комментариях.
📎 Официальная документация по модификаторам
А какие модификаторы используете чаще всего? Делитесь в комментариях! 👀
Когда только начинаешь разрабатывать на Flutter, многие возможности языка остаются незамеченными. А с опытом начинаешь глубже разбираться в деталях и повышать свою экспертность. С модификаторами классов у меня было так же: изначально в моем арсенале был лишь abstract... и все, наверное. А со временем я узнала и про sealed, и про base. Делюсь этим и с вами!
Зачем нужны модификаторы?
Модификаторы в Dart помогают управлять наследованием и доступностью классов. Они задают ограничения, предотвращают нежелательное расширение или, наоборот, определяют четкие правила для будущих реализаций.
Основные модификаторы:
Если вам не нужна реализация всех методов, а вы хотите создать класс-шаблон, используйте abstract.
Разрешает наследование (extends) и реализацию (implements), но только в пределах текущего пакета.
Принуждает использовать класс только через implements, запрещая наследование (extends).
Полезно, если хотите создать чистый контракт, без возможности переиспользовать реализацию.
Запрещает любое наследование (extends) или реализацию (implements) класса за пределами текущей библиотеки.
Гарантирует, что класс — конечная точка в иерархии. Его нельзя расширить или изменить поведение через подклассы вне вашего контроля.
Позволяет переиспользовать код без наследования.
Позволяет создавать закрытый набор подтипов.
Как использовать модификаторы?
Добавьте перед классом нужное ключевое слово. Например:
sealed class GameState {
// ...
}
А какие модификаторы используете чаще всего? Делитесь в комментариях! 👀
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥15❤4👍3💅2⚡1