TOPZNANIE

Цифровая практика без сложностей

Выберите город
MET Norway · CC BY 4.0
← На главную

· Общий обзор

Dart 3.14 — что нового в Dart?

Вместе с 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 <package>:<command> и dart test теперь кэшируется в .dart_tool/dartdev/bin/ (раньше в .dart_tool/pub/bin/) и пересобирается при изменении любого исходника. Раньше для path-зависимостей снапшот собирался на каждом запуске, а для hosted-пакетов с path dependency_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 в JSPromise JS-ошибки (JSAny) теперь передаются в rejected-промис как есть, в JS Error заворачиваются только 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.

Теги:

  • dart migrate
  • dart js interop
  • flutter
  • wasm
  • 3.14

Хабы:

  • Dart
  • Flutter
  • WebAssembly
  • Разработка мобильных приложений
  • Программирование