Обратно в блог
  • product
  • mobile
  • backend

Будьте в курсе всех возможностей

Размер имеет значение. Советы по оптимизации Flutter-приложений

Знаете ли вы, что каждые 6 мегабайт вашего приложения могут стоить порядка 1% органических установок? И это не просто цифры — это реальные потери пользователей, которые отказываются от скачивания вашего приложения из-за того, что оно весит слишком много.

Всем привет! Меня зовут Костя Довнар, я разработчик в Яндекс Go. В этой статье я поделюсь с вами реальным опытом оптимизации Flutter-приложений, расскажу о современных инструментах анализа и покажу, как найти баланс между размером и функциональностью, не жертвуя качеством продукта.

Но прежде чем мы углубимся в технические детали, хочу сделать важное предисловие. Я убеждён, что каждое приложение прекрасно по-своему и не должно слепо подгоняться под некие универсальные стандарты. Гораздо важнее, чтобы оно приносило пользу и доставляло удовольствие вашим пользователям. У нас в Яндексе, например, есть немало крупных приложений, включая несколько супер-аппов, которые весят достаточно много. Мы приняли такое решение осознанно, потому что считаем: доставить пользователям качественный и полезный функционал, ради которого они будут возвращаться, — это главная задача.

Как мегабайты всё портят

Давайте будем честны: никто не любит скачивать большие приложения. И дело не только в том, что они занимают драгоценное место на телефоне, которое можно было бы отвести под фотографии наших прекрасных котиков или коллекцию гифок для самых неожиданных ситуаций. Операционная система, служебные файлы, кэши различных программ — всё это тоже отнимает гигабайты, и вот пользователь оглянуться не успевает, как у него уже ни на что не хватает места. Это первая и, на мой взгляд, самая главная проблема — несчастье пользователей. Большие приложения делают их жизнь сложнее из-за долгих загрузок, установки, обновлений и, как мы уже упомянули, проблем с памятью. Если пользователь физически не может скачать ваше приложение, он этого не сделает. Логично.

01 (8).png

Но даже если техническая возможность скачать есть, пользователь всё ещё может отказаться от установки, в том числе из-за размера. Причины здесь довольно очевидны, но не все о них задумываются:

  • Стоимость трафика. К сожалению, реалии таковы, что не все живут в регионах с доступным и безлимитным интернетом. Во многих странах люди буквально считают каждый потраченный мегабайт.
  • Время загрузки. Пользователи хотят получить приложение «здесь и сейчас», а не ждать, пока оно скачается.
  • Обрывы соединений. Это просто то, что все ненавидят. Нестабильный интернет — норма для многих регионов, и чем больше приложение, тем выше шанс, что загрузка прервётся.
  • Недостаток памяти. Об этом мы уже говорили, но это ключевой фактор принятия решения.

К счастью, компания Google уже сделала за нас половину работы тем, что провела исследования на тему влияния размера приложения на установки и конверсии. К несчастью, последнее такое исследование проводилось в 2017 году. Впрочем, это не помешает нам прикинуть масштаб проблемы. Тем более, что вывод всё равно получился довольно простой: чем больше ваше приложение, тем меньше установок. Но куда интереснее конкретные цифры:

  • Google посчитали, что каждые 6 мегабайт вашего приложения стоят примерно 1% органических установок. Так что факт из самого начала статьи не на пустом месте взялся.
  • В одном из экспериментов Google выложили одно и то же приложение в двух вариантах: 10 МБ и 100 МБ. Версию, которая весила 10 МБ, скачивали на 30% чаще. А вот это уже более существенная разница.

Особенно остро эти проблемы стоят в развивающихся рынках, а это, на минуточку, порядка 85% населения Земли. Причём у этих рынков есть своя специфика:

  • Около 70% пользователей там задумываются о размере приложения перед установкой. Это не значит, что все откажутся, но они точно подумают: «А нужно ли мне это приложение, или я могу обойтись без него?»
  • Доступность Wi-Fi — серьезная проблема. По данным того же Google, около половины пользователей интернета в Индии и Индонезии (два крупнейших развивающихся рынка) не имеют постоянного доступа к Wi-Fi. Для них подключение к Wi-Fi — это не повседневность, а скорее редкость.
  • Как следствие, люди сидят на мобильной сети, которая часто бывает дорогой, некачественной и нестабильной. Вышки есть не везде, а где есть — могут обеспечивать плохое соединение. Пользователи снова считают каждый мегабайт.

