Migra fuera de describeEnum en Flutter antes de que lo eliminen
describeEnum está obsoleto desde Flutter 3.16 y el PR que lo elimina ya está aprobado. Cómo reemplazar cada llamada por Enum.name (Flutter 3.47.4, Dart 3.13), manejar clases tipo enum y diagnósticos, encontrar las llamadas escondidas en dependencias como flutter_svg 1.x y cómo se ve el error de compilación una vez que desaparece.
Para casi cualquier base de código esto es un buscar y reemplazar de 30 minutos: describeEnum(x) pasa a ser x.name, describeEnum pasado como tear-off pasa a ser (e) => e.name, y los bucles de “string de vuelta a enum” que lo acompañaban pasan a ser MyEnum.values.byName(s). dart fix no lo hace por ti, y los únicos puntos de llamada que requieren pensar son los que pasan algo que no es un Enum real de Dart. Lo que de verdad cuesta tiempo es tu grafo de dependencias: un paquete viejo como flutter_svg 1.1.6 todavía llama a describeEnum, y el día que lo eliminen tu app deja de compilar en un archivo que no es tuyo. Todo lo que sigue lo verifiqué en Flutter 3.47.4 (Dart 3.13.3), la versión estable actual, y contra una compilación local de Flutter con la eliminación pendiente aplicada.
En qué punto está realmente la eliminación
La línea de tiempo es lo bastante confusa como para fijarla antes de tocar código, porque hoy la documentación oficial y el SDK no coinciden.
describeEnumse marcó como obsoleto en flutter/flutter#125016, que llegó en 3.14.0-2.0.pre y se publicó en la estable 3.16. El mensaje de obsolescencia dice “Use thenamegetter on enums instead. This feature was deprecated after v3.14.0-2.0.pre.”- La eliminación es flutter/flutter#190076, abierto el 2026-07-27. Borra la función de
packages/flutter/lib/src/foundation/diagnostics.dartjunto con sus pruebas. Tiene tres aprobaciones pero, al 2026-09-18, sigue abierto: el check “Google testing” falla porque el monorepo interno de Google primero tiene que subirflutter_svgpor encima de 2.0.0. - La guía de cambios incompatibles para la eliminación (flutter/website#13682) se fusionó el 2026-08-18, y el índice de cambios incompatibles ya lista “Removal of
describeEnum” bajo Released in Flutter 3.47. Eso va por delante de la realidad. Revisédiagnostics.darten el tag3.47.4, en el tag beta3.48.0-0.5.prey enmaster:describeEnumsigue definido en los tres.
Así que hoy no se rompe nada en el canal estable. Lo que obtienes es una sugerencia deprecated_member_use de nivel info que la mayoría de los equipos viene ignorando desde 2023. En cuanto se fusione #190076, master se rompe de inmediato y la siguiente beta se rompe para todos los que estén en beta. Migrar ahora cuesta lo mismo que migrar después, solo que después te toca en medio de una actualización que querías hacer por otro motivo.
Qué se rompe
| Área | Cambio | Severidad |
|---|---|---|
describeEnum(value) en tu código | Error de compilación: la función ya no existe | alta, pero trivial de arreglar |
describeEnum en una dependencia | Error de compilación en el archivo del paquete, la app no compila | alta, requiere actualizar el paquete |
describeEnum sobre clases que no son Enum | No hay getter .name al cual cambiar | media, requiere un helper local |
describeEnum usado como tear-off (.map(describeEnum)) | El mismo error de compilación | baja |
StringProperty(name, describeEnum(v)) en debugFillProperties | Funciona si lo reescribes a .name, pero EnumProperty es el mejor reemplazo | baja |
Soporte de dart fix | Ninguno. La guía de Flutter lo dice explícitamente, y dart fix --dry-run reporta “Nothing to fix!“ | informativo |
Lista de verificación previa
- Flutter 3.16 o más reciente. Todas las estables desde entonces traen la obsolescencia, así que el analizador puede encontrar tus puntos de llamada por ti. Aquí la base es código en 3.47.4.
- Dart 2.15 o más reciente para el getter
nameyvalues.byName. Ambos llegaron con los helpers de enum dedart:coreen Dart 2.15.0 (la guía de Flutter dice 2.14, pero el changelog de Dart los lista bajo 2.15.0). Cualquier proyecto Flutter 3.x ya cumple con esto. - Una línea base limpia de
flutter analyze, para que las sugerencias de obsolescencia no queden enterradas bajo advertencias no relacionadas. - La salida de
flutter pub outdatedpara tu app, porque el paso de dependencias de más abajo puede obligarte a subir una versión mayor.
Cómo se ve el fallo después de la eliminación
Para obtener el texto real del error en lugar de adivinarlo, apliqué el diff de #190076 a un checkout temporal de Flutter 3.47.4 y corrí un proyecto de prueba contra él. El analizador reporta:
error • The function 'describeEnum' isn't defined. Try importing the library that defines 'describeEnum', correcting the name to the name of an existing function, or defining a function named 'describeEnum' • lib/legacy.dart:27:20 • undefined_function
flutter run, flutter test y flutter build pasan en cambio por el compilador front-end, que imprime:
lib/legacy.dart:27:20: Error: Method not found: 'describeEnum'.
String simple() => describeEnum(ThemeChoice.dark);
^^^^^^^^^^^^
Y un proyecto que depende de flutter_svg: 1.1.6 falla antes de que se ejecute nada de tu código, con el error apuntando dentro de la caché de pub:
/Users/marius/.pub-cache/hosted/pub.dev/flutter_svg-1.1.6/lib/src/picture_provider.dart:196:33: Error: The method 'describeEnum' isn't defined for the type 'PictureConfiguration'.
result.write('platform: ${describeEnum(platform!)}');
^^^^^^^^^^^^
Si llegaste a este artículo por ese último mensaje, salta directo al paso 5.
Pasos de la migración
-
Lista cada punto de llamada con el analizador. Ejecuta
flutter analyzey filtra por la obsolescencia. En 3.47.4 cada coincidencia es una líneainfoque termina endeprecated_member_use:# Flutter 3.47.4 flutter analyze --no-fatal-infos | grep "'describeEnum' is deprecated"Un simple
grep -rn "describeEnum" lib testencuentra los mismos puntos, más las menciones en comentarios de documentación y en cualquier archivo que tuanalysis_options.yamlexcluya. Verifica: tienes una lista de archivos y números de línea, y sabes cuáles están en archivos generados (regenéralos, no los edites a mano). -
Reemplaza las llamadas sobre enums reales por
.name. Para cualquier valor cuyo tipo estático sea unenum, la reescritura es mecánica. Esto cubre enums simples, enums mejorados, enums anulables y tear-offs:// Flutter 3.47.4, Dart 3.13 enum ThemeChoice { light, dark } // Before String simple() => describeEnum(ThemeChoice.dark); String? nullable(ThemeChoice? c) => c == null ? null : describeEnum(c); List<String> tearOff() => ThemeChoice.values.map(describeEnum).toList(); // After String simple() => ThemeChoice.dark.name; String? nullable(ThemeChoice? c) => c?.name; List<String> tearOff() => ThemeChoice.values.map((e) => e.name).toList();El comportamiento es idéntico: desde Flutter 3.0,
describeEnumempieza conif (enumEntry is Enum) return enumEntry.name;, así que para enums reales ya era solo un envoltorio de.name. Eso incluye los enums mejorados que sobrescribentoString(). Un enum cuyotoString()devuelveLevel(H)igual dabahighcondescribeEnum, y dahighcon.name. Verifica:flutter analyzeno muestra sugerencias restantes para estos archivos. -
Reemplaza la búsqueda inversa por
values.byName. La mayor parte del código condescribeEnumestá junto a un parser hecho a mano que recorrevaluescomparando strings. Reemplaza ambas mitades a la vez:// Flutter 3.47.4, Dart 3.13 // Before Map<String, Object?> toJson(ThemeChoice c) => {'theme': describeEnum(c)}; ThemeChoice fromJson(Map<String, Object?> json) => ThemeChoice.values.firstWhere((e) => describeEnum(e) == json['theme']); // After Map<String, Object?> toJson(ThemeChoice c) => {'theme': c.name}; ThemeChoice fromJson(Map<String, Object?> json) => ThemeChoice.values.byName(json['theme']! as String);Los strings serializados no cambian, así que el JSON almacenado, las shared preferences y los eventos de analítica siguen funcionando. Lo que sí cambia es el modo de fallo: para un valor desconocido el bucle viejo lanzaba
StateError: Bad state: No element, mientras quebyNamelanzaArgumentError: Invalid argument (name): No enum value with that name: "blue". Si capturasStateErroralrededor de ese parseo, actualiza elcatch. Verifica: una prueba que haga el viaje de ida y vuelta de cada valor deThemeChoice.valuesportoJson/fromJson, más una prueba con un string desconocido. -
Dale a las clases tipo enum un helper local.
describeEnumaceptabaObject, y para cualquier cosa que no fuera unEnumtomabatoString()y devolvía todo lo que venía después del primer punto. Eso estaba pensado para las clases “tipo enum” anteriores a Dart 2.17 como esta, que todavía existen en bases de código antiguas y en algunos paquetes:// Flutter 3.47.4, Dart 3.13 class Channel { const Channel._(this._value); final String _value; static const Channel stable = Channel._('stable'); static const Channel beta = Channel._('beta'); @override String toString() => 'Channel.$_value'; }Channel.beta.nameno compila, porque no existename. Tienes dos opciones. La mejor es convertirChannelen unenumreal, algo que normalmente es posible ahora que los enums mejorados admiten campos y constructores. Cuando no es posible (la clase viene de un paquete, o tiene instancias que no son const), copia la rama de respaldo a tu propio código:// Flutter 3.47.4, Dart 3.13 /// Local copy of the only describeEnum behaviour `.name` cannot replace. String enumLikeName(Object value) { final String description = value.toString(); final int indexOfDot = description.indexOf('.'); assert( indexOfDot != -1 && indexOfDot < description.length - 1, 'The provided object "$value" is not an enum.', ); return description.substring(indexOfDot + 1); } String fromObject(Object value) => value is Enum ? value.name : enumLikeName(value);La comprobación
value is Enumimporta en los puntos de llamada tipados comoObjectodynamic, que es justo donde la gente le pasaba adescribeEnumuna mezcla de enums y clases tipo enum. Ten en cuenta que elassertsolo se ejecuta en compilaciones de depuración. En release,describeEnum(42)nunca lanzaba nada:indexOfdevolvía -1,substring(0)devolvía"42"y tu código seguía de largo. El helper conserva ese comportamiento a propósito, para que nada cambie en producción. Verifica: las pruebas en modo depuración para cada tipo tipo enum devuelven los mismos strings que antes. -
Arregla las llamadas en tus dependencias. Tu propio código es la parte fácil. Un paquete que llama a
describeEnumrompe tu compilación el día que la función desaparece, y no puedes parchearlo con un buscar y reemplazar. Hacer grep sobre la caché de pub genera mucho ruido porque contiene todas las versiones que alguna vez descargaste, así que escanea solo las versiones de paquetes que tu app realmente resuelve, usando.dart_tool/package_config.json:// Dart 3.13: list every describeEnum call in the packages your app resolves. // Save as tool/find_describe_enum.dart, run: dart run tool/find_describe_enum.dart import 'dart:convert'; import 'dart:io'; void main() { final config = File('.dart_tool/package_config.json'); final json = jsonDecode(config.readAsStringSync()) as Map<String, dynamic>; final call = RegExp(r'\bdescribeEnum\s*[(),;]'); for (final pkg in (json['packages'] as List).cast<Map<String, dynamic>>()) { if (pkg['name'] == 'flutter') continue; // defines it var rootUri = pkg['rootUri'] as String; if (!rootUri.endsWith('/')) rootUri += '/'; final root = config.parent.uri.resolve(rootUri); final lib = Directory.fromUri(root.resolve(pkg['packageUri'] as String)); if (!lib.existsSync()) continue; for (final f in lib.listSync(recursive: true).whereType<File>()) { if (!f.path.endsWith('.dart')) continue; final lines = f.readAsLinesSync(); for (var i = 0; i < lines.length; i++) { if (call.hasMatch(lines[i]) && !lines[i].trimLeft().startsWith('//')) { print('${pkg['name']}: ${f.path}:${i + 1}'); } } } } }El arreglo de la barra final no es decorativo.
package_config.jsonguarda los paquetes alojados comofile:///.../flutter_svg-1.1.6sin barra final, y resolverlib/contra eso apunta en silencio a la carpeta padre. La primera versión de este script tenía ese bug y reportaba cero coincidencias paraflutter_svg1.1.6. La versión corregida imprime:flutter_svg: /Users/marius/.pub-cache/hosted/pub.dev/flutter_svg-1.1.6/lib/src/picture_provider.dart:196Para cada paquete que reporte, revisa si una versión más nueva eliminó la llamada. Para
flutter_svgla respuesta es cualquier 2.x: hice grep sobre 2.0.0 y 2.2.1 y ninguna hace referencia adescribeEnum(la última versión es 2.3.0). El salto de 1.x a 2.x es una migración en sí misma, porque 2.0 pasó avector_graphicsy cambió las APIs de carga, pero es el mismo salto que el código interno de Google tiene que dar antes de que #190076 pueda fusionarse. Si un paquete está abandonado, haz un fork, aplica el paso 2 al fork y apunta una entrada dedependency_overridesa tu fork. Verifica: el script no imprime ninguna línea para paquetes de terceros. -
Reescribe los diagnósticos para usar
EnumProperty. Un uso habitual dentro de widgets y render objects eradebugFillProperties. Una reescritura mecánica a.namecompila, pero la propiedad tipada es mejor:// Flutter 3.47.4, Dart 3.13 // Before properties.add(StringProperty('choice', describeEnum(choice))); // After properties.add(EnumProperty<ThemeChoice>('choice', choice));La salida es ligeramente distinta.
StringPropertypone su valor entre comillas, así que DevTools ytoStringDeep()mostrabanchoice: "dark", mientras queEnumPropertyimprimechoice: dark. Si tienes pruebas golden sobretoStringDeep()odebugDescribeChildren, actualízalas. Desde Flutter 3.16,EnumProperty<T>exigeT extends Enum?, así que para una clase tipo enum usaDiagnosticsProperty<Channel>en su lugar. Verifica: las pruebas de diagnósticos pasan después de regenerar sus strings esperados. -
Evita que la obsolescencia vuelva.
deprecated_member_useesinfopor defecto, y por eso estas llamadas sobrevivieron tres años de obsolescencia. Súbelo de nivel enanalysis_options.yaml:# Flutter 3.47.4 include: package:flutter_lints/flutter.yaml analyzer: exclude: - build/** - android/** errors: deprecated_member_use: errorFusiona
errors:dentro de tu bloqueanalyzer:existente. Cuando agregué una segunda claveanalyzer:de nivel superior en su lugar, el analizador no se quejó y siguió reportandoinfo, así que parecía que el cambio estaba aplicado y no lo estaba. Con el bloque fusionado,flutter analyze --no-fatal-infosreportaerrory termina con código 1. Ten en cuenta que esto sube de nivel todas las obsolescencias, no solodescribeEnum. Si es demasiado para un solo PR, déjalo enwarningy haz fallar la CI con--fatal-warnings. Verifica: agrega una llamada adescribeEnumen un archivo temporal y confirma que la CI falla.
Verificación
Ejecuté las versiones antes y después de cada patrón anterior lado a lado en un solo flutter test sobre Flutter 3.47.4:
| Patrón | Resultado de describeEnum | Resultado migrado |
|---|---|---|
| Enum simple | dark | dark |
Enum mejorado que sobrescribe toString() | high | high |
| Clase tipo enum | beta | beta |
Anulable, valor null | null | null |
Tear-off sobre values | [light, dark] | [light, dark] |
Valor enum tipado como Object | light | light |
| Ida y vuelta por JSON | ThemeChoice.dark | ThemeChoice.dark |
| Valor JSON desconocido | StateError | ArgumentError |
debugFillProperties | choice: "dark" | choice: dark |
Después de la migración, la lista de verificación es corta: flutter analyze queda limpio con deprecated_member_use: error, el escaneo de dependencias no imprime nada para paquetes de terceros y la suite de pruebas pasa. Para tener certeza extra, haz checkout de una rama de Flutter con #190076 aplicado y ejecuta flutter test. Así es como capturé los mensajes de error de más arriba.
Plan de reversión
No hay nada que revertir en tu propio código: .name y values.byName funcionan en todas las versiones de Flutter desde 3.0, así que el código migrado corre en el SDK que tienes hoy y en todos los SDK posteriores a la eliminación. El único paso que puede doler es una actualización mayor de un paquete en el paso 5. Hazla en su propio commit, para poder revertir el cambio de pubspec.yaml y pubspec.lock por separado y conservar la limpieza de describeEnum.
Trampas
dart fixno te va a ayudar. A diferencia de la mayoría de las obsolescencias de Flutter,describeEnumno tiene un arreglo basado en datos enpackages/flutter/lib/fix_data, y la guía de eliminación indica que la migración no está soportada pordart fix. Si ejecutas una pasada dedart fixsobre todo el repo para otras migraciones, esta sigue siendo manual.- No reemplaces
describeEnum(e)pore.toString().split('.').last. Es la respuesta más común en Stack Overflow, y es incorrecta para los enums mejorados que sobrescribentoString():Level.high.toString().split('.').lastdevuelveLevel(H). - Código generado. Si una coincidencia del paso 1 está en un archivo
.g.darto.freezed.dart, arregla el generador (actualízalo o cambia tu plantilla) y regenera. Editar la salida a mano solo dura hasta la siguiente ejecución debuild_runner. - La documentación dice 3.47, el SDK no. Si un revisor señala el índice de cambios incompatibles y pregunta por qué 3.47.4 todavía compila, es porque la guía se fusionó antes que el cambio de código. Sigue #190076 para conocer la fecha real. El propio campo “Landed in version” de la guía todavía dice TBD.
Relacionados
- Si tienes una pila de obsolescencias de Flutter por limpiar de una vez, ejecutar
dart fixsobre todo un repo se encarga de todo lo que sí tiene un arreglo basado en datos. - Otra obsolescencia que requiere una reescritura manual: reemplazar
groupValueyonChangedobsoletos deRadioporRadioGroup. - La migración grande del grafo de dependencias que le llega a toda app Flutter: pasar a los paquetes independientes
material_uiycupertino_ui. - Si el parseo de tus enums vive dentro de la decodificación de JSON, corregir
FormatException: Unexpected characteren Dart cubre la otra mitad de ese camino de código.
Fuentes
- flutter/flutter#190076: Remove deprecated
describeEnumfrom framework - flutter/flutter#125016: Deprecate
describeEnum - Cambio incompatible en Flutter: Remove describeEnum
- Cambio incompatible en Flutter: Migration guide for describeEnum and EnumProperty
- Referencia de la API de
describeEnum - Referencia de la API de
EnumProperty - Lenguaje Dart: Enumerated types
- Changelog del SDK de Dart, 2.15.0
- flutter_svg en pub.dev
Comments
Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.