Anonymous Poll
37%
Сравнение популярных TMS: Lokalise, Phrase, Crowdin и Localizely
62%
Примеры интеграции с Flutter
2%
Свой вариант в комментариях
🔥3❤1
Сегодня расскажу, как сделать кнопку загрузки во Flutter, которая умеет менять свое состояние: от обычной кнопки до загрузки с индикатором и кнопкой отмены. Такой подход пригодится, если вы хотите, чтобы пользователь видел прогресс, не нажимал на кнопку несколько раз или знал, когда загрузка завершилась.
Что мы хотим сделать?
Кнопка будет вести себя вот так:
Сначала — кнопка с текстом GET ➡️ нажимаем — она превращается в спиннер (идет подготовка загрузки) ➡️ потом появляется круговая загрузка с иконкой отмены ➡️ когда все скачалось — кнопка показывает OPEN
1. Создаем виджет DownloadButton
Это обычный StatelessWidget, который получает извне текущее состояние загрузки и отображает нужный UI.
enum DownloadStatus {
notDownloaded,
fetchingDownload,
downloading,
downloaded,
}
2. Показываем форму кнопки
Когда ничего не загружается — кнопка с закругленными краями.
Когда начинается загрузка — кнопка превращается в прозрачный круг (чтобы потом анимировать индикатор поверх него). Пример:
AnimatedContainer(
duration: transitionDuration,
decoration: ShapeDecoration(
shape: isDownloading || isFetching
? CircleBorder()
: StadiumBorder(),
color: isDownloading || isFetching
? Colors.transparent
: CupertinoColors.lightBackgroundGray,
),
child: ...
);
3. Добавляем текст
GET → когда еще не загружено
OPEN → когда уже загружено
В промежуточных состояниях текст скрыт
AnimatedOpacity(
opacity: isDownloading || isFetching ? 0.0 : 1.0,
duration: transitionDuration,
child: Text(
isDownloaded ? 'OPEN' : 'GET',
style: TextStyle(
fontWeight: FontWeight.bold,
color: CupertinoColors.activeBlue,
),
),
);
4. Добавляем индикатор загрузки
Если статус fetchingDownload — отображаем крутящийся спиннер.
Если downloading — прогресс-бар и иконка «стоп» по центру, чтобы отменить загрузку.
Stack(
alignment: Alignment.center,
children: [
CircularProgressIndicator(value: progress),
if (isDownloading)
Icon(Icons.stop, size: 14, color: CupertinoColors.activeBlue),
],
);
5. Обработка нажатий
Когда пользователь нажимает кнопку — вызывается соответствующий колбэк:
void _onPressed() {
switch (status) {
case DownloadStatus.notDownloaded:
onDownload();
break;
case DownloadStatus.downloading:
onCancel();
break;
case DownloadStatus.downloaded:
onOpen();
break;
case DownloadStatus.fetchingDownload:
break;
}
}
Подробнее можно прочитать в официальной документации.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10❤6👍5
This media is not supported in your browser
VIEW IN TELEGRAM
Всем привет! С вами Анна, Flutter Team Lead Friflex⭐️
Сегодня я поделюсь своим личным топ-5 практик для работы с изображениями и иконками в крупном Flutter-проекте.
1. Структурируйте файлы изображений
Когда проект масштабный, картинок и иконок может быть очень много. Чтобы было легко и просто ими управлять, хорошая практика — папку assets делить на вложенные папки по назначению изображений и группировать изображения по функционалу, где они применяются. Например, отделять фотографии от SVG-иконок, а иконки BottomNavigationBar от иконок фичи Корзина.
2. Храните изображения в оптимальном формате
Это тот формат, который позволяет минимизировать размер файла, при этом сохраняя максимальное качество. Как показывает практика, для больших картинок и фотографий оптимальным можно считать WEBP формат, а для небольших иконок — SVG.
P.S: а еще лучшебольшие изображения не вшивать в приложение, а хранить на сервере
3. Кэшируйте сетевые изображения
Здесь могу рекомендовать плагин cached_network_image. Он позволяет загружать изображение из сети всего один раз. При повторном появлении картинки на экране загрузка будет локальная, очень быстрая.
4. Добавляйте заглушки и лоадеры для загрузки сетевых изображений
В зависимости от размера изображения и скорости интернета пользователя изображения могут грузиться долго или не загрузиться совсем. Чтобы подобные сценарии не испортили внешний вид вашего интерфейса, стоит добавить лоадер на процесс загрузки и виджет-заглушку на случай падения ошибки загрузки. Так ваш ui будет всегда предсказуем и приятен для пользователя.
5. Актуализируйте assets и pubspec
Чем старше и больше становится проект, тем реже разработчики вспоминают об этом пункте. Какие-то картинки заменились на новые, какие-то случайно продублировались при создании новой фичи — за этим важно следить. Все файлы, добавленные в assets и включенные в pubspec.yaml, попадают в сборку, непосредственно увеличивая ее размер.
Возможно, некоторые пункты кажутся слишком банальными, но именно на подобных мелочах строится надежный проект, способный долгие годы поддерживаться и масштабироваться.
Делитесь своими советами по работе с картинками в проекте👇
Сегодня я поделюсь своим личным топ-5 практик для работы с изображениями и иконками в крупном Flutter-проекте.
1. Структурируйте файлы изображений
Когда проект масштабный, картинок и иконок может быть очень много. Чтобы было легко и просто ими управлять, хорошая практика — папку assets делить на вложенные папки по назначению изображений и группировать изображения по функционалу, где они применяются. Например, отделять фотографии от SVG-иконок, а иконки BottomNavigationBar от иконок фичи Корзина.
assets
|-- images
|-- feature_1
|-- feature_2
|-- icons
|-- bottom_navigation_bar
|-- features
|-- feature_1
|-- feature_2
2. Храните изображения в оптимальном формате
Это тот формат, который позволяет минимизировать размер файла, при этом сохраняя максимальное качество. Как показывает практика, для больших картинок и фотографий оптимальным можно считать WEBP формат, а для небольших иконок — SVG.
P.S: а еще лучше
3. Кэшируйте сетевые изображения
Здесь могу рекомендовать плагин cached_network_image. Он позволяет загружать изображение из сети всего один раз. При повторном появлении картинки на экране загрузка будет локальная, очень быстрая.
4. Добавляйте заглушки и лоадеры для загрузки сетевых изображений
В зависимости от размера изображения и скорости интернета пользователя изображения могут грузиться долго или не загрузиться совсем. Чтобы подобные сценарии не испортили внешний вид вашего интерфейса, стоит добавить лоадер на процесс загрузки и виджет-заглушку на случай падения ошибки загрузки. Так ваш ui будет всегда предсказуем и приятен для пользователя.
5. Актуализируйте assets и pubspec
Чем старше и больше становится проект, тем реже разработчики вспоминают об этом пункте. Какие-то картинки заменились на новые, какие-то случайно продублировались при создании новой фичи — за этим важно следить. Все файлы, добавленные в assets и включенные в pubspec.yaml, попадают в сборку, непосредственно увеличивая ее размер.
Возможно, некоторые пункты кажутся слишком банальными, но именно на подобных мелочах строится надежный проект, способный долгие годы поддерживаться и масштабироваться.
Делитесь своими советами по работе с картинками в проекте
Please open Telegram to view this post
VIEW IN TELEGRAM
❤10👍6🔥4
В прошлом посте мы обсудили TMS-системы — что это такое и зачем они нужны в локализации.
Сегодня перейдем от теории к практике: посмотрим, как подключить TMS к Flutter-приложению на примере Localizely.
Сначала все просто: регистрируемся, создаем проект, выбираем базовый язык и языки перевода. Потом приглашаем команду и добавляем ключи — можно создать их с нуля или импортировать уже существующие.
Допустим, вы это сделали. Что дальше? Конечно, приступаем к внедрению!
Тут все зависит от того, какой генератор локализации вы используете:
▪️Если intl_utils — подключаете библиотеку, настраиваете нужные конфиги (ID проекта, API-ключ) и запускаете команду синхронизации
▪️Если другие генераторы — скачиваете файлы генерации (через интерфейс Localizely или API) и подключаете их в проект по стандартному шаблону
🚀Это все круто, но мы хотим больше. По первому плану вам нужно после каждого изменения ключей вручную синхронизировать переводы с сервисом. Но это можно автоматизировать — и еще добавить OTA (Over-the-Air), чтобы получать свежие переводы без релиза новой версии приложения.
Давайте по порядку!
✔️Автоматизация при помощи gitHub
Здесь все строится вокруг конфигурационного файла localizely.yml в репозитории. В нем указываете, какие файлы переводов импортировать (pull) из GitHub в Localizely и какие экспортировать (push) обратно. После этого в разделе Integrations настраиваете автоматическую синхронизацию и привязываете GitHub для автоматического push/pull. А чтобы было еще удобнее — добавляете веб-хук. Тогда переводы обновляются в Localizely сразу после пуша изменений в репозиторий.
✔️Так, а теперь давайте к самому интересному — OTA
С ним вы можете доставлять новые переводы прямо в приложение без релиза в сторы. Пользователь просто открывает приложение, а там уже актуальные тексты.
Устроено это примерно таким образом: вы подключаете библиотеку для работы с OTA, добавляете SDK-токен и указываете ID дистрибутива — ветку с версиями релизов приложения. Далее при запуске приложение запрашивает сервис и сохраняет в кэш самую свежую версию переводов. Можно настроить обновление не только при старте, но и в фоне — например, раз в несколько часов или дней.
Это полезно, если у вас много динамичного контента или часто правятся тексты.
⛓️💥В итоге у нас получается такая цепочка:
GitHub следит за синхронизацией файлов → Localizely получает свежие ключи и переводы → OTA мгновенно доставляет их в приложение
Все, процесс локализации живет своей жизнью, а вы тратите время только на сами переводы и улучшение приложения.
❤️ — если было полезно
Please open Telegram to view this post
VIEW IN TELEGRAM
❤10🔥2
Привет, это Катя, Flutter Dev Friflex 👋
Сегодня расскажу про распознавание лиц на Flutter. Это важный инструмент в современных мобильных приложениях: от разблокировки смартфона по лицу до автоматического заполнения данных по фото.
Используя Flutter, мы можем достаточно просто реализовать распознавание лиц, текста или звука, при этом кроссплатформенно. Сегодня я расскажу, как добавить распознавание лиц в свое приложение на Flutter с помощью Google ML Kit.
Зачем нужно распознавание лиц?
✔️Безопасность — контроль доступа по лицу
✔️Идентификация — определение пользователя среди базы данных
✔️Интерактивные приложения — фильтры, маски и другие AR-фичи
✔️Автоматизация — например, распознавание посетителей в системе учета
Что нам понадобится
Для примера будем использовать пакет:
▪️google_mlkit_face_detection — для обнаружения лиц на изображениях
▪️image_picker — для выбора фото из галереи или камеры
Добавим в pubspec.yaml:
Настройка и инициализация
1️⃣ Импортируем пакеты:
2️⃣ Создаем переменные:
3️⃣ Инициализируем в initState:
4️⃣Закрываем детектор в dispose:
Логика распознавания
▫️Метод для получения и обработки изображения:
▫️Отображение результата
Здесь FacePainter — это кастомный класс, который рисует рамки вокруг найденных лиц. Про CustomPainter рассказывала в этом посте.
Как видите, добавить распознавание лиц на Flutter можно буквально в несколько шагов. ML Kit обрабатывает изображение прямо на устройстве, без отправки данных на сервер, что повышает безопасность и скорость работы.
Сегодня расскажу про распознавание лиц на Flutter. Это важный инструмент в современных мобильных приложениях: от разблокировки смартфона по лицу до автоматического заполнения данных по фото.
Используя Flutter, мы можем достаточно просто реализовать распознавание лиц, текста или звука, при этом кроссплатформенно. Сегодня я расскажу, как добавить распознавание лиц в свое приложение на Flutter с помощью Google ML Kit.
Зачем нужно распознавание лиц?
✔️Безопасность — контроль доступа по лицу
✔️Идентификация — определение пользователя среди базы данных
✔️Интерактивные приложения — фильтры, маски и другие AR-фичи
✔️Автоматизация — например, распознавание посетителей в системе учета
Что нам понадобится
Для примера будем использовать пакет:
▪️google_mlkit_face_detection — для обнаружения лиц на изображениях
▪️image_picker — для выбора фото из галереи или камеры
Добавим в pubspec.yaml:
Yaml
dependencies:
google_mlkit_face_detection: ^0.13.1
image_picker: ^1.1.2
Настройка и инициализация
1️⃣ Импортируем пакеты:
import 'package:google_mlkit_face_detection/google_mlkit_face_detection.dart';
import 'package:image_picker/image_picker.dart';
2️⃣ Создаем переменные:
late ImagePicker _picker;
late FaceDetector _faceDetector;
dynamic _image;
Size? _imageSize;
List<Face> _faces = [];
3️⃣ Инициализируем в initState:
@override
void initState() {
super.initState();
_picker = ImagePicker();
_faceDetector = FaceDetector(
options: FaceDetectorOptions(),
);
}
4️⃣Закрываем детектор в dispose:
@override
void dispose() {
_faceDetector.close();
super.dispose();
}
Логика распознавания
▫️Метод для получения и обработки изображения:
Future<void> _getAndScanImage({final bool? isFromCamera}) async {
// Очищаем данные
setState(() {
_image = null;
_faces = [];
_imageSize = null;
});
// Получаем изображение
final imageXFile = await _picker.pickImage(
source: isFromCamera != null && isFromCamera
? ImageSource.camera
: ImageSource.gallery,
);
// Обрабатываем
if (imageXFile != null) {
final inputImage = InputImage.fromFilePath(imageXFile.path);
final facesList = await _faceDetector.processImage(inputImage);
final imageAsBytes = await imageXFile.readAsBytes();
final imageDecoded = await decodeImageFromList(imageAsBytes);
setState(() {
_faces = facesList;
_image = imageDecoded;
_imageSize = Size(
imageDecoded.width.toDouble(),
imageDecoded.height.toDouble(),
);
});
}
}
▫️Отображение результата
Expanded(
child: FittedBox(
child: SizedBox(
width: _imageSize!.width,
height: _imageSize!.height,
child: CustomPaint(
painter: FacePainter(
faceList: _faces,
imageFile: _image,
),
),
),
),
)
Здесь FacePainter — это кастомный класс, который рисует рамки вокруг найденных лиц. Про CustomPainter рассказывала в этом посте.
Как видите, добавить распознавание лиц на Flutter можно буквально в несколько шагов. ML Kit обрабатывает изображение прямо на устройстве, без отправки данных на сервер, что повышает безопасность и скорость работы.
🔥6❤4👍1
Привет! С вами Анна, Friflex Flutter Team Lead🚀
Определение местоположения пользователя — достаточно часто встречающийся запрос на разработку в мобильном приложении. Эта функция может быть очень полезной, так как дает возможность сильно сократить пользовательский путь, например, при поиске ближайшего магазина. Сегодня поговорим о том, как же интегрировать ее во Flutter-приложение.
1 шаг — выбор инструмента определения геопозиции
Для получения данных о местоположении пользователя можно использовать следующие инструменты:
▫️ geolocator — популярный плагин с широким функционалом. С его помощью можно получить текущую геопозицию, отслеживать перемещения пользователя и даже выполнять расчеты дистанции между точками
▫️ location — не менее популярный плагин, имеет функционал схожий с geolocator
▫️кастомная реализация — при желании и необходимости можно написать свой собственный плагин, который будет обращаться к платформе для получения данных геопозиции
2 шаг — настройка разрешений на Android
На Android необходимо в
Для Android API 29 и выше при необходимости использования геопозиции в фоне нужно добавить разрешение:
Важно! Необходимо следить за тем, какие разрешения вы запрашиваете, не запрашивать лишнего. При модерации Google Play может отклонить вашу сборку, если посчитает, что вы выполняете лишние запросы.
При запросе разрешений вам стоит предусмотреть сценарии, когда пользователь отклонил запрос 1 раз и больше. Если пользователь выбрал пункт «Больше не спрашивать» или на последних версиях Android отклонил разрешение дважды, выдать разрешение он сможет только вручную из настроек. Здесь важно обеспечить ему наиболее простой путь.
3 шаг — настройка разрешений на iOS
На iOS необходимо в
Здесь процесс выдачи разрешения немного отличается от Android. Если пользователь один раз отклонил разрешение в приложении, выдать его возможно будет только через настройки устройства. Этот сценарий также важно учесть и уведомить об этом пользователя.
4 шаг — интеграция функционала в приложение
Здесь могу порекомендовать создавать некоторый сервис-обертку для доступа к функциям подключенного плагина. В случае необходимости такой подход позволит вам легко и с наименьшими усилиями заменить реализацию с минимальным рефакторингом.
Готово!✅
❤️ — если было полезно
Определение местоположения пользователя — достаточно часто встречающийся запрос на разработку в мобильном приложении. Эта функция может быть очень полезной, так как дает возможность сильно сократить пользовательский путь, например, при поиске ближайшего магазина. Сегодня поговорим о том, как же интегрировать ее во Flutter-приложение.
1 шаг — выбор инструмента определения геопозиции
Для получения данных о местоположении пользователя можно использовать следующие инструменты:
▫️ geolocator — популярный плагин с широким функционалом. С его помощью можно получить текущую геопозицию, отслеживать перемещения пользователя и даже выполнять расчеты дистанции между точками
▫️ location — не менее популярный плагин, имеет функционал схожий с geolocator
▫️кастомная реализация — при желании и необходимости можно написать свой собственный плагин, который будет обращаться к платформе для получения данных геопозиции
2 шаг — настройка разрешений на Android
На Android необходимо в
AndroidManifest.xml
выдать специальные разрешения.
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
Для Android API 29 и выше при необходимости использования геопозиции в фоне нужно добавить разрешение:
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
Важно! Необходимо следить за тем, какие разрешения вы запрашиваете, не запрашивать лишнего. При модерации Google Play может отклонить вашу сборку, если посчитает, что вы выполняете лишние запросы.
При запросе разрешений вам стоит предусмотреть сценарии, когда пользователь отклонил запрос 1 раз и больше. Если пользователь выбрал пункт «Больше не спрашивать» или на последних версиях Android отклонил разрешение дважды, выдать разрешение он сможет только вручную из настроек. Здесь важно обеспечить ему наиболее простой путь.
3 шаг — настройка разрешений на iOS
На iOS необходимо в
Info.plist
добавить ключи с необходимым разрешением.
<key>NSLocationWhenInUseUsageDescription</key>
<string>Для определения вашего местоположения</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Для фонового доступа к местоположению</string>
Здесь процесс выдачи разрешения немного отличается от Android. Если пользователь один раз отклонил разрешение в приложении, выдать его возможно будет только через настройки устройства. Этот сценарий также важно учесть и уведомить об этом пользователя.
4 шаг — интеграция функционала в приложение
Здесь могу порекомендовать создавать некоторый сервис-обертку для доступа к функциям подключенного плагина. В случае необходимости такой подход позволит вам легко и с наименьшими усилиями заменить реализацию с минимальным рефакторингом.
Готово!✅
❤️ — если было полезно
❤11🔥3💯2
Anonymous Poll
69%
Плагин geolocator
16%
Плагин location
15%
Кастомную реализацию
This media is not supported in your browser
VIEW IN TELEGRAM
Всем привет, это Роза, Flutter Dev Friflex💫
Представьте: у вас есть данные, которые нужно защитить и передать через API. Как это сделать безопасно?
Здесь можно использовать JWT (JSON Web Token).
Веб-токены JSON (JWT) — это компактный и безопасный способ передачи информации между сторонами в виде JSON-объекта. JWT подписывается секретным ключом или парой ключей (RSA/ECDSA), что позволяет убедиться в целостности данных.
Почему JWT лучше логина/пароля?
✔️ Ограниченный срок жизни: пароль может «утечь» и жить вечно, а токен живет, например, всего 15 минут. Даже если его украдут, через короткое время он станет бесполезным.
✔️ Нет нагрузки на БД: проверка пароля требует похода в базу. JWT-сервер проверяет «на месте», без лишних запросов.
❕ Если вы используете «черный список» для отзыва токенов, то придётся обращаться к базе или Redis для проверки, не был ли токен отозван.
Виды JWT
▪️Access token — токен для доступа к API, живет недолго (~15 мин). При истечении срока сервер возвращает 401. По умолчанию — многоразовый
▪️ Refresh token — токен для обновления пары токенов, живет дольше (несколько дней). Используется на эндпоинте ~/auth/refresh, когда истекает срок access токена
Структура JWT
Токен состоит из 3 частей, разделенных точкой: xxxxx.yyyyy.zzzzz.
1. Header — заголовок, который хранит тип токена (JWT) и алгоритм подписи (например, HMAC SHA256)
2. Payload — данные о пользователе и токене. Стандартные поля:
▫️ iss — кто выдал токен (issuer)
▫️ sub — идентификатор пользователя (subject)
▫️ aud — для кого предназначен токен (audience)
▫️ exp — время, в течение которого токен считается валидным (expiration)
▫️ iat — время создания токена (issued at)
▫️ jti — уникальный идентификатор токена (JWT ID)
3. Signature — подпись, гарантия целостности
⚠️ Важно: данные в payload видны всем. JWT подписывается, а не шифруется
Как JWT защищает наши данные?
JWT нельзя подделать без секретного ключа, но содержимое токена можно прочитать. Поэтому не стоит хранить там пароли или чувствительные данные.
Black-list токенов
При выходе из аккаунта или смене пароля токены нужно отзывать. Для этого используют «черный список».
При проверке сервер сначала смотрит, не попал ли токен туда, и только потом валидирует его. Если токен найден в «черном списке» → возвращается 401.
Аутентификация с JWT
Когда говорят об аутентификации с помощью JWT, то имеют в виду, как приложения подтверждают личность пользователя. Устроено это примерно таким образом:
✔️ Пользователь вводит логин и пароль
✔️ Сервер проверяет данные и, если они верны, генерирует access и refresh токены
✔️ Клиент сохраняет токены (например, в Secure Storage)
✔️ При каждом запросе к защищенному API клиент отправляет access token в заголовке Authorization: Bearer <token>
✔️ Сервер проверяет подпись и срок действия токена
✔️ Если access token истек, клиент использует refresh token для получения новой пары токенов
Таким образом, серверу не нужно хранить сессии и пользователь остается аутентифицированным, пока токены действуют.
❤️ — и в следующем посте разберем, как реализовать авторизацию во Flutter с помощью JWT
Представьте: у вас есть данные, которые нужно защитить и передать через API. Как это сделать безопасно?
Здесь можно использовать JWT (JSON Web Token).
Веб-токены JSON (JWT) — это компактный и безопасный способ передачи информации между сторонами в виде JSON-объекта. JWT подписывается секретным ключом или парой ключей (RSA/ECDSA), что позволяет убедиться в целостности данных.
Почему JWT лучше логина/пароля?
✔️ Ограниченный срок жизни: пароль может «утечь» и жить вечно, а токен живет, например, всего 15 минут. Даже если его украдут, через короткое время он станет бесполезным.
✔️ Нет нагрузки на БД: проверка пароля требует похода в базу. JWT-сервер проверяет «на месте», без лишних запросов.
❕ Если вы используете «черный список» для отзыва токенов, то придётся обращаться к базе или Redis для проверки, не был ли токен отозван.
Виды JWT
▪️Access token — токен для доступа к API, живет недолго (~15 мин). При истечении срока сервер возвращает 401. По умолчанию — многоразовый
▪️ Refresh token — токен для обновления пары токенов, живет дольше (несколько дней). Используется на эндпоинте ~/auth/refresh, когда истекает срок access токена
Структура JWT
Токен состоит из 3 частей, разделенных точкой: xxxxx.yyyyy.zzzzz.
1. Header — заголовок, который хранит тип токена (JWT) и алгоритм подписи (например, HMAC SHA256)
2. Payload — данные о пользователе и токене. Стандартные поля:
▫️ iss — кто выдал токен (issuer)
▫️ sub — идентификатор пользователя (subject)
▫️ aud — для кого предназначен токен (audience)
▫️ exp — время, в течение которого токен считается валидным (expiration)
▫️ iat — время создания токена (issued at)
▫️ jti — уникальный идентификатор токена (JWT ID)
3. Signature — подпись, гарантия целостности
⚠️ Важно: данные в payload видны всем. JWT подписывается, а не шифруется
Как JWT защищает наши данные?
JWT нельзя подделать без секретного ключа, но содержимое токена можно прочитать. Поэтому не стоит хранить там пароли или чувствительные данные.
Black-list токенов
При выходе из аккаунта или смене пароля токены нужно отзывать. Для этого используют «черный список».
При проверке сервер сначала смотрит, не попал ли токен туда, и только потом валидирует его. Если токен найден в «черном списке» → возвращается 401.
Аутентификация с JWT
Когда говорят об аутентификации с помощью JWT, то имеют в виду, как приложения подтверждают личность пользователя. Устроено это примерно таким образом:
✔️ Пользователь вводит логин и пароль
✔️ Сервер проверяет данные и, если они верны, генерирует access и refresh токены
✔️ Клиент сохраняет токены (например, в Secure Storage)
✔️ При каждом запросе к защищенному API клиент отправляет access token в заголовке Authorization: Bearer <token>
✔️ Сервер проверяет подпись и срок действия токена
✔️ Если access token истек, клиент использует refresh token для получения новой пары токенов
Таким образом, серверу не нужно хранить сессии и пользователь остается аутентифицированным, пока токены действуют.
❤️ — и в следующем посте разберем, как реализовать авторизацию во Flutter с помощью JWT
❤12👍4🔥3😁1
Привет, это Катя, Friflex Flutter Dev👋
В прошлом посте мы разбирали распознавание лиц на Flutter, а сегодня расскажу про распознавание текста.
Распознавание текста (OCR) — это одна из самых популярных функций в мобильных приложениях:
▪️сканирование документов
▪️автоматическое заполнение форм по фото
▪️извлечение текста из визиток, чеков, квитанций
▪️перевод текста прямо с картинки
С помощью Flutter и Google ML Kit добавить OCR в приложение можно всего за несколько шагов.
Зачем нужно распознавание текста?
✔️Автоматизация — больше не нужно вручную переписывать текст
✔️Удобство — пользователь делает фото, а приложение достаёт оттуда данные
✔️Инклюзивность — для слабовидящих можно превращать картинку с текстом в озвученный контент
✔️Применимость в бизнесе — распознавание документов, счетов, ценников
Что нам понадобится
Для примера будем использовать пакеты:
▪️google_mlkit_text_recognition — для распознавания текста
▪️image_picker — для выбора фото из галереи или камеры
Добавим в pubspec.yaml:
Настройка и инициализация
1️⃣ Импортируем пакеты:
2️⃣ Создаем переменные:
3️⃣ Инициализируем в initState:
4️⃣ Закрываем в dispose:
Логика распознавания
Метод для получения и обработки изображения:
Отображение результата
Полученный текст можно вывести обычным Text:
А изображение показать через Image.file(_imageFile!), обернув все в Column:
✅Как видите, добавить распознавание текста на Flutter тоже очень просто. Эта функция подойдет для любых приложений, где пользователю важно быстро извлечь текст с фото или документа.
В прошлом посте мы разбирали распознавание лиц на Flutter, а сегодня расскажу про распознавание текста.
Распознавание текста (OCR) — это одна из самых популярных функций в мобильных приложениях:
▪️сканирование документов
▪️автоматическое заполнение форм по фото
▪️извлечение текста из визиток, чеков, квитанций
▪️перевод текста прямо с картинки
С помощью Flutter и Google ML Kit добавить OCR в приложение можно всего за несколько шагов.
Зачем нужно распознавание текста?
✔️Автоматизация — больше не нужно вручную переписывать текст
✔️Удобство — пользователь делает фото, а приложение достаёт оттуда данные
✔️Инклюзивность — для слабовидящих можно превращать картинку с текстом в озвученный контент
✔️Применимость в бизнесе — распознавание документов, счетов, ценников
Что нам понадобится
Для примера будем использовать пакеты:
▪️google_mlkit_text_recognition — для распознавания текста
▪️image_picker — для выбора фото из галереи или камеры
Добавим в pubspec.yaml:
Yaml
dependencies:
google_mlkit_text_recognition: ^0.13.1
image_picker: ^1.1.2
Настройка и инициализация
1️⃣ Импортируем пакеты:
import 'dart:io';
import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
import 'package:image_picker/image_picker.dart';
2️⃣ Создаем переменные:
late TextRecognizer _recognizer;
File? _imageFile;
String _scanResults = '';
3️⃣ Инициализируем в initState:
@override
void initState() {
super.initState();
_recognizer = TextRecognizer();
}
4️⃣ Закрываем в dispose:
@override
void dispose() {
_recognizer.close();
super.dispose();
}
Логика распознавания
Метод для получения и обработки изображения:
Future<void> _getAndScanImage({final bool? isFromCamera}) async {
// Очищаем данные
setState(() {
_imageFile = null;
_scanResults = '';
});
// Получаем изображение
final pickedImage = await ImagePicker().pickImage(
source: isFromCamera != null && isFromCamera
? ImageSource.camera
: ImageSource.gallery,
);
// Обрабатываем
if (pickedImage != null) {
final file = File(pickedImage.path);
setState(() {
_imageFile = file;
});
final results = await _recognizer.processImage(
InputImage.fromFile(file),
);
setState(() {
_scanResults = results.text;
});
}
}
Отображение результата
Полученный текст можно вывести обычным Text:
Text(_scanResults)
А изображение показать через Image.file(_imageFile!), обернув все в Column:
Column(
children: [
if (_imageFile != null) Image.file(_imageFile!),
const SizedBox(height: 16),
Text(_scanResults),
],
)
✅Как видите, добавить распознавание текста на Flutter тоже очень просто. Эта функция подойдет для любых приложений, где пользователю важно быстро извлечь текст с фото или документа.
👍9🔥8❤3
Совсем недавно команда Flutter выпустила новую версию фреймворка — 3.35. Она принесла достаточно много интересных обновлений, о которых подробнее можно ознакомиться в обзорной статье на Medium.
Сегодня рассмотрим новую экспериментальную фичу, выпущенную с версией 3.35 — Flutter Widget Previewer.
Flutter Widget Previewer позволяет отобразить текущий вид конкретного виджета без запуска всего приложения на эмуляторе или реальном устройстве. На сегодня превью доступно только для отображения в Chrome, но команда Flutter планирует в скором будущем поддерживать эту функцию и в IDE, и на веб-сервере.
Как запустить?
В первую очередь вам необходимо установить Flutter версии 3.35.0 или выше.
Для запуска требуется в терминале выполнить команду
flutter widget-preview start
Она запустит локальный сервер и откроет превью виджета в Chrome.
Как использовать?
Flutter новой версии дает доступ к аннотации
@Preview
. Эту аннотацию можно подключить к:▪️конструкторам и фабрикам публичных виджетов без обязательных аргументов
▪️ верхнеуровневым методам, возвращающим
Widget
или WidgetBuilder
▪️ статическим методам внутри класса, которые тоже возвращают Widget или WidgetBuilder
Аннотации можно передать дополнительные параметры, такие как тему, локализацию или размер. Это позволяет легко имитировать те или иные условия системы, которые в дальнейшем могут повлиять на интерфейс приложения.
После запуска превью можно вносить любые изменения в исследуемый виджет. Превью автоматически обновляется, выполнять дополнительные операции для этого не нужно.
Рассмотрим на примере
Создадим простой виджет-карточку с картинкой, заголовком и описанием. Добавим к конструктору этого виджета аннотацию и запустим превью.
class CustomCard extends StatelessWidget {
@Preview()
const CustomCard({super.key});
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
Container(
height: 80,
width: 80,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.0),
image: const DecorationImage(
image: NetworkImage(
'https://images.unsplash.com/photo-1755133314246-2103970d4726?q=80&w=988&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
),
fit: BoxFit.cover,
),
),
),
const SizedBox(width: 16),
const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Фото #2345',
style: TextStyle(fontSize: 20),
),
SizedBox(width: 16),
Text(
'Описание к фотографии #2345',
style: TextStyle(fontSize: 16),
),
],
),
],
),
),
);
}
}
Вид, который отобразится на выходе в превью, показала на картинке👆
Какие плюсы/минусы?
Лично для себя я нашла и преимущества, и недостатки.
➕ запуск превью очень быстрый, в разы быстрее сборки
➕ так как превью в вебе — легко проверять адаптивность верстки, достаточно поменять размер окна Chrome
➕ можно имитировать разные системные условия, например, тему, локализацию
➕ снижает нагрузку на компьютер разработчика, так как не надо запускать эмуляторы (актуально для слабых устройств)
➕ позволяет детальнее проверять отдельные части интерфейса
➖ фича экспериментальная, а значит еще «сырая»
➖ пока нет поддержки IDE (а было бы очень удобно)
➖ в текущей реализации не может обеспечить полноценный анализ интерфейса, так как хорошо работает только с простыми и статичными виджетами, без анимаций и зависимости от внешних состояний
📎Больше информации можно найти в официальной документации.
А какие у вас впечатления от этой фичи?
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11❤6👍5🤩1
Привет, это Роза, Flutter Dev Friflex🎙
Недавно у меня была задача — нужно было сделать сайт документации для одного проекта. Хочу поделиться удобным инструментом для этого, пока готовлю продолжение по jwt.
🦖А поговорим мы про Docusaurus! Это опенсорс-фреймворк от Meta, который позволяет быстро развернуть сайт документации.
Идея Docusaurus заключается в том, чтобы писать документацию с использованием Markdown и MDX на основе React. Такое сочетание позволяет легко создавать как обычные страницы, так и интерактивный контент — например, вставлять компоненты прямо в документацию.
Основные возможности:
✔️ Основан на React, поэтому все легко кастомизировать
✔️ Есть готовые темы и поддержка Markdown, чтобы писать проще и быстрее
✔️ Простая интеграция с GitHub Pages или любым хостингом
✔️ Много фич «из коробки»: поиск, версионирование, темная тема
✔️ Поддерживает миграцию
Как работать с Docusaurus?
Давайте быстренько пройдемся по шагам. Для начала вам нужно создать Docusaurus-проект в вашем репозитории. Для этого (после установки Node.js) запустите:
В результате у вас появится примерно такая структура:
Ключевые каталоги:
▫️blog/ — записи блога, которые можно писать на Markdown или MDX
▫️docs/ — ваши файлы документации
▫️src/ — кастомные компоненты, страницы и стили на React
▫️static/ — статические ресурсы, например картинки и шрифты
▫️docusaurus.config.js — главный файл конфигурации сайта
▫️sidebars.js — настройка боковой панели и структуры документации
Запуск и публикация
Чтобы посмотерть, что у вас получилось:
1. Установите зависимости:
2. Запустите локальный сервер разработки:
Сайт будет доступен по адресу:
Все изменения в *
На этом этапе ваш сайт уже работает — осталось лишь наполнить его контентом и опубликовать.
Далее все зависит от ваших умений.
✔️ Хотите полноценный сайт с главной страницей, кастомными компонентами и выпадающими окнами? Используйте React/JS/TS и расширяйте проект
✔️ Нужна просто документация? Достаточно оформить всё в
Для публикации ващего сайта вам нужно будет запустить команду:
В папке
Чтобы убедиться, что все работает так, как задумано, протестируйте сборку локально, выполнив следующую команду:
Это запустит локальный сервер, и вы сможете просматривать свой сайт, перейдя по указанному URL.
Развернуть сайт можно где угодно: GitHub Pages, Vercel, Netlify, Docker + собственный домен.
В итоге с Docusaurus можно буквально за пару часов поднять удобный и красивый сайт документации, который легко поддерживать и расширять🙌
Недавно у меня была задача — нужно было сделать сайт документации для одного проекта. Хочу поделиться удобным инструментом для этого, пока готовлю продолжение по jwt.
🦖А поговорим мы про Docusaurus! Это опенсорс-фреймворк от Meta, который позволяет быстро развернуть сайт документации.
Идея Docusaurus заключается в том, чтобы писать документацию с использованием Markdown и MDX на основе React. Такое сочетание позволяет легко создавать как обычные страницы, так и интерактивный контент — например, вставлять компоненты прямо в документацию.
Основные возможности:
✔️ Основан на React, поэтому все легко кастомизировать
✔️ Есть готовые темы и поддержка Markdown, чтобы писать проще и быстрее
✔️ Простая интеграция с GitHub Pages или любым хостингом
✔️ Много фич «из коробки»: поиск, версионирование, темная тема
✔️ Поддерживает миграцию
Как работать с Docusaurus?
Давайте быстренько пройдемся по шагам. Для начала вам нужно создать Docusaurus-проект в вашем репозитории. Для этого (после установки Node.js) запустите:
npx create-docusaurus@latest my-website classic
В результате у вас появится примерно такая структура:
my-website
├── blog
├── docs
├── src
├── static
├── docusaurus.config.js
├── package.json
└── sidebars.js
Ключевые каталоги:
▫️blog/ — записи блога, которые можно писать на Markdown или MDX
▫️docs/ — ваши файлы документации
▫️src/ — кастомные компоненты, страницы и стили на React
▫️static/ — статические ресурсы, например картинки и шрифты
▫️docusaurus.config.js — главный файл конфигурации сайта
▫️sidebars.js — настройка боковой панели и структуры документации
Запуск и публикация
Чтобы посмотерть, что у вас получилось:
1. Установите зависимости:
npm install
2. Запустите локальный сервер разработки:
npx docusaurus start
Сайт будет доступен по адресу:
https://localhost:3000
. Все изменения в *
.md
-файлах или компонентах будут применяться мгновенно благодаря горячей перезагрузке.На этом этапе ваш сайт уже работает — осталось лишь наполнить его контентом и опубликовать.
Далее все зависит от ваших умений.
✔️ Хотите полноценный сайт с главной страницей, кастомными компонентами и выпадающими окнами? Используйте React/JS/TS и расширяйте проект
✔️ Нужна просто документация? Достаточно оформить всё в
.md
-файлах в docs
— роутинг создаётся автоматически (можно и вручную)Для публикации ващего сайта вам нужно будет запустить команду:
npm run build
В папке
build
появятся статические файлы, которые можно развернуть на любом хостинге.Чтобы убедиться, что все работает так, как задумано, протестируйте сборку локально, выполнив следующую команду:
npm run serve
Это запустит локальный сервер, и вы сможете просматривать свой сайт, перейдя по указанному URL.
Развернуть сайт можно где угодно: GitHub Pages, Vercel, Netlify, Docker + собственный домен.
В итоге с Docusaurus можно буквально за пару часов поднять удобный и красивый сайт документации, который легко поддерживать и расширять🙌
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤4🔥3
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7🔥6👍4
На днях Google объявил о грядущих глобальных изменениях в политике безопасности Android. С 2027 года компания планирует ужесточить правила установки приложений из неизвестных источников по всему миру. Это затронет и создателей софта.
✔️Google Play становится единственным местом установки приложений. Тут непонятно, конечно, с RuStore
✔️Установка APK извне Google Play усложняется
✔️Google вводит механизм верификации разработчиков. Те, кто уже публикуется в Google Play, будут верифицированы автоматически
✔️Сайлоудинг останется доступным, дебажные сборки не будут затронуты
✔️Верификация будет бесплатной для студентов и независимых разработчиков, но регистрация аккаунта обойдется в 25$
✔️Разработчикам придется платить за публикацию приложений в Google Play
✔️Новая консоль предназначена для распространения приложений за пределами Google Play
✔️Пользователи, привязывающие свои сборки к аккаунтам разработчиков, могут нести юридическую ответственность
Еще есть время оценить свои каналы распространения и начать процесс верификации заранее.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8🔥4👍2
Сегодня начнем разбирать вопрос про Provider, который нам прислал один прекрасный подписчик. Разработчики provider описывают этот класс как некоторую обертку для InheritedWidget. Чтобы лучше его понять, сегодня вспомним, что такое InheritedWidget и как он работает.
InheritedWidget — виджет, который позволяет передавать некоторые данные дальше по дереву виджетов. При его использовании пропадает необходимость прокидывать данные через конструкторы. Для примера создадим виджет CustomInherited.
class CustomInherited extends InheritedWidget {
const CustomInherited({required this.data, required Widget child})
: super(child: child);
final String data;
static CustomInherited? of(BuildContext context) =>
context.dependOnInheritedWidgetOfExactType<CustomInherited>();
@override
bool updateShouldNotify(CustomInherited oldWidget) => data != oldWidget.data;
}
CustomInherited реализует статический метод
of(context)
. Под капотом этот метод вызывает context.dependOnInheritedWidgetOfExactType<T>()
, который в свою очередь по переданному контексту выполняет поиск самого ближайшего экземпляра виджета типа CustomInherited и возвращает его.Теперь CustomInherited готов к внедрению в дерево виджетов.
CustomInherited(
data: 'inherited data',
child: Container(
padding: const EdgeInsets.all(16),
child: Text(CustomInherited.of(context)?.data ?? ''),
),
),
Так как InheritedWidget предоставляет данные по контексту дальше, то его необходимо располагать выше виджетов, которые должны иметь доступ к его данным. Здесь, например, виджету Text необходимо передать строку из CustomInherited, поэтому инхерит располагаем выше.
По необходимости официальная документация Flutter предлагает создавать два метода —
maybeOf(context)
и of(context)
, где первый возвращает nullable экземпляр, а второй — non-nullable.
static CustomInherited? maybeOf(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<CustomInherited>();
}
static CustomInherited of(BuildContext context) {
final result = maybeOf(context);
assert(result != null, 'No CustomInherited found in context');
return result!;
}
Также InheritedWidget обязует нас переопределять метод
updateShouldNotify
. Этот метод возвращает bool значение, которое отвечает за то, должны ли зависимые виджеты пересобираться при изменении данных внутри InheritedWidget.Делитесь своим опытом работы с InheritedWidget в комментариях.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8❤🔥4🔥3
В предыдущих постах мы уже разбирали распознавание лиц и распознавание текста. Сегодня расскажу про распознавание звука.
Распознавание речи — это один из самых востребованных инструментов в мобильных приложениях:
✔️голосовой ввод текста
✔️создание заметок голосом
✔️управление приложением и голосовыми командами
✔️перевод речи с одного языка на другой
✔️доступность для пользователей с ограниченными возможностями
Что нам понадобится
Для примера будем использовать пакет для преобразования речи в текст в реальном времени — speech_to_text
Добавим в pubspec.yaml:
dependencies:
speech_to_text: ^6.6.0
Настройка и инициализация
1️⃣ Импортируем пакеты:
import 'package:speech_to_text/speech_recognition_result.dart';
import 'package:speech_to_text/speech_to_text.dart';
2️⃣ Создаем переменные:
late SpeechToText _speechToText;
String _recognizedText = '';
3️⃣ Инициализируем в initState:
@override
void initState() {
super.initState();
_speechToText = SpeechToText();
}
4️⃣ Закрываем в dispose:
@override
void dispose() {
_speechToText.cancel();
super.dispose();
}
Логика распознавания речи
Метод для начала прослушивания:
Future<void> _startListening() async {
// Очищаем данные
setState(() {
_recognizedText = '';
});
await _speechToText.listen(
pauseFor: const Duration(seconds: 2),
listenOptions: SpeechListenOptions(
listenMode: ListenMode.dictation,
),
onResult: _onSpeechResult,
);
}
Метод для остановки прослушивания:
Future<void> _stopListening() async {
await _speechToText.stop();
setState(() {});
}
Метод обратного вызова для результата:
void _onSpeechResult(SpeechRecognitionResult result) {
setState(() {
_recognizedText = result.recognizedWords;
});
}
Отображение результата
Полученный текст можно вывести обычным Text:
Text(_recognizedText)
Теперь у нас есть три мощных примера работы с ML Kit и дополнительными библиотеками в Flutter:
▪️распознавание лиц — аутентификация и AR-фичи
▪️распознавание текста — сканирование документов и перевод
▪️распознавание звука — голосовой ввод, заметки и управление приложением
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2🔥2👍1
Anonymous Poll
25%
Распознавание лиц
45%
Распознавание текста
30%
Распознавание звука
❤2🔥1