Flutter 3.50: что нового и что сделать при обновлении

Егор Долгов · 12.11.2026
изображение: flutter.dev
Flutter 3.50 · Dart 3.14
11 ноября 2026
Flutter 3.50 за три минуты
Субтитры
Flutter 3.50 вышел 11 ноября вместе с Dart 3.14. За три минуты — что изменилось и что проверить в своём проекте.

Flutter 3.50 вышел 11 ноября 2026 года вместе с Dart 3.14. Здесь только то, что касается вашего кода и сборки: что может сломаться, что обновить и какие новые возможности пригодятся. Полный список изменений — в анонсе и release notes .

Содержание:

  1. Перед обновлением
  2. material.dart устарел: переходим на material_ui
  3. Android: старый проект может перестать собираться
  4. Android: release-сборка не берёт маршрут из Intent
  5. iOS и macOS: CocoaPods и Mac на Intel
  6. Веб: Wasm в Safari и Firefox, хеши файлов, старый JS-interop
  7. Что сломается в коде
  8. Новое в коде
  9. Dart 3.14
  10. Коротко об остальном
  11. Итог

1. Перед обновлением

Обновите Flutter командой flutter upgrade . Если вы держите несколько версий Flutter через FVM, поставьте 3.50 рядом со старой — так проще вернуться, если проект не соберётся: FVM: несколько версий Flutter .

Дальше разделы в порядке важности: сначала то, без чего проект может не собраться, потом новые возможности.

2. material.dart устарел: переходим на material_ui

Виджеты Material и Cupertino переехали из Flutter в отдельные пакеты material_ui и cupertino_ui . С 3.50 старые библиотеки официально устарели : строка import 'package:flutter/material.dart'; работает, но flutter analyze помечает её как устаревшую ( deprecated_member_use ).

Перевести проект можно одной командой в папке проекта:

dart fix --apply --code=migrate_design_widgets

Она заменяет импорты. Если пакет не появился в pubspec.yaml , добавьте его сами и запустите dart fix ещё раз:

flutter pub add material_ui

Для Cupertino — то же с cupertino_ui . Импорт после перехода:

import 'package:material_ui/material_ui.dart';
  • Команда flutter create в 3.50 создаёт проекты сразу с material_ui .
  • Переводы виджетов ( flutter_localizations ) тоже переехали в пакет: вместо списка делегатов достаточно localizationsDelegates: GlobalMaterialLocalizations.delegates из material_ui .
  • Последние версии на дату проверки: material_ui 1.11.0, cupertino_ui 1.1.6. Оба требуют Flutter 3.47 или новее.

Если какой-то пакет из ваших зависимостей ещё использует старый material.dart , его виджеты не увидят тему и переводы из material_ui . На время, пока пакет не обновится, есть мост `MaterialUiCompatibilityBridge` : им оборачивают приложение в MaterialApp.builder . Мост временный — сам он тоже помечен устаревшим — и не поможет, если пакет принимает или возвращает старые типы Material в своём API.

3. Android: старый проект может перестать собираться

Flutter 3.50 при сборке проверяет версии инструментов Android и останавливает сборку, если они ниже минимальных:

  • Android Gradle Plugin (AGP) — 9.1.1;
  • Gradle — 9.3.1;
  • Java — 17;
  • Kotlin Gradle Plugin — 2.4.10 (проверяется, только если проект подключает его сам, а не использует встроенный в AGP Kotlin).

В Flutter 3.47 минимумы были AGP 8.11.1 и Gradle 8.14.0, так что проектам на AGP 8 придётся перейти на девятую версию.

Так выглядит ошибка, если в проекте AGP 8.11.1:

Error: Your project's Android Gradle Plugin version (Android Gradle Plugin version 8.11.1) is lower than Flutter's minimum supported version of Android Gradle Plugin version 9.1.1. Please upgrade your Android Gradle Plugin version.
  • Версия AGP — в файле android/settings.gradle.kts (или settings.gradle ), в блоке plugins , у плагина com.android.application . В старых проектах без этого блока — в android/build.gradle , строка classpath 'com.android.tools.build:gradle:<версия>' .
  • Версия Kotlin Gradle Plugin — там же, в блоке plugins , у плагина org.jetbrains.kotlin.android .
  • Версия Gradle — в android/gradle/wrapper/gradle-wrapper.properties , строка distributionUrl .
  • Флаг --android-skip-build-dependency-validation отключает проверку, но собрать проект со старыми инструментами он не гарантирует.

