Flutter Pulse
1.02K subscribers
639 photos
1 file
1.79K links
На канале будут новости про flutter с сайтов, информация об обновлении пакетов, а также авторский контент.
Download Telegram
Принцип подстановки Барбары Лисков: основа правильного полиморфизма в программировании 🌟
Привет, друзья! Сегодня мы поговорим о третьей букве в акрониме SOLID - L, которая означает принцип подстановки Лисков 🤔. Этот принцип гласит, что объекты подклассов должны быть взаимозаменяемы с объектами их базового класса без нарушения корректности работы программы 💻.

Почему это важно? 🤔 Нарушение LSP приводит к непредсказуемому поведению программы, код начинает проверять типы объектов с помощью if/else или is, что противоречит принципу открытости/закрытости и делает систему хрупкой 🌪. Соблюдение LSP гарантирует, что полиморфизм работает правильно и подклассы действительно являются специализацией базового класса 🔩.

Давайте рассмотрим пример нарушения LSP 🚫:

class Bird {
void fly() {
print("Flying");
}
}

class Penguin extends Bird {
@override
void fly() {
throw Exception("Cannot fly"); // Нарушение LSP
}
}

Здесь функция, принимающая Bird, ожидает, что любой потомок сможет летать. Но при подстановке пингвина код валится с ошибкой, значит, иерархия нарушает принцип Лисков 🚫.

А теперь пример правильного использования LSP 🌟:

abstract class Bird {
void move();
}

class Sparrow extends Bird {
@override
void move() {
print("Flying");
}
}

class Penguin extends Bird {
@override
void move() {
print("Swimming");
}
}

Ключевые правила LSP 📝:
✔️ Предусловия не могут быть усилены в подклассе — подкласс не должен требовать больше, чем базовый класс
✔️ Постусловия не могут быть ослаблены в подклассе — подкласс должен гарантировать как минимум то, что гарантирует базовый класс
✔️ Инварианты должны сохраняться — свойства, которые истинны для базового класса, должны оставаться истинными для подклассов
✔️ Исключения — подкласс не должен выбрасывать новые типы исключений, которые не ожидаются от базового класса

Полную новость читайте здесь.

FlutterPulse — канал о мире Flutter!

#flutter #dart #FlutterPulse #FlutterPulseNews #flutterfriendly
🔥2
🪙 Жизненный цикл Flutter-приложения: как отслеживать изменения состояний 📱
Каждое мобильное приложение проходит через ряд состояний, определяемых перечислением AppLifecycleState. Чтобы корректно реагировать на эти события, нам нужно понимать, какие состояния существуют и как на них реагировать.
Существует пять основных состояний:
- resumed: приложение находится на переднем плане и готово к взаимодействию с пользователем 📈
- inactive: приложение временно неактивно, например, при поступлении звонка 📞
- paused: приложение уходит в фон и не реагирует на действия пользователя 📊
- hidden: приложение скрыто от пользователя, но процесс остается в памяти и готов к быстрому возобновлению 🔒
- detached: приложение больше не активно и готовится к завершению 🔴

Чтобы отслеживать изменения этих состояний, мы используем WidgetsBindingObserver и его метод didChangeAppLifecycleState, который вызывается каждый раз, когда система переводит приложение между состояниями.

Пример работы с didChangeAppLifecycleState:

@override
void didChangeAppLifecycleState(AppLifecycleState state) {
  super.didChangeAppLifecycleState(state);
  setState(() {
    if (state == AppLifecycleState.resumed) {
      appState = 'Возобновлено';
    } else if (state == AppLifecycleState.inactive) {
      appState = 'Неактивно';
    } else if (state == AppLifecycleState.paused) {
      appState = 'Приостановлено';
    } else if (state == AppLifecycleState.detached) {
      appState = 'Отключено';
    } else if (state == AppLifecycleState.hidden) {
      appState = 'Скрыто';
    }
  });
}


Полную новость читайте здесь.

FlutterPulse — канал о мире Flutter!

