You Have Until November to Delete package:flutter/material.dart
material_ui 1.0 is on pub.dev, the in-SDK libraries die in November, and dart fix only solves the easy half.
import 'package:flutter/material.dart'; is the most-typed line in the Flutter ecosystem. It's the first line of nearly every widget file, every tutorial, every StackOverflow answer, every Codelab Google has ever published. It is muscle memory.
In November, it starts throwing deprecation warnings.
Flutter 3.47 landed on August 12 with material_ui 1.0 and cupertino_ui 1.0 on pub.dev — the same widgets you already use, no longer welded to the SDK. The release notes make it sound like a one-liner: run dart fix --apply --code=migrate_design_widgets and go get coffee.
That’s true for your own source files. It is emphatically not true for your dependency graph, your localizations setup, or your widget tests. Here’s the part that isn’t in the announcement.
What actually changed
Material and Cupertino have lived inside the core SDK since the beginning. That coupling had a cost the Flutter team has been increasingly blunt about: design-system fixes could only ship on the quarterly SDK cadence, and outside contributions moved at the speed of a monorepo that also contains an entire rendering engine.
So they cut them loose. As of 3.47:
material_uiandcupertino_uiare standalone packages at 1.0 on pub.dev.- They ship on their own weekly release schedule, independent of the SDK.
- Contributions — frozen since April to keep the migration clean — are now open again.
- You can adopt new Material and Cupertino widget styles without upgrading your entire Flutter SDK.
The core SDK still bundles the old libraries in 3.47, so nothing breaks today. This release is opt-in. But the in-SDK versions are scheduled for formal deprecation in the Fall stable release in November. You have roughly three months of grace.
That last row is the whole point of the exercise. Everything else is logistics.
The happy path
For a self-contained app, the migration really is three commands:
flutter upgrade
flutter pub add material_ui # and cupertino_ui, if you use it
dart fix --apply --code=migrate_design_widgetsThe fix rewrites your imports across the project. Nothing else in your widget code changes — the libraries in material_ui are byte-for-byte the ones you were already using, which is exactly why the team froze contributions back in April.
One wrinkle worth knowing before you hit it: the migration tool has a known early bug updating pubspec.yaml. If it fails there, add the packages manually with flutter pub add material_ui first, then run dart fix --apply again. It's documented, but it's a footnote in a twelve-minute release post, which is to say most people will discover it as a confusing error message instead.
Localizations moved too — and the new API is better
This one caught me off guard, because flutter_localizations felt like infrastructure rather than design. But the delegates and translated strings for Material and Cupertino widgets were always design-system concerns, so they moved into the design-system packages.
Before:
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter/material.dart';
// ...
localizationsDelegates: const <LocalizationsDelegate<dynamic>>[
GlobalCupertinoLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],After:
import 'package:material_ui/material_ui.dart';
// ...
localizationsDelegates: GlobalMaterialLocalizations.delegates,GlobalMaterialLocalizations.delegates now rolls up the Cupertino and Widgets delegates for you. Three lines of boilerplate collapse into one. A rare migration that leaves the code shorter than it found it.
The hard part: your dependencies haven’t migrated
Here’s where the one-command story falls apart.
Get Antoni Wijaya’s stories in your inbox
Join Medium for free to get updates from this writer.
Your app now imports material_ui. A third-party package in your pubspec.yaml — a date picker, a chart library, some UI kit whose maintainer last shipped in March — still imports package:flutter/material.dart. Both libraries exist in 3.47. So both are in your build.
You now have two parallel lineages of Material types in one widget tree, and the theme your app configures is not necessarily the theme that package’s widgets read.
The escape hatch is MaterialUiCompatibilityBridge, which both packages ship for exactly this window:
import 'package:material_ui/material_ui.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF6750A4)),
),
builder: (BuildContext context, Widget? child) {
return MaterialUiCompatibilityBridge(child: child!);
},
home: const HomeScreen(),
);
}
}Wrap once at the root, migrate on your own schedule, stop waiting on maintainers.
And if you are one of those maintainers: ship this as a major version bump. Not a patch, not a minor. Flutter’s own guidance says to treat the move as a major release for ecosystem packages, and every day you don’t is a day your users are stuck on the compatibility bridge because of you.
Your tests will break before your app does
This is the part I’d flag hardest, because it’s the part nobody warns you about and the failure looks like something else entirely.
flutter_test stayed in the SDK. Your test files did not get migrated by anything, and they carry their own imports. So the very common shape of a widget test —
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart'; // ← stale— against app code that now resolves ElevatedButton from package:material_ui gives you two structurally identical but distinct Dart types with the same name. find.byType(ElevatedButton) matches zero widgets. Your test fails with "expected 1, found 0" while the screenshot in your head shows a button sitting right there. Dart's type identity is by declaration, not by shape, and this is the ugliest possible way to be reminded of that.
Check your test/ directory imports by hand. dart fix fixes what it's pointed at; make sure it was pointed at everything.
The second trap is golden tests, and it’s a timing problem rather than a code problem. Flutter 3.47 also made Impeller the default renderer on macOS, Windows, and Linux, added Signed Distance Function text rendering on desktop, and turned on wide-gamut color by default on macOS. Every one of those changes moves pixels. If you upgrade the SDK and migrate the design packages in the same commit, your golden diffs will be a soup of two unrelated causes and you’ll spend an afternoon blaming the wrong one.
Do it in two PRs. Upgrade the SDK, re-baseline goldens, confirm green. Then migrate the design packages. The second diff should be nearly empty — and if it isn’t, that’s real signal instead of noise.
While you’re in there
3.47 quietly bundled several other things that will bite you this fall, mostly on Apple platforms:
- Minimum OS versions bumped: iOS 13 → 15, macOS 10.15 → 12.
UIScenelifecycle is now mandatory under the iOS 27 SDK. Apps built with Xcode 27 that haven't adopted it fail to launch. The Flutter CLI handles the migration automatically for most projects, but customAppDelegatecode or plugins still on the legacy application lifecycle need manual work.- CocoaPods is in maintenance mode. 92 of the top 100 iOS plugins are already on Swift Package Manager. If you turned SwiftPM off at some point, try
flutter config --enable-swift-package-manageragain. - Intel Macs are winding down. Warnings today, errors later.
None of these are the headline. All of them are cheaper to handle in August than in the week Xcode 27 goes stable.
This isn’t a refactor. It’s a strategy change.
The release notes frame the un-bundling as a velocity fix — faster releases, easier contributions. True, and a bit of a understatement. The real intent is one line in Flutter’s 2026 roadmap:
…lay the groundwork for a style-neutral Flutter core widget catalog, making it easier to build custom design systems in the future.
Read that again. Google is deliberately demoting its own design system to peer status with whatever you build. For nearly a decade, Material was the path of least resistance in Flutter, and a generation of apps look the way they look because of it. That gravity is being switched off on purpose.
Why now? I think the answer is in the other half of the roadmap. Flutter is betting hard on GenUI and the A2UI protocol — interfaces generated at runtime by a model rather than assembled at compile time by you. They’re even investigating interpreted bytecode in the Dart runtime so slices of an app can be delivered on demand without an app store round trip. If UI is going to be produced dynamically against a widget catalog, a hard-coded design system sitting in the framework core is an obstacle, not a feature. The un-bundling and the agentic-apps push are the same bet placed twice.
The honest counterpoint: decoupling has a bill, and we’re going to pay it in dependency resolution. Three independently versioned moving parts — the SDK, material_ui, cupertino_ui, on two different cadences — where there used to be one. Weekly releases are wonderful until you're pinning versions across a monorepo and something in the middle of your graph disagrees. The JavaScript ecosystem has been running this experiment for fifteen years and the results are mixed. I still think it's the right call. I just don't think it's free, and the release notes don't mention the cost at all.
In two years, material_ui will be a package you choose — the same way you choose a state manager. That's either liberating or exhausting, depending on how long your pubspec.yaml already is.
Flutter 3.47 shipped August 12, 2026. Migration details are in the release notes and the breaking changes page; the strategy behind it is in the 2026 roadmap.









