This media is not supported in your browser
VIEW IN TELEGRAM
Слышали про
covariant? Как показывает практика, многие о нем слышали, но не до конца понимают, для чего он нужен. Разберемся!C
ovariant — это ключевое слово, которое используется для параметров методов. Оно разрешает переопределяющему методу в наследнике сузить тип параметра, указать более конкретный. Разберем на примере. У нас есть класс
Vehicle. Создадим классы более конкретного транспорта — Car и Bike.
class Vehicle {}
class Car extends Vehicle {}
class Bike extends Vehicle {}
Теперь создадим класс
Human, который будет реализовывать метод drive c определенным переданным объектом Vehicle.
class Human {
void drive(Vehicle vehicle) {}
}
В таком случае, когда мы захотим создать наследника
Human — Driver, мы не сможем уточнить класс транспорта в параметрах drive. Нам придется сделать так:
class Driver extends Human {
@override
void drive(Vehicle vehicle) {} // правильный вариант
@override
void drive(Car vehicle) // вызовет ошибку переопределения
}
И здесь нам на помощь придет
covariant. Чтобы он работал, метод drive класса Human нужно видоизменить.
class Human {
void drive(covariant Vehicle vehicle) {}
}
Теперь в реализации
Driver легко можно уточнить, какой конкретный тип требуется передать при вызове метода.
class Driver extends Human {
@override
void drive(Car vehicle) {}
}
В таком случае метод
drive у Driver принимает только Car, и попытка передать Bike приведет к ошибке типов.
void main() {
final car = Car();
final bike = Bike();
final driver = Driver();
driver.drive(bike); // ошибка argument_type_not_assignable
driver.drive(car); // верное использование
}
❤️ — если было полезно
Please open Telegram to view this post
VIEW IN TELEGRAM
❤44🔥8😍1
В прошлом посте мы разобрали основы работы с библиотекой dart_amqp 0.3.1: подключение к серверу, создание каналов, работу с очередями и обменниками. Сегодня я покажу, как работать с несколькими RabbitMQ инстансами одновременно в проекте.
AmqpMessage
Класс AmqpMessage — это обертка над входящими сообщениями, которая предоставляет удобные методы для работы с данными.
consumer.listen((AmqpMessage message) {
// Сырые байты
Uint8List rawBytes = message.payload;
// UTF8 строка
String text = message.payloadAsString;
print('Текст: $text');
// JSON
Map<String, dynamic> json = message.payloadAsJson;
print('User ID: ${json['userId']}');
// Метаданные
print('Exchange: ${message.exchangeName}');
print('Routing key: ${message.routingKey}');
});Publisher Confirms
Publisher Confirms — это механизм подтверждения от брокера о том, что сообщение было принято и обработано.
Важно: ACK от брокера означает, что сообщение сохранено на сервере, но не гарантирует, что его получил потребитель
Зачем нужно несколько RabbitMQ соединений?
В микросервисной архитектуре часто возникает необходимость подключаться к разным RabbitMQ серверам:
✔️Разные окружения (dev, staging, production)
✔️Географически распределенные кластеры
✔️Разделение ответственности между сервисами
✔️Миграция между серверами
Паттерн Registry для управления соединениями
Вместо создания множества отдельных клиентов, используйте паттерн Registry — централизованное хранилище соединений с разными RabbitMQ инстансами:
class RabbitRegistry {
RabbitRegistry();
final Map<String, RabbitService> _registry = {};
/// Инициализация соединений для всех хостов
void saveAll(List<String> hosts) {
_removeAllExcept(hosts.toSet());
for (final host in hosts) {
_registry[host] ??= RabbitService(
host: host,
)..initialize();
}
}
/// Получить соединение по хосту
RabbitService read(String host, {bool canCreate = false}) {
if (canCreate) {
return _registry.putIfAbsent(
host,
() => RabbitService(
host: host,
)..initialize(),
);
}
final rabbit = _registry[host];
if (rabbit == null) {
throw StateError(
'RabbitService для "$host" не найден. '
'Инициализируйте его через saveAll().',
);
}
return rabbit;
}
/// Удалить соединение
void remove(String host) {
_registry.remove(host)?.dispose();
}
/// Закрыть все соединения
void dispose() {
for (final service in _registry.values) {
service.dispose();
}
_registry.clear();
}
}Как это работает
1️⃣ Инициализация реестра при старте приложения
При запуске приложения создаем экземпляр RabbitRegistry и передаем ему список хостов RabbitMQ-серверов из конфигурации. Реестр автоматически создаст и инициализирует соединения для каждого хоста.
2️⃣ Использование конкретного соединения
Когда нужно опубликовать сообщение на конкретный сервер, просто запрашиваем соединение по имени хоста через метод read(). Получаем готовый клиент, создаем канал, объявляем очередь и публикуем данные — все как обычно, но с правильным RabbitMQ инстансом.
3️⃣ Динамическое добавление нового хоста
Если в процессе работы приложения нужно подключиться к новому серверу, который не был инициализирован при старте, используем флаг canCreate: true при вызове read(). Реестр автоматически создаст и инициализирует новое соединение.
4️⃣ Обновление списка хостов
При изменении конфигурации (например, добавился новый сервер или убрали старый) просто вызываем saveAll() с обновленным списком. Реестр умный — он добавит новые соединения и автоматически закроет те, которых больше нет в списке.
Преимущества такого подхода
▪️Централизованное управление — все соединения в одном месте
▪️Автоматическая очистка — старые соединения закрываются при обновлении списка
▪️Ленивая инициализация — создание соединения только при необходимости
▪️Безопасность — защита от использования неинициализированных соединений
▪️Масштабируемость — легко добавлять новые хосты
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7🔥5👍2
Anonymous Poll
19%
Одно глобальное соединение на все приложение
7%
Создаем новое соединение для каждой операции
12%
Используем паттерн Registry
62%
Что такое RabbitMQ?
Сегодня поговорим про механизмы ограничения доступа объектов в Dart. Простыми словами — какие есть варианты подсветить, что объект не должен использоваться извне.
1️⃣ Начнем с банального — использования «
_»
final String _privateData;
void _doExample() {}
Символ «_» в начале названия говорит о том, что объект может использоваться только в рамках текущей библиотеки.
Кроме того, что он самый известный, он — фактически единственный действительно ограничивающий. Проект не сможет быть скомпилирован, пока приватный объект будет использоваться вне допустимого участка кода.
2️⃣
@protectedАннотация
@protected говорит о том, что объект доступен только внутри класса и в классах наследниках. Здесь очень важно понимать, что аннотация никак не ограничивает компиляцию и работу программы. Единственное, что вы получите при неправильном использовании защищенного объекта — замечание анализатора
invalid_use_of_protected_member
// файл parent.dart
class Parent {
@protected
void doProtected() {}
}
...
// файл child.dart
class Child extends Parent {
void doExample() {
doProtected(); // допустимо
}
}
void main() {
final parent = Parent();
parent.doProtected(); // вызывает предупреждение анализатора
}
3️⃣
@visibleForTesting
Еще одна ограничивающая аннотация. Она как бы говорит — «да, я публичный, но только для того, чтобы быть доступным в тестах». Будет точно полезно для тех, кто покрывает проект тестами
// файл lib/parent.dart
class Parent {
@visibleForTesting
void doExample() {}
}
...
// файл lib/main.dart
void main() {
final parent = Parent();
parent.doExample(); // вызывает предупреждение анализатора invalid_use_of_visible_for_testing_member
}
...
// файл test/parent_test.dart
void main() {
test('Тестируем @visibleForTesting', () {
final test = Parent();
test.doExample(); // допустимо
});
}
Важно понимать — хотя реальное ограничение всего одно, аннотации тоже не стоит списывать со счетов. Они подсвечивают намерение разработчика, обозначают границы ответственности и помогают поддерживать архитектурную дисциплину.
❤️ — если было полезно
Please open Telegram to view this post
VIEW IN TELEGRAM
❤14👍8🔥4🤡3🤩1
11 февраля вышел Flutter 3.41 — важный релиз с 868 коммитами от 145 контрибьюторов! Давайте разберем ключевые моменты.
Публичные окна релизов
Теперь мы знаем точные даты релизов на 2026 год:
Flutter 3.41 — февраль
Flutter 3.44 — май
Flutter 3.47 — август
Flutter 3.50 — ноябрь
Модульные библиотеки Material и Cupertino
Material и Cupertino переезжают в отдельные пакеты. Преимущества:
▫️Обновления дизайна без ожидания выхода SDK
▫️Можно обновить дизайн-систему независимо от версии Flutter
▫️Быстрая адаптация к изменениям iOS/Android
Поддержка современных стандартов
iOS:
▪️Swift Package Manager теперь стандарт (CocoaPods устаревает)
▪️Полная поддержка UIScene lifecycle
Android:
▪️Важно: пока НЕ обновляйтесь на AGP 9 — поддержка в разработке
▪️Новые плагины используют Kotlin DSL по умолчанию
Платформо-специфичные ассеты
Теперь можно указать, для каких платформ нужен ассет:
flutter:
assets:
- path: assets/web_worker.js
platforms: [web]
- path: assets/desktop_icon.png
platforms: [windows, linux, macos]
Это уменьшает размер приложения.
Новый Getting Started
Полностью переработанный гайд для новичков:
▫️Быстрая установка с hot-reload в вебе
▫️8 новых видео от команды Flutter
▫️Создание 4 приложений с нуля
Улучшения Fragment Shader
▪️Синхронная декодировка изображений — текстуры доступны в том же кадре
▪️Поддержка до 128-bit float текстур для фото-фильтров и высококачественных эффектов
Widget Previews (экспериментально)
▫️Встроенный Flutter Inspector
▫️Поддержка зависимостей с dart:ffi
iOS улучшения
▪️Bounded blur — устранение цветовых артефактов в BackdropFilter
▪️Нативная стилизация перетаскивания в CupertinoSheet
Add-to-App
Flutter view теперь может автоматически подстраиваться под размер контента — идеально для встраивания в нативные скроллы!
Навигация
▫️Navigator.popUntilWithResult — закрытие нескольких экранов с передачей результата
▫️Улучшенный StretchingOverscrollIndicator (портирован из Android 12)
Desktop
▪️Экспериментальные API для popup и tooltip окон
▪️Поддержка диалоговых окон на Linux, macOS, Windows
▪️Merged threads по умолчанию на Linux
DevTools
▫️Скомпилированы с dart2wasm для лучшей производительности
▫️Автоматические переподключения к Dart Tooling Daemon
▫️Установка: просто выполните flutter upgrade (а потом несколько дней резолвите конфликты)
Кто уже перешел? Много правок пришлось вносить?
🔗Полные release notes можно посмотреть в официальной документации
Please open Telegram to view this post
VIEW IN TELEGRAM
❤🔥8🔥5❤4
This media is not supported in your browser
VIEW IN TELEGRAM
В мобильных приложениях считается хорошей практикой оповестить пользователя о проблемах с подключением к Интернету.
И сегодня поговорим о том, как эффективно во Flutter-приложении отслеживать это подключение. Рассмотрим 3 инструмента, которые точно будут полезны.
◾️connectivity_plus
У этого плагина всего две основные функци.
1️⃣
checkConnectivity() — позволяет вернуть список активных типов подключения на устройстве. И здесь очень важно: полученные типы не гарантируют реальный доступ в Интернет. Это скорее чисто аппаратная проверка, подключен ли девайс к Wi-Fi, мобильному Интернету и прочему. Для части устройств этот метод может быть полезен для определения VPN-подключения, что очень актуально сейчас. Но учтите, что на iOS и macOS тип
vpn никогда не вернется, вместо него вернется просто other.
Future<void> main() async {
final connectivity = Connectivity();
final connectionsList = await connectivity.checkConnectivity();
print(connectionsList);
}
2️⃣ А вот вторая —
onConnectivityChanged — представляет собой поток обновлений списка активных подключений. Через него будет удобно в реальном времени следить за изменениями через подписку. ◾️internet_connection_checker
Этот плагин уже проверит реальный доступ в Интернет.
Сделать это можно, используя базовые настройки плагина, через экземпляр
InternetConnectionChecker.instance. В этом объекте уже зашиты адреса по умолчанию, куда сервис будет пытаться достучаться.
Future<void> main() async {
final hasConnection = await InternetConnectionChecker.instance.hasConnection;
print(hasConnection);
}
Здесь не забывайте про знаменитые белые списки. Если ваше приложение включено в них, то фактический доступ к серверу у него будет, а вот проверка покажет, что подключения нет.
И тут на помощь может прийти кастомный «чекер». В нем вы можете задать необходимые адреса для проверки.
Future<void> main() async {
final customChecker = InternetConnectionChecker.createInstance(
addresses: [AddressCheckOption(uri: Uri.parse('custom_resource'))],
);
final hasConnection = await customChecker.hasConnection;
print(hasConnection);
}
Кроме однократной проверки, как и в предыдущей библиотеке, можно подписаться на смену статуса через поток
onStatusChange. ◾️network_info_plus
Если вам мало простого индикатора: есть Интернет или нет, этот инструмент будет точно полезен.
Он дает возможность получить более развернутые данные о Wi-Fi подключении на устройстве. Тут можно получить и название сети, и ip-адрес, и прочее-прочее.
Future<void> main() async {
final networkInfo = NetworkInfo();
print(networkInfo.getWifiName());
print(networkInfo.getWifiIP());
print(networkInfo.getWifiIPv6());
print(networkInfo.getWifiBroadcast());
print(networkInfo.getWifiBSSID());
}
Делитесь своими лучшими практиками в комментариях
Please open Telegram to view this post
VIEW IN TELEGRAM
❤11🔥7🥰4👍3🤩2🤡2
Сегодня хочу поговорить о том, как легко сделать приложение красивее с помощью кастомных шрифтов во Flutter.
Почему это важно?
Дефолтные шрифты — это, конечно, хорошо, но если вы хотите, чтобы ваше приложение выделялось, кастомные шрифты — это must-have. Плюс — это занимает буквально 5 минут!
Как это сделать?
Шаг 1: Скачайте шрифт. Я обычно использую Google Fonts. Там много бесплатных и красивых вариантов
Шаг 2: Создайте папку fonts в корне проекта и положите туда файлы шрифта (обычно это .ttf или .otf)
Шаг 3: Откройте pubspec.yaml и добавьте:
flutter:
fonts:
- family: Montserrat
fonts:
- asset: fonts/Montserrat-Regular.ttf
- asset: fonts/Montserrat-Bold.ttf
weight: 700
Шаг 4: Используйте в коде:
Text(
'Привет, Flutter!',
style: TextStyle(
fontFamily: 'Montserrat',
fontSize: 24,
fontWeight: FontWeight.bold,
),
)
Или можете установить шрифт для всего приложения в ThemeData:
MaterialApp(
theme: ThemeData(
fontFamily: 'Montserrat',
),
home: MyHomePage(),
)
Автоподгрузка
Если не хотите возиться с загрузкой файлов, используйте пакет google_fonts:
Text(
'Привет, Flutter!',
style: GoogleFonts.montserrat(
fontSize: 24,
fontWeight: FontWeight.bold,
),
)
Шрифты загружаются автоматически из Интернета (или кешируются). Удобно для прототипирования!
Если есть вопросы по Flutter или идеи для следующих постов — пишите в комментариях
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8
Сегодня поговорим про параметр
dirty у элемента во Flutter. Что такое Element?
Согласно официальной документации Flutter, виджет представляет собой конфигурацию, которая описывает, как именно должен выглядеть интерфейс. А вот элемент представляет конкретный виджет в определенном положении в дереве и отвечает за его жизненный цикл и состояние.
А
dirty представляет собой индикатор состояния элемента. Он явно говорит — тут требуется перестроение. Как это работает?
По умолчанию
dirty всегда false. Изменение значения флага наглядно можно увидеть на примере StatefulWidget'a. Допустим, нам надо обновить его состояние, и мы вызываем метод setState(). Посмотрим, что он делает под капотом.
@protected
void setState(VoidCallback fn) {
...
_element!.markNeedsBuild();
}
После ряда проверок выполняется вызов метода
markNeedsBuild(). Внутри него выполняется проверка: помечался ли ранее элемент для ребилда. Если не был, то он помечается и помещается в список dirty элементов.
void markNeedsBuild() {
...
if (dirty) {
return;
}
_dirty = true;
owner!.scheduleBuildFor(this);
}
И здесь очень хорошо видно — на самом деле
setState() не запускает моментальное перестроение виджета. Он лишь ставит элемент в очередь. А уже в следующем кадре Flutter обработает список dirty-элементов и перестроит их точечно.Со Stateful-виджетами в целом понятно, а что с Stateless? Тут тоже ничего сложного. Хотя у виджета и нет своего состояния, но он все же может быть перестроен по ряду причин.
Например, если обновился непосредственный родитель: его метод build() выполнится заново и создаст новые конфигурации дочерних виджетов. Если конфигурация Stateless-виджета изменилась, его элемент будет обновлен и отмечен, как требующий перестроения.
Другой вариант — если виджет зависит от InheritedWidget, который обновился. В этом случае соответствующий элемент также будет помечен как dirty и перестроен.
❤️ — если было полезно
Please open Telegram to view this post
VIEW IN TELEGRAM
❤17👍4🔥3🤩1
Что такое BuildContext?
BuildContext — это специальный объект, который представляет собой ссылку на конкретное место виджета в дереве элементов. По сути, это идентификатор позиции виджета в иерархии приложения.
Каждый виджет при построении получает свой уникальный BuildContext. Именно через него виджет может взаимодействовать с другими частями дерева: получать доступ к родительским виджетам, темам, навигации и многому другому.
Как это работает на практике?
Когда мы пишем метод build(), мы всегда получаем параметр context:
@override
Widget build(BuildContext context) {
return Container(
child: Text('Привет'),
);
}
Этот context и есть BuildContext. Он связывает наш виджет с конкретным элементом в дереве. Через него Flutter понимает, где именно находится виджет и как с ним работать.
Почему BuildContext так важен?
Возьмем простой пример — доступ к теме приложения:
final theme = Theme.of(context);
Метод Theme.of(context) использует BuildContext, чтобы подняться вверх по дереву виджетов и найти ближайший ThemeData. Без context это было бы невозможно — Flutter просто не знал бы, откуда начинать поиск.
То же самое происходит с навигацией:
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => NextScreen()),
);
Navigator ищет ближайший Navigator в дереве, используя переданный BuildContext как отправную точку.
Распространенная ошибка
Часто встречается ситуация, когда разработчик пытается использовать BuildContext до того, как виджет добавили в дерево, или после того, как его удалили. Например:
@override
Widget build(BuildContext context) {
Future.delayed(Duration(seconds: 2), () {
// Опасно! Context может быть уже невалидным
showDialog(context: context, builder: (_) => AlertDialog());
});
return Container();
}
Если виджет удалят из дерева за эти две секунды, использование context приведет к ошибке. Для таких случаев стоит проверять mounted в StatefulWidget или использовать более безопасные подходы.
BuildContext и InheritedWidget
Особенно важную роль BuildContext играет при работе с InheritedWidget. Именно через context виджеты подписываются на изменения:
final data = MyInheritedWidget.of(context);
При таком вызове Flutter регистрирует зависимость текущего виджета от MyInheritedWidget через BuildContext. Когда InheritedWidget обновится, все зависимые от него виджеты будут автоматически перестроены.
❤️ — если было полезно
Please open Telegram to view this post
VIEW IN TELEGRAM
❤18
Привет, это Катя, Flutter Dev Friflex! Сегодня разберем, зачем нужны Keys во Flutter, как они работают и когда их стоит (и не стоит) использовать.
Что такое Key?
Key — идентификатор виджета, который помогает Flutter сопоставлять новые виджеты с уже существующими элементами при обновлении дерева.
Как Flutter сопоставляет виджеты без ключей
Когда Flutter получает новый список виджетов при ребилде, он пытается сопоставить их с существующими элементами по позиции и типу (runtimeType). Если порядок элементов изменился или виджеты одного типа поменялись местами, состояние может перескочить на другой элемент — потому что Flutter повторно использовал элемент по индексу.
Пример проблемы без ключей
При перестановке элементов состояния могут перепутаться
Типы
▫️ValueKey<T> — ключ по значению (идеален для id-модели).
▫️ObjectKey — сравнение по == объекта.
▫️UniqueKey — каждый раз новый ключ (заставляет создать новый Element; сбрасывает состояние).
▫️GlobalKey — глобальная уникальность + доступ к State/Context; использовать экономно (дорогой).
Когда использовать
▫️Динамические списки с добавлением/удалением/реордером — ValueKey(id).
▫️Формы, доступ к State извне — GlobalKey (только при необходимости).
Когда не нужен
В статичных списках и простых элементах без внутреннего состояния. Не ставьте ключи на всякий — если они лишние, то усложняют и могут ухудшать производительность.
Рекомендации
▫️Для сущностей с постоянным id — ValueKey(id).
▫️Если хотите сбросить состояние — UniqueKey.
▫️GlobalKey — только для специфических задач (формы, навигация, тесты).
▫️Если состояние перескакивает — сначала добавьте ключи, а не перестраивайте архитектуру.
И помните, правильный выбор ключа решает распространенные баги с состоянием🦋
Что такое Key?
Key — идентификатор виджета, который помогает Flutter сопоставлять новые виджеты с уже существующими элементами при обновлении дерева.
Как Flutter сопоставляет виджеты без ключей
Когда Flutter получает новый список виджетов при ребилде, он пытается сопоставить их с существующими элементами по позиции и типу (runtimeType). Если порядок элементов изменился или виджеты одного типа поменялись местами, состояние может перескочить на другой элемент — потому что Flutter повторно использовал элемент по индексу.
Пример проблемы без ключей
import 'package:flutter/material.dart';
class ItemWidget extends StatefulWidget {
final String title;
ItemWidget(this.title);
@override
_ItemWidgetState createState() => _ItemWidgetState();
}
class _ItemWidgetState extends State<ItemWidget> {
int counter = 0;
@override
Widget build(BuildContext context) {
return ListTile(
title: Text('${widget.title} ($counter)'),
trailing: IconButton(
icon: Icon(Icons.add),
onPressed: () => setState(() => counter++),
),
);
}
}
При перестановке элементов состояния могут перепутаться
Типы
▫️ValueKey<T> — ключ по значению (идеален для id-модели).
▫️ObjectKey — сравнение по == объекта.
▫️UniqueKey — каждый раз новый ключ (заставляет создать новый Element; сбрасывает состояние).
▫️GlobalKey — глобальная уникальность + доступ к State/Context; использовать экономно (дорогой).
Когда использовать
▫️Динамические списки с добавлением/удалением/реордером — ValueKey(id).
▫️Формы, доступ к State извне — GlobalKey (только при необходимости).
Когда не нужен
В статичных списках и простых элементах без внутреннего состояния. Не ставьте ключи на всякий — если они лишние, то усложняют и могут ухудшать производительность.
Рекомендации
▫️Для сущностей с постоянным id — ValueKey(id).
▫️Если хотите сбросить состояние — UniqueKey.
▫️GlobalKey — только для специфических задач (формы, навигация, тесты).
▫️Если состояние перескакивает — сначала добавьте ключи, а не перестраивайте архитектуру.
И помните, правильный выбор ключа решает распространенные баги с состоянием
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3🔥1
Привет! Это Анна, лидер Flutter-команды Friflex.
Сегодня познакомлю вас с пятью библиотеками, которые помогут вам в создании красивого пользовательского интерфейса во Flutter-приложении без боли и огромных полотен кода.
flutter_staggered_animations
Эта библиотека позволит вам красиво анимировать отрисовку списков и сеток. Работает с виджетами ListView, GridView, Column и Row. Вид анимации и ее длительность можно задавать самостоятельно, есть возможность комбинировать эффекты между собой. В результате получаем интересную каскадную отрисовку каждого элемента последовательно.
liquid_glass_renderer
Эта библиотека понравится тем, кому пришлась по душе новинка iOS 26 — эффект жидкого стекла. Проект довольно новый, но уже имеет несколько вариантов виджетов. Тут можно создать и единичные стеклянные формы, и формы из нескольких смешанных объектов.
Очень важно — в боевую версию внедрять его пока не стоит, об этом предупреждает и сам автор. Но потрогать на досуге и в личных проектах будет точно интересно.
И тут можно сразу вспомнить более стабильный аналог glass_kit. Он по функциональности, возможно, чуть проще, но все так же достоин внимания.
percent_indicator
Очень простая, но полезная библиотека. Если вам нужно создать какой-то составной индикатор прогресса, она точно придется кстати.
С ее помощью вы можете создать как круговой индикатор, так и линейный. Оба дают возможность самостоятельно рассчитывать прогресс в том соотношении, которое вы зададите.
Индикаторы поддерживают анимации, позволяют добавлять дополнительные виджеты с информацией. А что, на мой взгляд, самое полезное — шкалу прогресса можно окрасить в градиент из нескольких цветов.
skeletonizer
Без скелетонов в современных мобильных приложениях уже никуда, поэтому skeletonizer точно пригодится.
Использование очень простое — вам нужно обернуть виджет, который требует скелетона, в виджет из библиотеки и передать состояние включить/выключить. А вот дальше вы можете модифицировать вид скелетона так, как душе угодно. Рекомендую заглянуть в документацию, там много интересных примеров.
confetti
Хочется добавить праздника в проект? Пожалуйста, конфетти будут в самый раз!
С помощью этой библиотеки вам не составит никакого труда добавить в проект такой сложный эффект в самые короткие сроки. Что самое приятное, внутри много параметров для кастомизации.
❤️ — если присматриваетесь к одной из этих библиотек
Сегодня познакомлю вас с пятью библиотеками, которые помогут вам в создании красивого пользовательского интерфейса во Flutter-приложении без боли и огромных полотен кода.
flutter_staggered_animations
Эта библиотека позволит вам красиво анимировать отрисовку списков и сеток. Работает с виджетами ListView, GridView, Column и Row. Вид анимации и ее длительность можно задавать самостоятельно, есть возможность комбинировать эффекты между собой. В результате получаем интересную каскадную отрисовку каждого элемента последовательно.
liquid_glass_renderer
Эта библиотека понравится тем, кому пришлась по душе новинка iOS 26 — эффект жидкого стекла. Проект довольно новый, но уже имеет несколько вариантов виджетов. Тут можно создать и единичные стеклянные формы, и формы из нескольких смешанных объектов.
Очень важно — в боевую версию внедрять его пока не стоит, об этом предупреждает и сам автор. Но потрогать на досуге и в личных проектах будет точно интересно.
И тут можно сразу вспомнить более стабильный аналог glass_kit. Он по функциональности, возможно, чуть проще, но все так же достоин внимания.
percent_indicator
Очень простая, но полезная библиотека. Если вам нужно создать какой-то составной индикатор прогресса, она точно придется кстати.
С ее помощью вы можете создать как круговой индикатор, так и линейный. Оба дают возможность самостоятельно рассчитывать прогресс в том соотношении, которое вы зададите.
Индикаторы поддерживают анимации, позволяют добавлять дополнительные виджеты с информацией. А что, на мой взгляд, самое полезное — шкалу прогресса можно окрасить в градиент из нескольких цветов.
skeletonizer
Без скелетонов в современных мобильных приложениях уже никуда, поэтому skeletonizer точно пригодится.
Использование очень простое — вам нужно обернуть виджет, который требует скелетона, в виджет из библиотеки и передать состояние включить/выключить. А вот дальше вы можете модифицировать вид скелетона так, как душе угодно. Рекомендую заглянуть в документацию, там много интересных примеров.
confetti
Хочется добавить праздника в проект? Пожалуйста, конфетти будут в самый раз!
С помощью этой библиотеки вам не составит никакого труда добавить в проект такой сложный эффект в самые короткие сроки. Что самое приятное, внутри много параметров для кастомизации.
❤️ — если присматриваетесь к одной из этих библиотек
❤21🔥1🤩1🤣1
Привет, это Катя, Flutter-разработчик Friflex! Сегодня коротко: про всплеск агентов и утилит вокруг них, модели, навыки, MCP и прочие инструменты.
Многие компании уже ищут ИИ-программистов или инженеров продукта, которые умеют не только писать код, но и проектировать и интегрировать агентов в продукт.
Ключевые игроки на рынке ИИ: Anthropic, OpenAI, Google (Vertex AI / PaLM), Microsoft, Meta, Hugging Face, Cohere, Mistral, Stability AI.
Понимаю, многие сейчас напряглись из‑за давления ИИ: страх потерять роль, ускоренные требования к навыкам, неопределенность задач. Именно поэтому важно не пугаться, а погружаться: изучать архитектуры агентов, безопасность, координацию (оркестрацию), CI/CD и продуктовый инжиниринг вокруг них.
Интересна ли вам эта тема? Если да, то дайте знать, какие моменты разобрать подробнее: выбор модели, архитектуру агентов, безопасность и приватность, управление навыками, CI/CD для агентов, кейсы инженера продукта или что-то еще? Пишите темы, а я соберу инфу
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍2
Используете ли вы агентов в работе?
Anonymous Poll
30%
Сам(а) пишу весь код и логику
29%
Копирую и вставляю код из браузера, вручную интегрирую
36%
Использую готовые агенты, не вникая глубоко
20%
Полностью настроил(а) — агенты делают большую часть работы
Какие инструменты и компоненты используете?
Anonymous Poll
48%
SDK провайдеров (OpenAI, Anthropic и другие)
31%
Skills
24%
MCP или платформы координации (оркестрации)
6%
Инструменты для CI/CD и мониторинга агентов
8%
Использую все перечисленное
38%
Ничего не использую, пока только ручной подход
Насколько вам комфортно доверять агентам критичные функции?
Anonymous Poll
24%
Совсем не доверяю
47%
Частично (только вспомогательные задачи)
21%
Доверяю в тестах и не критичных местах
21%
Полностью доверяю и уже использую в разработке
Привет! Это Анна, лидер Flutter-команды Friflex.
Если перед вами стоит задача добавить во Flutter-приложение аутентификацию по биометрии, с этим может помочь плагин local_auth. Сегодня разберем его функционал и принцип работы.
Библиотека помогает провести локальную аутентификацию с помощью настроек, которые есть на устройстве. Например, пин-кода, сканирования отпечатка пальца или идентификации по лицу (FaceID).
Все работает очень просто: плагин не проверяет код или лицо и отпечаток пальца самостоятельно. Он обращается в систему, запрашивает проверку, и возвращает в приложение простое булево значение. Разберем методы плагина подробнее⬇️
Для начала нужно создать экземпляр класса
Метод
Еще можно получить список всех доступных на устройстве способов аутентификации по биометрии. Если на устройстве не настроен вход по отпечатку пальца или по лицу, то список придет пустым.
А здесь внимательно:
Дальше самое интересное — проверка. Она выполняется с помощью метода
Когда будете интегрировать этот плагин в проект, уделите особое внимание обработке разных сценариев. Потому что смартфонов на рынке много, и важно, чтобы у каждого пользователя был доступ.
С вас ❤️, если было полезно
Если перед вами стоит задача добавить во Flutter-приложение аутентификацию по биометрии, с этим может помочь плагин local_auth. Сегодня разберем его функционал и принцип работы.
Библиотека помогает провести локальную аутентификацию с помощью настроек, которые есть на устройстве. Например, пин-кода, сканирования отпечатка пальца или идентификации по лицу (FaceID).
Все работает очень просто: плагин не проверяет код или лицо и отпечаток пальца самостоятельно. Он обращается в систему, запрашивает проверку, и возвращает в приложение простое булево значение. Разберем методы плагина подробнее
Для начала нужно создать экземпляр класса
LocalAuthentication. Через него будут выполняться все операции.
final localAuth = LocalAuthentication();
Метод
isDeviceSupported() проверит наличие любого способа аутентификации на устройстве. А canCheckBiometrics() ответит, доступна ли аутентификация именно по биометрии.
final isDeviceSupported = await localAuth.isDeviceSupported();
final canAuthenticate = await localAuth.canCheckBiometrics;
Еще можно получить список всех доступных на устройстве способов аутентификации по биометрии. Если на устройстве не настроен вход по отпечатку пальца или по лицу, то список придет пустым.
final list = await localAuth.getAvailableBiometrics();
А здесь внимательно:
getAvailableBiometrics вернет список только тех биометрических функций, которые настроены пользователем на устройстве. А флаг canAuthenticate просто покажет их наличие. Дальше самое интересное — проверка. Она выполняется с помощью метода
authenticate(). В него можно добавить строку с описанием причины запроса аутентификации. Этот текст пользователь увидит на экране. Также можно задавать ограничения. Например, установить true флаг biometricOnly. Тогда ввод системного пин-кода не будет запрашиваться.
final successAuth = await localAuth.authenticate(
localizedReason: 'ВОЙДИТЕ',
biometricOnly: true,
);
Когда будете интегрировать этот плагин в проект, уделите особое внимание обработке разных сценариев. Потому что смартфонов на рынке много, и важно, чтобы у каждого пользователя был доступ.
С вас ❤️, если было полезно
Please open Telegram to view this post
VIEW IN TELEGRAM
❤16🔥8🤩2
This media is not supported in your browser
VIEW IN TELEGRAM
Знаете, как во Flutter-приложении получать информацию от аппаратных датчиков движения? Сейчас узнаете!
В этой задаче вам поможет плагин sensors_plus. Как описывают его разработчики — он дает возможность вашему Flutter-приложению обращаться к сенсорам устройства, таким как:
▪️ акселерометр
▪️ гироскоп
▪️ барометр
▪️ магнитометр
Как это работает?
Через плагин приложение обращается в платформу. Натив считывает данные с сенсоров и полученные данные передает во Flutter посредством Streams (потоков). В приложении же вам достаточно подписаться на необходимый поток с данными.
Библиотека дает возможность отслеживать данные по пяти основным событиям:
✔️
AccelerometerEvent — ускорение устройства. Это событие не фильтрует гравитацию, поэтому в состоянии покоя покажет всегда 9.8 м/с² вверх✔️
UserAccelerometerEvent — в отличие от AccelerometerEvent отражает только фактическое ускорение устройства. Поток исключает гравитацию, что в состоянии покоя покажет 0✔️
GyroscopeEvent — вращение устройства✔️
MagnetometerEvent — данные окружающего магнитного поля. ✔️
BarometerEvent — текущее атмосферное давлениеИспользовать очень просто. Достаточно подписаться на поток данных по необходимому событию.
late StreamSubscription<AccelerometerEvent> _accelerometerSubscription;
...
_accelerometerSubscription = accelerometerEventStream().listen((event) {
print('x: ${event.x}, y: ${event.y}, z: ${event.z}');
});
Не забывайте закрывать подписки
_accelerometerSubscription.cancel();
Для чего может быть полезно?
Представим, вам нужно сделать реализацию как в банках — при перевороте экрана требуется скрывать или открывать данные на экране. Или при тряске устройства небходимо выполнять перезапрос данных. Для всех этих задач sensors_plus точно будет полезен.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5🔥3🤩1
Всегда с нетерпением ждем этого дня, чтобы сделать подборку ИТ-мемов
Пусть поводов для улыбки будет больше💛
Пусть поводов для улыбки будет больше
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7🔥6😁4👍2