#flutter #dart #FlutterPulse #FlutterPulseNews #flutterfriendly #mobiledevelopment #appdevelopment
👍3🔥1
💎 Выбор аккаунта разработчика: индивидуальный или корпоративный?
Привет, друзья! 💬 Сегодня мы поговорим о важном этапе перед публикацией приложения в Google Play и AppStore - создании аккаунта разработчика. 📈 Этот аккаунт может быть двух видов: индивидуальный и корпоративный. 🤔

Индивидуальный аккаунт - это аккаунт физического лица, который регистрируется на конкретного человека. 📝 Преимущества такого аккаунта включают быстрый процесс регистрации, минимальное количество документов и проверок, отсутствие необходимости наличия официально зарегистрированного юридического лица, меньше бюрократии и более низкие затраты на содержание. 💸 Однако, есть и недостатки: ограничения в доступах, даже для администраторов, указание имени владельца аккаунта в карточке приложения, что может повлиять на репутацию продукта, доход поступает как доход физического лица и требуются особые условия для публикации. 📊

Корпоративный аккаунт регистрируется на официальное юридическое лицо. 📈 Преимущества включают более высокое доверие клиентов, поскольку они видят перед собой целую компанию, а не одного человека, огромный выбор ролевых моделей в управлении проектом, возможность делегирования ключевых прав и удобное ведение бизнеса. 📈 Однако, есть и недостатки: сложный процесс регистрации, требующий много документов и времени, более строгие требования к соответствию внутренним правилам и возможные ограничения для разработчиков из определенных стран. 🚫

Итак, какой аккаунт выбрать? 🤔 Если вы работаете над pet-проектом, стартапом или экспериментальным продуктом, индивидуальный аккаунт может быть оптимальным вариантом. 🌟 Однако, если ваше приложение является частью полноценного бизнеса или у вас большая команда разработчиков, лучше выбрать аккаунт юридического лица. 📈

Полную новость читайте здесь.

FlutterPulse — канал о мире Flutter!

#flutter #dart #FlutterPulse #FlutterPulseNews #flutterfriendly #mobiledevelopment #appdevelopment
💭 Реализация перетаскивания файлов в Flutter с помощью пакета desktop_drop 📁💻

Привет, друзья! Сегодня я хочу рассказать вам о пакете desktop_drop, который позволяет реализовать перетаскивание файлов в ваших десктоп- или веб-приложениях на Flutter. 🌟

Пакет предоставляет виджет DropTarget, который определяет область, на которую можно перетаскивать файлы. Этот виджет невидим, но он позволяет вам определять область для перетаскивания вокруг своего child. 📈

Основные свойства DropTarget включают:

✔️ onDragEntered — вызывается, когда файл входит в область виджета
✔️ onDragExited — вызывается, когда файл покидает область виджета
✔️ onDragDone — вызывается после того, как файл был отпущен внутри области
✔️ onDragUpdated — вызывается при движении файла внутри области
✔️ child — виджет, который отображается внутри области DropTarget

Пример использования DropTarget:

DropTarget(
onDragDone: (detail) {
for (final file in detail.files) {
print(file.path);
}
},
onDragEntered: (detail) => print('Файл в области'),
onDragExited: (detail) => print('Файл вне области'),
child: // some child
)


Также можно визуально показывать, что область готова принять файл, используя состояния, отслеживаемые через onDragEntered и onDragExited.

При работе с desktop_drop есть несколько нюансов, о которых стоит помнить:

▪️ Разные платформы могут по-разному обрабатывать события перетаскивания
▪️ С помощью DropDoneDetails можно получать пути к файлам и обрабатывать их без необходимости загружать весь файл в память
▪️ Для безопасной работы всегда проверяйте тип, размер и количество файлов

Для полноценного функционала работы с файлами desktop_drop можно комбинировать с file_picker и другими пакетами.

❤️ — если было полезно

Полную новость читайте здесь.

FlutterPulse — канал о мире Flutter!