Теперь о памяти на самих устройствах. Приложения постоянно растут — сейчас примерно на 30% в год. Цифра большая, хотя, ещё десять лет назад они росли на 60%. Тенденция на замедление есть, но 30% — это все ещё много. Интересный факт: на Android размеры приложений традиционно меньше, чем на iOS, поэтому у Android-пользователей ожидания по размеру могут быть выше.

02 (9).png

Когда память устройства заполняется приложениями на 30–50%, пользователи часто начинают задумываться о чистке. И угадайте, кто первым летит «под нож»? Правильно, большие приложения — они просто висят наверху списка в настройках. В развивающихся странах, о которых мы говорили, очень популярны старые смартфоны. Например, в Африке до сих пор активно используются iPhone 8, 7, а иногда и 6, с 64 или даже 32 ГБ памяти. Разумеется, память на них забивается просто по щелчку пальцев.

А вот ещё одна интересная цифра от Google Play: 13% активных устройств в их сторе имеют менее двух гигабайт свободной памяти. Если сопоставить это с общим количеством устройств, получится порядка 400 миллионов. Терять такой кусок пирога из-за размера приложения точно не хочется.

«Но позвольте! — скажете вы. — Ты же сам говорил, что эти данные Google датируются 2017 годом! С тех пор многое изменилось». И будете правы. Интернет действительно стал доступнее по миру. Например, в Африке доступность выросла с 20% до 37% с 2017 года, а в целом в развивающихся странах — с 40% до 60%. Памяти на устройствах тоже стало больше: тогда как раз выходил свежий iPhone 8 с 64 ГБ в базе, а сейчас актуальный iPhone 16 имеет уже минимум 128 ГБ на борту.

Однако, несмотря на улучшения, цифры все ещё далеки от идеальных. Большое количество пользователей по-прежнему сидит либо без постоянного интернета, либо с плохим, дорогим и нестабильным соединением. А что касается памяти — давайте признаемся, мы быстро находим, чем забить и 128 ГБ, и даже больше. Мне, например, для всех моих селфи и терабайта мало будет! Поэтому проблема «лишнего веса» приложений, возможно и стала чуть менее острой, чем в 2017 году, но она никуда не делась. И её влияние на пользовательский опыт и бизнес-метрики всё так же актуально.

Вскрытие покажет

Итак, мы поняли, что «лишний вес» приложения — это не просто абстракция, а вполне реальная проблема, влияющая и на пользователей, и на бизнес. Что же делать? Любой хороший специалист, прежде чем переходить к активным действиям, соберёт «анализы». Ведь если мы не установим конкретную причину проблемы, все наши усилия по оптимизации могут оказаться напрасными, а в худшем случае — даже навредят приложению. Давайте подумаем, как в мобильном мире, и в частности во Flutter, мы можем собрать эти анализы и понять, откуда растут ноги у лишнего веса?

Не самые надёжные методы диагностики

Первое, что приходит на ум, — просто собрать релизный артефакт и посмотреть, сколько он весит. Этот способ, к сожалению, несостоятелен для точной оценки того, что получит пользователь. Дело в том, что пользователи скачивают из сторов (Google Play, App Store) уже оптимизированную сборку. Сторы сами готовят для пользователя наиболее подходящий вариант приложения: без лишних ресурсов, иногда даже без модулей, скомпилированных не под архитектуру конкретного устройства.

Однако! Этот метод отлично подходит для отслеживания динамики роста приложения. Например, мы в Яндекс Про используем его, чтобы следить, как меняется размер приложения от версии к версии. На каждый pull request мы собираем две сборки (до и после) и сравниваем их. Это помогает быстро выявлять неожиданные «прибавки в весе». Была у меня история: я обновлял одно из наших приложений и добавил несколько больших картинок, забыв их оптимизировать и перегнать в более подходящий формат. В результате приложение к следующему релизу выросло на целых 20 мегабайт! Я этим, конечно, не горжусь, но горжусь тем, что после этого у нас появилась практика следить за оптимизацией изображений, и теперь такие ошибки исключены.

03 (8).png

