Flutter Pulse
1K subscribers
605 photos
1 file
1.67K links
На канале будут новости про flutter с сайтов, информация об обновлении пакетов, а также авторский контент.
Download Telegram
👣 Троян в assets: как хакеры меняют ваше Flutter-приложение без исходного кода. 🚨

Когда мы говорим о безопасности мобильных приложений, первое, что приходит в голову - защита исходного кода, обфускация, шифрование API-ключей. Но есть менее очевидный вектор атаки, который многие упускают из виду: ресурсы приложения. Те самые изображения, JSON-файлы, HTML-шаблоны и конфигурации, которые лежат в папке assets и кажутся безобидными. На самом деле они могут стать троянским конем, превращающим ваше приложение в инструмент мошенников без единого изменения в коде на Dart.

Представьте стандартный процесс публикации Flutter-приложения. Вы собираете APK или AAB, подписываете его и отправляете в магазин. Но что происходит с этим APK после сборки? Любой человек может скачать ваше приложение, выполнить несколько команд:

# Извлекаем ресурсы
apktool d yourapp.apk

# Редактируем файлы в папке assets/
# ...
# Перепаковываем
apktool b yourapp -o modified.apk

# Подписываем (да, даже без вашего ключа!)
zipalign -v 4 modified.apk aligned.apk
apksigner sign --ks fake.keystore aligned.apk

И вот уже существует модифицированная версия вашего приложения, которая выглядит и работает так же, но содержит измененные ресурсы. Этот APK может распространяться через сторонние магазины, мессенджеры или фишинговые сайты.

Сценарии, которые уже происходят:
- WebView становится оружием: Вы используете локальный HTML-файл для отображения справки или условий использования. Кажется безопасным? Хакер заменяет ваш help.html на страницу с фишинговой формой входа, которая крадет логины и пароли пользователей. При этом адресная строка показывает file:///android_asset/help.html - выглядит доверительно.
- Конфигурация как бэкдор: Приложение загружает настройки из config.json. В оригинале там безобидные цвета и тексты. После замены в конфиге появляются:

{
"api_endpoint": "https://malicious-server.com/collect",
"enable_debug": true,
"disable_ssl_checks": true
}

Теперь ваше приложение отправляет все данные на сервер злоумышленника и отключает проверки безопасности.
- Кража ключей, которые «никто не найдет»: Вы положили Firebase-ключ или токен Stripe в assets/secrets.json, решив, что «это же не в коде». Хакер извлекает APK, находит файл за 30 секунд и получает доступ к вашей инфраструктуре.

Защита - многослойный подход:
- Минимизация ущерба: Первый и главный принцип: не храните в ресурсах того, что можно не хранить. Конфигурации должны приходить с защищенного бэкенда. Ключи API - использовать через нативные хранилища или серверные прокси. Если ресурс не критичен для безопасности - проблема отпадает сама собой.
- Верификация целостности: Для ресурсов, которые действительно должны быть локальными, добавьте проверку контрольных сумм:

import 'package:crypto/crypto.dart';
import 'dart:convert';

Future<bool> verifyAsset(String assetPath, String expectedHash) async {
final data = await rootBundle.load(assetPath);
final bytes = data.buffer.asUint8List();
final hash = sha256.convert(bytes).toString();
return hash == expectedHash;
}

Храните хэши в нативной части приложения или получайте их с сервера при первом запуске.
- Шифрование на лету: Критичные данные в ресурсах можно хранить в зашифрованном виде. Например, конфигурационный JSON шифруется AES на этапе сборки и расшифровывается только в памяти при запуске. Ключ шифрования не должен лежать в том же APK.

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

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

#flutter #dart #FlutterPulse #FlutterPulseNews #hardworkerFlutter #мобильнаябезопасность #fluttersecurity
2
👣 Как виджеты AbsorbPointer и IgnorePointer управляют поведением интерфейса. 🤔

В арсенале Flutter-разработчика есть десятки виджетов для построения визуала, но ключевое качество современного интерфейса - не только красота, но и его предсказуемое поведение 📈. Как элегантно запретить двойное нажатие на кнопку, сделать слайдер только для чтения или временно приостановить все жесты в сложной форме? 🤷‍♂️ Для этих задач существуют специальные виджеты-контроллеры, которые оставаясь невидимыми, кардинально меняют логику взаимодействия 🔮.

Сегодня разберем двух таких стражей порядка: AbsorbPointer и IgnorePointer 🚀.