#flutter #dart #FlutterPulse #FlutterPulseNews #flutterfriendly
👍3
Принцип инверсии зависимостей в Flutter: как сделать код более гибким и поддерживаемым 🌟
Привет, друзья! Сегодня мы поговорим о последнем принципе SOLID — принципе инверсии зависимостей (Dependency Inversion Principle) 🤔. Этот принцип помогает нам сделать код более гибким, поддерживаемым и легким для тестирования 🌈.
Итак, что же такое принцип инверсии зависимостей? 🤔 Он говорит о том, что высокоуровневые модули не должны зависеть от низкоуровневых, а оба должны зависеть от абстракций 📚. Абстракции, в свою очередь, не должны зависеть от деталей, а детали должны зависеть от абстракций 🔄.
По-простому, это означает, что «верх» приложения (экран, бизнес-логика) не должен быть привязан к конкретным реализациям «низа» (HTTP-клиент, база данных, SharedPreferences и т.д.) 🚫. Вместо этого, он должен зависеть только от интерфейсов 📝.
Но почему это так важно? 🤔 Когда высокоуровневый код напрямую знает о конкретных классах нижнего уровня, это приводит к проблемам 🚨:
- Любое изменение реализации «внизу» требует правок в бизнес-логике 📝
- Код сложно тестировать 🤯
- Система становится хрупкой 🌪️
- Нарушаются другие принципы SOLID 🚫
Принцип инверсии зависимостей как раз про то, чтобы «перевернуть» направление зависимости 🔄. Не высокоуровневый модуль зависит от деталей, а детали зависят от контракта, который описывает высокоуровневый модуль 📜.
Давайте рассмотрим пример с авторизацией 🔒. Мы можем создать абстракцию IAuthRepository и две реализации: NetworkAuthRepository и FakeAuthRepository 📈.
Тогда наш LoginViewModel будет зависеть только от интерфейса IAuthRepository 📝, и мы сможем легко подменить реализацию в тестах или при изменении механизма авторизации 🔩.
Итак, если вы видите в коде new внутри бизнес-логики или ViewModel, создающий конкретные репозитории, сервисы и клиенты, — это хороший сигнал задуматься: не пора ли ввести интерфейс и развернуть зависимость? 🤔
Полную новость читайте здесь.

FlutterPulse — канал о мире Flutter!

#flutter #dart #FlutterPulse #FlutterPulseNews #flutterfriendly #SOLID #DependencyInversionPrinciple #cleanCode #programmingPrinciples
👍2
Приготовьтесь к проверке в AppStore! 📈
Сегодня мы поговорим о том, как увеличить вероятность успеха при отправке приложения на проверку в AppStore 📊.
Первая проверка нового приложения может занять около 7 дней, но повторные проверки проходят быстрее - обычно в течение 1-2 суток 🕒.
При подготовке приложения к проверке обратите внимание на следующие моменты:
- Совместимость: приложение должно быть совместимо с последними версиями iOS 📱.
- Стабильность: приложение не должно вылетать 🚫.
- Отсутствие вывода отладочной информации: перед публикацией стоит почистить все вызовы методов вывода отладочной информации 📝.
- Разрешения: приложение не должно запрашивать неиспользуемые разрешения, и каждое разрешение должно иметь четкое обоснование 📝.
- Нативные интеграции: обязательно обратите особое внимание на соблюдение всех этапов подключения и досконально проверьте работоспособность 📈.
- Интерактивность: при проверке всегда обращается внимание на реагирование приложения на действия пользователя 📊.
- Описание и оформление: особое внимание стоит уделить заполнению карточки приложения, описание должно быть достоверным и не должно содержать оценивающих суждений 📄.
- Сбор информации: перед публикацией вам требуется заполнить несколько форм с описанием того, какие данные собирает/передает ваше приложение 📊.
- Тестовые данные и контактная информация: если ваше приложение содержит какое-то ограничение в доступе, вам обязательно необходимо приложить инструкцию, контактные данные и тестовый аккаунт 📝.

