Hooks
Ejecutar scripts de construcción personalizados.
Esta guía explica qué son los hooks y cómo usarlos con un paquete.
Introducción
#Actualmente puedes usar hooks para hacer cosas como compilar o descargar assets nativos (código escrito en otros lenguajes que se compila en código máquina), y luego llamar a estos assets desde el código Dart de un paquete.
Los hooks son scripts de Dart ubicados en el directorio hook/ de
tu paquete Dart. Tienen un formato predefinido para su
entrada y salida, lo que permite al SDK de Dart:
- Descubrir los hooks.
- Ejecutar los hooks con la entrada necesaria.
- Consumir la salida producida por los hooks.
Proyecto de ejemplo con un hook de construcción:
-
example_project/// Proyecto con hooks.
hook/// Agrega scripts de hook aquí.
- build.dart
lib/ // Usa tus assets aquí.
- example.dart
src/ // Agrega fuentes nativas aquí.
- example_native_library.c
- example_native_library.h
test/// Prueba tus assets aquí.
- example_test.dart
Hooks
#Currently, build hooks and link hooks are available. To learn more, see the following.
Hooks de construcción
#Con los hooks de construcción, un paquete puede hacer cosas como compilar o descargar assets nativos, como bibliotecas de C o Rust. Después, estos assets se pueden llamar desde el código Dart de un paquete.
El SDK de Dart invoca automáticamente el hook de construcción de un paquete en el momento adecuado durante el proceso de construcción. Los hooks de construcción se ejecutan en paralelo con la compilación de Dart y podrían realizar operaciones de mayor duración, como descargar o llamar a un compilador nativo.
Usa la función build para analizar la entrada
del hook con BuildInput
y luego escribe la salida del hook
con BuildOutputBuilder. El hook debe colocar los
assets descargados y generados en
BuildInput.sharedOutputDirectory.
Los assets producidos para tu paquete podrían depender de los
assets
o los metadatos
producidos por los hooks de construcción
de los paquetes en las dependencias directas en el pubspec. Por lo tanto,
los hooks de construcción se ejecutan en el orden de las dependencias en el pubspec, y las dependencias
cíclicas entre paquetes no son compatibles cuando se usan hooks.
Link hooks
#With link hooks, a package can filter, optimize, or tree-shake code assets generated by build hooks before bundling them into an application.
The Dart SDK automatically invokes a package's link hook (hook/link.dart)
during the application bundling phase. Unlike build hooks, which run
for each package, link hooks run with application-level context so they
can inspect which symbols the application actually uses.
Use the link function to parse the hook input with
LinkInput
and
write the hook output with LinkOutputBuilder.
If tree-shaking removes all symbols for an asset, the link hook can omit
the asset entirely to avoid bundling it with the application.
Variables de entorno
#Los hooks se ejecutan en un entorno semihermético.
Esto significa que Platform.environment no
expone todas las variables de entorno del proceso principal.
Esto garantiza que las invocaciones de los hooks sean reproducibles y almacenables en caché, y
no dependan de variables de entorno accidentales.
Sin embargo, algunas variables de entorno son necesarias para localizar herramientas (como compiladores) o configurar el acceso a la red. Las siguientes variables de entorno se pasan al proceso del hook:
-
Path and system roots:
PATH: Invocar herramientas nativas.HOME,USERPROFILE: Buscar herramientas en las ubicaciones de instalación predeterminadas.SYSTEMDRIVE,SYSTEMROOT,WINDIR: Invocaciones de procesos y CMake en Windows.PROGRAMDATA: Paravswhere.exeen Windows.
-
Temporary directories:
TEMP,TMP,TMPDIR: Directorios temporales.
-
HTTP proxies:
HTTP_PROXY,HTTPS_PROXY,NO_PROXY: Acceso a la red detrás de proxies.
-
Clang/LLVM:
LIBCLANG_PATH:bindgende Rust +clang-sys.
-
Android NDK:
ANDROID_HOME: Ubicación estándar para el SDK/NDK de Android.ANDROID_NDK,ANDROID_NDK_HOME,ANDROID_NDK_LATEST_HOME,ANDROID_NDK_ROOT: Ubicaciones alternativas para el NDK.
- Nix:
- Cualquier variable que comience con
NIX_.
- Cualquier variable que comience con
Cualquier cambio en estas variables de entorno provoca la invalidación de la caché de los hooks.
Todas las demás variables de entorno se eliminan.
Assets
#Los assets son los archivos producidos por un hook y luego
empaquetados en una aplicación Dart. Se puede acceder a los assets
en tiempo de ejecución desde el código Dart. Actualmente, el SDK de Dart puede
usar el tipo CodeAsset, pero se planean más tipos de assets.
Para obtener más información, consulta lo siguiente.
Tipo CodeAsset
#Un CodeAsset
representa un asset de código. Un asset de código es una biblioteca dinámica
compilada a partir de un lenguaje que no sea Dart, como C, C++, Rust o Go.
CodeAsset es parte del paquete code_asset. Se accede a las APIs proporcionadas por los assets de código
en tiempo de ejecución a través de los correspondientes miembros externos de Dart anotados
con la anotación @Native
de dart:ffi.
Usar un hook
#Para agregar assets a tu proyecto, usa un hook. Para más detalles, consulta las siguientes secciones.
Agregar dependencias
#Para usar un hook, primero debes agregar los paquetes auxiliares
hooks y code_assets a las dependencias de tu pubspec.yaml:
dart pub add hooks code_assets.
Si necesitas compilar fuentes en C, también necesitarás el paquete native_toolchain_c:
dart pub add native_toolchain_c.
Dependencias de ejemplo para un hook de construcción:
name: native_add_library
description: Sums two numbers with native code.
version: 0.1.0
environment:
sdk: '^3.10.0'
dependencies:
# ...
code_assets: any
hooks: any
native_toolchain_c: any
dev_dependencies:
# ...
ffigen: ^18.0.0
Crear un hook de construcción para generar assets nativos
#Si deseas usar un hook de construcción para compilar de forma transparente
assets nativos (como bibliotecas de C o Rust), que luego
estén disponibles para ser llamados desde el código Dart de un paquete,
crea un script build.dart similar al siguiente:
En tu proyecto Dart, crea o abre
hook/build.dart.-
En el método
main, llama a la funciónbuilddepackage:hooks/hooks.darty usa la cadena de herramientas (toolchain) adecuada para compilar la biblioteca nativa. Por ejemplo:hook/build.dartdartimport 'package:hooks/hooks.dart'; import 'package:native_toolchain_c/native_toolchain_c.dart'; void main(List<String> args) async { await build(args, (input, output) async { final packageName = input.packageName; final cLibrary = CLibrary( name: packageName, assetName: '$packageName.dart', sources: ['src/$packageName.c'], ); await cLibrary.build( input: input, output: output, ); }); }El segundo parámetro de
buildespera una función a la que le pasará dos argumentos:input: La entrada de solo lectura para el hook. Incluye información para que el hook produzca el tipo de asset correcto (por ejemplo, SO de destino, arquitectura de destino, directorio de salida y más). Para más detalles, consulta la claseBuildInput.output: El constructor de solo escritura para la salida del hook. Después de que el hook de construcción lee la entrada, produce un asset y luego proporciona lo que produjo como salida. Para más detalles, consulta la claseBuildOutputBuilder.
Create a link hook to tree-shake native assets
#If your package generates native assets with a build hook (hook/build.dart),
you can add a link hook (hook/link.dart) to tree-shake unused code assets
before bundling.
During compilation, the Dart compiler records which @Native symbols are
actually referenced by the application and provides them to link hooks through
LinkInput.recordedUses.
If input.recordedUses is null, the link hook disables tree-shaking and
keeps all symbols.
In your Dart project, create or open
hook/link.dart.-
In the
mainmethod, call thelinkfunction frompackage:hooks/hooks.dartand pass tree-shaking options toCLibrary.link. For example:hook/link.dartdartimport 'package:hooks/hooks.dart'; import 'package:native_toolchain_c/native_toolchain_c.dart'; import 'package:record_use/record_use.dart'; import 'record_use_mapping.dart'; void main(List<String> args) async { await link(args, (input, output) async { final packageName = input.packageName; final cLibrary = CLibrary( name: packageName, assetName: '$packageName.dart', sources: ['src/$packageName.c'], ); final linkerOptions = LinkerOptions.treeshake( symbolsToKeep: input.recordedUses?.calls.keys .cast<Method>() .map((e) => recordUseMapping[e.name]!), ); await cLibrary.link( input: input, output: output, linkerOptions: linkerOptions, ); }); }
Dart symbols in bindings generated by tools such as ffigen don't always map
one-to-one to native C symbol names.
When generating FFI bindings, ffigen can automatically generate a mapping
(recordUseMapping) from Dart method identifiers to native symbols.
Map each recorded Dart method call (input.recordedUses?.calls.keys)
using recordUseMapping to pass the corresponding native symbols to
LinkerOptions.treeshake.
When symbolsToKeep is null (for example, when input.recordedUses
is
null), LinkerOptions.treeshake preserves all symbols in the native library.
When symbolsToKeep is an empty list ([]), meaning the application doesn't
reference any symbols from the library, CLibrary.link automatically skips
compiling and bundling the dynamic library entirely.
Assets empaquetados automáticamente
#Los hooks se ejecutan automáticamente al invocar los comandos run, build o test.
Los assets resultantes se almacenan en el directorio de salida especificado en
la entrada del hook. El SDK de Dart luego empaqueta automáticamente esos assets con
tu aplicación Dart para que se pueda acceder a ellos en tiempo de ejecución.
Usar assets
#Assets are the files that hooks create. Once an asset is
created, you can reference it in your code and at run time
with its asset ID (assetId). Asset IDs are structured
as package:<package-name>/<asset-name>. Build hooks can
only output assets in their own package. CLibrary in
the build hook in the previous example outputs the asset ID
package:native_add_library/native_add_library.dart, and is
based on the packageName and assetName.
El siguiente ejemplo ilustra cómo vincular a la función nativa
de C add de native_add_library.c y llamarla:
import 'dart:ffi';
@Native<Int32 Function(Int32, Int32)>()
external int add(int a, int b);
import 'package:my_package/my_package.dart';
void main() {
print(add(24, 18));
}
El ID de asset en @Native es opcional y por defecto es la
URI de la biblioteca. En el ejemplo anterior, esto es
package:native_add_library/native_add_library.dart, que
es el mismo ID de asset producido por el hook de construcción. Esto
permite a Dart conectar un asset referenciado en tiempo de ejecución con
el proporcionado por el hook durante el proceso de construcción.
Probar assets
#Después de escribir un hook que genera un asset y de haber usado ese asset en tu código Dart, considera escribir una prueba para verificar que el hook y el asset generado funcionen como se espera.
En el siguiente ejemplo, se crea una prueba para
native_add_library.dart, un script que hace referencia a una
función nativa de C llamada add:
import 'package:native_add_library/native_add_library.dart';
import 'package:test/test.dart';
void main() {
test('invoke native function', () {
expect(add(24, 18), 42);
});
}
Configuración del hook
#Pasa parámetros personalizados o rutas de archivos locales a los hooks de construcción y enlace
desde el entorno de construcción de tu proyecto.
Configura estos parámetros bajo la clave hooks en tu archivo pubspec.yaml.
Actualmente, este bloque solo admite la opción user_defines.
Configurar user-defines
#Configura parámetros personalizados dentro del archivo pubspec.yaml del paquete raíz,
o en el archivo pubspec.yaml del espacio de trabajo si usas uno.
Solo los usuarios finales (los autores de la aplicación raíz o el paquete que consume
las dependencias) pueden configurar user-defines.
Las dependencias no pueden proporcionar sus propios user-defines predeterminados.
Los valores configurados bajo user_defines pueden ser de cualquier tipo compatible con JSON,
como booleanos, cadenas, números, mapas anidados o listas.
Los user-defines se filtran por paquete.
Un hook dentro de my_package solo puede acceder a las claves configuradas bajo
hooks.user_defines.my_package.
No puede acceder a los user-defines de otros paquetes.
El siguiente es un ejemplo de cómo pasar dos user-defines
a los hooks del paquete my_package:
hooks:
user_defines:
my_package:
enable_experimental: true
custom_lib: assets/libnative.so
Acceder a user-defines en un hook
#Para acceder a los user-defines configurados en tu
script de hook build.dart o link.dart,
usa el objeto input.userDefines:
- Lee valores sin procesar (raw) usando el operador de corchetes, como
input.userDefines['key']. - Resuelve rutas relativas a una
Uriusando el métodopath(), comoinput.userDefines.path('key'). Esto resuelve la ruta relativa con respecto al directorio delpubspec.yamldonde se declaran los user-defines.
Si el hook lee un archivo o directorio resuelto,
regístralo como una dependencia usando
output.dependencies.add().
Esto garantiza que el sistema de construcción invalide la caché y
vuelva a ejecutar el hook cuando el archivo cambie.
El siguiente es un ejemplo de script de hook que accede a los
user-defines enable_experimental y custom_lib:
import 'dart:io';
import 'package:hooks/hooks.dart';
void main(List<String> args) async {
await build(args, (input, output) async {
final experimental = input.userDefines['enable_experimental'];
if (experimental is! bool?) {
throw const FormatException(
'hooks.user_defines.my_package.enable_experimental must be a '
'boolean (or omitted)',
);
}
if (experimental ?? false) {
print('Experimental features enabled.');
}
final customLibUri = input.userDefines.path('custom_lib');
if (customLibUri != null) {
final file = File.fromUri(customLibUri);
output.dependencies.add(file.uri);
// Use the file...
}
});
}
Proyectos de ejemplo
#Hay varios proyectos de ejemplo para ayudarte a comenzar con los hooks y los assets de código:
| Project | Description |
|---|---|
sqlite
|
A package compiling, bundling, tree-shaking, and using a native database engine. |
mini_audio
|
A package compiling, bundling, tree-shaking, and using a native audio player. |
stb_image
|
A package compiling, bundling, tree-shaking, and using a native image library. |
host_name
|
Un paquete que usa una biblioteca del sistema nativa. |
native_add_library
|
Un paquete que compila, empaqueta y usa algo de código C simple. |
native_add_app
|
Una aplicación CLI de Dart que depende de native_add_library. |
download_asset
|
Un paquete que empaqueta y usa assets preconstruidos que se descargan en el hook de construcción. |
native_dynamic_linking
|
Un paquete que compila, empaqueta y usa tres bibliotecas nativas que dependen entre sí. |
use_dart_api
|
Un paquete que usa la API C de la Dart VM. |
Más información
#Consulta los siguientes enlaces para más información:
Unless stated otherwise, the documentation on this site reflects Dart 3.12.2. Page last updated on 2026-07-15. Ver fuente oreportar un problema.