Material- und Cupertino-Importe in Flutter auf die Pakete material_ui und cupertino_ui migrieren
Die vollständige Migration von package:flutter/material.dart und package:flutter/cupertino.dart auf material_ui 1.1.1 und cupertino_ui 1.0.2: was dart fix --code=migrate_design_widgets umschreibt, warum Widgets aus Drittpaketen plötzlich Ancestor-Lookups scheitern lassen, was MaterialUiCompatibilityBridge tatsächlich behebt und wie sich die Abhängigkeit zu flutter_localizations ändert.
Für eine App, deren einzige Material-Oberfläche ihr eigener Code ist, ist das eine Migration von einem Befehl und einem Nachmittag: flutter pub add material_ui, dann dart fix --apply --code=migrate_design_widgets, dann die Tests laufen lassen. Die Widget-APIs sind eine identische Kopie dessen, was im SDK lag, also rendert nichts anders und kein Golden sollte sich bewegen. Zeit kostet der Abhängigkeitsgraph. Jedes Paket, das noch package:flutter/material.dart importiert, zieht eine zweite, typinkompatible Kopie von Theme, Material und MaterialLocalizations in Ihr Programm, und dessen Widgets scheitern an Ancestor-Lookups in Ihrem migrierten Baum, bis Sie die App in MaterialUiCompatibilityBridge einwickeln. Diese Anleitung zielt auf den aktuellen Stable-Kanal, Flutter 3.47.2 mit Dart 3.13.2, plus material_ui 1.1.1 und cupertino_ui 1.0.2.
Die Uhr läuft hier mit. Die Bibliotheken im SDK sind bereits eingefroren, und die formale Deprecation ist für das Stable-Release im November 2026 geplant.
Warum das keine optionale Aufräumaktion ist
- Die Kopien im SDK erhalten keine Korrekturen. Flutter hat die Material- und Cupertino-Verzeichnisse in
flutter/flutteram 2026-04-07 für alle Beiträge geschlossen. Jede Fehlerkorrektur seitdem landete influtter/packages.material_ui1.1.1 enthält bereits Korrekturen, die die SDK-Kopie nie bekommt, darunter die Race Condition inSearchAnchor, bei der ein veralteter Satz asynchroner Vorschläge einen neueren ersetzte, und die Wertanzeige-Labels vonSlider, die am Bildschirmrand abgeschnitten statt mit Auslassungspunkten gekürzt wurden. - Design-Updates warten nicht mehr auf den SDK-Zug. Material und Cupertino erschienen bisher im Quartalsrhythmus von Flutter, also wartete eine Token-Anpassung oder ein neues
MenuAnchor-Argument auf den nächsten Stable-Schnitt.material_ui: ^1.1.1zu pinnen entkoppelt das: 1.1.0 und 1.1.1 kamen beide zwischen Stable 3.47 und heute. - Sie können endlich ein Designsystem loswerden, das Sie nie genutzt haben. Sobald die SDK-Kopien gelöscht sind, schleppt eine reine Cupertino-App das Theming, die Typografie und die Icon-Metadaten von Material nicht mehr durch das Tree-Shaking, und umgekehrt.
- Die Lokalisierungen ziehen mit den Widgets um. Die übersetzten Strings und Delegates von Material und Cupertino liegen jetzt in den Paketen, und deshalb müssen Sie
flutter_localizationsnicht mehr selbst angeben. - Wenn Sie ein Paket veröffentlichen, sind Sie ein Blocker. Ein einziges nicht migriertes Blattpaket erzwingt die Kompatibilitätsbrücke bei allen weiter unten.
Was bricht
| Bereich | Änderung | Schweregrad |
|---|---|---|
| Importe | package:flutter/material.dart wird package:material_ui/material_ui.dart; package:flutter/cupertino.dart wird package:cupertino_ui/cupertino_ui.dart | hoch, vollständig automatisierbar |
| Typidentität | Das Material des SDK und das Material aus material_ui sind zur Laufzeit verschiedene Typen, deshalb überschreiten Ancestor-Lookups die Grenze nicht | hoch, benötigt die Brücke |
| Lokalisierungs-Delegates | GlobalMaterialLocalizations und GlobalCupertinoLocalizations kommen aus den Paketen, nicht aus flutter_localizations | mittel |
pubspec.yaml | Zwei neue direkte Abhängigkeiten; flutter_localizations ist keine direkte Abhängigkeit mehr, die Sie brauchen | mittel |
| Generierter Code | Alles, was package:flutter/material.dart in eine .g.dart- oder .freezed.dart-Datei schreibt, muss nach dem Durchlauf über den Quellcode neu generiert werden | mittel |
| Veröffentlichte Pakete | Ihr eigenes Paket zu migrieren ist eine Breaking Change für Konsumenten und braucht daher einen Major-Versionssprung | mittel |
| Widget-APIs | Keine. Konstruktoren, Parameter und Rendering bleiben unverändert | keine |
Diese letzte Zeile ist der ganze Grund, warum diese Migration machbar ist. material_ui 1.0.0 ist eine Kopie der mitgelieferten Bibliothek im Stand des Freeze vom April 2026, kein Redesign.
Checkliste vor dem Start
- Flutter 3.44 oder neuer.
material_uihat seine Untergrenze auf Flutter 3.44 / Dart 3.12 gehoben, als der Code ausflutter/flutterauszog, und 3.47.2 ist die aktuelle Stable. Prüfen mitflutter --version. - Ein sauberes
flutter analyzevor dem Start. Der Durchlauf nach der Migration soll vergleichbar sein. - Ein Branch.
dart fix --applyschreibt jede passende Datei in einem Durchgang um, und es gibt keinen Undo-Schalter. - Eine Inventur der Abhängigkeiten, die Material- oder Cupertino-Widgets rendern.
flutter pub deps --style=compactplusflutter pub outdatedliefert die Liste; alles, was zuletzt vor August 2026 veröffentlicht wurde, ist nicht migriert. - Falls Sie Golden-Tests haben, lassen Sie sie zuerst laufen und committen Sie die Baseline. Sie sollten sich nicht ändern, und genau das ist die Aussage.
Migrationsschritte
-
Fügen Sie die Pakete hinzu, bevor Sie einen einzigen Import anfassen. Die
dart fix-Regel schreibt Import-Strings um; sie bearbeitetpubspec.yamlnicht. In der falschen Reihenfolge bekommen Sie eine Datei voller nicht auflösbarer Importe.# Flutter 3.47.2, Dart 3.13.2 flutter pub add material_ui flutter pub add cupertino_uiDas löst heute zu
material_ui: ^1.1.1undcupertino_ui: ^1.0.2auf. Ist Ihre App reines Material, erhalten Siecupertino_uitrotzdem transitiv, dennmaterial_uihängt seit Release 1.0.1 voncupertino_ui: ^1.0.0ab; geben Sie es aber explizit an, wenn Sie es direkt importieren. Prüfen mitflutter pub deps --style=compact | grep -E 'material_ui|cupertino_ui'und bestätigen, dass beide auflösen. -
Schreiben Sie die Importe mit dem mitgelieferten Fix um. Beide Pakete registrieren denselben Analyzer-Fix, ein Befehl erledigt also Material und Cupertino gemeinsam.
dart fix --dry-run --code=migrate_design_widgets # review first dart fix --apply --code=migrate_design_widgetsDas Ergebnis ist ein einzeiliger Diff pro Datei:
// Before: Flutter 3.43 and earlier import 'package:flutter/material.dart'; // After: material_ui 1.1.1 import 'package:material_ui/material_ui.dart';Unterhalb der Import-Zeile ändert sich nichts.
MaterialApp,Scaffold,ThemeData,Colors,showDialogund jeder andere Name werden unter demselben Identifier exportiert. Prüfen mitgrep -rn "package:flutter/material.dart\|package:flutter/cupertino.dart" lib test, das nichts zurückgibt, danachflutter analyze. -
Richten Sie die Lokalisierungs-Delegates auf die Pakete aus. Die Delegates und die übersetzten Strings sind nach
material_uiundcupertino_uigewandert, und die Pakete bieten einen Sammel-Getter, der Ihnen das Auflisten von drei Delegates erspart.// Before: flutter_localizations, Flutter 3.43 import 'package:flutter_localizations/flutter_localizations.dart'; localizationsDelegates: const <LocalizationsDelegate<Object>>[ GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, GlobalWidgetsLocalizations.delegate, ],// After: material_ui 1.1.1 import 'package:material_ui/material_ui.dart'; localizationsDelegates: GlobalMaterialLocalizations.delegates,GlobalMaterialLocalizations.delegatesenthält die Cupertino- und Widgets-Delegates bereits. Wenn Sie zusätzlichgen-l10nnutzen, bleibt Ihr generiertesAppLocalizations.delegateunberührt und wird wie bisher an diese Liste angehängt.flutter_localizationskönnen Sie nun aus Ihren eigenendependenciesentfernen, es bleibt aber inpubspec.lock:cupertino_ui1.0.2 hängt weiterhin davon ab, nebencollection: ^1.19.1undintl: ^0.20.2. Prüfen, indem Sie mit einer nicht-englischen Locale starten und einen eingebauten String kontrollieren, zum Beispiel einTextFieldlang drücken und bestätigen, dass die Einfügen-Option übersetzt ist. -
Überbrücken Sie die Abhängigkeiten, die nicht migriert sind. Das ist der Schritt, den man überspringt und anschließend eine Stunde debuggt. Auf App-Ebene mit
MaterialApp.buildereinwickeln:// material_ui 1.1.1 MaterialApp( theme: ThemeData(useMaterial3: true), builder: (BuildContext context, Widget? child) { return MaterialUiCompatibilityBridge(child: child!); }, home: const HomeScreen(), )Die Cupertino-Seite ist symmetrisch:
// cupertino_ui 1.0.2 CupertinoApp( builder: (BuildContext context, Widget? child) { return CupertinoUiCompatibilityBridge(child: child!); }, home: const HomeScreen(), )Sie können auch einen engeren Teilbaum einwickeln, wenn nur ein Screen alte Widgets einbettet; das hält die zusätzlichen Inherited Widgets aus dem restlichen Baum heraus. Prüfen, indem Sie jeden Screen aufrufen, der ein Widget aus einem Drittpaket enthält. Die Brücke ist temporäres Baugerüst: Löschen Sie sie, sobald
flutter pub outdatednichts mehr mit den alten Importen zeigt. -
Generieren Sie alles neu, was ein Codegenerator geschrieben hat.
dart fixsieht Ihren Quellcode, nicht die Templates, die ihn erzeugt haben. Lassen Sie den Generator nach Schritt 2 erneut laufen, damit die erzeugten Dateien die SDK-Bibliothek nicht mehr importieren:dart run build_runner build --delete-conflicting-outputsPrüfen Sie danach die Reste, die
dart fixnicht erreicht:export-Barrel-Dateien, die Material für Konsumenten re-exportieren, bedingte Importe, die pro Plattform eine Material-Implementierung wählen, und jedes eigene Generator-Template, in dem der Importpfad als String fest hinterlegt ist. Prüfen mit demselbengrepaus Schritt 2, aber über das gesamte Repository statt nurlibundtest. -
Wenn Sie ein Paket veröffentlichen, erhöhen Sie die Major-Version. Ein veröffentlichtes Paket auf
material_uiumzustellen ändert, was Konsumenten in ihrer eigenenpubspec.yamlhaben müssen. Das als Minor-Release auszuliefern bricht Apps stillschweigend: Ihr Widget-Baum mischt am Ende Quellen, ohne dass ein Compilerfehler darauf zeigt. Springen Sie auf die nächste Major-Version, notieren Sie die nötigematerial_ui-Constraint im Changelog, und halten Sie die vorige Major-Version auf einem Wartungsbranch, wenn Sie ältere Flutter-Versionen unterstützen. Prüfen mitdart pub publish --dry-run.
Verifikation
flutter analyzemeldet die gleiche Anzahl wie Ihre Baseline vor der Migration, ohneuri_does_not_existund ohnedeprecated_member_usein einer Import-Zeile.grep -rn "package:flutter/material.dart\|package:flutter/cupertino.dart" .findet nichts außerhalb von.dart_toolundpubspec.lock.flutter testläuft durch, Golden-Tests eingeschlossen und unverändert. Ein verschobenes Golden bedeutet, dass zwei Kopien der Bibliothek im selben Baum rendern, nicht dass Material sich geändert hat.- Die App läuft auf einem Gerät, und jeder Screen mit einem eingebetteten Drittpaket-Widget rendert mit Ihrem Theme, nicht mit Defaults.
- Eine nicht-englische Locale zeigt nach Schritt 3 weiterhin übersetzte eingebaute Strings.
flutter build apk --release --analyze-size(oder das iOS-Äquivalent) als Größen-Baseline für später, sobald die SDK-Kopien gelöscht sind und das Tree-Shaking das ungenutzte Designsystem wirklich verwerfen kann.
Rollback
Heute vollständig umkehrbar. Die Änderungen sind ein pubspec.yaml-Diff, eine Import-Zeile pro Datei, eine Delegates-Liste und ein optionales Brücken-Widget, ein git revert des Migrations-Commits bringt Sie also zurück auf die SDK-Bibliotheken, ohne Daten oder Build-Artefakte zurückzudrehen. Zwei Einschränkungen: Es gibt kein umgekehrtes dart fix, ein manuelles Rollback heißt also, jeden Import per Hand zurückzuschreiben, weshalb Schritt Null ein Branch ist. Und nach dem Stable-Release im November 2026 parkt ein Revert Sie auf formal deprecated APIs, die gelöscht werden; behandeln Sie Rollback daher als Mittel, ein Release freizubekommen, nicht als Entscheidung.
Fallstricke
“Could not find an ancestor of type MaterialLocalizations” aus Code, den Sie nicht geschrieben haben. Das ist das Typidentitätsproblem zur Laufzeit. Ein Widget, das gegen die SDK-Bibliothek kompiliert wurde, ruft MaterialLocalizations.of(context) auf, was den Baum nach dem Inherited Widget seines MaterialLocalizations-Typs durchsucht. Ihre MaterialApp aus material_ui hat einen anderen Typ mit gleichem Namen eingefügt, der Lookup schlägt fehl, und der Assert greift. Theme.of(context) scheitert genauso, mit “Could not find an ancestor of type Theme”. Die Brücke aus Schritt 4 existiert genau dafür, die alten Inherited Widgets neben den neuen einzufügen, damit beide Lookups auflösen. Sie ist kein Ersatz für ein fehlendes Scaffold: Kommt der Fehler aus Ihrem eigenen migrierten Code, haben Sie das gewöhnliche Problem aus no Material widget found in Flutter, und die Brücke hilft nicht.
Nicht auflösbarer Import direkt nach dem Fix. Sie haben dart fix vor flutter pub add ausgeführt. Paket hinzufügen, dann dart fix --apply --code=migrate_design_widgets erneut laufen lassen; die Regel ist idempotent.
Lassen Sie nicht beide Importe in einer Datei. package:flutter/material.dart und package:material_ui/material_ui.dart exportieren die gleichen Identifier, jede Datei mit beiden bekommt also Fehler wegen mehrdeutiger Importe bei Material, Theme, Colors und Co. Einen davon zu präfixen kompiliert, gibt Ihnen aber zwei Designsysteme in einer Datei, was schlimmer als der Fehler ist. Pro Datei eines wählen.
Freeze-Datum und Deprecation-Datum sind nicht dasselbe. Die Ankündigung des Code Freeze sagte, die SDK-Bibliotheken würden im Stable-Release nach 3.44 deprecated. Das hat sich verschoben: 3.47 erschien am 2026-08-12 ohne die Deprecation, und die Release Notes zu 3.47 setzen die formale Deprecation nun auf die November-Stable. Eingefroren seit April, deprecated im November, später gelöscht. Planen Sie gegen November, nicht gegen das, worüber Ihr Analyzer heute schweigt.
Asset-Manifeste können sich verschieben, auch wenn die Widgets es nicht tun. material_ui 1.1.0 hat das Shader-Asset ink_sparkle über die eigene pubspec.yaml bereitgestellt und den stretch_effect-Shader entfernt. Wenn Sie auf das Asset-Manifest testen oder unbenutzte Assets in einem Build-Schritt entfernen, ist das ein echter Diff zum Prüfen.
Migrieren Sie Importe und Flutter-Versionen in getrennten Commits. Wenn Sie im selben Durchgang SDK-Versionen springen, hat jede visuelle Regression zwei Kandidaten als Ursache. Erst das SDK-Upgrade landen, bestätigen, dass die App sauber ist, dann die Importe migrieren.
Verwandte Beiträge
- Die Ankündigung, an die diese Migration anschließt, samt dem SwiftPM-Default aus demselben Release, steht in Flutter 3.44 löst Material und Cupertino aus dem SDK.
- Strukturell ist das derselbe breite, mechanische Durchgang wie eine Flutter-Web-App von dart:html auf package:web migrieren, inklusive des Teils, in dem
dart fixdie einfachen 95 % erledigt und der Abhängigkeitsgraph Sie erledigt. - Für eine Deprecation, die
dart fixausdrücklich nicht automatisieren kann, vergleichen Sie Radio.groupValue und onChanged durch RadioGroup ersetzen. - Wenn Sie in diesem Zyklus zugleich auf die aktuelle Stable wechseln, lesen Sie was Flutter 3.47 am Desktop-Rendering geändert hat, bevor Sie eine visuelle Regression dem Paketwechsel zuschreiben.
- Fehlgeschlagene Ancestor-Lookups sind eine Familie, kein Einzelfall. ScaffoldMessenger.of(context) does not contain a Scaffold ist dieselbe Debugging-Methode auf ein anderes Inherited Widget angewandt.
Quellen
- material_ui auf pub.dev, Version 1.1.1, und das Changelog
- cupertino_ui auf pub.dev, Version 1.0.2
- Flutter’s Material and Cupertino code freeze, der Flutter-Blog
- What’s new in Flutter 3.44, der Flutter-Blog
- What’s new in Flutter 3.47, der Flutter-Blog
- Tracking-Issue zur Entkopplung des Designsystems, flutter/flutter
- Flutter 3.47.0 Release Notes, docs.flutter.dev
Comments
Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.