Полную новость читайте здесь.

FlutterPulse — канал о мире Flutter!

#flutter #dart #FlutterPulse #FlutterPulseNews #flutterfriendly 📈💻📊
👍1
Создаем стеклянные интерфейсы в Flutter с помощью BackdropFilter! 🐈

Вы помните, как обновление дизайна от Apple с активным использованием glass effect вызвало у многих споры? Кому-то понравилось, а кто-то был просто в ужасе. Чуть позже к этому тренду подтянулись и другие продукты, и в итоге мы внезапно оказались в мире стеклянных интерфейсов.

А теперь давайте представим, что мы ТОЖЕ ХОТИМ ТАК ЖЕ! Как же быть? Есть 2 решения: воспользоваться готовыми библиотеками или реализовать все самостоятельно.

Для самостоятельной реализации нам понадобится виджет BackdropFilter. Он работает не с самим виджетом, а с тем, что находится под ним. BackdropFilter берет уже отрисованный фон и применяет к нему фильтр, в нашем случае — размытие.

Простейшая реализация может выглядеть так:

class GlassContainer extends StatelessWidget {
  final double width;
  final double height;
  final Widget child;

  const GlassContainer({
    Key? key,
    required this.width,
    required this.height,
    required this.child,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ClipRRect(
      borderRadius: BorderRadius.circular(25),
      child: BackdropFilter(
        filter: ImageFilter.blur(sigmaX: 18, sigmaY: 18),
        child: Container(
          width: width,
          height: height,
          decoration: BoxDecoration(
            color: Colors.white.withOpacity(0.15),
            borderRadius: BorderRadius.circular(25),
            border: Border.all(
              color: Colors.white.withOpacity(0.2),
              width: 1.2,
            ),
          ),
          child: child,
        ),
      ),
    );
  }
}


Если хочется воспользоваться готовыми решениями, можно присмотреться, например, к пакету glass_kit — внутри он более глубоко работает с BackdropFilter и помогает добиться аккуратного эффекта размытия.

Полную новость читайте здесь.


FlutterPulse — канал о мире Flutter!

#flutter #dart #FlutterPulse #FlutterPulseNews #flutterfriendly
3
Подойдут и для Android, и для iOS 🎄
Представьте, что вы можете настроить свой телефон новогодней заставкой, созданной с помощью технологии Flutter! 🎅️
Теперь это стало возможным! Для всех любителей вайба разработки на Flutter созданы специальные новогодние заставки, которые можно установить как на Android, так и на iOS.
Это отличный способ добавить немного праздничного настроения на ваш экран и поделиться им с друзьями или коллегами в рабочем чате.
Полную новость читайте здесь.

FlutterPulse — канал о мире Flutter!

#flutter #dart #FlutterPulse #FlutterPulseNews #flutterfriendly 🎄⬇️
Революция в создании интерфейсов: GenUI SDK для Flutter! 🚀
Привет, друзья! Сегодня я хочу рассказать вам о невероятном инструменте, который изменит способ создания динамических пользовательских интерфейсов в ваших приложениях Flutter - GenUI SDK! 🤩

Этот инструмент использует генеративный ИИ для создания адаптивных визуальных компонентов, таких как выпадающие списки, слайдеры, карусели товаров и формы с выбором даты. И все это меняется в реальном времени, в зависимости от намерений пользователя! 🔮

Но как это работает? 🤔 Процесс устроен как интерактивный цикл. Пользователь вводит запрос, а приложение отправляет его ИИ-агенту вместе с описанием доступных виджетов. Дальше ИИ-агент генерирует не только текст, но и описание интерфейса с помощью инструментов GenUI SDK. Обычно это структура в формате JSON, которую приложение десериализует и превращает в соответствующие Flutter-виджеты. 📈

GenUI SDK может отрисовывать стандартные компоненты постепенно, по мере генерации ответа большой языковой моделью (LLM). Так интерфейс появляется быстрее, и пользователю не нужно ждать, пока придет ответ целиком! 🕒

Что еще может GenUI SDK? 🤔
🔴 Генерировать UI из структурированных данных
🔴 Работать с настраиваемым каталогом виджетов
🔴 Обрабатывать события и поддерживать интерактивный диалог

Каталог виджетов задает словарь Flutter-компонентов, которые ИИ может использовать. Каждый CatalogItem содержит имя виджета, JSON-схему его свойств и builder-функцию для рендеринга. 📚

Реактивная система автоматически перестраивает виджеты при изменении данных в клиентской модели. А обработка событий позволяет фиксировать действия пользователя (клики, ввод текста) и отправлять обновленное состояние обратно ИИ для следующего шага. 🔄

Практическое применение GenUI SDK? 🤔 Подходит для AI-нативных приложений, где вместо текстового списка продуктов можно показать кликабельную карусель, а при планировании поездки — сгенерировать форму с полями ввода и слайдерами. 🗺

Полную новость читайте здесь и в блоге.

FlutterPulse — канал о мире Flutter!

#flutter #dart #FlutterPulse #FlutterPulseNews #flutterfriendly #AI #GenUI #SDK
👎1
💠 Версионирование Flutter-приложений: простая, но важная тема! 🤔

Привет, друзья! Сегодня мы поговорим про версионирование Flutter-приложений 📱. Это тема простая, но часто вызывает вопросы у начинающих разработчиков 🤔.

Во Flutter-приложениях версия указывается в pubspec.yaml с ключом version. Например:

version: 1.2.3+4

Для удобства восприятия можно использовать буквенное представление:

version: A.B.C+D

Сочетание A.B.C отвечает за версию приложения, а значение D — за версию сборки 📈.

Когда повышать каждый уровень версии приложения? 🤔

* A — мажорные изменения 🚀. Повышать этот уровень нужно, если ваше приложение сильно менялось, например, если вы полностью изменили дизайн, переработали пользовательские пути или радикально изменили ключевые функции 🔄.
* B — минорные изменения 📈. Сюда обычно относятся новые функционал и масштабирование старого, новые пользовательские пути без критичного изменения старых 📊.
* C — мелкие фиксы и доработки 🛠️. Стоит повысить уровень C, когда вы вносите правки по багам или верстке, повышаете производительность приложения или выполняете рефакторинг без влияния на ранее реализованные функции 📈.

При повышении версии важно помнить: оно выполняется по уровню наиболее значимых изменений 📊.

А что с версией сборки? 🤔

* D — версия конкретного билда 📈. Стандартно это значение инкрементируется в каждый новый билд вашего приложения, независимо от изменений внутри 📊.

Полную новость читайте здесь.

FlutterPulse — канал о мире Flutter!

#flutter #dart #FlutterPulse #FlutterPulseNews #flutterfriendly 📱💻🔥
🔥3
🎅 Важные нюансы in-app покупок в Flutter-приложениях через App Store 📱💸

Привет, друзья! Сегодня мы поговорим о процессе покупки и получения информации о них в Flutter-приложениях через App Store 📈. Это важно знать, если вы создаете приложение с контентом, доступным только пользователям с подпиской 📊.

Мы рассмотрим три участника процесса: приложение, бэкенд и App Store 🤝. Итак, начнем!

1️⃣ Приложение: пользователь знакомится с доступными продуктами для покупки и осуществляет in-app покупку через определенные элементы интерфейса 🛍. Для этого необходимо интегрировать in_app_purchase в проект 📈.

2️⃣ App Store: выполняет процесс списания средств с привязанного счета и передает данные о транзакции дальше 📊. Здесь два варианта развития событий:
✔️ данные о транзакции возвращаются колбэком в приложение
✔️ данные о транзакции получает сам сервер через технологию Server-to-Server Notifications 📣

3️⃣ Бэкенд: получает колбэк от App Store с данными о транзакции и проверяет покупку перед выдачей прав пользователю 🔒.

4️⃣ Приложение: стучится на бэкенд, просит данные для конкретного авторизованного пользователя и отображает полученные данные 📊.

Полную новость читайте здесь.

FlutterPulse — канал о мире Flutter!

#flutter #dart #FlutterPulse #FlutterPulseNews #flutterfriendly
Понимать код, а не копировать 🤖💻
Привет, друзья! Сегодня мы хотим поговорить о понимании кода 🤓. Мы часто сталкиваемся с ситуациями, когда нам нужно использовать сторонние библиотеки или компоненты, но не всегда понимаем, как они работают 🤔. Это может привести к багам, неожиданным побочным эффектам и уязвимостям 🚨.

Почему это критично? 🤔
Непонимание контракта поля/метода может привести к багам и неожиданным побочным эффектам 🐜. Непроверенные апдейты зависимости могут сломать сборки и привести к runtime-ошибкам 🚧. Понимание реализации помогает правильно тестировать, оптимизировать и писать корректную миграцию при изменениях 📈.

Куда смотреть? 🗺
Мы можем найти информацию о библиотеках и компонентах на pub.dev, GitHub и в исходных кодах 📊. Мы должны читать README, CHANGELOG, примеры и отслеживать issues и PR 📝.

На что обращать внимание в поле/компоненте? 🔍
Мы должны понимать контракт, эффекты, производительность и депрексации 📊. Мы должны читать документацию и исходные коды, чтобы понять, как работает компонент 📚.

Практики по версиям и апдейтам 📈
Мы должны понимать major = breaking changes и читать CHANGELOG перед апдейтом 📝. Мы должны использовать pubspec.lock для воспроизводимости билдов и команды flutter pub outdated и flutter pub deps --style=compact 📊.

Краткий процесс обновления зависимости 📝
Мы должны исследовать пакет, обновить зависимость, прогнать тесты и развернуть на стейдже 🚀.

Полную новость читайте здесь.

FlutterPulse — канал о мире Flutter!

#flutter #dart #FlutterPulse #FlutterPulseNews #flutterfriendly #mobiledevelopment #programming
🪙 dart_amqp: полнофункциональный клиент для работы с протоколом AMQP 📚
Сегодня Катя из Flutter Dev Friflex хочет рассказать вам о библиотеке dart_amqp — полнофункциональном клиенте для работы с протоколом AMQP (Advanced Message Queue Protocol). Эта библиотека позволяет приложениям взаимодействовать с брокерами сообщений, такими как RabbitMQ 🐰.

Что такое dart_amqp?
dart_amqp — это клиентская библиотека для работы с AMQP-серверами, которая предоставляет удобный API для создания распределенных систем обмена сообщениями. Она поддерживает все основные возможности протокола AMQP, включая очереди, обменники, подтверждения сообщений и транзакции 📝.

Создание клиента
Для тонкой настройки подключения используется класс ConnectionSettings, который позволяет переопределить параметры по умолчанию:
Client client = Client(
settings: ConnectionSettings(
host: "127.0.0.1",
port: 5672,
virtualHost: "/",
authProvider: PlainAuthenticationProvider("guest", "guest"),
maxConnectionAttempts: 1,
reconnectWaitTime: Duration(milliseconds: 1500),
),
);


Аутентификация
Библиотека поставляется с двумя провайдерами аутентификации:
▫️ PlainAuthenticationProvider — для простой аутентификации по логину и паролю
▫️ AmqPlainAuthenticationProvider — альтернативный вариант Plain-аутентификации
▫️ Можно создать собственный провайдер, реализовав интерфейс Authenticator

Работа с TLS
Для защищенных соединений можно передать SecurityContext:
Client client = Client(
settings: ConnectionSettings(
tlsContext: SecurityContext()
..setTrustedCertificates(path/to/cert.pem),
onBadCertificate: (certificate) => false,
),
);


Heartbeat
Heartbeat позволяет клиенту и серверу отслеживать активность соединения. Если обе стороны указывают ненулевой период (> 1 секунды), механизм активируется автоматически:
Client client = Client(
settings: ConnectionSettings(
tuningSettings: TuningSettings(
heartbeatPeriod: const Duration(seconds: 60),
),
),
);


Работа с каналами
Каналы (Channels) — это виртуальные соединения внутри одного TCP-подключения:
Channel channel = await client.channel();


Работа с очередями
// Создание очереди
Queue queue = await channel.queue("my_queue");
// Публикация сообщения
queue.publish("Flutter Friendly");
// Потребление сообщений
Consumer consumer = await queue.consume();
consumer.listen((AmqpMessage message) {
print("Получено: ${message.payloadAsString}");
message.ack(); // Подтвердить обработку
});


Полную новость читайте здесь.

FlutterPulse — канал о мире Flutter!

#flutter #dart #FlutterPulse #FlutterPulseNews #flutterfriendly #amqp #rabbitmq
💭 Ограничение доступа объектов в Dart: варианты и лучшие практики 🤔
Привет, друзья! Сегодня мы поговорим о механизмах ограничения доступа объектов в Dart 📚. Это важно для поддержания архитектурной дисциплины и предотвращения непредвиденного использования кода 🚫.

Есть несколько вариантов ограничения доступа объектов:
1️⃣ Использование символа _ в начале названия объекта. Это самый известный и эффективный способ ограничить доступ к объекту, поскольку он не позволяет использовать объект вне текущей библиотеки 🚫.

final String _privateData;
void _doExample() {}

2️⃣ Аннотация @protected. Она указывает, что объект доступен только внутри класса и в классах-наследниках 👪. Однако, она не ограничивает компиляцию и работу программы, а только выдает предупреждение анализатора 📝.

class Parent {
  @protected
  void doProtected() {}
}

3️⃣ Аннотация @visibleForTesting. Она позволяет сделать объект доступным только для тестов 📊. Это полезно для тех, кто покрывает проект тестами 📈.

class Parent {
  @visibleForTesting
  void doExample() {}
}


Важно понимать, что хотя реальное ограничение всего одно, аннотации тоже не стоит списывать со счетов 📝. Они подсвечивают намерение разработчика, обозначают границы ответственности и помогают поддерживать архитектурную дисциплину 🚀.

Полную новость читайте здесь.

FlutterPulse — канал о мире Flutter!

#flutter #dart #FlutterPulse #FlutterPulseNews #flutterfriendly 💻📱🔥
💭 Понимание параметра dirty в Flutter 🤔
Привет, друзья! Сегодня мы поговорим о параметре dirty у элемента во Flutter. 📱
Согласно официальной документации Flutter, виджет представляет собой конфигурацию, которая описывает, как именно должен выглядеть интерфейс. А вот элемент представляет конкретный виджет в определенном положении в дереве и отвечает за его жизненный цикл и состояние. 🌟

А dirty представляет собой индикатор состояния элемента. Он явно говорит — тут требуется перестроение. 🔄
По умолчанию dirty всегда false. Изменение значения флага наглядно можно увидеть на примере StatefulWidgeta. Допустим, нам надо обновить его состояние, и мы вызываем метод 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 и перестроен.

Полную новость читайте здесь.

FlutterPulse — канал о мире Flutter!

#flutter #dart #FlutterPulse #FlutterPulseNews #flutterfriendly 💻📊👍
👍1
Раскрываем секреты BuildContext в Flutter! 🤔
Привет, друзья! Сегодня мы поговорим о чем-то действительно важном в Flutter — BuildContext. 📚
Это специальный объект, который представляет собой ссылку на конкретное место виджета в дереве элементов. По сути, это идентификатор позиции виджета в иерархии приложения. 🗺️
Каждый виджет при построении получает свой уникальный BuildContext. Именно через него виджет может взаимодействовать с другими частями дерева: получать доступ к родительским виджетам, темам, навигации и многому другому. 🌐
Давайте посмотрим, как это работает на практике:

@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. Именно через context виджеты подписываются на изменения:

final data = MyInheritedWidget.of(context);

При таком вызове Flutter регистрирует зависимость текущего виджета от MyInheritedWidget через BuildContext. Когда InheritedWidget обновится, все зависимые от него виджеты будут автоматически перестроены. 🔄
Полную новость читайте здесь.

FlutterPulse — канал о мире Flutter!

#flutter #dart #FlutterPulse #FlutterPulseNews #flutterfriendly #BuildContext #InheritedWidget
👍2
Понимание Keys во Flutter: зачем они нужны и как их использовать 🦋

Привет, друзья! Сегодня мы поговорим о Keys во Flutter, которые помогают сопоставлять новые виджеты с уже существующими элементами при обновлении дерева. Но для чего же они нужны и как их использовать? 🤔

Когда Flutter получает новый список виджетов при ребилде, он пытается сопоставить их с существующими элементами по позиции и типу. Но если порядок элементов изменился или виджеты одного типа поменялись местами, состояние может перескочить на другой элемент. Это происходит потому, что 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++),
),
);
}
}