Новый проект flutter create в 3.50 получает AGP 9.3.1, Gradle 9.5.0, Kotlin Gradle Plugin 2.4.20 и Java 17. Удобный способ узнать нужные значения для своего проекта — создать рядом пустой проект и сравнить эти два файла. Отдельного руководства по переходу существующего проекта на AGP 9 в документации Flutter нет. Если после перехода в android/gradle.properties появились строки android.builtInKotlin=false и android.newDsl=false , их добавил сам Flutter: так старый проект продолжает собираться без встроенного Kotlin. Как перейти на него — страница миграции .

4. Android: release-сборка не берёт маршрут из Intent

Если другое приложение открывало ваше через Intent с маршрутом ( EXTRA_INITIAL_ROUTE ) или точкой входа, в release-сборке это больше не работает : Flutter принимает такие данные только от самого приложения. В debug и profile проверка не мешает работе, но если Intent её не прошёл, в лог пишется предупреждение — так затронутые места видно ещё до релиза.

Что делать: открывать нужный экран через обычные deep links — Intent.ACTION_VIEW с адресом, который объявлен в <intent-filter> манифеста.

Флаги движка ( --dart-flags ) release-сборка из Intent тоже больше не читает . Для замеров производительности используйте flutter run --profile .

5. iOS и macOS: CocoaPods и Mac на Intel

  • CocoaPods. 2 декабря 2026 года репозиторий CocoaPods перестаёт принимать новые версии библиотек ; уже работающие сборки не сломаются. Flutter с 3.44 по умолчанию использует Swift Package Manager. Если вы его выключали, Flutter 3.50 при сборке для iOS и macOS предупреждает: «Enabling Swift Package Manager will be required in a future version of Flutter». Включите его обратно:
  flutter config --enable-swift-package-manager

Если SwiftPM выключен в pubspec.yaml проекта ( enable-swift-package-manager: false в разделе flutter: config: ), уберите эту строку — она важнее общей настройки . Для зависимостей, которые ещё не поддерживают SwiftPM, Flutter по-прежнему использует CocoaPods.

  • Mac на Intel. Flutter отказывается от поддержки Mac на процессорах Intel: при первом запуске команды flutter на таком компьютере 3.50 один раз предупреждает, что в будущей версии понадобится Mac с Apple Silicon. Если ваше macOS-приложение не нужно пользователям Mac на Intel, release- и profile-сборки можно делать только для Apple Silicon: flutter config --enable-macos-arm64-only .

6. Веб: Wasm в Safari и Firefox, хеши файлов, старый JS-interop

Wasm в Safari и Firefox. Сборка flutter build web --wasm теперь может работать в Safari 26.2+ и Firefox 147+, но по умолчанию эти браузеры получают JavaScript-версию. Chrome и Edge (Chromium 137+) запускают Wasm по умолчанию. Чтобы включить Wasm для Safari и Firefox, создайте в папке web проекта файл flutter_bootstrap.js — Flutter возьмёт его вместо стандартного ( подробнее ):

{{flutter_js}}
{{flutter_build_config}}

_flutter.loader.load({
  config: {
    wasmAllowList: { blink: true, webkit: true, gecko: true },
  },
});

Хеши в именах файлов. Флаг --web-content-hash ( flutter build web --web-content-hash ) добавляет к именам файлов сборки хеш содержимого — чтобы после выкладки новой версии браузер не брал старые файлы из кэша. У index.html , flutter_bootstrap.js и flutter.js хеша нет: документация советует отдавать их с заголовком Cache-Control: no-cache , а файлы с хешем — с public, max-age=31536000, immutable . С отложенными импортами ( deferred as ) флаг пока не работает.

