Start Debugging

Migra las importaciones de Material y Cupertino de Flutter a los paquetes material_ui y cupertino_ui

La migración completa desde package:flutter/material.dart y package:flutter/cupertino.dart hacia material_ui 1.1.1 y cupertino_ui 1.0.2: qué reescribe dart fix --code=migrate_design_widgets, por qué los widgets de terceros empiezan a lanzar errores de búsqueda de ancestros, qué arregla realmente MaterialUiCompatibilityBridge y cómo cambia la dependencia de flutter_localizations.

Para una aplicación cuya única superficie Material es su propio código, esta es una migración de un comando y una tarde: flutter pub add material_ui, luego dart fix --apply --code=migrate_design_widgets, y después ejecuta las pruebas. Las APIs de los widgets son una copia idéntica de lo que había en el SDK, así que nada se renderiza distinto y ningún golden debería moverse. Lo que realmente cuesta tiempo es el grafo de dependencias. Cada paquete que todavía importa package:flutter/material.dart arrastra a tu programa una segunda copia, incompatible a nivel de tipos, de Theme, Material y MaterialLocalizations, y sus widgets fallarán al buscar ancestros dentro de tu árbol migrado hasta que envuelvas la aplicación en MaterialUiCompatibilityBridge. Esta guía apunta al canal stable actual, Flutter 3.47.2 con Dart 3.13.2, más material_ui 1.1.1 y cupertino_ui 1.0.2.

Aquí el reloj importa. Las bibliotecas dentro del SDK ya están congeladas, y la deprecación formal está programada para la versión stable de noviembre de 2026.

Por qué esto no es una limpieza opcional

Qué se rompe

ÁreaCambioSeveridad
Importacionespackage:flutter/material.dart pasa a package:material_ui/material_ui.dart; package:flutter/cupertino.dart pasa a package:cupertino_ui/cupertino_ui.dartalta, totalmente automatizable
Identidad de tiposEl Material del SDK y el Material de material_ui son tipos distintos en runtime, así que las búsquedas de ancestros no cruzan la fronteraalta, requiere el puente
Delegados de localizaciónGlobalMaterialLocalizations y GlobalCupertinoLocalizations vienen de los paquetes, no de flutter_localizationsmedia
pubspec.yamlDos dependencias directas nuevas; flutter_localizations ya no es una dependencia directa que necesitesmedia
Código generadoTodo lo que emita package:flutter/material.dart en un archivo .g.dart o .freezed.dart necesita regenerarse después de la pasada sobre el código fuentemedia
Paquetes publicadosMigrar tu propio paquete es un cambio incompatible para quienes lo consumen, así que necesita un incremento de versión mayormedia
APIs de widgetsNinguna. Constructores, parámetros y renderizado quedan igualninguna

Esa última fila es la razón entera de que esta migración sea manejable. material_ui 1.0.0 es una copia de la biblioteca incluida en el SDK tal como estaba en el congelamiento de abril de 2026, no un rediseño.

Lista de comprobación previa

Pasos de la migración

  1. Agrega los paquetes antes de tocar una sola importación. La regla de dart fix reescribe cadenas de importación; no edita pubspec.yaml. Hazlo en el orden equivocado y terminarás con un archivo lleno de importaciones sin resolver.

    # Flutter 3.47.2, Dart 3.13.2
    flutter pub add material_ui
    flutter pub add cupertino_ui

    Eso hoy resuelve a material_ui: ^1.1.1 y cupertino_ui: ^1.0.2. Si tu aplicación es solo Material, igual obtienes cupertino_ui de forma transitiva, porque material_ui depende de cupertino_ui: ^1.0.0 desde su versión 1.0.1, pero decláralo explícitamente si lo importas directamente. Verifica con flutter pub deps --style=compact | grep -E 'material_ui|cupertino_ui' y confirma que ambos se resuelven.

  2. Reescribe las importaciones con la corrección incluida. Ambos paquetes registran la misma corrección del analizador, así que un comando cubre Material y Cupertino a la vez.

    dart fix --dry-run --code=migrate_design_widgets   # review first
    dart fix --apply  --code=migrate_design_widgets

    El resultado es un diff de una línea por archivo:

    // Before: Flutter 3.43 and earlier
    import 'package:flutter/material.dart';
    
    // After: material_ui 1.1.1
    import 'package:material_ui/material_ui.dart';

    Nada por debajo de la línea de importación cambia. MaterialApp, Scaffold, ThemeData, Colors, showDialog y cualquier otro nombre se exportan bajo el mismo identificador. Verifica con grep -rn "package:flutter/material.dart\|package:flutter/cupertino.dart" lib test sin resultados, y luego flutter analyze.

  3. Apunta los delegados de localización a los paquetes. Los delegados y las cadenas traducidas se mudaron a material_ui y cupertino_ui, y los paquetes exponen un getter agregado que te ahorra listar tres delegados a mano.

    // 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.delegates ya incluye los delegados de Cupertino y de Widgets. Si además usas gen-l10n, tu AppLocalizations.delegate generado no se ve afectado y se agrega a esa lista como antes. Ahora puedes quitar flutter_localizations de tus propias dependencies, aunque seguirá en pubspec.lock: cupertino_ui 1.0.2 todavía depende de él, junto con collection: ^1.19.1 e intl: ^0.20.2. Verifica arrancando con una configuración regional distinta del inglés y comprobando una cadena integrada, por ejemplo mantén pulsado un TextField y confirma que la opción de pegar está traducida.

  4. Pon un puente para las dependencias que no han migrado. Este es el paso que la gente se salta y luego depura durante una hora. Envuelve a nivel de aplicación con MaterialApp.builder:

    // material_ui 1.1.1
    MaterialApp(
      theme: ThemeData(useMaterial3: true),
      builder: (BuildContext context, Widget? child) {
        return MaterialUiCompatibilityBridge(child: child!);
      },
      home: const HomeScreen(),
    )

    El lado de Cupertino es simétrico:

    // cupertino_ui 1.0.2
    CupertinoApp(
      builder: (BuildContext context, Widget? child) {
        return CupertinoUiCompatibilityBridge(child: child!);
      },
      home: const HomeScreen(),
    )

    También puedes envolver un subárbol más reducido si solo una pantalla incrusta widgets heredados, lo que mantiene los widgets heredados adicionales fuera del resto del árbol. Verifica navegando a cada pantalla que aloje un widget de terceros. El puente es un andamio temporal: elimínalo en cuanto flutter pub outdated no muestre nada que siga con las importaciones antiguas.

  5. Regenera todo lo que haya escrito un generador de código. dart fix ve tu código fuente, no las plantillas que lo produjeron. Vuelve a ejecutar el generador después del paso 2 para que los archivos emitidos dejen de importar la biblioteca del SDK:

    dart run build_runner build --delete-conflicting-outputs

    Luego revisa los restos que dart fix no puede alcanzar: los export barrel que reexportan Material para quienes te consumen, las importaciones condicionales que eligen una implementación de Material por plataforma, y cualquier plantilla propia de generador con la ruta de importación escrita a mano como cadena. Verifica con el mismo grep del paso 2, ampliado a todo el repositorio en lugar de solo lib y test.

  6. Si publicas un paquete, incrementa la versión mayor. Cambiar un paquete publicado a material_ui altera lo que quienes lo consumen deben tener en su propio pubspec.yaml. Publicar eso como una versión menor rompe aplicaciones en silencio: su árbol de widgets acaba mezclando orígenes sin ningún error de compilación que lo señale. Sube a la siguiente versión mayor, anota en el changelog la restricción de material_ui requerida, y mantén la versión mayor anterior en una rama de mantenimiento si das soporte a versiones antiguas de Flutter. Verifica con dart pub publish --dry-run.