При перестановке элементов состояния могут перепутаться. Чтобы избежать этого, мы можем использовать Keys. 🚀

Существует несколько типов ключей:
▫️ ValueKey<T> — ключ по значению (идеален для id-модели).
▫️ ObjectKey — сравнение по == объекта.
▫️ UniqueKey — каждый раз новый ключ (заставляет создать новый Element; сбрасывает состояние).
▫️ GlobalKey — глобальная уникальность + доступ к State/Context; использовать экономно (дорогой).

Итак, когда использовать Keys?
▫️ Динамические списки с добавлением/удалением/реордером — ValueKey(id).
▫️ Формы, доступ к State извне — GlobalKey (только при необходимости).

А когда не нужен Key?
В статичных списках и простых элементах без внутреннего состояния. Не ставьте ключи на всякий — если они лишние, то усложняют и могут ухудшать производительность.

Рекомендации:
▫️ Для сущностей с постоянным id — ValueKey(id).
▫️ Если хотите сбросить состояние — UniqueKey.
▫️ GlobalKey — только для специфических задач (формы, навигация, тесты).
▫️ Если состояние перескакивает — сначала добавьте ключи, а не перестраивайте архитектуру.

Полную новость читайте здесь.

FlutterPulse — канал о мире Flutter!

#flutter #dart #FlutterPulse #FlutterPulseNews #flutterfriendly
1
Добавляем биометрическую аутентификацию в наше Flutter-приложение с помощью плагина local_auth 🚀