AbsorbPointer - полная блокировка: это стена 🚧. Когда absorbing: true, все касания останавливаются на этом виджете 🛑. События не проходят к дочерним виджетам и не ищут другие цели 🔍.

Пример: кнопка отправки формы 📝. Нужно заблокировать ее во время загрузки, но оставить видимой с анимацией 🔄:

AbsorbPointer(
absorbing: isLoading,
child: ElevatedButton(...),
)


IgnorePointer - сквозное игнорирование: это невидимка 🔮. При ignoring: true виджет пропускает события сквозь себя 🔁. Hit-тестирование продолжается, события могут попасть в виджеты ниже 🔝.

Пример: полупрозрачный баннер поверх карты 🗺. Баннер виден, но карта остается интерактивной 📍:

Stack(
children: [
InteractiveMap(),
IgnorePointer(
child: PromoBanner(),
),
],
)


Главное отличие:
🔵 AbsorbPointer: события не проходят вообще 🚫.
🔵 IgnorePointer: события проходят сквозь к виджетам позади 🔜.

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

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

#flutter #dart #FlutterPulse #FlutterPulseNews #hardworkerFlutter #mobiledevelopment #uxdesign
👍1😁1
Offstage: пререндеринг без боли. 👣
Во Flutter, где каждое изменение состояния может запускать перестроение дерева виджетов, управление производительностью часто сводится к искусству скрытия. Не буквального, а архитектурного. Когда перед нами встает задача заранее подготовить сложный фрагмент интерфейса, но не показывать его немедленно, на помощь приходит неочевидный, но мощный виджет Offstage.

Ключевое отличие Offstage от Visibility с флагом visible: false или условного оператора (if (condition) Widget()) - в его отношении к дереву. Когда вы оборачиваете виджет в Offstage(offstage: true), происходит следующее:
🔵 Виджет физически исключается из процесса лейаута (layout). Система его не измеряет и не размещает, как если бы его не существовало.
🔵 Виджет остается активной частью дерева виджетов. Его состояние (State), контроллеры анимаций (AnimationController), подписки (StreamSubscription, Listenable) продолжают жить и работать.
🔵 Виджет не отрисовывается (не вызывает paint). Это экономит вычислительные ресурсы GPU.

Этот принцип «жить, но не мешать» создает уникальные возможности.

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

Технические нюансы и ограничения:
🔵 Размер.
🔵 Не для всего.
🔵 Альтернатива IndexedStack.

Пример - быстрое переключение вкладок:

Stack(
children: [
Offstage(
offstage: _currentTab != 0,
child: SettingsScreen(), // Живое состояние
),
Offstage(
offstage: _currentTab != 1,
child: ProfileScreen(), // Контроллеры активны
),
],
)


💡 Вывод:
Offstage - это не просто скрытие виджета. Он сохраняет его состояние и ресурсы, предотвращая пересоздание. Используйте его для пребилдинга вкладок, сохранения анимаций и быстрых переходов.

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

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

#flutter #dart #FlutterPulse #FlutterPulseNews #hardworkerFlutter
👣 Flutter: как FractionallySizedBox и FittedBox спасают адаптивную верстку. 👀

При создании адаптивного интерфейса во Flutter мы часто используем MediaQuery и расчеты на основе размеров экрана. Но есть два менее известных, но исключительно мощных виджета, которые решают специфичные задачи адаптивности на уровне композиции, без сложной математики: FractionallySizedBox и FittedBox.

FractionallySizedBox задает размер дочернего элемента как долю от родителя. Например, кнопка на 80% ширины контейнера:

FractionallySizedBox(
  widthFactor: 0.8,
  child: ElevatedButton(...),
)

Главное условие: родитель должен иметь конкретный размер.

FittedBox масштабирует контент, сохраняя пропорции. Типичный случай - крупный заголовок в маленькой карточке:

FittedBox(
  child: Text(Заголовок, style: TextStyle(fontSize: 40)),
)

Это предотвращает OverflowError и автоматически подбирает размер.

В чем разница между FractionallySizedBox и FittedBox:
- FractionallySizedBox управляет контейнером (задает его размер как процент от родителя).
- FittedBox управляет содержимым (масштабирует виджет внутри существующего контейнера).

Когда что использовать:
- FractionallySizedBox для кнопок фиксированной ширины, прогресс-баров, колонок сетки.
- FittedBox для текста в ограниченной области, иконок в CircleAvatar, изображений-превью.

