Componentes diferidos para Android y web
Cómo crear componentes diferidos para mejorar el rendimiento de descarga.
Introducción
#Con Flutter, las aplicaciones Android y web tienen la capacidad de descargar componentes diferidos (código y activos adicionales) mientras la aplicación ya se está ejecutando. Esto es útil si tienes una aplicación grande y solo deseas instalar componentes cuando el usuario los necesite.
Aunque Flutter admite la carga diferida en Android y la web, las implementaciones difieren. Ambas requieren importaciones diferidas de Dart.
-
Los dynamic feature modules de Android entregan los componentes diferidos empaquetados como módulos de Android.
Al compilar para Android, aunque puedes diferir la carga de módulos, debes compilar toda la aplicación y cargarla como un único Android App Bundle (AAB). Flutter no admite el envío de actualizaciones parciales sin volver a subir nuevos Android App Bundles para toda la aplicación.
Flutter realiza la carga diferida cuando compilas tu aplicación Android en modo release o profile, pero el modo debug trata todos los componentes diferidos como importaciones normales.
La web crea los componentes diferidos como archivos
*.jsindependientes.
Para profundizar en los detalles técnicos de cómo funciona esta característica, consulta Componentes diferidos (Deferred Components) en la wiki de Flutter.
Cómo configurar tu proyecto Android para componentes diferidos
#Las siguientes instrucciones explican cómo configurar tu aplicación Android para la carga diferida.
Paso 1: Dependencias y configuración inicial del proyecto
#Añade Play Core a las dependencias de build.gradle de la aplicación Android. En
android/app/build.gradleañade lo siguiente:android/app/build.gradle.ktskotlin... dependencies { ... implementation("com.google.android.play:core:1.8.0") ... }android/app/build.gradlegroovy... dependencies { ... implementation "com.google.android.play:core:1.8.0" ... }Si utilizas Google Play Store como modelo de distribución para funciones dinámicas, la aplicación debe admitir
SplitCompaty proporcionar una instancia de unPlayStoreDeferredComponentManager. Ambas tareas se pueden realizar configurando la propiedadandroid:nameen la aplicación enandroid/app/src/main/AndroidManifest.xmlcomoio.flutter.embedding.android.FlutterPlayStoreSplitApplication:xml<manifest ... <application android:name="io.flutter.embedding.android.FlutterPlayStoreSplitApplication" ... </application> </manifest>io.flutter.app.FlutterPlayStoreSplitApplicationmaneja ambas tareas por ti. Si utilizasFlutterPlayStoreSplitApplication, puedes pasar al paso 1.3.Si tu aplicación Android es grande o compleja, es posible que desees admitir por separado
SplitCompaty proporcionar elPlayStoreDynamicFeatureManagermanualmente.Para admitir
SplitCompat, existen tres métodos (como se detalla en la documentación de Android), cualquiera de los cuales es válido:Haz que tu clase de aplicación extienda
SplitCompatApplication:javapublic class MyApplication extends SplitCompatApplication { ... }Llama a
SplitCompat.install(this);en el métodoattachBaseContext():java@Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); // Emulates installation of future on demand modules using SplitCompat. SplitCompat.install(this); }Declara
SplitCompatApplicationcomo la subclase de aplicación y añade el código de compatibilidad de Flutter deFlutterApplicationa tu clase de aplicación:xml<application ... android:name="com.google.android.play.core.splitcompat.SplitCompatApplication"> </application>
El embedder se basa en una instancia inyectada de
DeferredComponentManagerpara manejar las solicitudes de instalación de componentes diferidos. Proporciona unPlayStoreDeferredComponentManageren el embedder de Flutter agregando el siguiente código a la inicialización de tu aplicación:javaimport io.flutter.embedding.engine.dynamicfeatures.PlayStoreDeferredComponentManager; import io.flutter.FlutterInjector; ... PlayStoreDeferredComponentManager deferredComponentManager = new PlayStoreDeferredComponentManager(this, null); FlutterInjector.setInstance(new FlutterInjector.Builder() .setDeferredComponentManager(deferredComponentManager).build());Opta por los componentes diferidos agregando la entrada
deferred-componentsal archivopubspec.yamlde la aplicación bajo la entradaflutter:yaml... flutter: ... deferred-components: ...La herramienta
flutterbusca la entradadeferred-componentsen el archivopubspec.yamlpara determinar si la aplicación debe compilarse como diferida o no. Esto se puede dejar vacío por ahora a menos que ya conozcas los componentes deseados y las bibliotecas Dart diferidas que van en cada uno. Completarás esta sección más adelante en el paso 3.3 una vez quegen_snapshotproduce las unidades de carga.
Paso 2: Implementar bibliotecas Dart diferidas
#A continuación, implementa bibliotecas Dart cargadas de forma diferida en el
código Dart de tu aplicación. No es necesario que la implementación esté
completa aún. El ejemplo en el resto de esta página agrega un nuevo widget
diferido simple como marcador de posición. También puedes convertir el código existente
para que sea diferido modificando las importaciones y
protegiendo el uso del código diferido detrás de loadLibrary()
Futures.
Crea una nueva biblioteca Dart. Por ejemplo, crea un nuevo widget
DeferredBoxque se pueda descargar en tiempo de ejecución. Este widget puede ser de cualquier complejidad pero, para los propósitos de esta guía, crea una caja simple como sustituto. Para crear un widget de caja azul simple, creabox.dartcon el siguiente contenido:box.dartdartimport 'package:flutter/material.dart'; /// A simple blue 30x30 box. class DeferredBox extends StatelessWidget { const DeferredBox({super.key}); @override Widget build(BuildContext context) { return Container(height: 30, width: 30, color: Colors.blue); } }Importa la nueva biblioteca Dart con la palabra clave
deferreden tu aplicación y llama aloadLibrary()(consulta carga diferida de una biblioteca). El siguiente ejemplo utilizaFutureBuilderpara esperar a que se complete elloadLibraryFuture(creado eninitState) y mostrar unCircularProgressIndicatorcomo marcador de posición. Cuando se completa elFuture, devuelve el widgetDeferredBox.SomeWidgetpuede usarse en la aplicación con normalidad y nunca intentará acceder al código Dart diferido hasta que se haya cargado correctamente.dartimport 'package:flutter/material.dart'; import 'box.dart' deferred as box; class SomeWidget extends StatefulWidget { const SomeWidget({super.key}); @override State<SomeWidget> createState() => _SomeWidgetState(); } class _SomeWidgetState extends State<SomeWidget> { late Future<void> _libraryFuture; @override void initState() { super.initState(); _libraryFuture = box.loadLibrary(); } @override Widget build(BuildContext context) { return FutureBuilder<void>( future: _libraryFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { if (snapshot.hasError) { return Text('Error: ${snapshot.error}'); } return box.DeferredBox(); } return const CircularProgressIndicator(); }, ); } }La función
loadLibrary()devuelve unFuture<void>que se completa con éxito cuando el código de la biblioteca está disponible para su uso y se completa con un error en caso contrario. Todo uso de símbolos de la biblioteca diferida debe estar protegido detrás de una llamadaloadLibrary()completada. Todas las importaciones de la biblioteca deben marcarse comodeferredpara que se compile adecuadamente para usarse en un componente diferido. Si un componente ya se ha cargado, las llamadas adicionales aloadLibrary()se completan rápidamente (pero no de forma síncrona). La funciónloadLibrary()también se puede llamar antes para activar una precarga que ayude a enmascarar el tiempo de carga.Puedes encontrar otro ejemplo de carga de importación diferida en
lib/deferred_widget.dartde Flutter Gallery.
Paso 3: Construir la aplicación
#Usa el comando de flutter para compilar una
aplicación con componentes diferidos:
flutter build appbundle
Este comando te ayuda validando que tu proyecto esté configurado correctamente para compilar aplicaciones con componentes diferidos. De forma predeterminada, la compilación falla si el validador detecta algún problema y te guía a través de los cambios sugeridos para solucionarlo.
El comando
flutter build appbundleejecuta el validador e intenta compilar la aplicación indicándole agen_snapshotque produzca bibliotecas compartidas AOT divididas como archivos SO separados. En la primera ejecución, el validador probablemente fallará al detectar problemas; la herramienta hace recomendaciones sobre cómo configurar el proyecto y solucionar estos problemas.El validador se divide en dos secciones: validación antes de la compilación (prebuild) y después de gen_snapshot. Esto se debe a que cualquier validación que haga referencia a las unidades de carga no se puede realizar hasta que
gen_snapshotse complete y produzca un conjunto final de unidades de carga.El validador detecta cualquier unidad de carga nueva, modificada o eliminada generada por
gen_snapshot. Las unidades de carga generadas actualmente se registran en tu archivo<projectDirectory>/deferred_components_loading_units.yaml. Este archivo debe incluirse en el control de versiones para garantizar que se puedan detectar los cambios en las unidades de carga realizados por otros desarrolladores.El validador también comprueba lo siguiente en el directorio
android:<projectDir>/android/app/src/main/res/values/strings.xml
Una entrada para cada componente diferido que mapea la clave${componentName}Namea${componentName}. Este recurso de cadena es utilizado por elAndroidManifest.xmlde cada módulo de función para definir lapropiedad dist:title. Por ejemplo:xml<?xml version="1.0" encoding="utf-8"?> <resources> ... <string name="boxComponentName">boxComponent</string> </resources><projectDir>/android/<componentName>
Existe un módulo de función dinámica de Android para cada componente diferido y contiene un archivobuild.gradleysrc/main/AndroidManifest.xml. Esto solo comprueba la existencia y no valida el contenido de estos archivos. Si un archivo no existe, genera uno recomendado por defecto.<projectDir>/android/app/src/main/res/values/AndroidManifest.xml
Contiene una entrada de meta-data que codifica el mapeo entre las unidades de carga y el nombre del componente con el que está asociada la unidad de carga. El embedder utiliza este mapeo para convertir el ID de la unidad de carga interna de Dart al nombre de un componente diferido para instalar. Por ejemplo:xml... <application android:label="MyApp" android:name="io.flutter.app.FlutterPlayStoreSplitApplication" android:icon="@mipmap/ic_launcher"> ... <meta-data android:name="io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager.loadingUnitMapping" android:value="2:boxComponent"/> </application> ...
El validador de
gen_snapshotno se ejecutará hasta que pase el validador previo a la compilación (prebuild).Para cada una de estas comprobaciones, la herramienta produce los archivos modificados o nuevos necesarios para pasar la comprobación. Estos archivos se colocan en el directorio
<projectDir>/build/android_deferred_components_setup_files. Se recomienda aplicar los cambios copiando y sobreescribiendo los mismos archivos en el directorioandroiddel proyecto. Antes de sobreescribir, el estado actual del proyecto debe confirmarse en el control de código fuente y los cambios recomendados deben revisarse para que sean apropiados. La herramienta no realizará ningún cambio en tu directorioandroid/automáticamente.Una vez que las unidades de carga disponibles se generan y se registran en
<projectDirectory>/deferred_components_loading_units.yaml, es posible configurar completamente la seccióndeferred-componentsdel pubspec para que las unidades de carga se asignen a los componentes diferidos como se desee. Para continuar con el ejemplo de la caja, el archivo generadodeferred_components_loading_units.yamlcontendría:yamlloading-units: - id: 2 libraries: - package:MyAppName/box.DartEl ID de la unidad de carga ('2' en este caso) se utiliza internamente por Dart y se puede ignorar. La unidad de carga base (ID '1') no aparece en la lista y contiene todo lo que no esté contenido explícitamente en otra unidad de carga.
Ahora puedes agregar lo siguiente a
pubspec.yaml:yaml... flutter: ... deferred-components: - name: boxComponent libraries: - package:MyAppName/box.Dart ...Para asignar una unidad de carga a un componente diferido, agrega cualquier biblioteca Dart de la unidad de carga en la sección de bibliotecas (libraries) del módulo de función. Ten en cuenta las siguientes pautas:
Las unidades de carga no deben incluirse en más de un componente.
Incluir una biblioteca Dart de una unidad de carga indica que toda la unidad de carga está asignada al componente diferido.
Todas las unidades de carga no asignadas a un componente diferido se incluyen en el componente base, que siempre existe implícitamente.
Las unidades de carga asignadas al mismo componente diferido se descargan, se instalan y se envían juntas.
El componente base es implícito y no necesita definirse en el pubspec.
Los activos (assets) también pueden incluirse agregando una sección de activos (assets) en la configuración del componente diferido:
yamldeferred-components: - name: boxComponent libraries: - package:MyAppName/box.Dart assets: - assets/image.jpg - assets/picture.png # wildcard directory - assets/gallery/Un activo se puede incluir en múltiples componentes diferidos, pero la instalación de ambos componentes da como resultado un activo duplicado. Los componentes que solo contienen activos también se pueden definir omitiendo la sección de bibliotecas. Estos componentes de solo activos deben instalarse con la clase de utilidad
DeferredComponenten los servicios en lugar deloadLibrary(). Dado que las bibliotecas Dart se empaquetan junto con los activos, si una biblioteca Dart se carga conloadLibrary(), también se cargan los activos del componente. Sin embargo, instalar por nombre de componente y la utilidad de servicios no cargará ninguna biblioteca Dart en el componente.Eres libre de incluir activos en cualquier componente, siempre que estén instalados y cargados cuando se haga referencia a ellos por primera vez, aunque normalmente es mejor empaquetar los activos y el código Dart que los utiliza en el mismo componente.
Agrega manualmente todos los componentes diferidos que definiste en
pubspec.yamldentro del archivoandroid/settings.gradlecomo includes. Por ejemplo, si hay tres componentes diferidos definidos en el pubspec llamadosboxComponent,circleComponentyassetComponent, asegúrate de queandroid/settings.gradlecontenga lo siguiente:android/settings.gradle.ktskotlininclude(":app", ":boxComponent", ":circleComponent", ":assetComponent") ...android/settings.gradlegroovyinclude ':app', ':boxComponent', ':circleComponent', ':assetComponent' ...Repite los pasos del 3.1 al 3.6 (este paso) hasta que se gestionen todas las recomendaciones del validador y la herramienta se ejecute sin más recomendaciones.
Cuando tiene éxito, este comando genera un archivo
app-release.aabenbuild/app/outputs/bundle/release.Una compilación exitosa no siempre significa que la aplicación se haya compilado de la manera esperada. Depende de ti asegurarte de que todas las unidades de carga y bibliotecas Dart se incluyan de la forma esperada. Por ejemplo, un error común es importar accidentalmente una biblioteca Dart sin la palabra clave
deferred, lo que da como resultado que una biblioteca diferida se compile como parte de la unidad de carga base. En este caso, la biblioteca Dart se cargaría correctamente porque siempre está presente en la base y la biblioteca no se dividiría. Esto se puede comprobar examinando el archivodeferred_components_loading_units.yamlpara verificar que las unidades de carga generadas se describan como se esperaba.Al ajustar las configuraciones de los componentes diferidos, o al realizar cambios en Dart que agreguen, modifiquen o eliminen unidades de carga, es de esperar que el validador falle. Sigue los pasos del 3.1 al 3.6 (este paso) para aplicar cualquier cambio recomendado para continuar con la compilación.
Ejecutar la aplicación localmente
#Una vez que tu aplicación haya compilado con éxito un archivo AAB,
usa bundletool de Android
para realizar
pruebas locales con la bandera --local-testing.
Para ejecutar el archivo AAB en un dispositivo de prueba, descarga el ejecutable jar de bundletool desde github.com/google/bundletool/releases y ejecuta:
java -jar bundletool.jar build-apks --bundle=<your_app_project_dir>/build/app/outputs/bundle/release/app-release.aab --output=<your_temp_dir>/app.apks --local-testing
java -jar bundletool.jar install-apks --apks=<your_temp_dir>/app.apks
Donde <your_app_project_dir> es la ruta al directorio del proyecto de tu
aplicación y <your_temp_dir> es cualquier directorio temporal
utilizado para almacenar las salidas de bundletool.
Esto desempaqueta tu archivo AAB en un archivo APK y
lo instala en el dispositivo. Todas las funciones dinámicas de Android disponibles
se cargan en el dispositivo localmente y se emula
la instalación de componentes diferidos.
Antes de ejecutar build-apks nuevamente,
elimina el archivo APK de la aplicación existente:
rm <your_temp_dir>/app.apks
Los cambios en la base de código Dart requieren incrementar el ID de compilación de Android o desinstalar y reinstalar la aplicación, ya que Android no actualizará los módulos de funciones a menos que detecte un nuevo número de versión.
Publicar en Google Play Store
#El archivo AAB compilado se puede subir directamente a
Play Store como de costumbre. Cuando se llama a loadLibrary(),
el motor de Flutter descarga el módulo de Android necesario que contiene la biblioteca Dart AOT y
los activos (assets) utilizando la función de entrega de Play Store.
A menos que se indique lo contrario, la documentación de este sitio refleja Flutter 3.44.0. Página actualizada por última vez el 2026-05-05. Ver código fuente oreportar un problema.