Старый JS-interop. dart:html , dart:js , dart:js_util и пакет js устарели и будут удалены из Dart; с Wasm они не работают уже сейчас. Замена — package:web и dart:js_interop , руководство по переходу . Найти старые импорты в проекте и его зависимостях: flutter build web --no-deprecated-js-interop — сборка завершится ошибкой на таких импортах.

7. Что сломается в коде

Удалён ignoringSemantics у IgnorePointer , AbsorbPointer и SliverIgnorePointer . Чтобы скрыть часть экрана от программ чтения с экрана, оберните её в ExcludeSemantics ( миграция ):

import 'package:material_ui/material_ui.dart';

class LockedForm extends StatelessWidget {
  const LockedForm({super.key});

  @override
  Widget build(BuildContext context) {
    // Было: IgnorePointer(ignoringSemantics: true, child: ...)
    return const ExcludeSemantics(
      child: IgnorePointer(
        child: TextField(),
      ),
    );
  }
}

Мягкие переносы теперь видны. Если строка переносится по мягкому переносу (символ U+00AD), на Android, iOS и компьютерах Flutter рисует дефис . Вернуть прежний вид — параметр hyphens: Hyphens.hidden :

import 'package:material_ui/material_ui.dart';

// \u00AD — мягкий перенос: место, где слово можно разорвать.
const longWord = 'Достопри\u00ADмечательности';

class Hyphenated extends StatelessWidget {
  const Hyphenated({super.key});

  @override
  Widget build(BuildContext context) {
    return const Column(
      children: [
        // С 3.50: при переносе виден дефис.
        SizedBox(width: 120, child: Text(longWord)),
        // Как раньше: перенос без дефиса.
        SizedBox(width: 120, child: Text(longWord, hyphens: Hyphens.hidden)),
      ],
    );
  }
}

Ещё два изменения касаются немногих:

  • WidgetSpan внутри текста справа налево (арабский, иврит) теперь стоит на правильном месте . Если вы раньше сдвигали такие виджеты вручную, уберите этот обход.
  • В вебе с viewport-fit=cover в web/index.html SafeArea и MediaQuery.paddingOf теперь учитывают вырез экрана . Если интерфейс уехал под вырез, оберните его в SafeArea — или уберите viewport-fit=cover из тега <meta name="viewport"> , если отображение до краёв экрана не нужно.

8. Новое в коде

Все эти API — в самом Flutter, их видно и через импорт material_ui .

Высота строк ListView.separated заранее. itemExtentBuilder заранее сообщает высоту каждого элемента — список быстрее прокручивается к далёким позициям. Номера идут вперемешку: чётные — строки, нечётные — разделители. Для 100 строк элементов 199, на номер за пределами надо вернуть null :

import 'package:material_ui/material_ui.dart';

class ContactList extends StatelessWidget {
  const ContactList({super.key});

  @override
  Widget build(BuildContext context) {
    return ListView.separated(
      itemCount: 100,
      itemBuilder: (context, index) => ListTile(title: Text('Контакт $index')),
      separatorBuilder: (context, index) => const Divider(height: 1),
      itemExtentBuilder: (index, dimensions) {
        if (index >= 199) return null;
        return index.isEven ? 56 : 1;
      },
    );
  }
}

Настройка «Уменьшить движение». MediaQuery.reduceMotionOf(context) — включил ли пользователь в настройках iOS уменьшение движения. Сам Flutter свои анимации из-за этого не выключает — решение за вами:

import 'package:material_ui/material_ui.dart';

class Pill extends StatelessWidget {
  const Pill({super.key, required this.selected});

  final bool selected;

  @override
  Widget build(BuildContext context) {
    final reduceMotion = MediaQuery.reduceMotionOf(context);
    return AnimatedContainer(
      duration: reduceMotion ? Duration.zero : const Duration(milliseconds: 300),
      width: selected ? 120 : 60,
      height: 40,
      color: selected ? Colors.blue : Colors.grey,
    );
  }
}

Свой фон под диалогом. barrierBuilder позволяет обернуть затемнение под диалогом, например размыть экран. В 3.50 он есть у showGeneralDialog ; у showDialog из material_ui 1.11.0 его пока нет, хотя анонс и release notes говорят иначе:

import 'dart:ui';