Привет, друзья! 👋 Сегодня мы поговорим о том, как добавить аутентификацию по биометрии в наше 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,
);


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

Полную новость читайте здесь.

FlutterPulse — канал о мире Flutter!

#flutter #dart #FlutterPulse #FlutterPulseNews #flutterfriendly #биометрическаяаутентификация #local_auth
🪙 Получаем информацию от аппаратных датчиков движения во Flutter! 🤩

Знаете, как во Flutter-приложении получать информацию от аппаратных датчиков движения? 🤔 Сейчас узнаете! 😊

В этой задаче вам поможет плагин sensors_plus. Как описывают его разработчики — он дает возможность вашему Flutter-приложению обращаться к сенсорам устройства, таким как:
▪️ акселерометр
▪️ гироскоп
▪️ барометр
▪️ магнитометр

Как это работает? Через плагин приложение обращается в платформу. Натив считывает данные с сенсоров и полученные данные передает во Flutter посредством Streams (потоков). В приложении же вам достаточно подписаться на необходимый поток с данными.

Библиотека дает возможность отслеживать данные по пяти основным событиям:
✔️ AccelerometerEvent — ускорение устройства.
✔️ UserAccelerometerEvent — в отличие от AccelerometerEvent отражает только фактическое ускорение устройства.
✔️ 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 точно будет полезен.

Полную новость читайте здесь.

FlutterPulse — канал о мире Flutter!

#flutter #dart #FlutterPulse #FlutterPulseNews #flutterfriendly
1