Вместе с Flutter 3.50 вышел Dart 3.14. После насыщенных 3.12 и 3.13, где появились приватные именованные параметры и primary constructors, этот релиз посвящён не языку, а инструментам и платформам. Главная новинка — команда dart migrate, которая сама поднимает версию SDK в пакетах. Кроме того, сильно расширен dart:js_interop, ускорен Wasm, а dart:mirrors объявлен устаревшим.
dart --version # если Dart установлен отдельноflutter upgrade # если вы работаете с Flutter
Коротко о главном
Направление
Что изменилось
Миграции
Новая команда dart migrate пошагово поднимает SDK-констрейнт и применяет фиксы
CLI
dart run -e для однострочников, быстрее dart run/dart test с экспериментами, dart pub upgrade pkg@...
Web / interop
Много новых конвертеров в dart:js_interop, тип JSError, флаг --no-deprecated-js-interop
Wasm
try_table по умолчанию, обязательные wasm:js-string builtins, SIMD через v128
Библиотеки
Большое расширение Int32x4, rangeEquals для typed data, arm64e в dart:ffi
Deprecations
dart:mirrors помечен @deprecated
Breaking
Убраны случайно попавшие в 3.13 API Isolate.*Sync, изменились toString() и min/max у SIMD-типов
IDE
Analysis server быстрее стартует и быстрее ищет ссылки
Примечание: в changelog SDK для 3.14 нет раздела Language. Новых языковых фич в этом релизе нет, и обновление ничего не меняет в синтаксисе вашего кода.
dart migrate: обновление SDK одной командой
Раньше переход на новую версию Dart выглядел так: вручную поднять sdk: в pubspec.yaml, прогнать dart fix, разобраться с тем, что сломалось, и повторить для каждого пакета монорепозитория. В 3.14 этим занимается dart migrate.
Как работает команда:
-
поднимает нижнюю границу SDK-констрейнта по одной минорной версии за шаг: 3.12 → 3.13 → 3.14;
-
на каждом шаге сначала применяет подготовительные фиксы (
prepare), затем поднимает констрейнт (bump); -
необязательные фиксы очистки (
cleanup), которые переводят код на новые возможности языка, запускаются только по явному запросу; -
по умолчанию обрабатывает текущий каталог, но можно передать список каталогов пакетов;
-
зависимые друг от друга пакеты мигрируют синхронно, поэтому констрейнты остаются разрешимыми на каждом шаге. Если какая-то зависимость не поддерживает новую версию, пакет пропускается;
-
пакет уже должен требовать Dart 3.0 или новее.
Основные флаги:
# Посмотреть, что будет измененоdart migrate --dry-run# Применить измененияdart migrate --apply# Перепрыгнуть сразу через несколько версийdart migrate --apply --target-sdk=3.14# Дополнительно перевести код на новые языковые фичиdart migrate --apply --step=cleanup # или --step=all
Шаги можно запускать и по отдельности: --step=prepare, --step=bump, --step=cleanup или --step=all.
Пример: пакет на Dart 3.12, который мы хотим перевести на 3.14.
ДО
ПОСЛЕ dart migrate --apply --target-sdk=3.14
sdk: ^3.12.0
sdk: ^3.14.0
код, который сломался бы на 3.13/3.14
исправлен подготовительными фиксами
старый синтаксис конструкторов
остаётся как есть, пока не запущен --step=cleanup
Последняя строка важна. cleanup может, например, перевести классы на primary constructors из Dart 3.13. Это стилистическое изменение и часто большой дифф, поэтому по умолчанию оно не выполняется.
Примечание:
dart migrateне заменяетdart fix.dart fixприменяет точечные исправления в рамках текущей версии языка, аdart migrateпереводит пакет на следующую версию SDK. Список флагов можно посмотреть черезdart migrate --help.
Подробнее: dart migrate, issue #63240.
Другие улучшения CLI
dart run -e: Dart как однострочник
dart run научился выполнять код прямо из командной строки, без файла-скрипта. Импорты пакетов берутся из конфигурации текущего пакета, а если её нет, зависимости можно подтянуть на лету через -P / --package-constraint:
dart run -e 'print(DateTime.now().toUtc());'dart run -P http:^1.0.0 -e 'import "package:http/http.dart" as http;void main() async { final r = await http.get(Uri.parse("https://dart.dev")); print(r.statusCode);}'
Есть и флаг --offline. Подробнее в issue #64298.
Быстрее dart run и dart test
-
Скомпилированный исполняемый файл для
dart run :иdart testтеперь кэшируется в.dart_tool/dartdev/bin/(раньше в.dart_tool/pub/bin/) и пересобирается при изменении любого исходника. Раньше для path-зависимостей снапшот собирался на каждом запуске, а для hosted-пакетов с pathdependency_overrideмог оказаться устаревшим. -
Platform.scriptв таком запуске указывает на Dart-исходник, а не на kernel-снапшот, поэтому относительные пути работают так же, как при запуске из исходников. -
--enable-experiment=...больше не заставляет запускать код из исходников: для экспериментов собирается свой снапшот, и старт перестаёт замедляться.
dart pub upgrade для конкретных пакетов
dart pub upgrade http@^1.4.0 # обновить в рамках констрейнтаdart pub upgrade http@latest # до последней версииdart pub upgrade http@resolvable # выяснить, что мешает взять версию новее
Подробнее: dart-lang/pub#4569.
Analysis server
Сервер анализа стал заметно отзывчивее: быстрее работают поиск ссылок (Find References), старт, перезапуск после правки analysis_options.yaml и плагины анализатора. Сильнее всего это заметно в больших монорепозиториях. Фоновые задачи теперь реже блокируют форматирование и другие запросы редактора.
Formatter
-
dart formatучитывает эксперименты из окружающегоanalysis_options.yaml. -
Исправлено падение при форматировании enum с primary constructor, когда сохраняются висячие запятые.
-
Исправлена пропущенная висячая запятая у значений такого enum. Это изменение не зависит от версии языка, поэтому затронет и код на Dart 3.13.
-
Исправлено падение на строке ровно со 128 пробелами отступа. Кто-то это действительно нашёл.
Анализатор и линтер
-
Новое правило
future_sync_valueсоветует использоватьFuture.syncValueвместоFuture.sync, если колбэк возвращает синхронное значение (#61740). -
Анализатор выдаёт ошибку
js_interop_extension_constructor_js_annotation_has_no_effect, если@JSповесили наexternal-конструктор или фабрику extension type изdart:js_interop. Аннотацию нужно перенести на само объявление extension type.
// ДО: линтер предложит заменитьfinal f = Future.sync(() => 42);// ПОСЛЕfinal f = Future.syncValue(42);
Web и JS interop
Это самая объёмная часть релиза. Цель понятна: убрать причины держаться за старые dart:html и package:js и открыть дорогу к Wasm.
Аудит устаревшего interop
В dart2js (dart compile js) и в DDC появился флаг --[no-]deprecated-js-interop. С --no-deprecated-js-interop импорт устаревших библиотек становится ошибкой:
dart:html, dart:html_common, dart:indexed_db, dart:js, dart:js_util, dart:svg, dart:web_audio, dart:web_gl.
Кроме того, соответствующие условия dart.library.* в условных импортах и в bool.fromEnvironment становятся false. Во Flutter этот же флаг доступен в flutter build web, flutter run и flutter test --platform chrome.
dart compile js --no-deprecated-js-interop -o out.js web/main.dart
Подробнее: issue #63919.
Новое в dart:js_interop
Конвертация массивов примитивов. Раньше приходилось писать .map(...).toList() с ручным приведением каждого элемента. Теперь есть готовые расширения для num, String и bool, включая nullable-варианты:
import 'dart:js_interop';final JSArray jsNums = [1, 2, 3].toJS;final List ints = jsNums.toDartIntList;final List doubles = jsNums.toDartDoubleList;final JSArray jsTags = ['dart', 'wasm'].toJS;final List tags = jsTags.toDartStringList;
Промисы и FutureOr.
-
Статические методы
JSPromise.resolveиJSPromise.reject. -
JSArray.fromAsyncсоздаёт массив из асинхронного генератора или из списка асинхронных значений. -
Расширения
toJSPromiseOrValueиtoDartFutureOrконвертируют значения, которые могут быть как синхронными, так и асинхронными, без лишнего оборачивания в промис. -
Function.toJSиFunction.toJSCaptureThisподдерживают функции, которые возвращаютFuture, гдеT— этоvoidили подтипJSAny?. ВозвращаемыйFutureавтоматически превращается вJSPromise(#63496). -
При конвертации
FutureвJSPromiseJS-ошибки (JSAny) теперь передаются в rejected-промис как есть, в JSErrorзаворачиваются только Dart-ошибки (#61353).
@JS()external set loadUser(JSFunction f);void main() { // Раньше пришлось бы вручную вызывать .toJS у Future внутри функции loadUser = ((JSString id) async { final user = await fetchUser(id.toDart); return user.name.toJS; }).toJS;}
Прочее:
-
новый тип
JSErrorдля JS-классаError; -
геттеры
JSFunction.lengthиJSFunction.name; -
toJSVarArgsиtoJSCaptureThisVarArgsсобирают аргументы JS-функции в массив, а не раскладывают их по отдельным параметрам; -
external-члены расширений можно объявлять на type-параметрах, ограниченных JS interop-типом, напримерextension on T(#61248); -
isAпроверяет одновременноArray.isArrayиinstanceof, и значение считается массивом, если сработало хотя бы одно условие (#62699).
dart2wasm
-
По умолчанию используются стандартизированные инструкции обработки исключений WebAssembly
try_table(#54394). -
Теперь обязательна нативная поддержка builtins
wasm:js-string. Заодно добавлены обходы баговWebAssembly.compileStreamingв Safari 26.5 и более ранних версиях (#63543). Вместе с изменениями во Flutter 3.50 это и делает возможным Wasm в Safari. -
Int32x4,Float32x4,Float64x2и их списки компилируются в аппаратные 128-битные SIMD-инструкции WebAssembly (v128) (#64170). -
dart.library.isolateпри компиляции в Wasm равноfalseв условных импортах (#64328). -
dart compile wasm --standaloneбольше не поддерживаетdart:js_interop.
Библиотеки
dart:typed_data: SIMD без костылей
Int32x4 долго был «бедным родственником» Float32x4. В 3.14 он получил почти полный набор операций:
-
конструкторы
Int32x4.splatиInt32x4.zero(); -
операторы
~, унарный-, сдвиги<<,>>,>>>; -
abs,andNot,min,max; -
сравнения по полосам (lane-wise):
equal,notEqual,lessThan,lessThanOrEqual,greaterThan,greaterThanOrEqual. Каждое возвращает-1в полосах, где условие выполнено, и0в остальных; -
геттеры
anyTrueиallTrue.
Кроме того, у всех целочисленных typed-списков и у ByteData появился метод rangeEquals для быстрого сравнения диапазонов (#64095).
import 'dart:typed_data';final a = Int32x4(1, -2, 3, -4);final mask = a.lessThan(Int32x4.zero()); // [0, -1, 0, -1]print(mask.anyTrue); // truefinal b = a.abs(); // полосы: 1, 2, 3, 4print(b.x); // 1print(b); // 'V128' — см. breaking changes нижеfinal header = Uint8List.fromList([0x89, 0x50, 0x4E, 0x47]);// rangeEquals позволяет сравнить, например, сигнатуру файла// с началом буфера без копирования подсписков.
dart:ffi
-
NativeFinalizer.callbackвозвращает колбэк, с которым был создан финализатор (#63811). -
Abi.iosArm64eиAbi.macosArm64eдля архитектурыarm64eс pointer authentication (#63420).
dart:io
На macOS запуск процессов переведён с fork + exec на posix_spawn. Из-за этого PATH теперь разрешается по окружению родительского процесса, а не дочернего. Рекомендация простая: передавайте в Process.start и соседние методы абсолютный путь к исполняемому файлу.
dart:mirrors объявлен устаревшим
Библиотека помечена @deprecated и будет удалена в одном из будущих релизов (#44489). Во Flutter и в вебе она давно недоступна, так что это касается в основном серверного и CLI-кода. Если в CI стоит dart analyze --fatal-infos, после обновления он упадёт на deprecated_member_use. Самое время переходить на кодогенерацию.
Breaking changes
-
dart:isolate: из публичного API убраны экспериментальныеIsolate.runSync,Isolate.create,Isolate.shutdownSync,Isolate.pinToCurrentThread,Isolate.isPinnedToCurrentThread,Isolate.runEventLoopSync,Isolate.onEventиIsolate.handleEvent. В Dart 3.13 они попали в релиз случайно.NativeCallable.isolateGroupBoundтеперь доступен только с VM-флагом--experimental-shared-data(#64285). -
dart:typed_data:toString()уFloat32x4,Int32x4иFloat64x2теперь возвращает'V128', а не список значений полос. Прежний вывод отличался на разных платформах и нигде не был специфицирован (#63847). -
dart:typed_data:min/maxуFloat32x4иFloat64x2следуют IEEE 754 на всех платформах: если в полосеNaN, результатом будетNaN, а-0.0считается меньше+0.0(#63962). -
dart:io(macOS): изменилось разрешениеPATHпри запуске процессов (см. выше).
Экосистема и документация
Между релизами 3.13 и 3.14 в блоге Dart вышли:
-
Bringing Primary Constructors to Dart (Bob Nystrom) — история самой ожидаемой фичи 3.13;
-
Skills CLI 1.0 (Jake Macdonald) — как поставлять skills для AI-агентов вместе с пакетами;
-
Announcing Genkit Dart 1.0 (Chris Gill) — фреймворк для агентных приложений на Dart и Flutter;
-
Google Summer of Code 2026 results (Jonas Finnemann Jensen).
На dart.dev появились руководства по dart migrate, по skills для AI-агентов (package:skills, package:skills_lint, Developer Knowledge MCP server), по data-driven фиксам, по компиляции и деплою Wasm, примеры миграции на package:web, установка Dart на Windows через winget, а также описание нескольких include в analysis_options.yaml. У страницы changelog появились фильтры, поиск и постоянные ссылки. Также обновлён DartPad.
Подробнее: What’s new на dart.dev.
Заключение
Dart 3.14 — спокойный инфраструктурный релиз, и в этом его ценность. Вместо новых языковых конструкций команда занялась тем, чтобы обновление стало рутинной операцией (dart migrate), а переход веба на Wasm перестал упираться в старый interop. Если вы уже на 3.13, обновление почти ничего не сломает. Исключения — экзотические SIMD-сценарии, случайно опубликованные API изолятов и dart:mirrors.
Что попробовать:
-
dart migrate --dry-runна одном пакете; -
dart compile js --no-deprecated-js-interopилиflutter build web --no-deprecated-js-interop; -
dart run -eвместо очередногоscratch.dart.
Источник: habr.com