Второй очевидный кандидат — информация из самих сторов. И App Store, и Google Play предоставляют довольно подробные страницы о том, сколько весит приложение, сколько весит обновление, сколько трафика потребуется на загрузку. Но это всё ещё несостоятельная метрика для глубокого анализа. Всё, что мы можем из неё понять: «ну, моё приложение толстенькое, классно, и что мне с этим делать?» Деталей для конкретных действий здесь нет.

К счастью, уже довольно давно существуют инструменты для анализа размера приложений. В iOS они свои, довольно специфичные, как это часто бывает у Apple. В Android всё немного проще, есть встроенные в Android Studio инструменты и несколько внешних. Но поскольку сейчас мы говорим именно про Flutter, я хочу рассказать о том, что он предоставляет нам для сбора таких вот анализов.

analyze-size: главный диагностический инструмент Flutter-разработчика

Что это такое? analyze-size — это аргумент в системе сборки Flutter (flutter build apk -analyze-size или flutter build appbundle -analyze-size и аналогично для iOS), который позволяет вместо сборки итогового артефакта получить довольно подробный и качественный отчёт о том, сколько весит приложение и, что самое важное, — почему.

04 (7).png

Поначалу отчёт может показаться не очень дружелюбным: это сплошная стена из названий переменных, каких-то цифр и символов. Глядя на такое, можно подумать: «Нет, пожалуй, анализировать своё приложение я не буду, тут ничего не понятно».

Радует что, команда Flutter тоже так подумала и предоставила нам инструмент, который превращает эту «непонятную ерунду» в классный интерактивный HTML-экран. И экран действительно интерактивный: на каждый кубик можно нажать, посмотреть, что внутри, проанализировать всё вплоть до каждого байта при необходимости. Это значительно упрощает понимание структуры веса вашего приложения.

  • Бонус #1. Эта штука умеет сравнивать между собой несколько отчётов. Такое бывает полезно, например, когда ваше приложение резко выросло в размере, и вы не понимаете почему. Закидываете два отчёта (до и после изменений), смотрите и видите: «Ага, вот здесь я зачем-то добавил Шрека в 8К разрешении».
  • Бонус #2. В качестве эксперимента я взял JSON-отчёт из analyze-size моего личного приложения и закинул его в ChatGPT. Тот, без каких-либо дополнительных вводных, довольно подробно и классно расписал, что у меня сколько места занимает, куда стоит обратить внимание, а куда — нет. Рекомендую, если вам самим лень копаться в деталях.
05 (3).png

На что смотрим в отчёте analyze-size?

Итак, мы собрали приложение с analyze-size, получили отчёт и готовы анализировать. Я выделяю два основных типа проблем или, скорее, областей, на которые стоит обратить пристальное внимание. На самом деле, это практически всё, что нужно смотреть:

  1. Ресурсы. Под этим подразумевается всё, что у вас лежит в assets. Это могут быть шрифты, картинки, иконки, звуки, JSON-файлы и так далее.
  2. Исходники и зависимости. То есть, в той или иной мере, ваш собственный исходный код и код библиотек, которые вы используете.

Именно анализ этих двух категорий чаще всего даёт понимание, где скрываются основные резервы для оптимизации размера вашего Flutter-приложения. Далее мы подробно поговорим о том, что делать с обнаруженными тяжеловесами в каждой из этих категорий.

Компактные ассеты

Хорошо, мы провели диагностику с помощью analyze-size и, скорее всего, обнаружили, что значительную часть нашего приложения занимают ресурсы. Это довольно частая ситуация, и хорошая новость в том, что оптимизация ассетов обычно даёт самый заметный результат при относительно небольших усилиях. Здесь главное — выработать определенную дисциплину и следовать нескольким простым, но эффективным практикам. Делюсь теми, которые нам в Яндекс Про помогли больше всего.

Совет 1: Выбирайте оптимальные форматы данных

Не все форматы одинаково полезны, когда речь заходит о размере. Выбор правильного формата для каждого типа ресурсов может существенно сократить их вес без потери качества, заметной пользователю.

Изображения:

WebP — ваш лучший друг. В подавляющем большинстве случаев формат WebP показывает себя значительно лучше, чем JPG, и почти всегда — лучше, чем PNG. Он поддерживает и сжатие с потерями, и без потерь, а также прозрачность и анимацию.

