[{"data":1,"prerenderedAt":6},["ShallowReactive",2],{"post-content-flutter-3-47-material-ui-migration":3},{"content":4,"lastModified":5},"\u003Cfigure>\n  \u003Cimg src=\"https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1587654780291-39c9404d746b?w=1200&q=80&auto=format&fit=crop\" alt=\"A heap of loose building bricks, separated from the set they came in\" loading=\"lazy\" \u002F>\n\u003C\u002Ffigure>\n\n\u003Cp>I put the \u003Ccode>material_ui\u003C\u002Fcode> migration off for a month because the release blog made it sound like one command. It is one command. I finally ran it last week, and the month of waiting turned out to be the interesting part, because the package did a lot of moving while I did nothing.\u003C\u002Fp>\n\n\u003Cp>Flutter 3.47 shipped on August 12 and pulled Material and Cupertino out of the core SDK. They live on pub.dev now as \u003Ccode>material_ui\u003C\u002Fcode> and \u003Ccode>cupertino_ui\u003C\u002Fcode>, with weekly releases planned instead of the quarterly SDK train. Nothing breaks today. The in-framework libraries still ship in 3.47 and are scheduled for formal deprecation in the Fall stable release in November, which is the deadline sitting under all of this.\u003C\u002Fp>\n\n\u003Ch2>The command works, the pubspec is the loose brick\u003C\u002Fh2>\n\n\u003Cp>The automated path is a dart fix rule that rewrites your imports. The class names do not change, so in theory that is the whole job.\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-bash\">dart fix --apply --code=migrate_design_widgets\nflutter pub add material_ui\nflutter pub add cupertino_ui   # only if you use Cupertino widgets\ndart fix --apply\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>Two things about that sequence. The \u003Ccode>pub add\u003C\u002Fcode> lines exist because the fix rule has a known early bug where it fails to add the new dependencies to your \u003Ccode>pubspec.yaml\u003C\u002Fcode>, so add them by hand and re-run. The second \u003Ccode>dart fix --apply\u003C\u002Fcode> is also deliberate: the first pass rewrites imports, the second cleans up what the rewrite disturbed, mostly import sorting and lint warnings.\u003C\u002Fp>\n\n\u003Ch2>The bridge is a theme bridge, not a type bridge\u003C\u002Fh2>\n\n\u003Cp>Your own code is the easy half. The other half is the third-party packages still importing \u003Ccode>package:flutter\u002Fmaterial.dart\u003C\u002Fcode>. That is where \u003Ccode>MaterialUiCompatibilityBridge\u003C\u002Fcode> comes in. Wrap your app inside \u003Ccode>MaterialApp.builder\u003C\u002Fcode>:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-dart\">import 'package:material_ui\u002Fmaterial_ui.dart';\n\nMaterialApp(\n  theme: ThemeData(\n    colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF6750A4)),\n  ),\n  builder: (BuildContext context, Widget? child) {\n    return MaterialUiCompatibilityBridge(child: child!);\n  },\n  home: const HomeScreen(),\n)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>You can wrap individual subtrees instead, which I did for one stubborn package.\u003C\u002Fp>\n\n\u003Cp>The bridge injects theme and localization data down the tree, so an unmigrated widget calling \u003Ccode>Theme.of(context)\u003C\u002Fcode> or \u003Ccode>MaterialLocalizations.of(context)\u003C\u002Fcode> still resolves and still looks right. What it does not do is reconcile types, and that is the part that bites.\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-dart\">import 'package:flutter\u002Fmaterial.dart' as legacy;\nimport 'package:material_ui\u002Fmaterial_ui.dart';\n\n\u002F\u002F Same names, two unrelated types. This will not compile.\nlegacy.ColorScheme toOldPath(ColorScheme modern) =&gt; modern;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>Dart enforces static typing across distinct libraries, so a \u003Ccode>ColorScheme\u003C\u002Fcode> from the old path and a \u003Ccode>ColorScheme\u003C\u002Fcode> from \u003Ccode>material_ui\u003C\u002Fcode> have no common supertype. Your own code fails loudly, which is fine. The quiet case is a dependency whose public API takes or returns those types, say a \u003Ccode>TextTheme\u003C\u002Fcode> parameter or a callback returning a \u003Ccode>FloatingActionButtonLocation\u003C\u002Fcode>. The bridge cannot reach into that signature. Sort your dependencies into two buckets: ones that only style their own output and work fine behind the bridge, and ones that put design types in their API. The second bucket is your critical path.\u003C\u002Fp>\n\n\u003Ch2>Read the version list before trusting the older advice\u003C\u002Fh2>\n\n\u003Cp>A lot of write-ups, including the ones I read in August, point out that \u003Ccode>material_ui\u003C\u002Fcode> 1.0.0 needed only Dart 3.12. The conclusion was appealing: migrate the UI packages on Flutter 3.44 now and upgrade the SDK later, as two separate jobs.\u003C\u002Fp>\n\n\u003Cp>That was true in August. It stopped being true. Here is the history, which pub.dev shows plainly and the release blog does not mention.\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-bash\">1.0.0   Aug 12   Flutter 3.44 \u002F Dart 3.12\n1.2.0   Sep 08   Flutter 3.44 \u002F Dart 3.12\n1.3.0   Sep 15   retracted\n1.4.0   Sep 22   Flutter 3.47 \u002F Dart 3.13\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>1.4.0 bumped the floor, and its changelog leads with that line. Run \u003Ccode>flutter pub add material_ui\u003C\u002Fcode> on Flutter 3.44 and nothing complains. It resolves down to 1.2.0 and says nothing, so you quietly miss six weeks of the fixes that justified decoupling in the first place.\u003C\u002Fp>\n\n\u003Cp>The retracted row deserves a mention. Retraction is only available for seven days after publication, and the version is not picked for new resolutions. If one is already in your \u003Ccode>pubspec.lock\u003C\u002Fcode> it keeps working until you run \u003Ccode>dart pub upgrade material_ui\u003C\u002Fcode>, and holding onto it on purpose means \u003Ccode>dependency_overrides\u003C\u002Fcode>. Use a caret constraint rather than a pin and you will never notice.\u003C\u002Fp>\n\n\u003Cp>Meanwhile the package shipped real fixes: SearchAnchor stopped letting slow suggestions overwrite newer ones, \u003Ccode>StyleVariant\u003C\u002Fcode> arrived for Material 3 Expressive, and DataTable got a \u003Ccode>sortIconBuilder\u003C\u002Fcode>. Under the old model each of those would have waited for a quarterly SDK release. Your builds now track two release trains instead of one.\u003C\u002Fp>\n\n\u003Ch2>Localizations got shorter while you were reading\u003C\u002Fh2>\n\n\u003Cp>If you use the global localization delegates, the setup collapses from three delegates to one. \u003Ccode>GlobalMaterialLocalizations.delegates\u003C\u002Fcode> now includes the Cupertino and Widgets delegates for you.\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-dart\">import 'package:material_ui\u002Fmaterial_ui.dart';\n\nMaterialApp(\n  localizationsDelegates: GlobalMaterialLocalizations.delegates,\n)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>If you never touched \u003Ccode>GlobalMaterialLocalizations\u003C\u002Fcode> or \u003Ccode>GlobalCupertinoLocalizations\u003C\u002Fcode>, there is nothing to do here. RTL support is not affected, which was my first worry.\u003C\u002Fp>\n\n\u003Ch2>The rest of the same upgrade\u003C\u002Fh2>\n\n\u003Cp>While you are in there anyway: minimum supported versions moved to iOS 15 and macOS 12, so check your analytics before assuming nobody is on iOS 13. iOS 27 requires the \u003Ccode>UIScene\u003C\u002Fcode> lifecycle, and apps built with Xcode 27 that skip it fail to launch rather than warn, though the CLI handles most projects.\u003C\u002Fp>\n\n\u003Cp>What I would actually do, and did: branch, run the fix, then count two numbers. How many files the rewrite touched, and how many dependencies still import the old \u003Ccode>package:flutter\u002Fmaterial.dart\u003C\u002Fcode>. Those tell you whether this is a Tuesday afternoon or something worth planning. I keep the commands and the dependency grep as a snippet in \u003Ca href=\"\u002Fsnippetark\u002F\">Snippet Ark\u003C\u002Fa>, because we have three more Flutter apps and I am not going through this from memory again.\u003C\u002Fp>\n\n\u003Cp>Our app came through clean. One package needs its own migration before I can pass it a modern \u003Ccode>ColorScheme\u003C\u002Fcode>, and the bridge holds that corner until it ships. Honest verdict: this is a one-command job for your own code and a dependency audit for everything else.\u003C\u002Fp>\n\n\u003Cp>It stays optional until November, which is why most of us will treat it as urgent around late October. If you want more Flutter upgrade pain to go with it, the \u003Ca href=\"\u002Fposts\u002Fflutter-listview-jank-scroll-performance-fixes\u002F\">scroll jank checklist\u003C\u002Fa> and the \u003Ca href=\"\u002Fposts\u002Fflutter-state-management-3-rewrites\u002F\">state management post-mortem\u003C\u002Fa> are the two I keep re-reading.\u003C\u002Fp>\n","2026-09-24",1790289963465]