Оглавление

    Как улучшить производительность во Flutter: 10 рекомендаций

    Производительность – важный аспект любого мобильного приложения. Если оно «съедает» кадры и висит, это оставляет неприятное впечатление у пользователей. Чтобы приложение работало плавно, ему нужно большую часть времени поддерживать кадровую частоту от 60 FPS (кадров в секунду). Современные сервисы используют множество графических элементов сразу, это сильно увеличивает нагрузку на аппаратную часть устройства. Если усовершенствовать код и архитектуру, можно повысить производительность приложения. В этой статье мы расскажем о том, как улучшить производительность Flutter-приложения.

    Flutter и другие платформы

    Flutter – это кроссплатформенный фреймворк, разработанный компанией Google и использующий язык программирования Dart. В 2021 он стал самой популярной кроссплатформенной технологией на рынке. Давайте проведём краткое сравнение производительности Flutter и других технологий для мобильной разработки.

    Flutter vs React Native

    Результаты тестов показывают, что и Flutter, и React Native обеспечивают кадровую частоту 60 FPS при стандартном скроллинге. Однако при этом React Native требует большего заряда батареи и объёма оперативной памяти. Кроме того, производительность фреймворка падает до 7 FPS, когда дело доходит до сложных анимаций с вращением, масштабированием и изменением уровня прозрачности. Для сравнения, у Flutter в аналогичной ситуации минимальное значение FPS составляет 19. Другие аспекты сравнения этих технологий вы можете прочитать в статье.

    Flutter vs Ionic

    В сравнении с Ionic, Flutter демонстрирует более высокую производительность. Это связано с доступностью нативных компонентов по умолчанию — для взаимодействия с ними Flutter не требуется «связующий мост» на JavaScript. Кроме того, у него есть собственный мощный графический движок Skia. Узнать больше о различиях между двумя технологиями вы можете в статье

    Xamarin vs Flutter

    Оба фреймворка обеспечивают почти нативную производительность. Но у Xamarin она сильно зависит от конкретного вида фреймворка. Xamarin.Android и Xamarin.iOS, в которых код более платформоспецифичен, обеспечивают отличную производительность. У Xamarin.Forms показатели хуже. 

    Flutter vs нативные технологии

    Натив, как правило, превосходит любую кроссплатформенную технологию по производительности. Код нативного приложения компилируется в тот же формат, что и нативный код устройства, в результате чего используется почти на 50% меньше памяти, чем в случае с большинством кроссплатформ. Но Flutter способен обеспечивать производительность, сопоставимую с нативом, благодаря использованию виджетов и высокооптимизированному графическому движку Skia. Больше о сравнении Flutter и нативных технологий читайте в статье.

    Как измерить производительность Flutter?

    Для измерения используются средства профилирования и реальное устройство вместо эмулятора (лучше — с низкой производительностью). Давайте разберём самые популярные способы оценки производительности приложений на Flutter. 

    Анализ производительности в оверлее 

    Один из способов протестировать производительность Flutter – использовать виджет Performance. Наложенный виджет отображает два графика поверх приложения. Верхний график GPU демонстрирует производительность растрового потока, другими словами, передачу информации между деревом слоёв приложения и графическим процессором (GPU) устройства. Несмотря на название, фактически график демонстрирует, как используются ресурсы ЦП. Нижний график UI отображает UI поток, который включает в себя написанный вручную код, выполняемый фреймворком Flutter.

    Если один кадр отображается дольше 16,6 миллисекунд (то есть производительность приложения падает ниже 60 FPS), голубая кривая идёт вниз, уступая место белому фону, а вертикальная линия становится красной. Если эти изменения вы видите на графике FPS, это означает, что графические элементы на экране слишком сложные и приложение не успевает вовремя их отрисовать. Если меняется график UI, Dart-код слишком сложный и приложение не успевает его вовремя выполнить. Если красная линия появилась на обоих графиках, стоит проверить UI поток.

    Анализ производительности в оверлее можно запустить несколькими способами. Вот два из них:

    • Flutter inspector. Запустите приложение в режиме профилирования, откройте DevTools и переключите флаг Inspector view. Там вы найдёте кнопку Performance Overlay.
    • Командная строка. Используйте команду flutter run –profile, а затем нажмите клавишу P, чтобы включить виджет анализа производительности.

    Подробнее о профилировании производительности во Flutter можно почитать в официальной документации.

    Performance view

    С помощью performance view, доступного в DevTools, можно узнать производительность приложения. Для этого используют три инструмента.

    • Flutter frames chart (график кадров)
    • Timeline events chart (хронологический график событий)
    • CPU profiler (профилирование ЦП)

    Данные в Performance view можно экспортировать и импортировать. Больше о возможностях Performance view можно узнать в документации.

    Эталонные тесты

    Производительность приложения также можно измерить с помощью эталонных тестов производительности Flutter, которые выполняют посредством интеграционного тестирования. Эти тесты оценивают такие метрики, как время запуска, энергопотребление и лаги (пропущенные кадры).

    Как оптимизировать производительность во Flutter

    Старайтесь не использовать метод build

    Тяжёлый циклический метод build() потебляет чрезмерные ресурсы ЦП. Так происходит, когда вы используете крупный Widget с «тяжёлой» функцией build(). Лучше разделить такой виджет на виджеты поменьше, исходя из их внутренней логики и того, как они будут изменяться. К примеру, локализовать вызов setState() до той части поддерева (другими словами, дочерних элементов узла), которой требуются изменения UI. Если setState() вызывается на слишком раннем этапе, он пересобирает все последующие виджеты дерева.

    Используйте виджеты const

    Постарайтесь избегать чрезмерной пересборки виджетов при помощи конструктора const. Так setState останется постоянным.

    Отдавайте предпочтение ленивым методам в списках и таблицах

    Используйте Lazy Lists и Lazy Grids для крупных UI-элементов. Таким образом при запуске приложения рендериться будет только видимая часть.

    Используйте Opacity только при необходимости

    С Opacity Widget виджет пересобирается в каждом кадре, что может вызвать проблемы с производительностью Flutter, особенно если вы используете анимацию. Если применить Opacity непосредственно к изображению, потребляется меньше ресурсов, чем в случае с виджетом. Также, чтобы повысить производительность, мы советуем выбирать виджеты TransparentImage, AnimatedOpacity, FadeInImage или FadeInTransition вместо виджета Opacity.

    Не применяйте savelayer

    Метод saveLayer() грузит аппаратную часть, и, по возможности, его следует избегать. Виджеты, потенциально способные запустить метод saveLayer(): Text (если используется overflowShader); Chip (если disabledColorAlpha != 0xff); ColorFilter и ShaderMask. 

    Сокращайте сборку и рендеринг до 16 мс

    Если вы заметили, что приложение «съедает» кадры, посмотрите, какие кадры собираются и рендерятся дольше 16 миллисекунд. Так как для сборки и рендеринга используются отдельные потоки, вам нужно, чтобы каждый кадр собирался и рендерился за 8 миллисекунд или быстрее, чтобы в сумме вышло 16 мс или меньше. 

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

    Выбирайте SizedBox вместо Container

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

    Передавайте в AnimatedBuilder уже предсобранные поддеревья в качестве дочерних параметров

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

    Не используйте ListsView для длинных списков

    Если список не помещается на экран, создайте его с помощью конструктора ListView.builder, а не ListView() или Column(). Так конструктор будет рендерить пункты по мере того, как пользователь будет их скроллить. Иначе приложение создаст все пункты сразу и снизит производительность.

    Скажите «нет» разделению виджетов на методы

    Если у вас есть крупный метод генерации с несколькими уровнями вложения, может показаться, что разделить его на несколько методов – отличная идея. Однако в результате Flutter будет пересобирать все дочерние виджеты с каждой пересборкой родительских, даже если некоторые из них абсолютно статичны. Чтобы не расходовать ресурсы ЦП на повторные сборки, разбивайте сложные виджеты на виджеты StalessWidget поменьше.

    Подведём итоги

    Фреймворк Flutter известен среди разработчиков тем, что обеспечивает почти нативную производительность даже в приложениях со сложными визуальными эффектами. Один из примеров — приложение, которое мы в Surf разработали для видеостриминговой платформы The Hole — Flutter справился с анимациями, обеспечил плавную работу приложения и отличный отклик элементов управления воспроизведением. Тем не менее, чтобы свести риск торможений, ошибок или лагов в приложении к минимуму, лучше следовать рекомендациям по улучшению производительности. Надеемся, что наш гайд вам в этом поможет.

    У нас в Surf большой опыт разработки приложений для разных видов бизнеса, и мы с радостью поможем реализовать ваш проект. Заполните небольшую анкету, и мы свяжемся с вами в течение суток.