История из жизни Яндекс Про: В одном из наших приложений мы провели эксперимент. Сначала просто заменили все JPG-изображения на WebP. Это позволило нам уменьшить итоговый размер артефакта на 4 мегабайта. Затем мы вспомнили, что WebP отлично справляется с прозрачностью, и заменили им все PNG-картинки. Результат — ещё минус 6 мегабайт. В сумме 10 мегабайт экономии просто за счёт смены формата, при этом для пользователей и для нас, разработчиков, в использовании ничего не изменилось. Десять мегабайт — это, согласно статистике Google, потенциальные +1.5–2% к органическим установкам. Неплохо!

Звуки:

Если у вас в приложении много коротких звуковых эффектов, рассмотрите использование формата WAV с низким битрейтом. Для коротких звуков это может быть более оптимальным решением, чем, например, MP3, который добавляет небольшую задержку в начале из-за особенностей кодека.

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

Совет 2: Избавьтесь от дубликатов ресурсов

Разработчики, будем честны, иногда бывают немного ленивы. Проверить, не добавлял ли кто-то уже эту иконку, картинку или шрифт в проект, бывает дольше, чем просто добавить свою копию. В результате ресурсы начинают дублироваться, раздувая приложение.

Ещё одна история из Яндекс Про: В том же приложении, где мы оптимизировали картинки, анализ показал, что несколько независимых модулей тянули за собой одну и ту же версию одного и того же шрифта. Мы вынесли этот шрифт в один общий модуль, удалили все дубликаты и настроили использование из единого источника. Это простое действие принесло нам ещё 2 мегабайта экономии. С учётом предыдущих 10 МБ от оптимизации изображений, мы уже получили 12 мегабайт сокращения.

Регулярный аудит ассетов на предмет дублирования и централизация общих ресурсов — хорошая практика, которая помогает держать размер приложения под контролем.

Совет 3: Максимально используйте бэкенд для доставки ресурсов

Этот совет подойдёт не всем, так как требует наличия бэкенда и его готовности к сотрудничеству, а также ресурсов на доработку как клиентской, так и серверной части. Но если такая возможность есть, это один из самых действенных способов уменьшить начальный размер артефакта. Вместо того чтобы упаковывать все возможные картинки, звуки, конфигурационные файлы и другие тяжёлые ассеты прямо в приложение, можно загружать их с сервера по мере необходимости.

Преимущества тут очевидны:

  • Значительное уменьшение размера первоначальной загрузки (APK/IPA).
  • Возможность обновлять ресурсы без выпуска новой версии приложения.
  • Более гибкое управление ресурсами (например, можно подгружать разные наборы ассетов для разных категорий пользователей или регионов).

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

Оптимизация исходников и зависимостей

Отлично! Мы разобрались с ресурсами и увидели, что грамотная работа с ними может существенно облегчить наше приложение. Но что делать с кодом? Исходники и зависимости — это, пожалуй, самая неоднозначная часть, когда речь заходит об оптимизации размера. Почему я называю её самой грустной? Да потому что код, который мы пишем, и библиотеки, которые мы подключаем, обычно нужны для работы приложения. Компилятор Dart и так достаточно умён, чтобы проводить оптимизации вроде Tree Shaking — удаления неиспользуемых фрагментов кода. Так что мы оказываемся в ситуации, когда код занимает значительную часть приложения, и кажется, что с этим уже ничего не поделаешь. Это ведь не картинка, которую можно просто пережать в другой формат. Но даже здесь есть несколько подходов, которые могут помочь.

Метод 1: Обфускация

06 (1).png

Во Flutter, как и во многих других средах, существует механизм обфускации кода. Важно понимать, что обфускация — это не шифрование. Её главная цель — сделать ваш скомпилированный код менее человекочитаемым, а в контексте размера — ещё и более компактным.

Как это работает: представьте, что все ваши осмысленные имена классов, методов и переменных в процессе сборки заменяются на короткие, ничего не значащие символы, типа A, B, C и так далее. За счёт того, что эти сгенерированные имена почти всегда короче исходных, итоговый скомпилированный код просто физически занимает меньше места.

Наш эксперимент в Яндекс Про: мы провели тесты с обфускацией. Просто включив её, мы смогли уменьшить размер итогового артефакта на 22%. Для большого супераппа это весьма ощутимая цифра.