Вывод: Изучение адаптивности во Flutter не должно начинаться и заканчиваться на MediaQuery.of(context).size.width. Такие виджеты, как FractionallySizedBox и FittedBox, предлагают декларативный и композиционный подход к решению распространенных проблем верстки.

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

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

#flutter #dart #FlutterPulse #FlutterPulseNews #hardworkerFlutter
🔨 Установка нескольких Xcode с разными версиями: решение для разработчиков! 🤩
При необходимости установки нескольких версий среды разработки Xcode на одной машине можно воспользоваться удобным приложением Xcodes, которое является удобным менеджером версий Xcode.

Зачем нужно:
🔵 Тестирование приложений на разных версиях Xcode (включая бета-версии).
🔵 Работа с проектами, которые требуют конкретной версии (например, legacy-код).
🔵 Возможность не обновлять основной Xcode, если новая версия вызывает баги.

Особенности данной утилиты:
🔵 Поддерживает процессоры Apple Silicon и Intel.
🔵 Показывает релизные заметки для каждой версии.
🔵 Упрощает установку и переключение между версиями Xcode.
🔵 Автоматически скачивает нужные версии (включая старые и beta).
🔵 Не требует ручного управления через xcode-select.

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

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

#flutter #dart #FlutterPulse #FlutterPulseNews #hardworkerFlutter
🤡1
Async/await и Isolate в Flutter: не путайте ожидание с работой 🚀
Когда ваше приложение тормозит, первая мысль: «нужно вынести это в отдельный поток». В Flutter эта мысль часто выливается в async/await или Isolate. Но это не взаимозаменяемые вещи, а инструменты для разных задач 🤔. И если перепутать, можно получить либо бесполезный код, либо вечно зависающий UI 😱.

Главное заблуждение: async/await сам по себе не переносит выполнение в другой поток. Он просто дает удобный способ работать с асинхронными операциями, которые уже неблокирующие по своей природе: запросы к сети, чтение с диска, ожидание таймера 🕒.

Для операций ввода-вывода async/await достаточно 📁. Сеть, базы данных, файловая система - все это уже асинхронно на уровне платформы. Достаточно дождаться результата, и UI останется отзывчивым 📊.

Но как только появляются вычисления, которые грузят процессор - парсинг большого JSON, обработка изображений, сложные математические расчеты, - async/await перестает помогать 🤯. Здесь нужен настоящий параллелизм. Isolate запускает код в отдельном потоке (или даже ядре) и не трогает основной 💻.

Самый простой способ - compute(). Он берет функцию и данные, запускает их в изоляте и возвращает результат 📈. Идеально для разовых тяжелых задач.

Если нужно постоянное взаимодействие с фоновым процессом, например, обработка потока данных или долгая работа с промежуточными результатами - придется использовать Raw Isolate 📝. Там уже ручное управление портами и сообщениями, но зато полный контроль.

Итак, что выбрать?
🔵 Ждете ответ от сети или диска? async/await.
🔵 Нужно один раз обработать большой кусок данных? compute().
🔵 Есть долгий фоновый процесс с обменом сообщениями? Raw Isolate.
🔵 Пытаетесь ускорить вычисления, просто добавив async? Бесполезно, почитайте заново 📚.

Вывод: Async/await и Isolate не конкуренты, а партнеры 🤝. Первый отвечает за ожидание, второй - за параллельное выполнение. Смешивать их нужно осознанно, а не по принципу «чтоб не тормозило» 🚫.

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

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

#flutter #dart #FlutterPulse #FlutterPulseNews #hardworkerFlutter #asyncawait #isolate #mobiledevelopment #programming
👣 Keys во Flutter: как не потерять состояние при перестройке списков 📱💻

Один из самых частых источников багов во Flutter - потеря состояния при перестройке списков или перестановке элементов 🤦‍♂️. Кажется, все работает, но при добавлении новой карточки счетчик перескакивает на другую 📊. Или при изменении порядка чекбоксы остаются отмеченными не там 📝. Чаще всего проблема решается при помощи Keys 🔑.

Для чего используются Keys:

Flutter при обновлении экрана сравнивает старый и новый список виджетов и пытается понять, что изменилось 🤔. По умолчанию он ориентируется на тип виджета и его позицию в дереве 🌳. Это быстро, но приводит к ошибкам, когда элементы одного типа меняются местами или добавляются новые 🔄.

