Вместе с 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
Коротко о главном
Направление |
Что изменилось |
|---|---|
Миграции |
Новая команда |
CLI |
|
Web / interop |
Много новых конвертеров в |
Wasm |
|
Библиотеки |
Большое расширение |
Deprecations |
|
Breaking |
Убраны случайно попавшие в 3.13 API |
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.
ДО |
ПОСЛЕ |
|---|---|
|
|
код, который сломался бы на 3.13/3.14 |
исправлен подготовительными фиксами |
старый синтаксис конструкторов |
остаётся как есть, пока не запущен |
Последняя строка важна. 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 <package>:<command>и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<JSNumber> jsNums = [1, 2, 3].toJS; final List<int> ints = jsNums.toDartIntList; final List<double> doubles = jsNums.toDartDoubleList; final JSArray<JSString> jsTags = ['dart', 'wasm'].toJS; final List<String> tags = jsTags.toDartStringList;
Промисы и FutureOr.
Статические методы
JSPromise.resolveиJSPromise.reject.JSArray.fromAsyncсоздаёт массив из асинхронного генератора или из списка асинхронных значений.Расширения
toJSPromiseOrValueиtoDartFutureOrконвертируют значения, которые могут быть как синхронными, так и асинхронными, без лишнего оборачивания в промис.Function.toJSиFunction.toJSCaptureThisподдерживают функции, которые возвращаютFuture<T>, где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 <T extends JSObject> on T(#61248);isA<JSArray>проверяет одновременно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); // true final b = a.abs(); // полосы: 1, 2, 3, 4 print(b.x); // 1 print(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.
dablow
Дарт развивается и становится всё лучше и лучше, это очень приятно) Последние годы слышу только про Дарт и Раст