Но и тут есть свои минусы:

  • Больше никакой завязки на имена. Если в вашем коде есть места, где вы каким-то образом полагаетесь на конкретные имена классов или методов — например, при работе с некоторыми нативными интеграциями или динамической генерацией чего-либо, обфускация может это сломать. Имена изменятся, и ваш код перестанет работать в релизной сборке.
  • Нужно хранить маппинг символов. Чтобы разбирать стектрейсы из крэш-репортов, вам понадобится специальный файл (mapping file), который связывает обфусцированные имена с вашими исходными, человекочитаемыми именами. Без него анализировать ошибки в проде будет практически невозможно. Этот файл генерируется во время сборки, и его нужно надёжно хранить.

Метод 2: Deferred Components

Идея отложенной загрузки компонентов не нова и заключается в том, чтобы не загружать весь код приложения сразу при старте, а подгружать некоторые его части только тогда, когда они действительно понадобятся пользователю.

Представьте, у вас есть какой-то крупный функционал, который занимает, скажем, 10% от общего размера приложения, но им пользуется всего 5% ваших пользователей. Зачем заставлять остальные 95% пользователей скачивать и хранить этот код? Deferred Components позволяют выделить такой функционал в отдельный модуль и загрузить его только тогда, когда пользователь явно захочет им воспользоваться.

07.png

Но и здесь не обошлось без минусов:

  1. Платформенные ограничения. На данный момент Deferred Components во Flutter полноценно работают только под Android при использовании Google Play Services. Для iOS нативной поддержки такого механизма во Flutter нет, и в ближайшее время, судя по всему, не предвидится.
  2. Необходимость доработки нативной части. Даже на Android для корректной работы отложенных компонентов потребуется небольшая, но всё же доработка нативной части вашего проекта.
  3. Особенности работы с импортами в Dart. При использовании Deferred Components вам придётся более внимательно следить за импортами. Модули, которые вы хотите загружать отложенно, нужно будет импортировать специальным образом и явно вызывать их загрузку перед использованием. Если этого не сделать, приложение просто упадёт при попытке доступа к ещё не загруженному коду.

Метод 3: Flavors

Flavors — это механизм, позволяющий создавать различные версии вашего приложения из единой кодовой базы. Классический пример — выпуск игры сразу в двух версиях: бесплатной (с несколькими уровнями) и платной (полной). Мы в Яндекс Про очень активно используем Flavors: для бета-версий, экспериментальных сборок, и даже для нескольких отдельных приложений, основанных на кодовой базе Яндекс Про.

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

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

Лайфхак 1: Константы + Tree Shaking

Идея: основная суть — в использовании констант, определенных во время компиляции. Мы прячем код, специфичный для определенного флейвора, или использование определенных зависимостей, внутрь if-блока, условие которого завязано на такую константу.

08.png

Проблемы и сложности:

  • Нативный код. К сожалению, Flutter (или, точнее, Dart-компилятор) не настолько умён, чтобы таким же образом удалять связанный с этим вырезанным Dart-кодом нативный код плагинов. То есть, если константа — это плагин с нативной частью, его Dart-код может и удалится, а вот нативная библиотека, скорее всего, останется в сборке. Это требует дополнительных ухищрений на нативном уровне.
  • Общая сложность. На самом деле, этот подход на практике оказывается гораздо сложнее, чем просто обернуть код в if. Нужно аккуратно управлять зависимостями, точками входа, и следить, чтобы неиспользуемый код действительно полностью отряхивался.

Лайфхак 2: Вынос опциональных зависимостей в отдельные пакеты + удаление из pubspec.yaml на CI

Этот лайфхак мы придумали позже, и он оказался проще как в реализации, так и в поддержке. Он просто прекрасен.

Суть очень проста:

  • Зависимости как модули. Те зависимости, которые мы хотим сделать опциональными для разных флейворов, должны быть оформлены как отдельные Flutter-модули.
  • Отсутствие использования в коде флейвора. Разумеется, в коде того флейвора, где эта зависимость не нужна, она не должна использоваться. Это можно обеспечить либо так же, через if с константой времени компиляции, либо, что ещё лучше, иметь отдельные main-файлы для разных флейворов, где инициализируется только нужный набор зависимостей.