Verificación

Plan de reversión

Totalmente reversible hoy. Los cambios son un diff de pubspec.yaml, una línea de importación por archivo, una lista de delegados y un widget puente opcional, así que un git revert del commit de migración te devuelve a las bibliotecas del SDK sin datos ni artefactos de compilación que deshacer. Dos advertencias: no existe un dart fix inverso, así que una reversión manual implica editar cada importación de vuelta a mano, y por eso el paso cero es una rama. Y después de la stable de noviembre de 2026, revertir te deja sobre APIs formalmente deprecadas que serán eliminadas, así que trata la reversión como una forma de desbloquear un lanzamiento, no como una decisión.

Detalles que muerden

“Could not find an ancestor of type MaterialLocalizations” en código que no escribiste. Es el problema de identidad de tipos apareciendo en runtime. Un widget compilado contra la biblioteca del SDK llama a MaterialLocalizations.of(context), que recorre el árbol buscando el inherited widget de su tipo MaterialLocalizations. Tu MaterialApp de material_ui insertó un tipo distinto con el mismo nombre, la búsqueda falla y salta el assert. Theme.of(context) falla de la misma manera, con “Could not find an ancestor of type Theme”. El puente del paso 4 existe precisamente para insertar los inherited widgets heredados junto a los nuevos, de modo que ambas búsquedas se resuelvan. No es un parche para un Scaffold ausente: si el error viene de tu propio código migrado, tienes el problema ordinario descrito en no Material widget found en Flutter, y el puente no ayudará.

Importación sin resolver justo después de ejecutar la corrección. Ejecutaste dart fix antes de flutter pub add. Agrega el paquete y vuelve a ejecutar dart fix --apply --code=migrate_design_widgets; la regla es idempotente.

No dejes ambas importaciones en un mismo archivo. package:flutter/material.dart y package:material_ui/material_ui.dart exportan los mismos identificadores, así que cualquier archivo con las dos recibe errores de importación ambigua en Material, Theme, Colors y compañía. Poner un prefijo a una de ellas compila, pero te deja dos sistemas de diseño en un archivo, que es peor que el error. Elige uno por archivo.

La fecha del congelamiento y la de la deprecación no son la misma. El anuncio del congelamiento de código decía que las bibliotecas del SDK quedarían deprecadas en la versión stable posterior a 3.44. Eso se corrió: 3.47 se publicó el 2026-08-12 sin la deprecación, y las notas de la versión 3.47 ahora sitúan la deprecación formal en la stable de noviembre. Congeladas desde abril, deprecadas en noviembre, eliminadas más tarde. Planifica contra noviembre, no contra lo que tu analizador calle hoy.

Los manifiestos de assets pueden moverse aunque los widgets no. material_ui 1.1.0 expuso el asset del shader ink_sparkle a través de su propio pubspec.yaml y descartó el shader stretch_effect. Si haces afirmaciones sobre el manifiesto de assets o eliminas assets sin usar en un paso de compilación, ese es un diff real que revisar.

Migra las importaciones y las versiones de Flutter en commits separados. Si saltas de versión del SDK en la misma pasada, cualquier regresión visual tendrá dos causas candidatas. Aterriza primero la actualización del SDK, confirma que la aplicación está limpia y luego migra las importaciones.

Relacionado

Fuentes

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< Volver