Flutter 3.50: что нового и что сделать при обновлении
Егор Долгов · 12.11.2026Flutter 3.50 вышел 11 ноября вместе с Dart 3.14. За три минуты — что изменилось и что проверить в своём проекте.
Flutter 3.50 вышел 11 ноября 2026 года вместе с Dart 3.14. Здесь только то, что касается вашего кода и сборки: что может сломаться, что обновить и какие новые возможности пригодятся. Полный список изменений — в анонсе и release notes .
Содержание:
- Перед обновлением
- material.dart устарел: переходим на material_ui
- Android: старый проект может перестать собираться
- Android: release-сборка не берёт маршрут из Intent
- iOS и macOS: CocoaPods и Mac на Intel
- Веб: Wasm в Safari и Firefox, хеши файлов, старый JS-interop
- Что сломается в коде
- Новое в коде
- Dart 3.14
- Коротко об остальном
- Итог
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_ui1.11.0,cupertino_ui1.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.htmlSafeAreaи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.