Магия на CI/CD. Во время сборки конкретного флейвора мы просто программно удаляем или комментируем строчки с ненужными для этого флейвора зависимостями из файла pubspec.yaml перед тем, как Flutter начнёт процесс сборки.

09.png

Я считаю, что этот подход прекрасен сразу по нескольким причинам:

  • Простота реализации. Все, что нужно — это скрипт на CI, который модифицирует pubspec.yaml перед сборкой.
  • Удаляется всё. Таким образом удаляется не только Dart-код зависимости, но и весь связанный с ней багаж: нативный код (если это плагин), ресурсы, которые мог приносить с собой этот пакет. Вы можете так удалять, например, целые дизайн-системы, если они у вас разные для разных флейворов и оформлены как отдельные пакеты.
  • Надёжность. Если по какой-то причине код вашего флейвора вдруг начнёт использовать зависимость, которую вы для него отключили таким образом, сборка просто не пройдёт — Flutter не сможет найти пакет. И это сразу будет заметно.

Единственный минус, если его можно так назвать, — это недостаточная гибкость такого метода по сравнению с if с константами, так как он работает на уровне целых пакетов. Вы не можете отключить только часть пакета.

Кодим экологично

Что ж, мы обсудили, как размер приложения влияет на пользовательский опыт и бизнес-показатели. Но есть ещё один аспект этой проблемы, о котором задумываются не так часто — экологический. Да-да, все эти лишние мегабайты вполне себе способны оставлять реальный углеродный след. Тут на самом деле просто:

Большое приложение → больше передаваемого трафика → больше затрат энергии на работу серверов, сетевого оборудования и пользовательских устройств → больше выбросов CO2 в атмосферу.

Первыми из крупных игроков об этой не самой очевидной взаимосвязи начали открыто говорить ребята из Spotify. Они провели подсчёты и выяснили, что за 2022 год их пользователи скачали обновления приложения порядка 20 миллионов раз. Это огромная цифра! Путём нехитрой математики они пришли к выводу, что это соответствует примерно 930 петабайтам трафика. Такое количество трафика стоило нашей планете примерно 65 000 тонн углекислого газа.

Много это или мало? Чтобы мы с вами не ломали голову над абстрактными тоннами, Spotify привели наглядное сравнение: это примерно как если бы 65 000 человек одновременно совершили перелёт из Лондона в Нью-Йорк и обратно на целой куче самолётов. Внушительный такой воздушный флот получается, не правда ли?

А вот если бы им удалось сократить размер своих артефактов всего на 1 мегабайт, это привело бы к уменьшению выбросов CO2 на 560 тонн в год. Один мегабайт! Кажется, такая мелочь, а эффект для окружающей среды может быть весьма ощутимым, особенно в масштабах популярных приложений.

10.png

Энтузиасты из «Geek Culture» пошли еще дальше и даже вывели специальную формулу, которая позволяет, имея на руках необходимые данные о количестве скачиваний, размере приложения и обновления, примерно рассчитать ежемесячный углеродный след вашего продукта.

Во всём важен баланс

Мы тут с вами подробно рассмотрели проблему размера Flutter-приложений буквально со всех сторон: от влияния на пользователей и бизнес до технических приёмов оптимизации и даже экологического аспекта. И, возможно, после всего этого у вас может сложиться впечатление, что я призываю всех немедленно бросать все свои дела и срочно ужимать приложения до неузнаваемости. Но это, конечно же, не так.

Я ещё раз хочу вернуться к мысли, с которой начал эту статью: на мой взгляд, каждое приложение прекрасно по-своему. Будь оно десять мегабайт, сто или даже гигабайт — куда важнее, чтобы оно нравилось вашим пользователям, приносило им реальную пользу и помогало решать их задачи. Слепая погоня за минимальным размером в ущерб функциональности или удобству — это точно не тот путь, который я бы рекомендовал.

Впрочем, надеюсь, эта статья хотя бы чуть-чуть помогла вам понять, что размер — это не просто абстрактная цифра. Это важный фактор, который может влиять на многое, и осознанный подход к нему, понимание доступных инструментов и методов оптимизации — это признак хорошего инженерного подхода. Анализируйте, экспериментируйте и создавайте классные Flutter-приложения, которые будут любить ваши пользователи, независимо от того, сколько эти приложения весят!

  • product
  • mobile
  • backend

Будьте в курсе всех возможностей