Keys дают Flutter дополнительную информацию: вместо «третий элемент в списке» он ищет «элемент с таким-то идентификатором» 📝. Благодаря этому состояние прикрепляется к конкретному объекту, а не к индексу 📈.

Типы ключей и когда их выбирать:

🔵 ValueKey - самый частый выбор 📈. Берет какое-то стабильное значение (id, уникальный заголовок) и использует его как идентификатор 📝. Идеально для списков, где у каждого элемента есть уникальный ключ из данных 📊.

🔵 ObjectKey - использует сам объект в качестве идентификатора, сравнивая его через стандартный оператор == 📊. Пригождается, когда у элемента нет уникального поля (например id), но сам объект достаточно стабилен и не меняется в процессе работы 🔄.

🔵 UniqueKey - каждый раз создает новый идентификатор 🔑. Это гарантирует, что Flutter не будет пытаться сопоставить виджет с предыдущим, а создаст новый элемент с нуля 📈. Удобно, когда нужно принудительно сбросить состояние (например пересоздать анимацию), но использовать на каждый чих не стоит - производительность пострадает 🚫.

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

Где без ключей не обойтись:

🔵 Списки, где элементы можно переставлять, добавлять или удалять 📈.

🔵 Виджеты, которые меняют порядок в зависимости от условий 📊.

🔵 Любые места, где важно сохранить состояние за конкретным экземпляром данных 📝.

Где ключи не нужны:

🔵 Статичные списки, которые не меняются 📝.

🔵 Виджеты без внутреннего состояния (статусные иконки, просто текст) 📄.

🔵 Случаи, где состояние вообще не важно 🤷‍♂️.

Чего делать не стоит:

Не надо оборачивать каждый виджет в Key просто потому, что так можно 🚫. Лишние ключи усложняют алгоритмы сравнения и могут замедлить рендеринг 🚀. Особенно это касается GlobalKey - его наличие в каждом втором виджете быстро приведет к утечкам и падению производительности 📉.

💡 Вывод:

Keys - это инструмент для точечного решения проблем с идентификацией виджетов 🔍. Если при перестройке интерфейса состояние прыгает или теряется - скорее всего, нужен ValueKey 📈. Если нужно сбросить внутреннее состояние - поможет UniqueKey 🔑. А если без доступа к виджету из другого места не обойтись - придется использовать GlobalKey 🚀. Во всех остальных случаях лучше обойтись без них 🙅‍♂️.

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

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

#flutter #dart #FlutterPulse #FlutterPulseNews #hardworkerFlutter #mobiledevelopment #flutterwidgets
👣 Flutter и нативный код: как работают MethodChannel и EventChannel 🤔

Вы знали, что Flutter позволяет использовать нативный код для расширения функциональности ваших приложений? 🤓 Это достигается с помощью платформенных каналов, которые обеспечивают обмен сообщениями между Dart и нативным кодом. 📱

MethodChannel - это как вызов функции на удаленной стороне. Вы вызываете метод с именем, передаете параметры и ждете ответа. Все это происходит асинхронно, чтобы не зависало ваше приложение. 🕒

EventChannel используется, когда данные приходят не по запросу, а сами. Например, акселерометр или датчик движения отправляют показатели постоянно, пока подписка активна. 📈

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


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

#flutter #dart #FlutterPulse #FlutterPulseNews #hardworkerFlutter #мобильнаяразработка #нативныйкод
👣 DataTable и Table: работа с таблицами во Flutter 📊
В повседневной работе мы привыкли использовать Column, Row, ListView для построения интерфейсов. Но иногда требуется настоящая таблица: строки, столбцы, сортировка, постраничная навигация. И здесь у Flutter есть несколько специализированных инструментов, о которых многие забывают.

DataTable - идеальный вариант для небольших объемов данных. Он строится на основе DataRow и DataCell, все объявляется декларативно. Подходит для экранов настроек, административных панелей, любых мест, где таблица не превышает десятка строк. Сортировка подключается через onSort, постраничное отображение контента - через PaginatedDataTable.

Table - для полного контроля над таблицей. Здесь вы сами определяете каждую строку как TableRow, а внутри - список ячеек с любыми виджетами. Можно задавать ширину колонок: фиксированную (FixedColumnWidth), пропорциональную (FlexColumnWidth) или по содержимому (IntrinsicColumnWidth).

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

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

#flutter #dart #FlutterPulse #FlutterPulseNews #hardworkerFlutter