import 'package:material_ui/material_ui.dart';

void showBlurDialog(BuildContext context) {
  showGeneralDialog<void>(
    context: context,
    barrierDismissible: true,
    barrierLabel: 'Закрыть',
    barrierBuilder: (context, details, barrier) => BackdropFilter(
      filter: ImageFilter.blur(sigmaX: 6, sigmaY: 6),
      child: barrier,
    ),
    pageBuilder: (context, animation, secondaryAnimation) => const Center(
      child: Card(
        child: Padding(padding: EdgeInsets.all(24), child: Text('Привет!')),
      ),
    ),
  );
}

Цифровая клавиатура для PIN-кода. TextInputType.numberWithOptions(password: true) на Android открывает цифровую клавиатуру для пароля. На iOS, в вебе и на компьютерах параметр ничего не меняет:

import 'package:material_ui/material_ui.dart';

class PinField extends StatelessWidget {
  const PinField({super.key});

  @override
  Widget build(BuildContext context) {
    return const TextField(
      keyboardType: TextInputType.numberWithOptions(password: true),
      obscureText: true,
      decoration: InputDecoration(labelText: 'PIN-код'),
    );
  }
}

Заглушка, пока картинка грузится. У DecorationImage появился placeholder — изображение, которое видно до загрузки основного. Картинку assets/avatar.png нужно добавить в проект и указать в pubspec.yaml :

import 'package:material_ui/material_ui.dart';

class Avatar extends StatelessWidget {
  const Avatar({super.key, required this.url});

  final String url;

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 64,
      height: 64,
      decoration: BoxDecoration(
        shape: BoxShape.circle,
        image: DecorationImage(
          image: NetworkImage(url),
          placeholder: const AssetImage('assets/avatar.png'),
          fit: BoxFit.cover,
        ),
      ),
    );
  }
}

Код из письма. AutofillHints.emailOTPCode подсказывает автозаполнению Android, что в поле вводят одноразовый код из письма.

Отключённый DropdownButtonFormField . В material_ui у него появился параметр enabled , а onChanged стал необязательным. Старый код с onChanged: null продолжает работать . Учтите разницу: enabled: false выключает у поля собственную проверку ( autovalidateMode ); если на отключённом поле нужны ошибки через AutovalidateMode.always , оставьте onChanged: null .

9. Dart 3.14

Новых возможностей языка в Dart 3.14 нет . Что пригодится:

  • Исправление в анализе типов. Подшаблон в шаблоне списка, словаря или записи больше не уточняет тип всего сопоставленного свойства. Исправление действует на любой код, независимо от версии языка в pubspec.yaml , поэтому после обновления анализатор может показать новые ошибки в коде с switch и case по шаблонам.
  • dart migrate поднимает версию SDK в pubspec.yaml и правит код под неё. Без флага не запускается: посмотреть изменения — dart migrate --dry-run , записать — dart migrate --apply . За один запуск версия поднимается на одну минорную (например, с ^3.12.0 до ^3.13.0 ).

10. Коротко об остальном

  • Отклик на касания в iOS. По данным анонса, на iOS изображение после касания появляется на кадр раньше: на 16 мс при 60 Гц и на 8 мс при 120 Гц. Делать ничего не нужно.
  • Hybrid Composition++ — экспериментальный (с Flutter 3.44) способ показывать нативные Android-представления (platform views) внутри Flutter; в 3.50 его доработали. По умолчанию выключен; нужен Android 14 (API 34) и Impeller на Vulkan. Как включить .
  • Авторам плагинов: Kotlin Gradle Plugin пора заменить встроенным Kotlin AGP — руководство .

11. Итог

  • Переведите импорты на material_ui : dart fix --apply --code=migrate_design_widgets .
  • Android: AGP не ниже 9.1.1, Gradle не ниже 9.3.1, Java 17 — иначе сборка остановится.
  • iOS: держите Swift Package Manager включённым — с 2 декабря 2026 года CocoaPods не получает новых версий библиотек.
  • Проверьте код на удалённый ignoringSemantics и мягкие переносы; веб-проектам — замените dart:html и package:js .

Вопросы и предложения по статье присылайте на contact@flutter.su.