Вышел 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+ (Ура!), появились флаг --web-content-hash и превью WebParagraph (CanvasKit примерно на 31% меньше)

Дизайн-системы

Встроенные material.dart и cupertino.dart объявлены deprecated, flutter create теперь генерирует проект на material_ui

iOS

Задержка касаний меньше на один кадр (−16 мс на 60 Гц, −8 мс на 120 Гц), исправлены проблемы с отладкой на iOS 27

Android

Release-сборки строже относятся к Intent, улучшен HCPP, есть opt-in для DSL AGP 9

Engine

SDF-рендеринг в Impeller на десктопе, texture arrays во Flutter GPU, много исправлений OpenGL

Эмбеддеры

Vulkan external textures и FlutterEngineNotifyLowMemoryWarning

Что сделать сразу после обновления

Если времени мало, вот короткий чек-лист:

  • 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

После этого можно смело ставить агрессивное кэширование. Рекомендации из документации:

Файлы

Cache-Control

Бандлы и ассеты с хешем

max-age=31536000, immutable

index.html, flutter_bootstrap.js

no-cache

Пример для 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

В типичном случае меняются зависимости и импорты:

ДО

ПОСЛЕ

import 'package:flutter/material.dart';

import 'package:material_ui/material_ui.dart';

import 'package:flutter/cupertino.dart';

import 'package:cupertino_ui/cupertino_ui.dart';

Примечание: если часть зависимостей ещё не переехала, документация предлагает переходный 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

flutter.compileSdkVersion / flutter.targetSdkVersion

API 36

flutter.minSdkVersion

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.

Комментарии (0)