Вышел Flutter 3.50, последний стабильный релиз 2026 года по публичному графику релизов. Ветку отрезали 6 октября, в стабильный канал она попала 11 ноября. Вместе с ним вышел Dart 3.14, ему посвящена статья.
В релиз вошло больше 1200 коммитов от 188 контрибьюторов, из них 69 прислали свой первый PR.
flutter upgrade
Коротко о главном
Направление | Что изменилось |
|---|---|
Нативный interop | FFIgen (Objective-C) и JNIgen (Java/Kotlin) стали stable, у Pigeon появился режим native interop (preview) |
Web | Wasm-сборки можно включить в Safari 26.2+ и Firefox 147+ (Ура!), появились флаг |
Дизайн-системы | Встроенные |
iOS | Задержка касаний меньше на один кадр (−16 мс на 60 Гц, −8 мс на 120 Гц), исправлены проблемы с отладкой на iOS 27 |
Android | Release-сборки строже относятся к |
Engine | SDF-рендеринг в Impeller на десктопе, texture arrays во Flutter GPU, много исправлений OpenGL |
Эмбеддеры | Vulkan external textures и |
Что сделать сразу после обновления
Если времени мало, вот короткий чек-лист:
iOS/macOS: включите Swift Package Manager, если ещё не включили. Trunk CocoaPods станет read-only в декабре 2026 года.
Material/Cupertino: запустите
dart fix --apply --code=migrate_design_widgetsи просмотрите дифф.Android-плагины: переходите на встроенную поддержку Kotlin в AGP (Built-in Kotlin).
Web: соберите проект с
--no-deprecated-js-interopи посмотрите, что мешает переходу на Wasm.Dart: попробуйте
dart migrate --dry-runна одном пакете (подробности в статье про Dart 3.14).
Ниже каждый пункт разобран подробно.
Прямой нативный interop стал стабильным
Об этом изменении стоит рассказать первым. Команда Flutter несколько лет вела направление direct native interop: Dart-код должен вызывать API платформы напрямую, без platform channels. В 3.50 эта работа дошла до production:
FFIgen для Objective-C стал stable. Поддержка C в нём была и раньше.
JNIgen для Java и Kotlin тоже стал stable.
Pigeon получил режим native interop в статусе preview. Под капотом он использует оба генератора и выдаёт типобезопасные биндинги для iOS, macOS и Android.
Чем это лучше platform channels? Канал асинхронно сериализует каждое сообщение, передаёт его через границу и десериализует на другой стороне. Сгенерированные биндинги вызывают нативный метод напрямую, поэтому:
вызовы бывают синхронными;
их можно делать из фоновых изолятов;
накладные расходы на сериализацию исчезают.
На практике это означает, что для свежего API iOS или Android больше не нужно ждать, пока кто-то обернёт его в плагин. Биндинги можно сгенерировать самостоятельно и вызвать API прямо из Dart. Можно ожидать, что плагины станут тоньше: сгенерированные биндинги плюс небольшой Dart-API поверх. Например, shared_preferences уже переведён на этот подход.
Команда отдельно благодарит участников программы Early Access: Jon Holtan, Rody Davis, Dominik Roszkowski, Alexander Thiele и Morgan McKenzie.
Подробнее: новый раздел документации Native interop собрал руководства для Android, iOS, macOS, Windows и Linux.
Web
Wasm в Safari и Firefox
До этого релиза Flutter web в режиме WebAssembly работал только в Chromium-браузерах: Chrome и Edge начиная со 137-й версии. Теперь Wasm можно включить и в Safari 26.2+ и Firefox 147+. По замерам команды на тяжёлых бенчмарках с большим числом виджетов эти браузеры показывают больше чем вдвое выше FPS, чем JS-сборка.
Команда сборки не изменилась, она по-прежнему создаёт сразу два варианта, Wasm и JS:
flutter build web --wasm
В Chrome и Edge Wasm используется по умолчанию. Для Safari и Firefox его нужно включить явно: в web/flutter_bootstrap.js добавьте движки webkit и gecko в wasmAllowList при вызове _flutter.loader.load, рядом с blink. Примерно так:
{{flutter_js}} {{flutter_build_config}} _flutter.loader.load({ config: { wasmAllowList: { blink: true, // Chrome, Edge webkit: true, // Safari gecko: true, // Firefox }, }, });
Примечание: точное расположение опции в объекте конфигурации лучше сверить с документацией по Wasm. Браузеры, которые не попали в allow-list или не поддерживают нужные возможности, автоматически получат JS-сборку.
Хеши в именах файлов: --web-content-hash
Кэширование Flutter web-приложений давно было больной темой. Новый флаг добавляет хеш содержимого в имена JS-, Wasm-файлов и ассетов из build/web/assets/**, а также обновляет ссылки в flutter_bootstrap.js:
flutter build web --wasm --web-content-hash
После этого можно смело ставить агрессивное кэширование. Рекомендации из документации:
Файлы |
|
|---|---|
Бандлы и ассеты с хешем |
|
|
|
Пример для nginx:
location = /index.html { add_header Cache-Control "no-cache"; } location = /flutter_bootstrap.js { add_header Cache-Control "no-cache"; } location / { add_header Cache-Control "public, max-age=31536000, immutable"; }
Вместе со сборкой генерируется precache_manifest.json. Его можно подключить к Workbox или к собственному service worker. Он заменяет устаревшие токены из flutter_service_worker.js.
Аудит старого JS interop
dart:html, dart:js, dart:js_util и package:js объявлены устаревшими, на смену им пришли dart:js_interop и package:web. Именно эти библиотеки чаще всего не дают перейти на Wasm, причём нередко они приходят из транзитивных зависимостей.
Чтобы найти все такие места, появился флаг, который прерывает сборку при импорте устаревших библиотек:
flutter build web --no-deprecated-js-interop flutter run -d chrome --no-deprecated-js-interop flutter test --platform chrome --no-deprecated-js-interop
WebParagraph: вёрстка текста силами браузера (preview)
Совместно с командой Chrome сделан новый способ раскладки текста. Он построен на экспериментальном Enhanced TextMetrics API: теперь текст размечает сам браузер, а не встроенный в CanvasKit движок. Что это даёт:
CanvasKit уменьшается примерно с 5,46 МБ до 3,75 МБ;
приложение больше не загружает резервные шрифты по сети;
вместо отсутствующих символов больше не рисуются «квадратики»;
цветные эмодзи рендерятся нативно.
Пока это превью, следить за ним можно в issue #172561.
Material и Cupertino: прощание со встроенными библиотеками
Отделение дизайн-систем от ядра продолжается. Пакеты material_ui и cupertino_ui вышли в 1.0 ещё во Flutter 3.47, а в 3.50 сделан следующий шаг:
package:flutter/material.dartиpackage:flutter/cupertino.dartформально объявлены deprecated, вместе с делегатами локализации изflutter_localizations;flutter createтеперь создаёт проект, который импортируетpackage:material_ui.
Deprecated пока не означает удалено, старый код продолжит собираться. Но переезжать рано или поздно придётся всем приложениям и пакетам. Для миграции есть автоматический фикс:
dart fix --apply --code=migrate_design_widgets
В типичном случае меняются зависимости и импорты:
ДО | ПОСЛЕ |
|---|---|
|
|
|
|
Примечание: если часть зависимостей ещё не переехала, документация предлагает переходный
MaterialUiCompatibilityBridge. Подробности есть в новом руководстве по миграции.
Ещё одно изменение, связанное с этим переездом: у DropdownButtonFormField в material_ui появилось свойство enabled. Теперь поле отключают через него, а не передачей onChanged: null.
Framework
Скролл и слайверы
Этот раздел почти полностью сделан силами сообщества:
SliverClipRectиSliverClipRRectобрезают содержимое слайвера, в том числе со скруглёнными углами. ОпцияclipOverlapотвечает за перекрытие закреплёнными заголовками. Автор — Emmanuel Lefebvre Dibon (@manu-sncf).itemExtentBuilderвListView.separated. Раньше задать размер элементов заранее можно было только в обычномListView.builder. Автор — @Mairramer.ScrollPhysics.shouldUpdateпозволяет точнее управлять пересозданием физики скролла. Тоже от @Mairramer.RawScrollbar.mouseCursorменяет курсор только над ползунком и дорожкой. Автор — Soroush Yousefpour (@gabrimatic).Исправлены
Scrollable.ensureVisible, overscroll вSliverFillRemainingи обрезка вTreeSliver.
Пример с itemExtentBuilder: если высоты элементов известны заранее, движку не нужно измерять каждый элемент, и прыжки по длинному списку становятся дешевле.
ListView.separated( itemCount: messages.length, itemExtentBuilder: (index, dimensions) => messages[index].isHeader ? 48 : 72, separatorBuilder: (_, __) => const Divider(height: 1), itemBuilder: (context, index) => MessageTile(messages[index]), );
Текст и ввод
TextInputType.numberWithOptionsполучил параметрpassword, удобный для PIN-кодов на Android. Автор — Alex Li (@AlexV525).Новая подсказка автозаполнения
AutofillHints.emailOTPCodeподставляет одноразовые коды из писем.Появилось перечисление
Hyphensдля мягких переносов. При переносе слова мягкий перенос теперь показывает видимый дефис, и это одно из breaking changes релиза.Исправлен «прыжок» экрана при перетаскивании верхнего маркера выделения (Nicodemus Lin, @nicolinx).
Исправлена навигация стрелками в полях с выравниванием по центру и по правому краю (Nickolas de Luca Alberton).
Выделение текста внутри
PageViewбольше не перелистывает страницы, маркеры корректно работают в смешанном LTR/RTL-тексте.
TextField( keyboardType: const TextInputType.numberWithOptions(password: true), autofillHints: const [AutofillHints.emailOTPCode], obscureText: true, );
Изображения и отрисовка
placeholderвDecorationImage(Kishan Rathore, @rkishan516).FadeInImageTransition.fadeInOver(Anas Saifi, @anazr9).useLogicalSizeвResizeImageиuseLogicalCacheSizeвImage(Koji Wakamiya, @koji-1009).blendModeвRawImage(Ishaq Hassan, @ishaquehassan).Плавная анимация между разными
BoxBorder.HSVColor.lerpиHSLColor.lerpтеперь интерполируют оттенок по кратчайшей дуге цветового круга. Переход от красного к пурпурному больше не проходит через всю радугу.
Доступность и навигация
SemanticsRole.sliderдля кастомных слайдеров.MediaQueryData.reduceMotionотражает системную настройку «уменьшить движение», так что анимации можно отключать по-честному.Escape закрывает
TooltipиRawTooltipи при этом не сбрасывает фокус.Исправлены VoiceOver и фокус в
WidgetsApp.routerна вебе.skipTraversalвFocusableActionDetector(Ishaq Hassan).barrierBuilderвshowDialogпозволяет полностью кастомизировать фон диалога, например сделать размытие (@Mairramer).Navigator.popUntilWithResultснимает маршруты до нужного и передаёт результат последнему снятому.
final reduceMotion = MediaQuery.of(context).reduceMotion; AnimatedContainer( duration: reduceMotion ? Duration.zero : const Duration(milliseconds: 300), // ... );
Многооконность на десктопе (эксперимент)
Работа идёт вместе с Canonical, участвуют Robert Ancell, Matthew Kosarek и Matej Knopp:
WindowControllerLinux: свои заголовки окон, прозрачность, перетаскивание и изменение размера;на macOS окна подстраивают размер под содержимое до показа;
RegularWindowпереименован вWindow, аsizedToContentзаменён наshrinkWrap;WindowManagerтеперь находится вышеWidgetsAppв дереве.
Примечание: API экспериментальный, и переименования выше это хорошо показывают. В продакшене пользоваться им пока рано.
Engine
SDF-рендеринг в Impeller на macOS, Linux и Windows. Линии, тонкие рамки, скруглённые углы и градиенты теперь строятся на GPU через signed distance fields. Отдельно ускорены градиенты на macOS и повторяющиеся тени текста.
Flutter GPU поддерживает 2D texture arrays, что удобно для батчинга спрайтов и тайлов. Ошибки конфигурации shader pipeline стали понятнее.
OpenGL и стабильность:
настройка шейдеров OpenGL вынесена в фоновый поток (@xiaowei-guan);
исправлены сбои при загрузке текстур на GPU Mali (@dfdgsdfg);
исправлены текстуры OpenGL ES 2 и прозрачность анимированных изображений (@krll-kov);
на GPU PowerVR серии B движок автоматически переключается с Vulkan на OpenGL.
Графика на десктопе: Windows по умолчанию запрашивает OpenGL ES 3 и откатывается на ES 2, если он недоступен (PR #191516). Также исправлена работа с путями, содержащими не-ASCII символы (кириллические имена пользователей Windows, это про вас), текст стал чётче, кадры копируются реже, сглаживание на Windows и Linux улучшено.
Встраиваемые платформы (embedders)
Для тех, кто запускает Flutter на нестандартном железе (автомобили, телевизоры, промышленные панели), расширены API Impeller и Vulkan:
Vulkan external textures позволяют передавать в движок внешние текстуры как для Impeller, так и для Skia (PR #188855, автор @xiaowei-guan).
FlutterEngineNotifyLowMemoryWarningочищает кэш offscreen render target в Impeller, когда системе не хватает памяти (PR #193497).
iOS и macOS
Это первый стабильный релиз после выхода Xcode 27 и iOS 27.
Касания стали отзывчивее
Matej Knopp переработал планирование кадров на iOS, и теперь касания и свайпы появляются на экране на один кадр раньше:
Дисплей | Выигрыш |
|---|---|
60 Гц | около 16 мс |
120 Гц (ProMotion) | около 8 мс |
Улучшение распространяется на устройства вплоть до iPhone 6s. Подробности есть в issue #191199. Код менять не нужно, достаточно обновиться.
Отладка на iOS 27
CLI сам подготавливает новое тестовое устройство, зависания на белом экране больше нет;
исправлены периодические падения при запуске через
flutter run;flutter screenshotснова работает на физических устройствах с iOS 17 и новее.
Swift Package Manager вместо CocoaPods
Trunk CocoaPods станет read-only в декабре 2026 года. Flutter 3.50 выводит предупреждение, если SPM отключён. В одном из будущих релизов флаг отключения SPM и fallback на CocoaPods уберут совсем.
Прощание с Intel Mac
На Intel-маках Flutter больше не тестируется, и CLI предупреждает при сборке на таких машинах. Если нужны сборки только под Apple Silicon, используйте:
flutter config --enable-macos-arm64-only
Android
Строже с Intent в release-сборках
В release-режиме Flutter раньше принимал из extras внешнего Intent параметры, которые влияли на запуск движка, и этим можно было злоупотребить. Теперь release-сборки:
игнорируют
route,dart_entrypointиcached_engine_id, пришедшие из extras внешнегоIntent;отклоняют runtime-флаги движка вроде
--dart-flags, переданные черезIntentили вместе с готовым release APK (--use-application-binary), даже если их передаёт само приложение.
Обычные deep links продолжают работать. В debug-режиме Flutter предупреждает о проигнорированных extras. Если вы проверяли флаги движка на release-сборке, переключитесь на profile-режим или пересоберите приложение.
Hybrid Composition++ (HCPP)
HCPP стал стабильным ещё во Flutter 3.44, а в beta включён по умолчанию начиная с 3.49.0-0.1.pre. В 3.50:
motion events в
AndroidViewSurfaceдоставляются без буферизации;исправлен порядок layout в
RenderAndroidView;исправлено падение, когда
FlutterEngineживёт дольшеActivity(Amaan Ansari, @amaan-0331).
Требования: Android 14 (API 34) и выше, а также Impeller на Vulkan. Если условия не выполнены, Flutter возвращается к прежней стратегии. Включение:
flutter config --enable-hcpp
Чтобы убедиться, что HCPP заработал, ищите в logcat строку Using HCPP platform view rendering strategy.
Built-in Kotlin для плагинов
Авторам плагинов с Android-кодом пора переходить на встроенную поддержку Kotlin в AGP. Если вручную применять Kotlin Gradle Plugin, даже с проверкой версии AGP, pub.dev снизит оценку пакета, а под AGP 10 сборка просто сломается. После миграции укажите минимальную версию Flutter в pubspec.yaml и добавьте запись в CHANGELOG.md:
environment: flutter: ">=3.44.0"
DSL AGP 9 (opt-in)
Flutter Gradle Plugin переписан на публичных API AGP 9. Включить новый DSL можно, когда все плагины проекта поддерживают Built-in Kotlin.
Значения по умолчанию для новых проектов
Параметр | Значение |
|---|---|
Android Gradle Plugin | 9.3.1 |
Gradle | 9.5.0 |
Kotlin Gradle Plugin | 2.4.20 |
Java (минимум) | 17 |
| API 36 |
| API 24 |
Инструменты разработчика и AI
Агент-плагины. Официальные плагины для AI-агентов включают skills, готовую конфигурацию MCP-серверов Dart и Flutter и правила для агентов. Например, правило
flutter-hot-reloadвызываетhot_reloadилиhot_restartчерез MCP после правок вlib/.Skills в пакетах. Пакет может поставлять skills в каталоге
skills/, они устанавливаются командойdart run skills@ get. Для проверкиSKILL.mdв CI есть линтерpackage:skills_lint.Analysis Server ускорился: благодаря дедупликации поиска каталогов пакетов холодное переименование в workspace DevTools из 16 контекстов стало на 57% быстрее (с 2,0 до 0,9 с).
DevTools сам перешёл на
material_uiиcupertino_ui.
Экосистема
Genkit Dart 1.0: типобезопасность от клиента до сервера, потоковые flows, tool calling и трассировка через OpenTelemetry.
MediaPipe на pub.dev: 20 задач для зрения, аудио и текста, которые работают на устройстве поверх LiteRT и LiteRT-LM.
Больше миллиона Flutter-приложений опубликовано в Google Play.
Среди кейсов, которые упоминает команда: Holafly (монорепозиторий за три месяца, больше 2 млн пользователей в месяц), Knowunity (Flutter + Go + Gemini, 8–10 млн активных учеников в месяц), Buzz, а также приложения Eloquent и Foresight от Google AI Edge.
У Flutter появился публичный процесс RFC (репозиторий
flutter/rfc): предложить крупное изменение во фреймворк теперь можно по прозрачной процедуре.
Breaking changes и deprecations
Коротко о том, что может сломаться или начать выдавать предупреждения:
deprecated
material.dart,cupertino.dartи делегатыflutter_localizations;строгая обработка
Intentв release-сборках на Android;enabledвместоonChanged: nullвDropdownButtonFormField(material_ui);мягкие переносы показывают видимый дефис;
WidgetSpanв RTL-тексте выводится в естественном порядке чтения;SafeAreaв мобильных браузерах учитывает вырезы и индикатор home приviewport-fit=cover;удалено устаревшее свойство
ignoringSemanticsуIgnorePointer,AbsorbPointer,SliverIgnorePointerи их render-объектов;computeDryBaselineвRenderProxyBoxи родственных объектах возвращаетnull, если у потомка нет baseline (раньше срабатывал assertion).
Подробнее: Breaking changes.
Заключение
Flutter 3.50 сложно назвать релизом одной громкой фичи. Скорее это год работы, который дошёл до финиша: нативный interop без каналов стал стабильным, Wasm заработал за пределами Chromium, а дизайн-системы окончательно уходят из ядра в отдельные пакеты. Для большинства проектов обновление пройдёт спокойно, но о переезде на material_ui и SPM лучше позаботиться заранее.
Домашнее задание:
flutter upgrade;dart fix --apply --code=migrate_design_widgetsв отдельной ветке;flutter build web --no-deprecated-js-interop, если у вас есть веб;проверить, что iOS-проект собирается через SPM.
