Data assets

Refiling of #169273, which itself is a rebase of #159675

This PR adds bundling support for the experimental dart data asset
feature: Dart packages with hooks can now emit data assets which the
flutter tool will bundle.

It relies on flutter's existing asset bundling mechanism (e.g. entries
in AssetManifest.json, DevFS syncing in reload/restart, ...).

The support is added under an experimental flag (similar to the existing
native assets experimental flag).

Also, kNativeAssets is removed to also bundle data assets on flutter
build bundle.

The chrome sandbox is disabled as per #165664.

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart
index 56ead85..901644f 100644
--- a/packages/flutter_tools/lib/executable.dart
+++ b/packages/flutter_tools/lib/executable.dart
@@ -11,6 +11,7 @@
 import 'src/base/terminal.dart';
 import 'src/base/user_messages.dart';
 import 'src/build_system/build_targets.dart';
+import 'src/build_system/targets/hook_runner_native.dart' show FlutterHookRunnerNative;
 import 'src/cache.dart';
 import 'src/commands/analyze.dart';
 import 'src/commands/assemble.dart';
@@ -47,6 +48,7 @@
 import 'src/features.dart';
 import 'src/globals.dart' as globals;
 // Files in `isolated` are intentionally excluded from google3 tooling.
+import 'src/hook_runner.dart' show FlutterHookRunner;
 import 'src/isolated/build_targets.dart';
 import 'src/isolated/mustache_template.dart';
 import 'src/isolated/native_assets/test/native_assets.dart';
@@ -105,6 +107,7 @@
     muteCommandLogging: muteCommandLogging,
     verboseHelp: verboseHelp,
     overrides: <Type, Generator>{
+      FlutterHookRunner: () => FlutterHookRunnerNative(),
       // The web runner is not supported in google3 because it depends
       // on dwds.
       WebRunnerFactory: () => DwdsWebRunnerFactory(),
diff --git a/packages/flutter_tools/lib/src/asset.dart b/packages/flutter_tools/lib/src/asset.dart
index 958ad85..d44fa51 100644
--- a/packages/flutter_tools/lib/src/asset.dart
+++ b/packages/flutter_tools/lib/src/asset.dart
@@ -25,6 +25,84 @@
 import 'package_graph.dart';
 import 'project.dart';
 
+class FlutterHookResult {
+  const FlutterHookResult({
+    required this.buildStart,
+    required this.buildEnd,
+    required this.dataAssets,
+    required this.dependencies,
+  });
+
+  FlutterHookResult.empty()
+    : this(
+        buildStart: DateTime.fromMillisecondsSinceEpoch(0),
+        buildEnd: DateTime.fromMillisecondsSinceEpoch(0),
+        dataAssets: <HookAsset>[],
+        dependencies: <Uri>[],
+      );
+
+  final List<HookAsset> dataAssets;
+
+  /// The timestamp at which we start a build - so the timestamp of the inputs.
+  final DateTime buildStart;
+
+  /// The timestamp at which we finish a build - so the timestamp of the
+  /// outputs.
+  final DateTime buildEnd;
+
+  /// The dependencies of the build are used to check if the build needs to be
+  /// rerun.
+  final List<Uri> dependencies;
+
+  /// Whether caller may need to re-run the Dart build.
+  bool hasAnyModifiedFiles(FileSystem fileSystem) =>
+      _wasAnyFileModifiedSince(fileSystem, buildStart, dependencies);
+
+  /// Whether the files produced by the build are up-to-date.
+  ///
+  /// NOTICE: The build itself may be up-to-date but the output may not be (as
+  /// the output may be existing on disk and not be produced by the build
+  /// itself - in which case we may not need to re-build if the file changes,
+  /// but we may need to make a new asset bundle with the modified file).
+  bool isOutputDirty(FileSystem fileSystem) => _wasAnyFileModifiedSince(
+    fileSystem,
+    buildEnd,
+    dataAssets.map((HookAsset e) => e.file).toList(),
+  );
+
+  static bool _wasAnyFileModifiedSince(FileSystem fileSystem, DateTime since, List<Uri> uris) {
+    for (final uri in uris) {
+      final DateTime modified = fileSystem.statSync(uri.toFilePath()).modified;
+      if (modified.isAfter(since)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @override
+  String toString() {
+    return dataAssets.toString();
+  }
+}
+
+/// A convenience class to wrap native assets
+///
+/// When translating from a `DartHooksResult` to a [FlutterHookResult], where we
+/// need to have different classes to not import `isolated/` stuff.
+class HookAsset {
+  const HookAsset({required this.file, required this.name, required this.package});
+
+  final Uri file;
+  final String name;
+  final String package;
+
+  @override
+  String toString() {
+    return 'HookAsset(file: $file, name: $name, package: $package)';
+  }
+}
+
 const defaultManifestPath = 'pubspec.yaml';
 
 const kFontManifestJson = 'FontManifest.json';
@@ -113,6 +191,7 @@
 
   /// Returns 0 for success; non-zero for failure.
   Future<int> build({
+    FlutterHookResult? flutterHookResult,
     String manifestPath = defaultManifestPath,
     required String packageConfigPath,
     bool deferredComponentsEnabled = false,
@@ -163,7 +242,8 @@
        _platform = platform,
        _flutterRoot = flutterRoot,
        _splitDeferredAssets = splitDeferredAssets,
-       _licenseCollector = LicenseCollector(fileSystem: fileSystem);
+       _licenseCollector = LicenseCollector(fileSystem: fileSystem),
+       _lastHookResult = FlutterHookResult.empty();
 
   final Logger _logger;
   final FileSystem _fileSystem;
@@ -188,6 +268,8 @@
 
   DateTime? _lastBuildTimestamp;
 
+  FlutterHookResult _lastHookResult;
+
   // We assume the main asset is designed for a device pixel ratio of 1.0.
   static const _kAssetManifestJsonFilename = 'AssetManifest.json';
   static const _kAssetManifestBinFilename = 'AssetManifest.bin';
@@ -207,13 +289,19 @@
 
   @override
   bool needsBuild({String manifestPath = defaultManifestPath}) {
-    final DateTime? lastBuildTimestamp = _lastBuildTimestamp;
-    if (lastBuildTimestamp == null) {
+    if (!wasBuiltOnce() ||
+        // We need to re-run the Dart build.
+        _lastHookResult.hasAnyModifiedFiles(_fileSystem) ||
+        // We don't have to re-run the Dart build, but some files the Dart build
+        // wants us to bundle have changed contents.
+        _lastHookResult.isOutputDirty(_fileSystem)) {
       return true;
     }
+    final DateTime lastBuildTimestamp = _lastBuildTimestamp!;
 
     final FileStat manifestStat = _fileSystem.file(manifestPath).statSync();
-    if (manifestStat.type == FileSystemEntityType.notFound) {
+    if (manifestStat.type == FileSystemEntityType.notFound ||
+        manifestStat.modified.isAfter(lastBuildTimestamp)) {
       return true;
     }
 
@@ -222,18 +310,19 @@
         return true; // directory was deleted.
       }
       for (final File file in directory.listSync().whereType<File>()) {
-        final DateTime dateTime = file.statSync().modified;
-        if (dateTime.isAfter(lastBuildTimestamp)) {
+        final DateTime lastModified = file.statSync().modified;
+        if (lastModified.isAfter(lastBuildTimestamp)) {
           return true;
         }
       }
     }
 
-    return manifestStat.modified.isAfter(lastBuildTimestamp);
+    return false;
   }
 
   @override
   Future<int> build({
+    FlutterHookResult? flutterHookResult,
     String manifestPath = defaultManifestPath,
     FlutterProject? flutterProject,
     required String packageConfigPath,
@@ -257,6 +346,7 @@
     // hang on hot reload, as the incremental dill files will never be copied to the
     // device.
     _lastBuildTimestamp = DateTime.now();
+    _lastHookResult = flutterHookResult ?? FlutterHookResult.empty();
     if (flutterManifest.isEmpty) {
       entries[_kAssetManifestJsonFilename] = AssetBundleEntry(
         DevFSStringContent('{}'),
@@ -424,9 +514,38 @@
         );
       }
     }
+    for (final HookAsset dataAsset in flutterHookResult?.dataAssets ?? <HookAsset>[]) {
+      final Package package = packageConfig[dataAsset.package]!;
+      final Uri fileUri = dataAsset.file;
+
+      final String baseDir;
+      final Uri relativeUri;
+      if (fileUri.isAbsolute) {
+        final String filePath = fileUri.toFilePath();
+        baseDir = _fileSystem.path.dirname(filePath);
+        relativeUri = Uri(path: _fileSystem.path.basename(filePath));
+      } else {
+        baseDir = package.root.toFilePath();
+        relativeUri = fileUri;
+      }
+
+      final asset = _Asset(
+        baseDir: baseDir,
+        relativeUri: relativeUri,
+        entryUri: Uri.parse(_fileSystem.path.join('packages', dataAsset.package, dataAsset.name)),
+        package: package,
+      );
+      if (assetVariants.containsKey(asset)) {
+        _logger.printError(
+          'Conflicting assets: The asset "$asset" was declared in the pubspec and the hook.',
+        );
+        return 1;
+      }
+      assetVariants[asset] = <_Asset>[asset];
+    }
 
     // Save the contents of each image, image variant, and font
-    // asset in entries.
+    // asset in [entries].
     for (final _Asset asset in assetVariants.keys) {
       final File assetFile = asset.lookupAssetFile(_fileSystem);
       final List<_Asset> variants = assetVariants[asset]!;
diff --git a/packages/flutter_tools/lib/src/build_info.dart b/packages/flutter_tools/lib/src/build_info.dart
index 43eb25d..f06e15c 100644
--- a/packages/flutter_tools/lib/src/build_info.dart
+++ b/packages/flutter_tools/lib/src/build_info.dart
@@ -618,6 +618,35 @@
     }
   }
 
+  String get osName {
+    switch (this) {
+      case TargetPlatform.linux_x64:
+      case TargetPlatform.linux_arm64:
+        return 'linux';
+      case TargetPlatform.darwin:
+        return 'macos';
+      case TargetPlatform.windows_x64:
+      case TargetPlatform.windows_arm64:
+        return 'windows';
+      case TargetPlatform.android:
+      case TargetPlatform.android_arm:
+      case TargetPlatform.android_arm64:
+      case TargetPlatform.android_x64:
+        return 'android';
+      case TargetPlatform.fuchsia_arm64:
+      case TargetPlatform.fuchsia_x64:
+        return 'fuchsia';
+      case TargetPlatform.ios:
+        return 'ios';
+      case TargetPlatform.tester:
+        return 'flutter-tester';
+      case TargetPlatform.web_javascript:
+        return 'web';
+      case TargetPlatform.unsupported:
+        throw UnsupportedError('Unexpected target platform $this');
+    }
+  }
+
   String get simpleName {
     switch (this) {
       case TargetPlatform.linux_x64:
@@ -744,6 +773,12 @@
   };
 }
 
+List<DarwinArch> getDarwinArchsFromEnv(Map<String, String> defines) {
+  const defaultDarwinArchitectures = <DarwinArch>[DarwinArch.x86_64, DarwinArch.arm64];
+  return defines[kDarwinArchs]?.split(' ').map(getDarwinArchForName).toList() ??
+      defaultDarwinArchitectures;
+}
+
 String getNameForTargetPlatform(TargetPlatform platform, {DarwinArch? darwinArch}) {
   return switch (platform) {
     TargetPlatform.ios when darwinArch != null => 'ios-${darwinArch.name}',
@@ -983,20 +1018,6 @@
 /// Whether to enable Dart obfuscation and where to save the symbol map.
 const kDartObfuscation = 'DartObfuscation';
 
-/// Whether to enable Native Assets.
-///
-/// If true, native assets are built and the mapping for native assets lookup
-/// at runtime is embedded in the kernel file.
-///
-/// If false, native assets are not built, and an empty mapping is embedded in
-/// the kernel file. Used for targets that trigger kernel builds but
-/// are not OS/architecture specific.
-///
-/// Supported values are 'true' and 'false'.
-///
-/// Defaults to 'true'.
-const kNativeAssets = 'NativeAssets';
-
 /// An output directory where one or more code-size measurements may be written.
 const kCodeSizeDirectory = 'CodeSizeDirectory';
 
diff --git a/packages/flutter_tools/lib/src/build_system/targets/android.dart b/packages/flutter_tools/lib/src/build_system/targets/android.dart
index c9cabad..85db93b 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/android.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/android.dart
@@ -9,6 +9,7 @@
 import '../../build_info.dart';
 import '../../devfs.dart';
 import '../../globals.dart' as globals show xcode;
+import '../../isolated/native_assets/dart_hook_result.dart';
 import '../../project.dart';
 import '../build_system.dart';
 import '../depfile.dart';
@@ -70,9 +71,11 @@
           .file(isolateSnapshotData)
           .copySync(outputDirectory.childFile('isolate_snapshot_data').path);
     }
+    final DartHookResult dartHookResult = await DartBuild.loadHookResult(environment);
     final Depfile assetDepfile = await copyAssets(
       environment,
       outputDirectory,
+      dartHookResult: dartHookResult,
       targetPlatform: TargetPlatform.android,
       buildMode: buildMode,
       flavor: environment.defines[kFlavor],
@@ -89,7 +92,11 @@
   }
 
   @override
-  List<Target> get dependencies => const <Target>[KernelSnapshot(), InstallCodeAssets()];
+  List<Target> get dependencies => const <Target>[
+    DartBuildForNative(),
+    KernelSnapshot(),
+    InstallCodeAssets(),
+  ];
 }
 
 /// An implementation of [AndroidAssetBundle] that includes dependencies on vm
diff --git a/packages/flutter_tools/lib/src/build_system/targets/assets.dart b/packages/flutter_tools/lib/src/build_system/targets/assets.dart
index 46d2acb..c622d7f 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/assets.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/assets.dart
@@ -12,6 +12,7 @@
 import '../../dart/package_map.dart';
 import '../../devfs.dart';
 import '../../flutter_manifest.dart';
+import '../../isolated/native_assets/dart_hook_result.dart';
 import '../build_system.dart';
 import '../depfile.dart';
 import '../exceptions.dart';
@@ -33,6 +34,7 @@
 Future<Depfile> copyAssets(
   Environment environment,
   Directory outputDirectory, {
+  required DartHookResult dartHookResult,
   Map<String, DevFSContent> additionalContent = const <String, DevFSContent>{},
   required TargetPlatform targetPlatform,
   required BuildMode buildMode,
@@ -48,6 +50,7 @@
     splitDeferredAssets: buildMode != BuildMode.debug && buildMode != BuildMode.jitRelease,
   ).createBundle();
   final int resultCode = await assetBundle.build(
+    flutterHookResult: dartHookResult.asFlutterResult,
     manifestPath: pubspecFile.path,
     packageConfigPath: findPackageConfigFileOrDefault(environment.projectDir).path,
     deferredComponentsEnabled: environment.defines[kDeferredComponents] == 'true',
@@ -244,7 +247,11 @@
   String get name => 'copy_assets';
 
   @override
-  List<Target> get dependencies => const <Target>[KernelSnapshot(), InstallCodeAssets()];
+  List<Target> get dependencies => const <Target>[
+    DartBuildForNative(),
+    KernelSnapshot(),
+    InstallCodeAssets(),
+  ];
 
   @override
   List<Source> get inputs => const <Source>[
@@ -270,9 +277,11 @@
     final buildMode = BuildMode.fromCliName(buildModeEnvironment);
     final Directory output = environment.buildDir.childDirectory('flutter_assets');
     output.createSync(recursive: true);
+    final DartHookResult dartHookResult = await DartBuild.loadHookResult(environment);
     final Depfile depfile = await copyAssets(
       environment,
       output,
+      dartHookResult: dartHookResult,
       targetPlatform: TargetPlatform.android,
       buildMode: buildMode,
       flavor: environment.defines[kFlavor],
diff --git a/packages/flutter_tools/lib/src/build_system/targets/common.dart b/packages/flutter_tools/lib/src/build_system/targets/common.dart
index 1be5f6a..3730520 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/common.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/common.dart
@@ -14,6 +14,7 @@
 import '../../darwin/darwin.dart';
 import '../../devfs.dart';
 import '../../globals.dart' as globals show xcode;
+import '../../isolated/native_assets/dart_hook_result.dart' show DartHookResult;
 import '../../project.dart';
 import '../build_system.dart';
 import '../depfile.dart';
@@ -83,9 +84,11 @@
           .file(isolateSnapshotData)
           .copySync(environment.outputDir.childFile('isolate_snapshot_data').path);
     }
+    final DartHookResult dartHookResult = await DartBuild.loadHookResult(environment);
     final Depfile assetDepfile = await copyAssets(
       environment,
       environment.outputDir,
+      dartHookResult: dartHookResult,
       targetPlatform: TargetPlatform.android,
       buildMode: buildMode,
       flavor: flavor,
@@ -102,7 +105,11 @@
   }
 
   @override
-  List<Target> get dependencies => const <Target>[KernelSnapshot(), InstallCodeAssets()];
+  List<Target> get dependencies => const <Target>[
+    DartBuildForNative(),
+    KernelSnapshot(),
+    InstallCodeAssets(),
+  ];
 }
 
 /// Copies the pre-built flutter bundle for release mode.
diff --git a/packages/flutter_tools/lib/src/build_system/targets/hook_runner_native.dart b/packages/flutter_tools/lib/src/build_system/targets/hook_runner_native.dart
new file mode 100644
index 0000000..7983576
--- /dev/null
+++ b/packages/flutter_tools/lib/src/build_system/targets/hook_runner_native.dart
@@ -0,0 +1,54 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import '../../asset.dart';
+import '../../base/logger.dart' show Logger;
+import '../../build_info.dart';
+import '../../globals.dart' as globals;
+import '../../hook_runner.dart' show FlutterHookRunner;
+import '../../isolated/native_assets/dart_hook_result.dart' show DartHookResult;
+import '../build_system.dart' show BuildResult, Environment, ExceptionMeasurement;
+import 'native_assets.dart' show DartBuild;
+
+/// This class is restricted to be used in non-g3 files, such as `isolated/` and
+/// `targets`.
+/// Its purpose is to run the Dart build and link hooks during a Flutter build
+/// and take care of caching.
+class FlutterHookRunnerNative implements FlutterHookRunner {
+  FlutterHookResult? _flutterHookResult;
+
+  @override
+  Future<FlutterHookResult> runHooks({
+    required TargetPlatform targetPlatform,
+    required Environment environment,
+    required Logger? logger,
+  }) async {
+    logger?.printTrace('runDartBuild() with ${environment.defines} and $targetPlatform');
+    final FlutterHookResult? hookResult = _flutterHookResult;
+    if (hookResult != null && !hookResult.hasAnyModifiedFiles(globals.fs)) {
+      logger?.printTrace('runDartBuild() - up-to-date already');
+      return hookResult;
+    }
+    logger?.printTrace('runDartBuild() - will perform dart build');
+
+    final BuildResult lastBuild = await globals.buildSystem.build(
+      DartBuild(specifiedTargetPlatform: targetPlatform),
+      environment,
+    );
+    if (!lastBuild.success) {
+      for (final ExceptionMeasurement exceptionMeasurement in lastBuild.exceptions.values) {
+        logger?.printError(
+          exceptionMeasurement.exception.toString(),
+          stackTrace: logger.isVerbose ? exceptionMeasurement.stackTrace : null,
+        );
+      }
+    }
+
+    final DartHookResult dartHooksResult = await DartBuild.loadHookResult(environment);
+    final FlutterHookResult flutterHookResult = dartHooksResult.asFlutterResult;
+    _flutterHookResult = flutterHookResult;
+    logger?.printTrace('runDartBuild() - done');
+    return flutterHookResult;
+  }
+}
diff --git a/packages/flutter_tools/lib/src/build_system/targets/ios.dart b/packages/flutter_tools/lib/src/build_system/targets/ios.dart
index 9fa5f98..4a2f9ec 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/ios.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/ios.dart
@@ -18,6 +18,7 @@
 import '../../devfs.dart';
 import '../../globals.dart' as globals;
 import '../../ios/mac.dart';
+import '../../isolated/native_assets/dart_hook_result.dart';
 import '../../macos/xcode.dart';
 import '../../project.dart';
 import '../build_system.dart';
@@ -606,6 +607,7 @@
 
   @override
   List<Target> get dependencies => const <Target>[
+    DartBuildForNative(),
     KernelSnapshot(),
     InstallCodeAssets(),
     _IssueLaunchRootViewControllerAccess(),
@@ -695,9 +697,11 @@
     final String? flavor = await flutterProject.ios.parseFlavorFromConfiguration(environment);
 
     // Copy the assets.
+    final DartHookResult dartHookResult = await DartBuild.loadHookResult(environment);
     final Depfile assetDepfile = await copyAssets(
       environment,
       assetDirectory,
+      dartHookResult: dartHookResult,
       targetPlatform: TargetPlatform.ios,
       buildMode: buildMode,
       additionalInputs: <File>[
diff --git a/packages/flutter_tools/lib/src/build_system/targets/linux.dart b/packages/flutter_tools/lib/src/build_system/targets/linux.dart
index 60932d0..27f72a8 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/linux.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/linux.dart
@@ -7,6 +7,7 @@
 import '../../build_info.dart';
 import '../../convert.dart';
 import '../../devfs.dart';
+import '../../isolated/native_assets/dart_hook_result.dart';
 import '../../project.dart';
 import '../build_system.dart';
 import '../depfile.dart';
@@ -93,6 +94,7 @@
 
   @override
   List<Target> get dependencies => <Target>[
+    const DartBuildForNative(),
     const KernelSnapshot(),
     const InstallCodeAssets(),
     UnpackLinux(targetPlatform),
@@ -127,9 +129,11 @@
           .copySync(outputDirectory.childFile('kernel_blob.bin').path);
     }
     final String versionInfo = getVersionInfo(environment.defines);
+    final DartHookResult dartHookResult = await DartBuild.loadHookResult(environment);
     final Depfile depfile = await copyAssets(
       environment,
       outputDirectory,
+      dartHookResult: dartHookResult,
       targetPlatform: targetPlatform,
       buildMode: buildMode,
       additionalContent: <String, DevFSContent>{
diff --git a/packages/flutter_tools/lib/src/build_system/targets/macos.dart b/packages/flutter_tools/lib/src/build_system/targets/macos.dart
index 9860e8b..1a2a1c3 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/macos.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/macos.dart
@@ -13,6 +13,7 @@
 import '../../darwin/darwin.dart';
 import '../../devfs.dart';
 import '../../globals.dart' as globals show xcode;
+import '../../isolated/native_assets/dart_hook_result.dart';
 import '../../project.dart';
 import '../build_system.dart';
 import '../depfile.dart';
@@ -206,9 +207,7 @@
       environment.fileSystem.path.join(environment.buildDir.path, 'App.framework', 'App'),
     );
 
-    final Iterable<DarwinArch> darwinArchs =
-        environment.defines[kDarwinArchs]?.split(' ').map(getDarwinArchForName) ??
-        <DarwinArch>[DarwinArch.x86_64, DarwinArch.arm64];
+    final Iterable<DarwinArch> darwinArchs = getDarwinArchsFromEnv(environment.defines);
 
     final Iterable<String> darwinArchArguments = darwinArchs.expand(
       (DarwinArch arch) => <String>['-arch', arch.name],
@@ -285,9 +284,7 @@
       kExtraGenSnapshotOptions,
     );
     final TargetPlatform targetPlatform = getTargetPlatformForName(targetPlatformEnvironment);
-    final List<DarwinArch> darwinArchs =
-        environment.defines[kDarwinArchs]?.split(' ').map(getDarwinArchForName).toList() ??
-        <DarwinArch>[DarwinArch.x86_64, DarwinArch.arm64];
+    final List<DarwinArch> darwinArchs = getDarwinArchsFromEnv(environment.defines);
     if (targetPlatform != TargetPlatform.darwin) {
       throw Exception('compile_macos_framework is only supported for darwin TargetPlatform.');
     }
@@ -378,6 +375,9 @@
   const MacOSBundleFlutterAssets();
 
   @override
+  List<Target> get dependencies => const <Target>[DartBuildForNative()];
+
+  @override
   List<Source> get inputs => const <Source>[
     Source.pattern('{BUILD_DIR}/App.framework/App'),
     ...IconTreeShaker.inputs,
@@ -438,9 +438,11 @@
     final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir);
     final String? flavor = await flutterProject.macos.parseFlavorFromConfiguration(environment);
 
+    final DartHookResult dartHookResult = await DartBuild.loadHookResult(environment);
     final Depfile assetDepfile = await copyAssets(
       environment,
       assetDirectory,
+      dartHookResult: dartHookResult,
       targetPlatform: TargetPlatform.darwin,
       buildMode: buildMode,
       flavor: flavor,
@@ -560,11 +562,12 @@
   String get name => 'debug_macos_bundle_flutter_assets';
 
   @override
-  List<Target> get dependencies => const <Target>[
-    KernelSnapshot(),
-    DebugMacOSFramework(),
-    DebugUnpackMacOS(),
-    InstallCodeAssets(),
+  List<Target> get dependencies => <Target>[
+    ...super.dependencies,
+    const KernelSnapshot(),
+    const DebugMacOSFramework(),
+    const DebugUnpackMacOS(),
+    const InstallCodeAssets(),
   ];
 
   @override
@@ -606,10 +609,11 @@
   String get name => 'profile_macos_bundle_flutter_assets';
 
   @override
-  List<Target> get dependencies => const <Target>[
-    CompileMacOSFramework(),
-    InstallCodeAssets(),
-    ProfileUnpackMacOS(),
+  List<Target> get dependencies => <Target>[
+    ...super.dependencies,
+    const CompileMacOSFramework(),
+    const InstallCodeAssets(),
+    const ProfileUnpackMacOS(),
   ];
 
   @override
diff --git a/packages/flutter_tools/lib/src/build_system/targets/native_assets.dart b/packages/flutter_tools/lib/src/build_system/targets/native_assets.dart
index 4e12771..a4e0cdb 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/native_assets.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/native_assets.dart
@@ -10,84 +10,86 @@
 import '../../build_info.dart';
 import '../../convert.dart';
 import '../../dart/package_map.dart';
+import '../../isolated/native_assets/dart_hook_result.dart';
 import '../../isolated/native_assets/native_assets.dart';
 import '../build_system.dart';
 import '../depfile.dart';
-import '../exceptions.dart';
+import '../exceptions.dart' show MissingDefineException;
 
 /// Runs the dart build of the app.
-abstract class DartBuild extends Target {
-  const DartBuild({@visibleForTesting FlutterNativeAssetsBuildRunner? buildRunner})
-    : _buildRunner = buildRunner;
+class DartBuild extends Target {
+  const DartBuild({
+    @visibleForTesting FlutterNativeAssetsBuildRunner? buildRunner,
+    this.specifiedTargetPlatform,
+  }) : _buildRunner = buildRunner;
 
   final FlutterNativeAssetsBuildRunner? _buildRunner;
 
+  /// The target OS and architecture that we are building for.
+  final TargetPlatform? specifiedTargetPlatform;
+
   @override
   Future<void> build(Environment environment) async {
     final FileSystem fileSystem = environment.fileSystem;
-    final String? nativeAssetsEnvironment = environment.defines[kNativeAssets];
+    final DartHookResult result;
 
-    final DartBuildResult result;
-    if (nativeAssetsEnvironment == 'false') {
-      result = const DartBuildResult.empty();
-    } else {
-      final TargetPlatform targetPlatform = _getTargetPlatformFromEnvironment(environment, name);
+    final TargetPlatform targetPlatform =
+        specifiedTargetPlatform ?? _getTargetPlatformFromEnvironment(environment, name);
 
-      final File packageConfigFile = fileSystem.file(environment.packageConfigPath);
-      final PackageConfig packageConfig = await loadPackageConfigWithLogging(
-        packageConfigFile,
-        logger: environment.logger,
+    final File packageConfigFile = fileSystem.file(environment.packageConfigPath);
+    final PackageConfig packageConfig = await loadPackageConfigWithLogging(
+      packageConfigFile,
+      logger: environment.logger,
+    );
+    final Uri projectUri = environment.projectDir.uri;
+    final String? runPackageName = packageConfig.packages
+        .where((Package p) => p.root == projectUri)
+        .firstOrNull
+        ?.name;
+    if (runPackageName == null) {
+      throw StateError(
+        'Could not determine run package name. '
+        'Project path "${projectUri.toFilePath()}" did not occur as package '
+        'root in package config "${environment.packageConfigPath}". '
+        'Please report a reproduction on '
+        'https://github.com/flutter/flutter/issues/169475.',
       );
-      final Uri projectUri = environment.projectDir.uri;
-      final String? runPackageName = packageConfig.packages
-          .where((Package p) => p.root == projectUri)
-          .firstOrNull
-          ?.name;
-      if (runPackageName == null) {
-        throw StateError(
-          'Could not determine run package name. '
-          'Project path "${projectUri.toFilePath()}" did not occur as package '
-          'root in package config "${environment.packageConfigPath}". '
-          'Please report a reproduction on '
-          'https://github.com/flutter/flutter/issues/169475.',
+    }
+    final String pubspecPath = packageConfigFile.uri.resolve('../pubspec.yaml').toFilePath();
+    final String? buildModeEnvironment = environment.defines[kBuildMode];
+    if (buildModeEnvironment == null) {
+      throw MissingDefineException(kBuildMode, name);
+    }
+    final buildMode = BuildMode.fromCliName(buildModeEnvironment);
+    final bool includeDevDependencies = !buildMode.isRelease;
+    final FlutterNativeAssetsBuildRunner buildRunner =
+        _buildRunner ??
+        FlutterNativeAssetsBuildRunnerImpl(
+          environment.packageConfigPath,
+          packageConfig,
+          fileSystem,
+          environment.logger,
+          runPackageName,
+          includeDevDependencies: includeDevDependencies,
+          pubspecPath,
         );
-      }
-      final String pubspecPath = packageConfigFile.uri.resolve('../pubspec.yaml').toFilePath();
-      final String? buildModeEnvironment = environment.defines[kBuildMode];
-      if (buildModeEnvironment == null) {
-        throw MissingDefineException(kBuildMode, name);
-      }
-      final buildMode = BuildMode.fromCliName(buildModeEnvironment);
-      final bool includeDevDependencies = !buildMode.isRelease;
-      final FlutterNativeAssetsBuildRunner buildRunner =
-          _buildRunner ??
-          FlutterNativeAssetsBuildRunnerImpl(
-            environment.packageConfigPath,
-            packageConfig,
-            fileSystem,
-            environment.logger,
-            runPackageName,
-            includeDevDependencies: includeDevDependencies,
-            pubspecPath,
-          );
-      result = await runFlutterSpecificDartBuild(
-        environmentDefines: environment.defines,
-        buildRunner: buildRunner,
-        targetPlatform: targetPlatform,
-        projectUri: projectUri,
-        fileSystem: fileSystem,
-      );
-    }
+    result = await runFlutterSpecificHooks(
+      environmentDefines: environment.defines,
+      buildRunner: buildRunner,
+      targetPlatform: targetPlatform,
+      projectUri: projectUri,
+      fileSystem: fileSystem,
+    );
 
-    final File dartBuildResultJsonFile = environment.buildDir.childFile(dartBuildResultFilename);
-    if (!dartBuildResultJsonFile.parent.existsSync()) {
-      dartBuildResultJsonFile.parent.createSync(recursive: true);
+    final File dartHookResultJsonFile = environment.buildDir.childFile(dartHookResultFilename);
+    if (!dartHookResultJsonFile.parent.existsSync()) {
+      dartHookResultJsonFile.parent.createSync(recursive: true);
     }
-    dartBuildResultJsonFile.writeAsStringSync(json.encode(result.toJson()));
+    dartHookResultJsonFile.writeAsStringSync(json.encode(result.toJson()));
 
     final depfile = Depfile(
       <File>[for (final Uri dependency in result.dependencies) fileSystem.file(dependency)],
-      <File>[fileSystem.file(dartBuildResultJsonFile)],
+      <File>[fileSystem.file(dartHookResultJsonFile)],
     );
     final File outputDepfile = environment.buildDir.childFile(depFilename);
     if (!outputDepfile.parent.existsSync()) {
@@ -116,22 +118,25 @@
   String get name => 'dart_build';
 
   @override
-  List<Source> get outputs => const <Source>[
-    Source.pattern('{BUILD_DIR}/$dartBuildResultFilename'),
-  ];
+  List<Source> get outputs => const <Source>[Source.pattern('{BUILD_DIR}/$dartHookResultFilename')];
 
   /// Dependent build [Target]s can use this to consume the result of the
   /// [DartBuild] target.
-  static Future<DartBuildResult> loadBuildResult(Environment environment) async {
-    final File dartBuildResultJsonFile = environment.buildDir.childFile(
-      DartBuild.dartBuildResultFilename,
+  static Future<DartHookResult> loadHookResult(Environment environment) async {
+    final File dartHookResultJsonFile = environment.buildDir.childFile(
+      DartBuild.dartHookResultFilename,
     );
-    return DartBuildResult.fromJson(
-      json.decode(dartBuildResultJsonFile.readAsStringSync()) as Map<String, Object?>,
-    );
+    return !dartHookResultJsonFile.existsSync()
+        ? DartHookResult.empty()
+        : DartHookResult.fromJson(
+            json.decode(dartHookResultJsonFile.readAsStringSync()) as Map<String, Object?>,
+          );
   }
 
-  static const dartBuildResultFilename = 'dart_build_result.json';
+  @override
+  List<Target> get dependencies => <Target>[];
+
+  static const dartHookResultFilename = 'dart_build_result.json';
   static const depFilename = 'dart_build.d';
 }
 
@@ -158,13 +163,13 @@
     final TargetPlatform targetPlatform = _getTargetPlatformFromEnvironment(environment, name);
 
     // We fetch the result from the [DartBuild].
-    final DartBuildResult dartBuildResult = await DartBuild.loadBuildResult(environment);
+    final DartHookResult dartHookResult = await DartBuild.loadHookResult(environment);
 
     // And install/copy the code assets to the right place and create a
     // native_asset.yaml that can be used by the final AOT compilation.
     final Uri nativeAssetsFileUri = environment.buildDir.childFile(nativeAssetsFilename).uri;
     await installCodeAssets(
-      dartBuildResult: dartBuildResult,
+      dartHookResult: dartHookResult,
       environmentDefines: environment.defines,
       targetPlatform: targetPlatform,
       projectUri: projectUri,
@@ -174,7 +179,7 @@
     assert(await fileSystem.file(nativeAssetsFileUri).exists());
 
     final depfile = Depfile(
-      <File>[for (final Uri file in dartBuildResult.filesToBeBundled) fileSystem.file(file)],
+      <File>[for (final Uri file in dartHookResult.filesToBeBundled) fileSystem.file(file)],
       <File>[fileSystem.file(nativeAssetsFileUri)],
     );
     final File outputDepfile = environment.buildDir.childFile(depFilename);
diff --git a/packages/flutter_tools/lib/src/build_system/targets/web.dart b/packages/flutter_tools/lib/src/build_system/targets/web.dart
index f3a8d03..6691170 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/web.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/web.dart
@@ -18,6 +18,7 @@
 import '../../dart/package_map.dart';
 import '../../flutter_plugins.dart';
 import '../../globals.dart' as globals;
+import '../../isolated/native_assets/dart_hook_result.dart';
 import '../../project.dart';
 import '../../web/bootstrap.dart';
 import '../../web/compile.dart';
@@ -29,6 +30,7 @@
 import '../exceptions.dart';
 import 'assets.dart';
 import 'localizations.dart';
+import 'native_assets.dart';
 
 /// Generates an entry point for a web target.
 // Keep this in sync with build_runner/resident_web_runner.dart
@@ -477,7 +479,11 @@
   String get name => 'web_release_bundle';
 
   @override
-  List<Target> get dependencies => <Target>[...compileTargets, templatedFilesTarget];
+  List<Target> get dependencies => <Target>[
+    ...compileTargets,
+    templatedFilesTarget,
+    const DartBuild(specifiedTargetPlatform: TargetPlatform.web_javascript),
+  ];
 
   Iterable<String> get buildPatternStems =>
       compileTargets.expand((Dart2WebTarget target) => target.buildPatternStems);
@@ -517,9 +523,11 @@
     final Directory outputDirectory = environment.outputDir.childDirectory('assets');
     outputDirectory.createSync(recursive: true);
 
+    final DartHookResult dartHookResult = await DartBuild.loadHookResult(environment);
     final Depfile depfile = await copyAssets(
       environment,
       environment.outputDir.childDirectory('assets'),
+      dartHookResult: dartHookResult,
       targetPlatform: TargetPlatform.web_javascript,
       buildMode: buildMode,
     );
diff --git a/packages/flutter_tools/lib/src/build_system/targets/windows.dart b/packages/flutter_tools/lib/src/build_system/targets/windows.dart
index 98b058e..0356cad 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/windows.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/windows.dart
@@ -6,6 +6,7 @@
 import '../../base/file_system.dart';
 import '../../build_info.dart';
 import '../../devfs.dart';
+import '../../isolated/native_assets/dart_hook_result.dart';
 import '../build_system.dart';
 import '../depfile.dart';
 import '../exceptions.dart';
@@ -106,6 +107,7 @@
 
   @override
   List<Target> get dependencies => <Target>[
+    const DartBuildForNative(),
     const KernelSnapshot(),
     const InstallCodeAssets(),
     UnpackWindows(targetPlatform),
@@ -141,9 +143,11 @@
           .childFile('app.dill')
           .copySync(outputDirectory.childFile('kernel_blob.bin').path);
     }
+    final DartHookResult dartHookResult = await DartBuild.loadHookResult(environment);
     final Depfile depfile = await copyAssets(
       environment,
       outputDirectory,
+      dartHookResult: dartHookResult,
       targetPlatform: targetPlatform,
       buildMode: buildMode,
       additionalContent: <String, DevFSContent>{
diff --git a/packages/flutter_tools/lib/src/bundle_builder.dart b/packages/flutter_tools/lib/src/bundle_builder.dart
index ee3488c..13d09c7 100644
--- a/packages/flutter_tools/lib/src/bundle_builder.dart
+++ b/packages/flutter_tools/lib/src/bundle_builder.dart
@@ -29,10 +29,6 @@
   ///
   /// The default `mainPath` is `lib/main.dart`.
   /// The default `manifestPath` is `pubspec.yaml`.
-  ///
-  /// If [buildNativeAssets], native assets are built and the mapping for native
-  /// assets lookup at runtime is embedded in the kernel file, otherwise an
-  /// empty native assets mapping is embedded in the kernel file.
   Future<void> build({
     required TargetPlatform platform,
     required BuildInfo buildInfo,
@@ -42,7 +38,6 @@
     String? applicationKernelFilePath,
     String? depfilePath,
     String? assetDirPath,
-    bool buildNativeAssets = true,
     @visibleForTesting BuildSystem? buildSystem,
   }) async {
     project ??= FlutterProject.current();
@@ -68,7 +63,6 @@
         kTargetFile: mainPath,
         kDeferredComponents: 'false',
         ...buildInfo.toBuildSystemEnvironment(),
-        if (!buildNativeAssets) kNativeAssets: 'false',
       },
       artifacts: globals.artifacts!,
       fileSystem: globals.fs,
diff --git a/packages/flutter_tools/lib/src/commands/attach.dart b/packages/flutter_tools/lib/src/commands/attach.dart
index 3b85448..6201d2f 100644
--- a/packages/flutter_tools/lib/src/commands/attach.dart
+++ b/packages/flutter_tools/lib/src/commands/attach.dart
@@ -21,6 +21,7 @@
 import '../daemon.dart';
 import '../device.dart';
 import '../device_vm_service_discovery_for_attach.dart';
+import '../hook_runner.dart' show hookRunner;
 import '../ios/devices.dart';
 import '../ios/simulators.dart';
 import '../macos/macos_ipad_device.dart';
@@ -487,8 +488,14 @@
             flutterProject: flutterProject,
             nativeAssetsYamlFile: stringArg(FlutterOptions.kNativeAssetsYamlFile),
             analytics: analytics,
+            logger: _logger,
           )
-        : ColdRunner(flutterDevices, target: targetFile, debuggingOptions: debuggingOptions);
+        : ColdRunner(
+            flutterDevices,
+            target: targetFile,
+            debuggingOptions: debuggingOptions,
+            dartBuilder: hookRunner,
+          );
   }
 
   Future<void> _validateArguments() async {}
@@ -513,6 +520,7 @@
     FlutterProject? flutterProject,
     String? nativeAssetsYamlFile,
     required Analytics analytics,
+    Logger? logger,
   }) => HotRunner(
     devices,
     target: target,
@@ -525,5 +533,7 @@
     stayResident: stayResident,
     nativeAssetsYamlFile: nativeAssetsYamlFile,
     analytics: analytics,
+    dartBuilder: hookRunner,
+    logger: logger,
   );
 }
diff --git a/packages/flutter_tools/lib/src/commands/build_bundle.dart b/packages/flutter_tools/lib/src/commands/build_bundle.dart
index e2252f2..927edb2 100644
--- a/packages/flutter_tools/lib/src/commands/build_bundle.dart
+++ b/packages/flutter_tools/lib/src/commands/build_bundle.dart
@@ -147,7 +147,6 @@
       mainPath: targetFile,
       depfilePath: stringArg('depfile'),
       assetDirPath: stringArg('asset-dir'),
-      buildNativeAssets: false,
     );
     return FlutterCommandResult.success();
   }
diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart
index 0cbbd5e..051eb2b 100644
--- a/packages/flutter_tools/lib/src/commands/daemon.dart
+++ b/packages/flutter_tools/lib/src/commands/daemon.dart
@@ -724,6 +724,7 @@
         hostIsIde: true,
         machine: machine,
         analytics: globals.analytics,
+        logger: globals.logger,
       );
     } else {
       runner = ColdRunner(
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart
index 458ed3e..f156223 100644
--- a/packages/flutter_tools/lib/src/commands/run.dart
+++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -16,6 +16,7 @@
 import '../device.dart';
 import '../features.dart';
 import '../globals.dart' as globals;
+import '../hook_runner.dart' show hookRunner;
 import '../ios/devices.dart';
 import '../project.dart';
 import '../resident_runner.dart';
@@ -710,6 +711,8 @@
         stayResident: stayResident,
         analytics: globals.analytics,
         nativeAssetsYamlFile: stringArg(FlutterOptions.kNativeAssetsYamlFile),
+        dartBuilder: hookRunner,
+        logger: globals.logger,
       );
     } else if (webMode) {
       return webRunnerFactory!.createWebRunner(
@@ -737,6 +740,7 @@
           ? null
           : globals.fs.file(applicationBinaryPath),
       stayResident: stayResident,
+      dartBuilder: hookRunner,
     );
   }
 
diff --git a/packages/flutter_tools/lib/src/features.dart b/packages/flutter_tools/lib/src/features.dart
index 2ee4bf2..6e05d75 100644
--- a/packages/flutter_tools/lib/src/features.dart
+++ b/packages/flutter_tools/lib/src/features.dart
@@ -56,6 +56,9 @@
   /// Whether native assets compilation and bundling is enabled.
   bool get isNativeAssetsEnabled;
 
+  /// Whether Dart data assets building and bundling is enabled.
+  bool get isDartDataAssetsEnabled => false;
+
   /// Whether Swift Package Manager dependency management is enabled.
   bool get isSwiftPackageManagerEnabled;
 
@@ -81,6 +84,7 @@
     flutterCustomDevicesFeature,
     cliAnimation,
     nativeAssets,
+    dartDataAssets,
     swiftPackageManager,
     omitLegacyVersionFile,
   ];
@@ -178,6 +182,14 @@
   beta: FeatureChannelSetting(available: true, enabledByDefault: true),
 );
 
+/// Enable Dart data assets building and bundling.
+const dartDataAssets = Feature(
+  name: 'Dart data assets building and bundling',
+  configSetting: 'enable-dart-data-assets',
+  environmentOverride: 'FLUTTER_DART_DATA_ASSETS',
+  master: FeatureChannelSetting(available: true),
+);
+
 /// Enable Swift Package Manager as a darwin dependency manager.
 const swiftPackageManager = Feature(
   name: 'support for Swift Package Manager for iOS and macOS',
diff --git a/packages/flutter_tools/lib/src/flutter_features.dart b/packages/flutter_tools/lib/src/flutter_features.dart
index 23bcc16..4089eb7 100644
--- a/packages/flutter_tools/lib/src/flutter_features.dart
+++ b/packages/flutter_tools/lib/src/flutter_features.dart
@@ -50,6 +50,9 @@
   bool get isNativeAssetsEnabled => isEnabled(nativeAssets);
 
   @override
+  bool get isDartDataAssetsEnabled => isEnabled(dartDataAssets);
+
+  @override
   bool get isSwiftPackageManagerEnabled => isEnabled(swiftPackageManager);
 
   @override
diff --git a/packages/flutter_tools/lib/src/hook_runner.dart b/packages/flutter_tools/lib/src/hook_runner.dart
new file mode 100644
index 0000000..6f9dae3
--- /dev/null
+++ b/packages/flutter_tools/lib/src/hook_runner.dart
@@ -0,0 +1,24 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'asset.dart' show FlutterHookResult;
+import 'base/context.dart' show context;
+import 'base/logger.dart' show Logger;
+import 'build_info.dart' show TargetPlatform;
+import 'build_system/build_system.dart' show Environment;
+
+FlutterHookRunner? get hookRunner => context.get<FlutterHookRunner>();
+
+/// This is the interface used to run hooks.
+///
+/// To not need `isolated/` imports, we use this interface to be passed around
+/// everywhere. It's implementation can run the build and link hooks during a
+/// Flutter build/run/test/etc.
+abstract interface class FlutterHookRunner {
+  Future<FlutterHookResult> runHooks({
+    required TargetPlatform targetPlatform,
+    required Environment environment,
+    required Logger? logger,
+  });
+}
diff --git a/packages/flutter_tools/lib/src/isolated/native_assets/dart_hook_result.dart b/packages/flutter_tools/lib/src/isolated/native_assets/dart_hook_result.dart
new file mode 100644
index 0000000..be602f8
--- /dev/null
+++ b/packages/flutter_tools/lib/src/isolated/native_assets/dart_hook_result.dart
@@ -0,0 +1,127 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:code_assets/code_assets.dart';
+import 'package:data_assets/data_assets.dart';
+import 'package:hooks/hooks.dart';
+import 'package:hooks_runner/hooks_runner.dart';
+import '../../asset.dart' show FlutterHookResult, HookAsset;
+import 'native_assets.dart' show FlutterCodeAsset;
+
+/// The assets produced by a Dart hook run and the dependencies of those assets.
+///
+/// If any of the dependencies change, then the Dart build should be performed
+/// again.
+final class DartHookResult {
+  const DartHookResult({
+    required this.buildStart,
+    required this.buildEnd,
+    required this.codeAssets,
+    required this.dataAssets,
+    required this.dependencies,
+  });
+
+  DartHookResult.empty()
+    : buildStart = DateTime.fromMillisecondsSinceEpoch(0),
+      buildEnd = DateTime.fromMillisecondsSinceEpoch(0),
+      codeAssets = const <FlutterCodeAsset>[],
+      dataAssets = const <DataAsset>[],
+      dependencies = const <Uri>[];
+
+  factory DartHookResult.fromJson(Map<String, Object?> json) {
+    if (json case {
+      _buildStartKey: final String buildStartString,
+      _buildEndKey: final String buildEndString,
+      _dependenciesKey: final List<Object?> dependenciesList,
+      _codeAssetsKey: final List<Object?> codeAssetsList,
+      _dataAssetsKey: final List<Object?> dataAssetsList,
+    }) {
+      final Iterable<(Map<String, Object?>, String)> codeAssetsWithTargets = codeAssetsList.map(
+        (Object? codeJson) => switch (codeJson) {
+          {_assetKey: final Map<String, Object?> codeAsset, _targetKey: final String target} => (
+            codeAsset,
+            target,
+          ),
+          _ => throw UnimplementedError(),
+        },
+      );
+      return DartHookResult(
+        buildStart: DateTime.parse(buildStartString),
+        buildEnd: DateTime.parse(buildEndString),
+        dependencies: <Uri>[
+          for (final Object? encodedUri in dependenciesList) Uri.parse(encodedUri! as String),
+        ],
+        codeAssets: <FlutterCodeAsset>[
+          for (final (Map<String, Object?> codeAsset, String target) in codeAssetsWithTargets)
+            FlutterCodeAsset(
+              codeAsset: CodeAsset.fromEncoded(EncodedAsset.fromJson(codeAsset)),
+              target: Target.fromString(target),
+            ),
+        ],
+        dataAssets: <DataAsset>[
+          for (final Object? dataAssetJson in dataAssetsList)
+            DataAsset.fromEncoded(EncodedAsset.fromJson(dataAssetJson! as Map<String, Object?>)),
+        ],
+      );
+    }
+    throw ArgumentError('Invalid JSON $json');
+  }
+
+  /// The timestamp at which we start a build - so the timestamp of the inputs.
+  final DateTime buildStart;
+
+  /// The timestamp at which we finish a build - so the timestamp of the
+  /// outputs.
+  final DateTime buildEnd;
+
+  /// The code assets produced by running the hooks.
+  final List<FlutterCodeAsset> codeAssets;
+
+  /// The data assets produced by running the hooks.
+  final List<DataAsset> dataAssets;
+
+  /// The dependencies from the hooks, indicating whether the hooks should be
+  /// re-run.
+  final List<Uri> dependencies;
+
+  Map<String, Object?> toJson() => <String, Object?>{
+    _buildStartKey: buildStart.toIso8601String(),
+    _buildEndKey: buildEnd.toIso8601String(),
+    _dependenciesKey: <Object?>[for (final Uri dep in dependencies) dep.toString()],
+    _codeAssetsKey: <Object?>[
+      for (final FlutterCodeAsset code in codeAssets)
+        <String, Object>{
+          _assetKey: code.codeAsset.encode().toJson(),
+          _targetKey: code.target.toString(),
+        },
+    ],
+    _dataAssetsKey: <Object?>[for (final DataAsset asset in dataAssets) asset.encode().toJson()],
+  };
+
+  static const _buildStartKey = 'build_start';
+  static const _buildEndKey = 'build_end';
+  static const _dependenciesKey = 'dependencies';
+  static const _codeAssetsKey = 'code_assets';
+  static const _dataAssetsKey = 'data_assets';
+  static const _assetKey = 'asset';
+  static const _targetKey = 'target';
+
+  /// The files that eventually should be bundled with the app.
+  List<Uri> get filesToBeBundled => <Uri>[
+    for (final FlutterCodeAsset code in codeAssets)
+      if (code.codeAsset.linkMode is DynamicLoadingBundled) code.codeAsset.file!,
+  ];
+
+  FlutterHookResult get asFlutterResult => FlutterHookResult(
+    buildStart: buildStart,
+    buildEnd: buildEnd,
+    dependencies: dependencies,
+    dataAssets: dataAssets
+        .map(
+          (DataAsset asset) =>
+              HookAsset(file: asset.file, name: asset.name, package: asset.package),
+        )
+        .toList(),
+  );
+}
diff --git a/packages/flutter_tools/lib/src/isolated/native_assets/linux/native_assets.dart b/packages/flutter_tools/lib/src/isolated/native_assets/linux/native_assets.dart
index be5cb9a..d98a649 100644
--- a/packages/flutter_tools/lib/src/isolated/native_assets/linux/native_assets.dart
+++ b/packages/flutter_tools/lib/src/isolated/native_assets/linux/native_assets.dart
@@ -17,7 +17,6 @@
   const kClangBinary = 'clang';
   const kArBinary = 'llvm-ar';
   const kLdBinary = 'ld.lld';
-
   final ProcessResult whichResult = await globals.processManager.run(<String>[
     'which',
     kClangPlusPlusBinary,
@@ -26,7 +25,8 @@
     throwToolExit('Failed to find $kClangPlusPlusBinary on PATH.');
   }
   File clangPpFile = globals.fs.file((whichResult.stdout as String).trim());
-  clangPpFile = globals.fs.file(await clangPpFile.resolveSymbolicLinks());
+  final String resolvedClangPpFile = await clangPpFile.resolveSymbolicLinks();
+  clangPpFile = globals.fs.file(resolvedClangPpFile);
 
   final Directory clangDir = clangPpFile.parent;
   final binaryPaths = <String, Uri>{};
diff --git a/packages/flutter_tools/lib/src/isolated/native_assets/native_assets.dart b/packages/flutter_tools/lib/src/isolated/native_assets/native_assets.dart
index d4ac0fd..67329d9 100644
--- a/packages/flutter_tools/lib/src/isolated/native_assets/native_assets.dart
+++ b/packages/flutter_tools/lib/src/isolated/native_assets/native_assets.dart
@@ -5,6 +5,7 @@
 // Logic for native assets shared between all host OSes.
 
 import 'package:code_assets/code_assets.dart';
+import 'package:data_assets/data_assets.dart';
 import 'package:hooks/hooks.dart';
 import 'package:hooks_runner/hooks_runner.dart';
 import 'package:logging/logging.dart' as logging;
@@ -20,64 +21,11 @@
 import '../../convert.dart';
 import '../../features.dart';
 import '../../globals.dart' as globals;
-import '../../macos/xcode.dart' as xcode;
 import 'android/native_assets.dart';
+import 'dart_hook_result.dart';
 import 'ios/native_assets.dart';
-import 'linux/native_assets.dart';
 import 'macos/native_assets.dart';
-import 'macos/native_assets_host.dart';
-import 'windows/native_assets.dart';
-
-/// The assets produced by a Dart build and the dependencies of those assets.
-///
-/// If any of the dependencies change, then the Dart build should be performed
-/// again.
-final class DartBuildResult {
-  const DartBuildResult(this.codeAssets, this.dependencies);
-
-  const DartBuildResult.empty()
-    : codeAssets = const <FlutterCodeAsset>[],
-      dependencies = const <Uri>[];
-
-  factory DartBuildResult.fromJson(Map<String, Object?> json) {
-    final dependencies = <Uri>[
-      for (final Object? encodedUri in json['dependencies']! as List<Object?>)
-        Uri.parse(encodedUri! as String),
-    ];
-    final codeAssets = <FlutterCodeAsset>[
-      for (final Object? json in json['code_assets']! as List<Object?>)
-        FlutterCodeAsset(
-          codeAsset: CodeAsset.fromEncoded(
-            EncodedAsset.fromJson(
-              (json! as Map<String, Object?>)['asset']! as Map<String, Object?>,
-            ),
-          ),
-          target: Target.fromString((json as Map<String, Object?>)['target']! as String),
-        ),
-    ];
-    return DartBuildResult(codeAssets, dependencies);
-  }
-
-  final List<FlutterCodeAsset> codeAssets;
-  final List<Uri> dependencies;
-
-  Map<String, Object?> toJson() => <String, Object?>{
-    'dependencies': <Object?>[for (final Uri dep in dependencies) dep.toString()],
-    'code_assets': <Object?>[
-      for (final FlutterCodeAsset code in codeAssets)
-        <String, Object>{
-          'asset': code.codeAsset.encode().toJson(),
-          'target': code.target.toString(),
-        },
-    ],
-  };
-
-  /// The files that eventually should be bundled with the app.
-  List<Uri> get filesToBeBundled => <Uri>[
-    for (final FlutterCodeAsset code in codeAssets)
-      if (code.codeAsset.linkMode is DynamicLoadingBundled) code.codeAsset.file!,
-  ];
-}
+import 'targets.dart';
 
 /// A [CodeAsset] for a specific [target].
 ///
@@ -90,57 +38,68 @@
 
   final CodeAsset codeAsset;
   final Target target;
-
-  @override
-  String toString() =>
-      'FlutterCodeAsset(codeAsset: ${codeAsset.id} ${codeAsset.file}, target: $target)';
 }
 
-/// Invokes the build of all transitive Dart packages and prepares code assets
+/// Matching [CodeAsset] and [DataAsset] in native assets - but Flutter could
+/// support more asset types in the future.
+enum SupportedAssetTypes { codeAssets, dataAssets }
+
+/// Invokes the build of all transitive Dart package hooks and prepares assets
 /// to be included in the native build.
-Future<DartBuildResult> runFlutterSpecificDartBuild({
+Future<DartHookResult> runFlutterSpecificHooks({
   required Map<String, String> environmentDefines,
   required FlutterNativeAssetsBuildRunner buildRunner,
   required TargetPlatform targetPlatform,
   required Uri projectUri,
   required FileSystem fileSystem,
 }) async {
-  final OS targetOS = getNativeOSFromTargetPlatform(targetPlatform);
-  final Uri buildUri = nativeAssetsBuildUri(projectUri, targetOS);
+  final Uri buildUri = nativeAssetsBuildUri(projectUri, targetPlatform.osName);
   final Directory buildDir = fileSystem.directory(buildUri);
-
-  final flutterTester = targetPlatform == TargetPlatform.tester;
-
   if (!await buildDir.exists()) {
     // Ensure the folder exists so the native build system can copy it even
     // if there's no native assets.
     await buildDir.create(recursive: true);
   }
 
-  if (!await _nativeBuildRequired(buildRunner)) {
-    return const DartBuildResult.empty();
+  if (!await _hookRunRequired(buildRunner)) {
+    return DartHookResult.empty();
   }
 
-  final BuildMode buildMode = _getBuildMode(environmentDefines, flutterTester);
-  final List<Architecture> architectures = flutterTester
-      ? <Architecture>[Architecture.current]
-      : _architecturesForOS(targetPlatform, targetOS, environmentDefines);
-  final DartBuildResult result = architectures.isEmpty
-      ? const DartBuildResult.empty()
-      : await _runDartBuild(
-          environmentDefines: environmentDefines,
-          buildRunner: buildRunner,
-          architectures: architectures,
-          projectUri: projectUri,
-          linkingEnabled: _nativeAssetsLinkingEnabled(buildMode),
-          fileSystem: fileSystem,
-          targetOS: targetOS,
-        );
-  return result;
+  final supportedAssetTypes = <SupportedAssetTypes>[
+    if (featureFlags.isNativeAssetsEnabled) SupportedAssetTypes.codeAssets,
+    if (featureFlags.isDartDataAssetsEnabled) SupportedAssetTypes.dataAssets,
+  ];
+  final List<AssetBuildTarget> targets = AssetBuildTarget.targetsFor(
+    targetPlatform: targetPlatform,
+    environmentDefines: environmentDefines,
+    fileSystem: fileSystem,
+    supportedAssetTypes: supportedAssetTypes,
+  );
+
+  // This is ugly, but sadly necessary as fetching the cCompilerConfig is async,
+  // while using it in native_assets_builder is not.
+  // It is not a performance hit, as there can be at most two targets, and the
+  // operation is fast enough.
+  for (final CodeAssetTarget target in targets.whereType<CodeAssetTarget>()) {
+    await buildRunner.precacheCCompilerConfig(target);
+  }
+
+  final BuildMode buildMode = _getBuildMode(
+    environmentDefines,
+    targetPlatform == TargetPlatform.tester,
+  );
+  final bool linkingEnabled = _nativeAssetsLinkingEnabled(buildMode);
+
+  return _runDartHooks(
+    buildRunner: buildRunner,
+    projectUri: projectUri,
+    linkingEnabled: linkingEnabled,
+    targets: targets,
+  );
 }
 
 Future<void> installCodeAssets({
-  required DartBuildResult dartBuildResult,
+  required DartHookResult dartHookResult,
   required Map<String, String> environmentDefines,
   required TargetPlatform targetPlatform,
   required Uri projectUri,
@@ -148,14 +107,14 @@
   required Uri nativeAssetsFileUri,
 }) async {
   final OS targetOS = getNativeOSFromTargetPlatform(targetPlatform);
-  final Uri buildUri = nativeAssetsBuildUri(projectUri, targetOS);
+  final Uri buildUri = nativeAssetsBuildUri(projectUri, targetOS.name);
   final flutterTester = targetPlatform == TargetPlatform.tester;
   final BuildMode buildMode = _getBuildMode(environmentDefines, flutterTester);
 
   final String? codesignIdentity = environmentDefines[kCodesignIdentity];
   final Map<FlutterCodeAsset, KernelAsset> assetTargetLocations = assetTargetLocationsForOS(
     targetOS,
-    dartBuildResult.codeAssets,
+    dartHookResult.codeAssets,
     flutterTester,
     buildUri,
   );
@@ -178,7 +137,7 @@
 /// Programmatic API to be used by Dart launchers to invoke native builds.
 ///
 /// It enables mocking `package:hooks_runner` package.
-/// It also enables mocking native toolchain discovery via [cCompilerConfig].
+/// It also enables mocking native toolchain discovery via [precacheCCompilerConfig].
 abstract interface class FlutterNativeAssetsBuildRunner {
   /// All packages in the transitive dependencies that have a `build.dart`.
   Future<List<String>> packagesWithNativeAssets();
@@ -195,11 +154,7 @@
     required BuildResult buildResult,
   });
 
-  /// The C compiler config to use for compilation.
-  Future<CCompilerConfig?> get cCompilerConfig;
-
-  /// The NDK compiler to use to use for compilation for Android.
-  Future<CCompilerConfig?> get ndkCCompilerConfig;
+  Future<void> precacheCCompilerConfig(CodeAssetTarget target);
 }
 
 /// Uses `package:hooks_runner` for its implementation.
@@ -306,26 +261,8 @@
   }
 
   @override
-  late final Future<CCompilerConfig?> cCompilerConfig = () {
-    if (globals.platform.isMacOS || globals.platform.isIOS) {
-      return cCompilerConfigMacOS();
-    }
-    if (globals.platform.isLinux) {
-      return cCompilerConfigLinux();
-    }
-    if (globals.platform.isWindows) {
-      return cCompilerConfigWindows();
-    }
-    if (globals.platform.isAndroid) {
-      throwToolExit('Should use ndkCCompilerConfig for Android.');
-    }
-    throwToolExit('Unknown target OS.');
-  }();
-
-  @override
-  late final Future<CCompilerConfig> ndkCCompilerConfig = () {
-    return cCompilerConfigAndroid();
-  }();
+  Future<void> precacheCCompilerConfig(CodeAssetTarget target) async =>
+      target.precacheCCompilerConfig();
 }
 
 Future<Uri> _writeNativeAssetsJson(
@@ -383,7 +320,7 @@
   }
 }
 
-Future<bool> _nativeBuildRequired(FlutterNativeAssetsBuildRunner buildRunner) async {
+Future<bool> _hookRunRequired(FlutterNativeAssetsBuildRunner buildRunner) async {
   final List<String> packagesWithNativeAssets = await buildRunner.packagesWithNativeAssets();
   if (packagesWithNativeAssets.isEmpty) {
     globals.logger.printTrace(
@@ -392,11 +329,12 @@
     return false;
   }
 
-  if (!featureFlags.isNativeAssetsEnabled) {
+  if (!featureFlags.isNativeAssetsEnabled && !featureFlags.isDartDataAssetsEnabled) {
     final String packageNames = packagesWithNativeAssets.join(' ');
     throwToolExit(
-      'Package(s) $packageNames require the native assets feature to be enabled. '
-      'Enable using `flutter config --enable-native-assets`.',
+      'Package(s) $packageNames require the dart assets feature to be enabled.\n'
+      '  Enable code assets using `flutter config --enable-native-assets`.'
+      '  Enable data assets using `flutter config --enable-dart-data-assets`.',
     );
   }
   return true;
@@ -429,9 +367,9 @@
 
 /// This should be the same for different archs, debug/release, etc.
 /// It should work for all macOS.
-Uri nativeAssetsBuildUri(Uri projectUri, OS os) {
+Uri nativeAssetsBuildUri(Uri projectUri, String osName) {
   final String buildDir = getBuildDirectory();
-  return projectUri.resolve('$buildDir/native_assets/$os/');
+  return projectUri.resolve('$buildDir/native_assets/$osName/');
 }
 
 Map<FlutterCodeAsset, KernelAsset> _assetTargetLocationsWindowsLinux(
@@ -570,92 +508,58 @@
 ///
 /// This will invoke `hook/build.dart` and `hook/link.dart` (if applicable) for
 /// all transitive dart packages that define such hooks.
-Future<DartBuildResult> _runDartBuild({
-  required Map<String, String> environmentDefines,
+Future<DartHookResult> _runDartHooks({
   required FlutterNativeAssetsBuildRunner buildRunner,
-  required List<Architecture> architectures,
+  required List<AssetBuildTarget> targets,
   required Uri projectUri,
-  required FileSystem fileSystem,
-  required OS? targetOS,
   required bool linkingEnabled,
 }) async {
-  final architectureString = architectures.length == 1
-      ? architectures.single.toString()
-      : architectures.toList().toString();
+  final buildStart = DateTime.now();
 
-  globals.logger.printTrace('Building native assets for $targetOS $architectureString.');
+  final String targetString = targets
+      .map((AssetBuildTarget target) => target.targetString)
+      .join(', ');
+
+  globals.logger.printTrace('Building native assets for $targetString.');
+
   final codeAssets = <FlutterCodeAsset>[];
+  final dataAssets = <DataAsset>[];
   final dependencies = <Uri>{};
-
-  final EnvironmentType? environmentType;
-  if (targetOS == OS.iOS) {
-    final String? sdkRoot = environmentDefines[kSdkRoot];
-    if (sdkRoot == null) {
-      throw MissingDefineException(kSdkRoot, 'native_assets');
+  for (var i = 0; i < targets.length; i++) {
+    final AssetBuildTarget target = targets[i];
+    final List<ProtocolExtension> extensions;
+    if (i > 0) {
+      // We do not have to rebuild assets for the same target.
+      extensions = target.extensions.whereType<CodeAssetExtension>().toList();
+    } else {
+      extensions = target.extensions;
     }
-    environmentType = xcode.environmentTypeFromSdkroot(sdkRoot, fileSystem);
-  } else {
-    environmentType = null;
-  }
+    final BuildResult buildResult = await _build(buildRunner, extensions, linkingEnabled);
 
-  final CCompilerConfig? cCompilerConfig = targetOS == OS.android
-      ? await buildRunner.ndkCCompilerConfig
-      : await buildRunner.cCompilerConfig;
-
-  final String? codesignIdentity = environmentDefines[kCodesignIdentity];
-  assert(codesignIdentity == null || targetOS == OS.iOS || targetOS == OS.macOS);
-
-  final AndroidCodeConfig? androidConfig = targetOS == OS.android
-      ? AndroidCodeConfig(targetNdkApi: targetAndroidNdkApi(environmentDefines))
-      : null;
-  final IOSCodeConfig? iosConfig = targetOS == OS.iOS
-      ? IOSCodeConfig(targetVersion: targetIOSVersion, targetSdk: getIOSSdk(environmentType!))
-      : null;
-  final MacOSCodeConfig? macOSConfig = targetOS == OS.macOS
-      ? MacOSCodeConfig(targetVersion: targetMacOSVersion)
-      : null;
-  for (final architecture in architectures) {
-    final target = Target.fromArchitectureAndOS(architecture, targetOS!);
-    final BuildResult? buildResult = await buildRunner.build(
-      extensions: <ProtocolExtension>[
-        CodeAssetExtension(
-          targetArchitecture: architecture,
-          linkModePreference: LinkModePreference.dynamic,
-          cCompiler: cCompilerConfig,
-          targetOS: targetOS,
-          android: androidConfig,
-          iOS: iosConfig,
-          macOS: macOSConfig,
-        ),
-      ],
-      linkingEnabled: linkingEnabled,
-    );
-    if (buildResult == null) {
-      _throwNativeAssetsBuildFailed();
-    }
-    dependencies.addAll(buildResult.dependencies);
-    codeAssets.addAll(_filterCodeAssets(buildResult.encodedAssets, target));
+    LinkResult? linkResult;
     if (linkingEnabled) {
-      final LinkResult? linkResult = await buildRunner.link(
-        extensions: <ProtocolExtension>[
-          CodeAssetExtension(
-            targetArchitecture: architecture,
-            linkModePreference: LinkModePreference.dynamic,
-            cCompiler: cCompilerConfig,
-            targetOS: targetOS,
-            android: androidConfig,
-            iOS: iosConfig,
-            macOS: macOSConfig,
+      linkResult = await _link(buildRunner, extensions, buildResult);
+      if (target is CodeAssetTarget) {
+        codeAssets.addAll(
+          _filterCodeAssets(
+            linkResult.encodedAssets,
+            Target.fromArchitectureAndOS(target.architecture, target.os),
           ),
-        ],
-        buildResult: buildResult,
-      );
-      if (linkResult == null) {
-        _throwNativeAssetsLinkFailed();
+        );
       }
-      codeAssets.addAll(_filterCodeAssets(linkResult.encodedAssets, target));
+      dataAssets.addAll(_filterDataAssets(linkResult.encodedAssets));
       dependencies.addAll(linkResult.dependencies);
     }
+    if (target is CodeAssetTarget) {
+      codeAssets.addAll(
+        _filterCodeAssets(
+          buildResult.encodedAssets,
+          Target.fromArchitectureAndOS(target.architecture, target.os),
+        ),
+      );
+    }
+    dataAssets.addAll(_filterDataAssets(buildResult.encodedAssets));
+    dependencies.addAll(buildResult.dependencies);
   }
   if (codeAssets.isNotEmpty) {
     globals.logger.printTrace(
@@ -664,8 +568,28 @@
       'https://dart.dev/interop/c-interop#native-assets for more details.',
     );
   }
-  globals.logger.printTrace('Building native assets for $targetOS $architectureString done.');
-  return DartBuildResult(codeAssets, dependencies.toList());
+
+  if (dataAssets.map((DataAsset asset) => asset.id).toSet().length != dataAssets.length) {
+    throwToolExit(
+      'Found duplicates in the data assets: ${dataAssets.map((DataAsset e) => e.id).toList()} while compiling for ${targets.map((AssetBuildTarget e) => e.targetString).toList()}.',
+    );
+  }
+
+  if (codeAssets.toSet().length != codeAssets.length) {
+    throwToolExit(
+      'Found duplicates in the code assets: ${codeAssets.map((FlutterCodeAsset e) => e.codeAsset.id).toList()} while compiling for ${targets.map((AssetBuildTarget e) => e.targetString).toList()}.',
+    );
+  }
+
+  globals.logger.printTrace('Building native assets for $targetString done.');
+
+  return DartHookResult(
+    buildStart: buildStart,
+    buildEnd: DateTime.now(),
+    codeAssets: codeAssets,
+    dataAssets: dataAssets,
+    dependencies: dependencies.toList(),
+  );
 }
 
 List<FlutterCodeAsset> _filterCodeAssets(List<EncodedAsset> assets, Target target) => assets
@@ -676,60 +600,39 @@
     )
     .toList();
 
-List<Architecture> _architecturesForOS(
-  TargetPlatform targetPlatform,
-  OS targetOS,
-  Map<String, String> environmentDefines,
-) {
-  switch (targetOS) {
-    case OS.linux:
-      return <Architecture>[_getNativeArchitecture(targetPlatform)];
-    case OS.windows:
-      return <Architecture>[_getNativeArchitecture(targetPlatform)];
-    case OS.macOS:
-      final List<DarwinArch> darwinArchs =
-          _emptyToNull(
-            environmentDefines[kDarwinArchs],
-          )?.split(' ').map(getDarwinArchForName).toList() ??
-          <DarwinArch>[DarwinArch.x86_64, DarwinArch.arm64];
-      return darwinArchs.map(getNativeMacOSArchitecture).toList();
-    case OS.android:
-      final String? androidArchsEnvironment = environmentDefines[kAndroidArchs];
-      final List<AndroidArch> androidArchs = _androidArchs(targetPlatform, androidArchsEnvironment);
-      return androidArchs.map(getNativeAndroidArchitecture).toList();
-    case OS.iOS:
-      final List<DarwinArch> iosArchs =
-          _emptyToNull(environmentDefines[kIosArchs])?.split(' ').map(getIOSArchForName).toList() ??
-          <DarwinArch>[DarwinArch.arm64];
-      return iosArchs.map(getNativeIOSArchitecture).toList();
-    default:
-      // TODO(dacoharkes): Implement other OSes. https://github.com/flutter/flutter/issues/129757
-      // Write the file we claim to have in the [outputs].
-      return <Architecture>[];
+List<DataAsset> _filterDataAssets(List<EncodedAsset> assets) => assets
+    .where((EncodedAsset asset) => asset.isDataAsset)
+    .map<DataAsset>(DataAsset.fromEncoded)
+    .toList();
+
+Future<BuildResult> _build(
+  FlutterNativeAssetsBuildRunner buildRunner,
+  List<ProtocolExtension> extensions,
+  bool linkingEnabled,
+) async {
+  final BuildResult? buildResult = await buildRunner.build(
+    extensions: extensions,
+    linkingEnabled: linkingEnabled,
+  );
+  if (buildResult == null) {
+    _throwNativeAssetsBuildFailed();
   }
+  return buildResult;
 }
 
-Architecture _getNativeArchitecture(TargetPlatform targetPlatform) {
-  switch (targetPlatform) {
-    case TargetPlatform.linux_x64:
-    case TargetPlatform.windows_x64:
-      return Architecture.x64;
-    case TargetPlatform.linux_arm64:
-    case TargetPlatform.windows_arm64:
-      return Architecture.arm64;
-    case TargetPlatform.android:
-    case TargetPlatform.ios:
-    case TargetPlatform.darwin:
-    case TargetPlatform.fuchsia_arm64:
-    case TargetPlatform.fuchsia_x64:
-    case TargetPlatform.tester:
-    case TargetPlatform.web_javascript:
-    case TargetPlatform.android_arm:
-    case TargetPlatform.android_arm64:
-    case TargetPlatform.android_x64:
-    case TargetPlatform.unsupported:
-      throw Exception('Unknown targetPlatform: $targetPlatform.');
+Future<LinkResult> _link(
+  FlutterNativeAssetsBuildRunner buildRunner,
+  List<ProtocolExtension> extensions,
+  BuildResult buildResult,
+) async {
+  final LinkResult? linkResult = await buildRunner.link(
+    extensions: extensions,
+    buildResult: buildResult,
+  );
+  if (linkResult == null) {
+    _throwNativeAssetsLinkFailed();
   }
+  return linkResult;
 }
 
 Future<void> _copyNativeCodeAssetsToBundleOnWindowsLinux(
@@ -798,42 +701,6 @@
   }
 }
 
-List<AndroidArch> _androidArchs(TargetPlatform targetPlatform, String? androidArchsEnvironment) {
-  switch (targetPlatform) {
-    case TargetPlatform.android_arm:
-      return <AndroidArch>[AndroidArch.armeabi_v7a];
-    case TargetPlatform.android_arm64:
-      return <AndroidArch>[AndroidArch.arm64_v8a];
-    case TargetPlatform.android_x64:
-      return <AndroidArch>[AndroidArch.x86_64];
-    case TargetPlatform.android:
-      if (androidArchsEnvironment == null) {
-        throw MissingDefineException(kAndroidArchs, 'native_assets');
-      }
-      return androidArchsEnvironment.split(' ').map(getAndroidArchForName).toList();
-    case TargetPlatform.darwin:
-    case TargetPlatform.fuchsia_arm64:
-    case TargetPlatform.fuchsia_x64:
-    case TargetPlatform.ios:
-    case TargetPlatform.linux_arm64:
-    case TargetPlatform.linux_x64:
-    case TargetPlatform.tester:
-    case TargetPlatform.web_javascript:
-    case TargetPlatform.windows_x64:
-    case TargetPlatform.windows_arm64:
-      throwToolExit('Unsupported Android target platform: $targetPlatform.');
-    case TargetPlatform.unsupported:
-      TargetPlatform.throwUnsupportedTarget();
-  }
-}
-
-String? _emptyToNull(String? input) {
-  if (input == null || input.isEmpty) {
-    return null;
-  }
-  return input;
-}
-
 extension OSArchitectures on OS {
   Set<Architecture> get architectures => _osTargets[this]!;
 }
diff --git a/packages/flutter_tools/lib/src/isolated/native_assets/targets.dart b/packages/flutter_tools/lib/src/isolated/native_assets/targets.dart
new file mode 100644
index 0000000..368df76
--- /dev/null
+++ b/packages/flutter_tools/lib/src/isolated/native_assets/targets.dart
@@ -0,0 +1,412 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:code_assets/code_assets.dart';
+import 'package:data_assets/data_assets.dart';
+import 'package:file/file.dart' show FileSystem;
+import 'package:hooks/hooks.dart';
+
+import '../../base/common.dart' show throwToolExit;
+import '../../build_info.dart'
+    show
+        AndroidArch,
+        DarwinArch,
+        EnvironmentType,
+        TargetPlatform,
+        getAndroidArchForName,
+        getDarwinArchsFromEnv,
+        getIOSArchForName,
+        kAndroidArchs,
+        kIosArchs,
+        kSdkRoot;
+import '../../build_system/exceptions.dart' show MissingDefineException;
+import '../../macos/xcode.dart' as xcode show environmentTypeFromSdkroot;
+import 'android/native_assets.dart'
+    show cCompilerConfigAndroid, getNativeAndroidArchitecture, targetAndroidNdkApi;
+import 'ios/native_assets.dart' show getIOSSdk, getNativeIOSArchitecture, targetIOSVersion;
+import 'linux/native_assets.dart';
+import 'macos/native_assets.dart' show getNativeMacOSArchitecture, targetMacOSVersion;
+import 'macos/native_assets_host.dart';
+import 'native_assets.dart';
+import 'windows/native_assets.dart';
+
+/// This is a translation layer between Flutter, which knows only
+/// [TargetPlatform]s, and `dart-lang/native`, which knows only asset types and
+/// how to build them based on things like [OS]s and [Architecture]s.
+sealed class AssetBuildTarget {
+  const AssetBuildTarget({required this.supportedAssetTypes});
+
+  /// The asset types supported by this target.
+  ///
+  /// For example, native code assets might not be supported on the web.
+  final List<SupportedAssetTypes> supportedAssetTypes;
+
+  /// The [ProtocolExtension]s are defined per asset type.
+  List<ProtocolExtension> get extensions;
+
+  /// A human readable string representing this target.
+  String get targetString;
+
+  List<DataAssetsExtension> get dataAssetExtensions => <DataAssetsExtension>[
+    if (supportedAssetTypes.contains(SupportedAssetTypes.dataAssets)) DataAssetsExtension(),
+  ];
+
+  /// Build the list of [AssetBuildTarget]s for a given [TargetPlatform].
+  ///
+  /// It needs access to other parameters such as the [fileSystem] or
+  /// [environmentDefines] to retrieve options for some of the targets.
+  static List<AssetBuildTarget> targetsFor({
+    required TargetPlatform targetPlatform,
+    required Map<String, String> environmentDefines,
+    required FileSystem fileSystem,
+    required List<SupportedAssetTypes> supportedAssetTypes,
+  }) {
+    switch (targetPlatform) {
+      case TargetPlatform.windows_x64:
+        return _windowsTarget(supportedAssetTypes, Architecture.x64);
+      case TargetPlatform.linux_x64:
+        return _linuxTarget(supportedAssetTypes, Architecture.x64);
+      case TargetPlatform.linux_arm64:
+        return _linuxTarget(supportedAssetTypes, Architecture.arm64);
+      case TargetPlatform.windows_arm64:
+        return _windowsTarget(supportedAssetTypes, Architecture.arm64);
+      case TargetPlatform.darwin:
+        return _macTargets(environmentDefines, supportedAssetTypes);
+      case TargetPlatform.android:
+      case TargetPlatform.android_arm:
+      case TargetPlatform.android_arm64:
+      case TargetPlatform.android_x64:
+        return _androidTargets(targetPlatform, environmentDefines, supportedAssetTypes);
+      case TargetPlatform.ios:
+        return _iosTargets(environmentDefines, fileSystem, supportedAssetTypes);
+      case TargetPlatform.web_javascript:
+        return _webTarget(supportedAssetTypes);
+      case TargetPlatform.tester:
+        return _flutterTesterTarget(supportedAssetTypes);
+      case TargetPlatform.fuchsia_arm64:
+      case TargetPlatform.fuchsia_x64:
+        throwToolExit('No targets defined for target platform $targetPlatform.');
+      case TargetPlatform.unsupported:
+        throw UnsupportedError('Unexpected target platform $targetPlatform');
+    }
+  }
+
+  static List<AssetBuildTarget> _linuxTarget(
+    List<SupportedAssetTypes> supportedAssetTypes,
+    Architecture architecture,
+  ) {
+    return <AssetBuildTarget>[
+      LinuxAssetTarget(architecture: architecture, supportedAssetTypes: supportedAssetTypes),
+    ];
+  }
+
+  static List<AssetBuildTarget> _windowsTarget(
+    List<SupportedAssetTypes> supportedAssetTypes,
+    Architecture architecture,
+  ) {
+    return <AssetBuildTarget>[
+      WindowsAssetTarget(architecture: architecture, supportedAssetTypes: supportedAssetTypes),
+    ];
+  }
+
+  static List<MacOSAssetTarget> _macTargets(
+    Map<String, String> environmentDefines,
+    List<SupportedAssetTypes> supportedAssetTypes,
+  ) {
+    return getDarwinArchsFromEnv(environmentDefines)
+        .map(getNativeMacOSArchitecture)
+        .map(
+          (Architecture architecture) => MacOSAssetTarget(
+            architecture: architecture,
+            supportedAssetTypes: supportedAssetTypes,
+          ),
+        )
+        .toList();
+  }
+
+  static List<AndroidAssetTarget> _androidTargets(
+    TargetPlatform targetPlatform,
+    Map<String, String> environmentDefines,
+    List<SupportedAssetTypes> supportedAssetTypes,
+  ) {
+    return _androidArchs(targetPlatform, environmentDefines[kAndroidArchs])
+        .map(getNativeAndroidArchitecture)
+        .map(
+          (Architecture architecture) => AndroidAssetTarget(
+            architecture: architecture,
+            supportedAssetTypes: supportedAssetTypes,
+            environmentDefines: environmentDefines,
+          ),
+        )
+        .toList();
+  }
+
+  static List<IOSAssetTarget> _iosTargets(
+    Map<String, String> environmentDefines,
+    FileSystem fileSystem,
+    List<SupportedAssetTypes> supportedAssetTypes,
+  ) {
+    final List<DarwinArch> iosArchitectures =
+        _emptyToNull(environmentDefines[kIosArchs])?.split(' ').map(getIOSArchForName).toList() ??
+        <DarwinArch>[DarwinArch.arm64];
+    return iosArchitectures
+        .map(getNativeIOSArchitecture)
+        .map(
+          (Architecture architecture) => IOSAssetTarget(
+            environmentDefines: environmentDefines,
+            fileSystem: fileSystem,
+            architecture: architecture,
+            supportedAssetTypes: supportedAssetTypes,
+          ),
+        )
+        .toList();
+  }
+
+  static List<AssetBuildTarget> _webTarget(List<SupportedAssetTypes> supportedAssetTypes) =>
+      <AssetBuildTarget>[WebAssetTarget(supportedAssetTypes: supportedAssetTypes)];
+
+  static List<AssetBuildTarget> _flutterTesterTarget(
+    List<SupportedAssetTypes> supportedAssetTypes,
+  ) {
+    return <AssetBuildTarget>[FlutterTesterAssetTarget(supportedAssetTypes: supportedAssetTypes)];
+  }
+}
+
+final class WebAssetTarget extends AssetBuildTarget {
+  WebAssetTarget({required super.supportedAssetTypes});
+
+  @override
+  List<ProtocolExtension> get extensions => <ProtocolExtension>[...dataAssetExtensions];
+
+  @override
+  String get targetString => 'web';
+}
+
+sealed class CodeAssetTarget extends AssetBuildTarget {
+  CodeAssetTarget({
+    required super.supportedAssetTypes,
+    required this.architecture,
+    required this.os,
+  });
+
+  final Architecture architecture;
+  final OS os;
+
+  /// The C compiler configuration for this target. This is populated by
+  /// [precacheCCompilerConfig].
+  late final CCompilerConfig? cCompilerConfigSync;
+
+  /// Precaching the C compiler config in [cCompilerConfigSync], to be able to
+  /// load it synchronously later.
+  Future<void> precacheCCompilerConfig();
+
+  List<CodeAssetExtension> get codeAssetExtensions {
+    return <CodeAssetExtension>[
+      if (supportedAssetTypes.contains(SupportedAssetTypes.codeAssets))
+        CodeAssetExtension(
+          targetArchitecture: architecture,
+          linkModePreference: LinkModePreference.dynamic,
+          cCompiler: cCompilerConfigSync,
+          targetOS: os,
+        ),
+    ];
+  }
+
+  @override
+  String get targetString => '${os.name}_${architecture.name}';
+}
+
+class WindowsAssetTarget extends CodeAssetTarget {
+  WindowsAssetTarget({required super.supportedAssetTypes, required super.architecture})
+    : super(os: OS.windows);
+
+  @override
+  List<ProtocolExtension> get extensions => <ProtocolExtension>[
+    ...codeAssetExtensions,
+    ...dataAssetExtensions,
+  ];
+
+  @override
+  Future<void> precacheCCompilerConfig() async =>
+      cCompilerConfigSync = await cCompilerConfigWindows();
+}
+
+final class LinuxAssetTarget extends CodeAssetTarget {
+  LinuxAssetTarget({required super.supportedAssetTypes, required super.architecture})
+    : super(os: OS.linux);
+
+  @override
+  Future<void> precacheCCompilerConfig() async =>
+      cCompilerConfigSync = await cCompilerConfigLinux();
+
+  @override
+  List<ProtocolExtension> get extensions => <ProtocolExtension>[
+    ...codeAssetExtensions,
+    ...dataAssetExtensions,
+  ];
+}
+
+final class IOSAssetTarget extends CodeAssetTarget {
+  IOSAssetTarget({
+    required super.supportedAssetTypes,
+    required super.architecture,
+    required this.environmentDefines,
+    required this.fileSystem,
+  }) : super(os: OS.iOS);
+
+  final Map<String, String> environmentDefines;
+  final FileSystem fileSystem;
+
+  @override
+  Future<void> precacheCCompilerConfig() async =>
+      cCompilerConfigSync = await cCompilerConfigMacOS();
+
+  IOSCodeConfig _getIOSConfig(Map<String, String> environmentDefines, FileSystem fileSystem) {
+    final String? sdkRoot = environmentDefines[kSdkRoot];
+    if (sdkRoot == null) {
+      throw MissingDefineException(kSdkRoot, 'native_assets');
+    }
+    final EnvironmentType? environmentType = xcode.environmentTypeFromSdkroot(sdkRoot, fileSystem);
+    return IOSCodeConfig(targetVersion: targetIOSVersion, targetSdk: getIOSSdk(environmentType!));
+  }
+
+  @override
+  List<ProtocolExtension> get extensions {
+    return <ProtocolExtension>[
+      if (supportedAssetTypes.contains(SupportedAssetTypes.codeAssets))
+        CodeAssetExtension(
+          targetArchitecture: architecture,
+          linkModePreference: LinkModePreference.dynamic,
+          cCompiler: cCompilerConfigSync,
+          targetOS: OS.iOS,
+          iOS: _getIOSConfig(environmentDefines, fileSystem),
+        ),
+      ...dataAssetExtensions,
+    ];
+  }
+}
+
+final class MacOSAssetTarget extends CodeAssetTarget {
+  MacOSAssetTarget({required super.supportedAssetTypes, required super.architecture})
+    : super(os: OS.macOS);
+
+  @override
+  List<ProtocolExtension> get extensions {
+    return <ProtocolExtension>[
+      if (supportedAssetTypes.contains(SupportedAssetTypes.codeAssets))
+        CodeAssetExtension(
+          targetArchitecture: architecture,
+          linkModePreference: LinkModePreference.dynamic,
+          cCompiler: cCompilerConfigSync,
+          targetOS: OS.macOS,
+          macOS: MacOSCodeConfig(targetVersion: targetMacOSVersion),
+        ),
+      ...dataAssetExtensions,
+    ];
+  }
+
+  @override
+  Future<void> precacheCCompilerConfig() async =>
+      cCompilerConfigSync = await cCompilerConfigMacOS();
+}
+
+final class AndroidAssetTarget extends CodeAssetTarget {
+  AndroidAssetTarget({
+    required super.architecture,
+    required Map<String, String> environmentDefines,
+    required super.supportedAssetTypes,
+  }) : _androidCodeConfig = AndroidCodeConfig(
+         targetNdkApi: targetAndroidNdkApi(environmentDefines),
+       ),
+       super(os: OS.android);
+
+  final AndroidCodeConfig? _androidCodeConfig;
+
+  @override
+  Future<void> precacheCCompilerConfig() async =>
+      cCompilerConfigSync = await cCompilerConfigAndroid();
+
+  @override
+  List<ProtocolExtension> get extensions => <ProtocolExtension>[
+    if (supportedAssetTypes.contains(SupportedAssetTypes.codeAssets))
+      CodeAssetExtension(
+        targetArchitecture: architecture,
+        linkModePreference: LinkModePreference.dynamic,
+        cCompiler: cCompilerConfigSync,
+        targetOS: OS.android,
+        android: _androidCodeConfig,
+      ),
+    ...dataAssetExtensions,
+  ];
+}
+
+final class FlutterTesterAssetTarget extends CodeAssetTarget {
+  FlutterTesterAssetTarget({required super.supportedAssetTypes})
+    : super(architecture: Architecture.current, os: OS.current) {
+    subtarget = switch (os) {
+      OS.linux => LinuxAssetTarget(
+        supportedAssetTypes: supportedAssetTypes,
+        architecture: architecture,
+      ),
+      OS.windows => WindowsAssetTarget(
+        supportedAssetTypes: supportedAssetTypes,
+        architecture: architecture,
+      ),
+      OS.macOS => MacOSAssetTarget(
+        supportedAssetTypes: supportedAssetTypes,
+        architecture: architecture,
+      ),
+      OS() => throw UnsupportedError('Flutter tester supports only Linux, Windows and MacOS.'),
+    };
+  }
+
+  /// The Flutter tester is a headless Flutter, but can run on different targets
+  /// itself. The subtarget thus captures the target OS, architecture, etc.
+  late final CodeAssetTarget subtarget;
+
+  @override
+  List<ProtocolExtension> get extensions => subtarget.extensions;
+
+  @override
+  CCompilerConfig? get cCompilerConfigSync => subtarget.cCompilerConfigSync;
+
+  @override
+  Future<void> precacheCCompilerConfig() async => subtarget.precacheCCompilerConfig();
+}
+
+List<AndroidArch> _androidArchs(TargetPlatform targetPlatform, String? androidArchsEnvironment) {
+  switch (targetPlatform) {
+    case TargetPlatform.android_arm:
+      return <AndroidArch>[AndroidArch.armeabi_v7a];
+    case TargetPlatform.android_arm64:
+      return <AndroidArch>[AndroidArch.arm64_v8a];
+    case TargetPlatform.android_x64:
+      return <AndroidArch>[AndroidArch.x86_64];
+    case TargetPlatform.android:
+      if (androidArchsEnvironment == null) {
+        throw MissingDefineException(kAndroidArchs, 'native_assets');
+      }
+      return androidArchsEnvironment.split(' ').map(getAndroidArchForName).toList();
+    case TargetPlatform.darwin:
+    case TargetPlatform.fuchsia_arm64:
+    case TargetPlatform.fuchsia_x64:
+    case TargetPlatform.ios:
+    case TargetPlatform.linux_arm64:
+    case TargetPlatform.linux_x64:
+    case TargetPlatform.tester:
+    case TargetPlatform.web_javascript:
+    case TargetPlatform.windows_x64:
+    case TargetPlatform.windows_arm64:
+      throwToolExit('Unsupported Android target platform: $targetPlatform.');
+    case TargetPlatform.unsupported:
+      throw UnsupportedError('Unexpected target platform $targetPlatform');
+  }
+}
+
+String? _emptyToNull(String? input) {
+  if (input == null || input.isEmpty) {
+    return null;
+  }
+  return input;
+}
diff --git a/packages/flutter_tools/lib/src/isolated/native_assets/test/native_assets.dart b/packages/flutter_tools/lib/src/isolated/native_assets/test/native_assets.dart
index 9b55b12..8c6de18 100644
--- a/packages/flutter_tools/lib/src/isolated/native_assets/test/native_assets.dart
+++ b/packages/flutter_tools/lib/src/isolated/native_assets/test/native_assets.dart
@@ -12,6 +12,7 @@
 import '../../../globals.dart' as globals;
 import '../../../native_assets.dart';
 import '../../../project.dart';
+import '../dart_hook_result.dart';
 import '../native_assets.dart';
 
 class TestCompilerNativeAssetsBuilderImpl implements TestCompilerNativeAssetsBuilder {
@@ -22,7 +23,7 @@
 
   @override
   String windowsBuildDirectory(FlutterProject project) =>
-      nativeAssetsBuildUri(project.directory.uri, OS.windows).toFilePath();
+      nativeAssetsBuildUri(project.directory.uri, OS.windows.name).toFilePath();
 }
 
 Future<Uri?> testCompilerBuildNativeAssets(BuildInfo buildInfo) async {
@@ -60,13 +61,13 @@
   // `build/native_assets/<os>/native_assets.json` file which uses absolute
   // paths to the shared libraries.
   final OS targetOS = getNativeOSFromTargetPlatform(TargetPlatform.tester);
-  final Uri buildUri = nativeAssetsBuildUri(projectUri, targetOS);
+  final Uri buildUri = nativeAssetsBuildUri(projectUri, targetOS.name);
   final Uri nativeAssetsFileUri = buildUri.resolve('native_assets.json');
 
   final environmentDefines = <String, String>{kBuildMode: buildInfo.mode.cliName};
 
   // First perform the dart build.
-  final DartBuildResult dartBuildResult = await runFlutterSpecificDartBuild(
+  final DartHookResult dartHookResult = await runFlutterSpecificHooks(
     environmentDefines: environmentDefines,
     buildRunner: buildRunner,
     targetPlatform: TargetPlatform.tester,
@@ -76,7 +77,7 @@
 
   // Then "install" the code assets so they can be used at runtime.
   await installCodeAssets(
-    dartBuildResult: dartBuildResult,
+    dartHookResult: dartHookResult,
     environmentDefines: environmentDefines,
     targetPlatform: TargetPlatform.tester,
     projectUri: projectUri,
diff --git a/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart b/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart
index 1570d18..0f94c81 100644
--- a/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart
+++ b/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart
@@ -29,6 +29,7 @@
 import '../device.dart';
 import '../flutter_plugins.dart';
 import '../globals.dart' as globals;
+import '../hook_runner.dart' show hookRunner;
 import '../project.dart';
 import '../reporting/reporting.dart';
 import '../resident_devtools_handler.dart';
@@ -124,6 +125,7 @@
            platform: platform,
            outputPreferences: outputPreferences,
          ),
+         dartBuilder: hookRunner,
        );
 
   final FileSystem _fileSystem;
@@ -742,6 +744,11 @@
     if (rebuildBundle) {
       _logger.printTrace('Updating assets');
       final int result = await assetBundle.build(
+        flutterHookResult: await dartBuilder?.runHooks(
+          targetPlatform: TargetPlatform.web_javascript,
+          environment: environment,
+          logger: _logger,
+        ),
         packageConfigPath: debuggingOptions.buildInfo.packageConfigPath,
         targetPlatform: TargetPlatform.web_javascript,
       );
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart
index d690ac4..d242046 100644
--- a/packages/flutter_tools/lib/src/resident_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -32,6 +32,7 @@
 import 'devfs.dart';
 import 'device.dart';
 import 'globals.dart' as globals;
+import 'hook_runner.dart' show FlutterHookRunner;
 import 'ios/application_package.dart';
 import 'ios/devices.dart';
 import 'project.dart';
@@ -983,6 +984,7 @@
     this.machine = false,
     ResidentDevtoolsHandlerFactory devtoolsHandler = createDefaultHandler,
     CommandHelp? commandHelp,
+    this.dartBuilder,
   }) : mainPath = globals.fs.file(target).absolute.path,
        packagesFilePath = debuggingOptions.buildInfo.packageConfigPath,
        projectRootPath = projectRootPath ?? globals.fs.currentDirectory.path,
@@ -1052,7 +1054,32 @@
   var _exited = false;
   var _finished = Completer<int>();
   BuildResult? _lastBuild;
-  Environment? _environment;
+
+  late final _environment = Environment(
+    artifacts: globals.artifacts!,
+    logger: globals.logger,
+    cacheDir: globals.cache.getRoot(),
+    engineVersion: globals.flutterVersion.engineRevision,
+    fileSystem: globals.fs,
+    flutterRootDir: globals.fs.directory(Cache.flutterRoot),
+    outputDir: globals.fs.directory(getBuildDirectory()),
+    processManager: globals.processManager,
+    platform: globals.platform,
+    analytics: globals.analytics,
+    projectDir: globals.fs.currentDirectory,
+    packageConfigPath: debuggingOptions.buildInfo.packageConfigPath,
+    generateDartPluginRegistry: generateDartPluginRegistry,
+    defines: <String, String>{
+      // Needed for Dart plugin registry generation.
+      kTargetFile: mainPath,
+      kBuildMode: debuggingOptions.buildInfo.mode.cliName,
+    },
+  );
+
+  Environment get environment => _environment;
+
+  /// Can dispatch [FlutterHookRunner.runHooks] to get new assets from the hooks.
+  final FlutterHookRunner? dartBuilder;
 
   @override
   bool hotMode;
@@ -1150,26 +1177,6 @@
 
   @override
   Future<void> runSourceGenerators() async {
-    _environment ??= Environment(
-      artifacts: globals.artifacts!,
-      logger: globals.logger,
-      cacheDir: globals.cache.getRoot(),
-      engineVersion: globals.flutterVersion.engineRevision,
-      fileSystem: globals.fs,
-      flutterRootDir: globals.fs.directory(Cache.flutterRoot),
-      outputDir: globals.fs.directory(getBuildDirectory()),
-      processManager: globals.processManager,
-      platform: globals.platform,
-      analytics: globals.analytics,
-      projectDir: globals.fs.currentDirectory,
-      packageConfigPath: debuggingOptions.buildInfo.packageConfigPath,
-      generateDartPluginRegistry: generateDartPluginRegistry,
-      defines: <String, String>{
-        // Needed for Dart plugin registry generation.
-        kTargetFile: mainPath,
-      },
-    );
-
     final compositeTarget = CompositeTarget(<Target>[
       globals.buildTargets.generateLocalizationsTarget,
       globals.buildTargets.dartPluginRegistrantTarget,
@@ -1177,7 +1184,7 @@
 
     _lastBuild = await globals.buildSystem.buildIncremental(
       compositeTarget,
-      _environment!,
+      _environment,
       _lastBuild,
     );
     if (!_lastBuild!.success) {
diff --git a/packages/flutter_tools/lib/src/run_cold.dart b/packages/flutter_tools/lib/src/run_cold.dart
index 3ab8bf8..e411fad 100644
--- a/packages/flutter_tools/lib/src/run_cold.dart
+++ b/packages/flutter_tools/lib/src/run_cold.dart
@@ -25,6 +25,7 @@
     super.stayResident,
     super.machine,
     super.devtoolsHandler,
+    super.dartBuilder,
   }) : super(hotMode: false);
 
   final bool traceStartup;
diff --git a/packages/flutter_tools/lib/src/run_hot.dart b/packages/flutter_tools/lib/src/run_hot.dart
index f01b79e..abee56c 100644
--- a/packages/flutter_tools/lib/src/run_hot.dart
+++ b/packages/flutter_tools/lib/src/run_hot.dart
@@ -84,6 +84,7 @@
     this.benchmarkMode = false,
     this.applicationBinary,
     this.hostIsIde = false,
+    Logger? logger,
     super.projectRootPath,
     super.dillOutputPath,
     super.stayResident,
@@ -94,11 +95,13 @@
     ReassembleHelper reassembleHelper = _defaultReassembleHelper,
     String? nativeAssetsYamlFile,
     required Analytics analytics,
+    super.dartBuilder,
   }) : _stopwatchFactory = stopwatchFactory,
        _reloadSourcesHelper = reloadSourcesHelper,
        _reassembleHelper = reassembleHelper,
        _nativeAssetsYamlFile = nativeAssetsYamlFile,
        _analytics = analytics,
+       _logger = logger,
        super(hotMode: true);
 
   final StopwatchFactory _stopwatchFactory;
@@ -125,7 +128,12 @@
 
   final benchmarkData = <String, List<int>>{};
 
-  String? _targetPlatform;
+  String? _targetPlatformName;
+  TargetPlatform get _targetPlatform => _targetPlatformName != null
+      ? getTargetPlatformForName(_targetPlatformName!)
+      : throw ArgumentError(
+          'Access to the target platform needs a call to _calculateTargetPlatform first',
+        );
   String? _sdkName;
   bool? _emulator;
 
@@ -133,26 +141,28 @@
 
   String? flavor;
 
+  final Logger? _logger;
+
   @override
   bool get supportsDetach => stopAppDuringCleanup;
 
   Future<void> _calculateTargetPlatform() async {
-    if (_targetPlatform != null) {
+    if (_targetPlatformName != null) {
       return;
     }
 
     switch (flutterDevices.length) {
       case 1:
         final Device device = flutterDevices.first.device!;
-        _targetPlatform = getNameForTargetPlatform(await device.targetPlatform);
+        _targetPlatformName = getNameForTargetPlatform(await device.targetPlatform);
         _sdkName = await device.sdkNameAndVersion;
         _emulator = await device.isLocalEmulator;
       case > 1:
-        _targetPlatform = 'multiple';
+        _targetPlatformName = 'multiple';
         _sdkName = 'multiple';
         _emulator = false;
       default:
-        _targetPlatform = 'unknown';
+        _targetPlatformName = 'unknown';
         _sdkName = 'unknown';
         _emulator = false;
     }
@@ -298,8 +308,13 @@
       return 3;
     }
 
+    await _calculateTargetPlatform();
+
     final initialUpdateDevFSsTimer = Stopwatch()..start();
-    final UpdateFSReport devfsResult = await _updateDevFS(fullRestart: needsFullRestart);
+    final UpdateFSReport devfsResult = await _updateDevFS(
+      fullRestart: needsFullRestart,
+      targetPlatform: _targetPlatform,
+    );
     _addBenchmarkData(
       'hotReloadInitialDevFSSyncMilliseconds',
       initialUpdateDevFSsTimer.elapsed.inMilliseconds,
@@ -433,7 +448,7 @@
       appStartedCompleter?.future.then((_) {
         HotEvent(
           'reload-ready',
-          targetPlatform: _targetPlatform!,
+          targetPlatform: _targetPlatformName!,
           sdkName: _sdkName!,
           emulator: _emulator!,
           fullRestart: false,
@@ -445,7 +460,7 @@
         _analytics.send(
           Event.hotRunnerInfo(
             label: 'reload-ready',
-            targetPlatform: _targetPlatform!,
+            targetPlatform: _targetPlatformName!,
             sdkName: _sdkName!,
             emulator: _emulator!,
             fullRestart: false,
@@ -485,12 +500,20 @@
     ];
   }
 
-  Future<UpdateFSReport> _updateDevFS({bool fullRestart = false}) async {
+  Future<UpdateFSReport> _updateDevFS({
+    bool fullRestart = false,
+    required TargetPlatform targetPlatform,
+  }) async {
     final bool isFirstUpload = !assetBundle.wasBuiltOnce();
     final bool rebuildBundle = assetBundle.needsBuild();
     if (rebuildBundle) {
       globals.printTrace('Updating assets');
       final int result = await assetBundle.build(
+        flutterHookResult: await dartBuilder?.runHooks(
+          targetPlatform: targetPlatform,
+          environment: environment,
+          logger: _logger,
+        ),
         packageConfigPath: debuggingOptions.buildInfo.packageConfigPath,
         flavor: debuggingOptions.buildInfo.flavor,
       );
@@ -605,7 +628,7 @@
     final restartTimer = Stopwatch()..start();
     UpdateFSReport updatedDevFS;
     try {
-      updatedDevFS = await _updateDevFS(fullRestart: true);
+      updatedDevFS = await _updateDevFS(fullRestart: true, targetPlatform: _targetPlatform);
     } finally {
       hotRunnerConfig!.updateDevFSComplete();
     }
@@ -783,7 +806,7 @@
 
     if (fullRestart) {
       final OperationResult result = await _fullRestartHelper(
-        targetPlatform: _targetPlatform,
+        targetPlatform: _targetPlatformName,
         sdkName: _sdkName,
         emulator: _emulator,
         reason: reason,
@@ -800,7 +823,7 @@
       return result;
     }
     final OperationResult result = await _hotReloadHelper(
-      targetPlatform: _targetPlatform,
+      targetPlatform: _targetPlatformName,
       sdkName: _sdkName,
       emulator: _emulator,
       reason: reason,
@@ -1022,7 +1045,7 @@
     final devFSTimer = Stopwatch()..start();
     UpdateFSReport updatedDevFS;
     try {
-      updatedDevFS = await _updateDevFS();
+      updatedDevFS = await _updateDevFS(targetPlatform: _targetPlatform);
     } finally {
       hotRunnerConfig!.updateDevFSComplete();
     }
@@ -1184,7 +1207,10 @@
               assetsDirectory: deviceAssetsDirectoryUri,
               uiIsolateId: view.uiIsolate!.id,
               viewId: view.id,
-              windows: device.targetPlatform == TargetPlatform.windows_x64,
+              windows:
+                  (device.targetPlatform == TargetPlatform.tester && globals.platform.isWindows) ||
+                  device.targetPlatform == TargetPlatform.windows_x64 ||
+                  device.targetPlatform == TargetPlatform.windows_arm64,
             ),
           ),
         );
diff --git a/packages/flutter_tools/lib/src/update_packages_pins.dart b/packages/flutter_tools/lib/src/update_packages_pins.dart
index a9b17be..905d0d5 100644
--- a/packages/flutter_tools/lib/src/update_packages_pins.dart
+++ b/packages/flutter_tools/lib/src/update_packages_pins.dart
@@ -23,6 +23,8 @@
   'archive': '3.6.1', // https://github.com/flutter/flutter/issues/115660
   'code_assets':
       '0.19.4', // Under active development with breaking changes until 1.0.0. Manually rolled by @dcharkes.
+  'data_assets':
+      '0.19.1', // Under active development with breaking changes until 1.0.0. Manually rolled by @mosuem.
   'flutter_gallery_assets': '1.0.2', // Tests depend on the exact version.
   'flutter_template_images': '5.0.0', // Must always exactly match flutter_tools template.
   'google_mobile_ads': '5.1.0', // https://github.com/flutter/flutter/issues/156912
diff --git a/packages/flutter_tools/lib/src/web/chrome.dart b/packages/flutter_tools/lib/src/web/chrome.dart
index 710f504..1431c6a 100644
--- a/packages/flutter_tools/lib/src/web/chrome.dart
+++ b/packages/flutter_tools/lib/src/web/chrome.dart
@@ -221,13 +221,9 @@
       // debugging purposes.
       // See: https://github.com/flutter/flutter/issues/153928
       '--disable-search-engine-choice-screen',
+      '--no-sandbox',
 
-      if (headless) ...<String>[
-        '--headless',
-        '--disable-gpu',
-        '--no-sandbox',
-        '--window-size=2400,1800',
-      ],
+      if (headless) ...<String>['--headless', '--disable-gpu', '--window-size=2400,1800'],
       ...webBrowserFlags,
       url,
     ];
diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml
index 97b371c..7c54f99 100644
--- a/packages/flutter_tools/pubspec.yaml
+++ b/packages/flutter_tools/pubspec.yaml
@@ -60,6 +60,7 @@
   hooks_runner: 0.21.0
   hooks: 0.19.5
   code_assets: 0.19.4
+  data_assets: 0.19.1
 
   # We depend on very specific internal implementation details of the
   # 'test' package, which change between versions, so when upgrading
@@ -126,4 +127,4 @@
 dartdoc:
   # Exclude this package from the hosted API docs.
   nodoc: true
-# PUBSPEC CHECKSUM: kr614i
+# PUBSPEC CHECKSUM: 5bblq6
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart
index aefff1d..9ff3b3f 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart
@@ -1610,10 +1610,10 @@
     String? packagesFilePath,
     String? dillOutputPath,
     bool stayResident = true,
-    bool ipv6 = false,
     FlutterProject? flutterProject,
-    Analytics? analytics,
     String? nativeAssetsYamlFile,
+    required Analytics analytics,
+    Logger? logger,
   }) {
     if (_artifactTester != null) {
       for (final device in devices) {
diff --git a/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart b/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart
index cb4705a..ea89222 100644
--- a/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart
+++ b/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart
@@ -308,7 +308,6 @@
           kIconTreeShakerFlag: 'false',
           kDeferredComponents: 'false',
           kDartObfuscation: 'false',
-          kNativeAssets: 'false',
         });
       }),
       FileSystem: fsFactory,
@@ -349,7 +348,6 @@
           kIconTreeShakerFlag: 'false',
           kDeferredComponents: 'false',
           kDartObfuscation: 'false',
-          kNativeAssets: 'false',
         });
       }),
       FileSystem: fsFactory,
@@ -390,7 +388,6 @@
           kIconTreeShakerFlag: 'false',
           kDeferredComponents: 'false',
           kDartObfuscation: 'false',
-          kNativeAssets: 'false',
         });
       }),
       FileSystem: fsFactory,
@@ -432,7 +429,6 @@
           kIconTreeShakerFlag: 'false',
           kDeferredComponents: 'false',
           kDartObfuscation: 'false',
-          kNativeAssets: 'false',
         });
       }),
       FileSystem: fsFactory,
@@ -474,7 +470,6 @@
           kIconTreeShakerFlag: 'false',
           kDeferredComponents: 'false',
           kDartObfuscation: 'false',
-          kNativeAssets: 'false',
         });
       }),
       FileSystem: fsFactory,
@@ -516,7 +511,6 @@
           kIconTreeShakerFlag: 'false',
           kDeferredComponents: 'false',
           kDartObfuscation: 'false',
-          kNativeAssets: 'false',
         });
       }),
       FileSystem: fsFactory,
@@ -565,7 +559,6 @@
           kIconTreeShakerFlag: 'false',
           kDeferredComponents: 'false',
           kDartObfuscation: 'false',
-          kNativeAssets: 'false',
         });
       }),
       FileSystem: fsFactory,
@@ -614,7 +607,6 @@
           kIconTreeShakerFlag: 'false',
           kDeferredComponents: 'false',
           kDartObfuscation: 'false',
-          kNativeAssets: 'false',
         });
       }),
       FileSystem: fsFactory,
diff --git a/packages/flutter_tools/test/general.shard/devfs_test.dart b/packages/flutter_tools/test/general.shard/devfs_test.dart
index be81085..53c48ac 100644
--- a/packages/flutter_tools/test/general.shard/devfs_test.dart
+++ b/packages/flutter_tools/test/general.shard/devfs_test.dart
@@ -979,6 +979,7 @@
 
   @override
   Future<int> build({
+    FlutterHookResult? flutterHookResult,
     String manifestPath = defaultManifestPath,
     String? assetDirPath,
     String? packageConfigPath,
diff --git a/packages/flutter_tools/test/general.shard/isolated/android/native_assets_test.dart b/packages/flutter_tools/test/general.shard/isolated/android/native_assets_test.dart
index 4639061..bf3c782 100644
--- a/packages/flutter_tools/test/general.shard/isolated/android/native_assets_test.dart
+++ b/packages/flutter_tools/test/general.shard/isolated/android/native_assets_test.dart
@@ -8,7 +8,7 @@
 import 'package:file_testing/file_testing.dart';
 import 'package:flutter_tools/src/android/gradle_utils.dart';
 import 'package:flutter_tools/src/artifacts.dart';
-import 'package:flutter_tools/src/base/common.dart';
+import 'package:flutter_tools/src/base/common.dart' show throwToolExit;
 import 'package:flutter_tools/src/base/file_system.dart';
 import 'package:flutter_tools/src/base/logger.dart';
 import 'package:flutter_tools/src/base/platform.dart';
@@ -17,6 +17,7 @@
 import 'package:flutter_tools/src/build_system/targets/native_assets.dart';
 import 'package:flutter_tools/src/features.dart';
 import 'package:flutter_tools/src/globals.dart' as globals;
+import 'package:flutter_tools/src/isolated/native_assets/dart_hook_result.dart';
 import 'package:flutter_tools/src/isolated/native_assets/native_assets.dart';
 
 import '../../../src/common.dart';
@@ -85,7 +86,7 @@
           kBuildMode: buildMode.cliName,
           kMinSdkVersion: minSdkVersion,
         };
-        final DartBuildResult result = await runFlutterSpecificDartBuild(
+        final DartHookResult result = await runFlutterSpecificHooks(
           environmentDefines: environmentDefines,
           targetPlatform: TargetPlatform.android_arm64,
           projectUri: projectUri,
@@ -93,7 +94,7 @@
           buildRunner: buildRunner,
         );
         await installCodeAssets(
-          dartBuildResult: result,
+          dartHookResult: result,
           environmentDefines: environmentDefines,
           targetPlatform: TargetPlatform.android_arm64,
           projectUri: projectUri,
@@ -103,8 +104,8 @@
         expect(
           (globals.logger as BufferLogger).traceText,
           stringContainsInOrder(<String>[
-            'Building native assets for android arm64.',
-            'Building native assets for android arm64 done.',
+            'Building native assets for android_arm64.',
+            'Building native assets for android_arm64 done.',
           ]),
         );
 
@@ -123,7 +124,7 @@
     () async {
       final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
       await packageConfig.create(recursive: true);
-      await runFlutterSpecificDartBuild(
+      await runFlutterSpecificHooks(
         environmentDefines: <String, String>{
           kBuildMode: BuildMode.debug.cliName,
           kMinSdkVersion: minSdkVersion,
@@ -148,7 +149,7 @@
       await packageConfig.parent.create();
       await packageConfig.create();
       expect(
-        () => runFlutterSpecificDartBuild(
+        () => runFlutterSpecificHooks(
           environmentDefines: <String, String>{
             kBuildMode: BuildMode.debug.cliName,
             kMinSdkVersion: minSdkVersion,
@@ -168,6 +169,6 @@
   _BuildRunnerWithoutNdk({super.packagesWithNativeAssetsResult = const <String>[]});
 
   @override
-  Future<CCompilerConfig> get ndkCCompilerConfig async =>
+  CCompilerConfig? get ndkCCompilerConfigResult =>
       throwToolExit('Android NDK Clang could not be found.');
 }
diff --git a/packages/flutter_tools/test/general.shard/isolated/build_system/targets/native_assets_test.dart b/packages/flutter_tools/test/general.shard/isolated/build_system/targets/native_assets_test.dart
index 10620cc..5332bd3 100644
--- a/packages/flutter_tools/test/general.shard/isolated/build_system/targets/native_assets_test.dart
+++ b/packages/flutter_tools/test/general.shard/isolated/build_system/targets/native_assets_test.dart
@@ -214,14 +214,14 @@
       //  * dart build output should depend on C source
       //  * installation output should depend on shared library from dart build
 
-      final File dartBuildResult = iosEnvironment.buildDir.childFile(
-        DartBuild.dartBuildResultFilename,
+      final File dartHookResult = iosEnvironment.buildDir.childFile(
+        DartBuild.dartHookResultFilename,
       );
       final File buildDepsFile = iosEnvironment.buildDir.childFile(DartBuild.depFilename);
       expect(buildDepsFile, exists);
       expect(
         buildDepsFile.readAsStringSync(),
-        stringContainsInOrder(<String>[dartBuildResult.path, ':', 'src/foo.c']),
+        stringContainsInOrder(<String>[dartHookResult.path, ':', 'src/foo.c']),
       );
 
       final File nativeAssetsYaml = iosEnvironment.buildDir.childFile(
diff --git a/packages/flutter_tools/test/general.shard/isolated/data_assets_test.dart b/packages/flutter_tools/test/general.shard/isolated/data_assets_test.dart
new file mode 100644
index 0000000..0f547a0
--- /dev/null
+++ b/packages/flutter_tools/test/general.shard/isolated/data_assets_test.dart
@@ -0,0 +1,99 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:data_assets/data_assets.dart';
+import 'package:file/file.dart' show File, FileSystem;
+import 'package:file/memory.dart' show MemoryFileSystem;
+import 'package:flutter_tools/src/artifacts.dart' show Artifacts;
+import 'package:flutter_tools/src/base/logger.dart' show BufferLogger;
+import 'package:flutter_tools/src/build_info.dart' show BuildMode, TargetPlatform, kBuildMode;
+import 'package:flutter_tools/src/build_system/build_system.dart' show Environment;
+import 'package:flutter_tools/src/features.dart' show FeatureFlags;
+import 'package:flutter_tools/src/isolated/native_assets/native_assets.dart'
+    show runFlutterSpecificHooks;
+
+import '../../src/common.dart' show expect, returnsNormally, setUp, throwsToolExit;
+import '../../src/context.dart'
+    show FakeProcessManager, Generator, ProcessManager, testUsingContext;
+import '../../src/fakes.dart' show TestFeatureFlags;
+import 'fake_native_assets_build_runner.dart'
+    show FakeFlutterNativeAssetsBuildRunner, FakeFlutterNativeAssetsBuilderResult;
+
+void main() {
+  late FakeProcessManager processManager;
+  late Environment environment;
+  late Artifacts artifacts;
+  late FileSystem fileSystem;
+  late BufferLogger logger;
+  late Uri projectUri;
+
+  setUp(() {
+    processManager = FakeProcessManager.empty();
+    logger = BufferLogger.test();
+    artifacts = Artifacts.test();
+    fileSystem = MemoryFileSystem.test();
+    environment = Environment.test(
+      fileSystem.currentDirectory,
+      inputs: <String, String>{},
+      artifacts: artifacts,
+      processManager: processManager,
+      fileSystem: fileSystem,
+      logger: logger,
+    );
+    environment.buildDir.createSync(recursive: true);
+    projectUri = environment.projectDir.uri;
+  });
+
+  testUsingContext(
+    'Data assets: no duplicate assets with linking',
+    overrides: <Type, Generator>{
+      FeatureFlags: () => TestFeatureFlags(isDartDataAssetsEnabled: true),
+      ProcessManager: FakeProcessManager.empty,
+    },
+    () async {
+      environment.projectDir
+          .childFile('.dart_tool/package_config.json')
+          .createSync(recursive: true);
+
+      final File imageFile = environment.projectDir.childFile('image.png');
+      imageFile.writeAsBytesSync(<int>[]);
+      final File textFile1 = environment.projectDir.childFile('text.txt');
+      textFile1.writeAsStringSync('test');
+      final File textFile2 = environment.projectDir.childFile('text.json');
+      textFile2.writeAsStringSync('{}');
+
+      DataAsset makeDataAsset(String name, Uri file) =>
+          DataAsset(package: 'bar', name: name, file: file);
+
+      for (final buildMode in <BuildMode>[BuildMode.debug, BuildMode.release]) {
+        expect(
+          () async => runFlutterSpecificHooks(
+            environmentDefines: <String, String>{kBuildMode: buildMode.cliName},
+            targetPlatform: TargetPlatform.linux_x64,
+            projectUri: projectUri,
+            fileSystem: fileSystem,
+            buildRunner: FakeFlutterNativeAssetsBuildRunner(
+              packagesWithNativeAssetsResult: <String>['bar'],
+              buildResult: FakeFlutterNativeAssetsBuilderResult.fromAssets(
+                dataAssets: <DataAsset>[makeDataAsset('direct', imageFile.uri)],
+                dataAssetsForLinking: <String, List<DataAsset>>{
+                  'package:bar': <DataAsset>[makeDataAsset('linkable', textFile1.uri)],
+                },
+              ),
+              linkResult: FakeFlutterNativeAssetsBuilderResult.fromAssets(
+                dataAssets: <DataAsset>[
+                  makeDataAsset('direct', imageFile.uri),
+                  makeDataAsset('linked', textFile2.uri),
+                ],
+              ),
+            ),
+          ),
+          buildMode == BuildMode.release
+              ? throwsToolExit(message: 'Found duplicates')
+              : returnsNormally,
+        );
+      }
+    },
+  );
+}
diff --git a/packages/flutter_tools/test/general.shard/isolated/fake_native_assets_build_runner.dart b/packages/flutter_tools/test/general.shard/isolated/fake_native_assets_build_runner.dart
index d25ca46..f8fedcc 100644
--- a/packages/flutter_tools/test/general.shard/isolated/fake_native_assets_build_runner.dart
+++ b/packages/flutter_tools/test/general.shard/isolated/fake_native_assets_build_runner.dart
@@ -3,7 +3,9 @@
 // found in the LICENSE file.
 
 import 'package:code_assets/code_assets.dart';
+import 'package:data_assets/data_assets.dart';
 import 'package:flutter_tools/src/isolated/native_assets/native_assets.dart';
+import 'package:flutter_tools/src/isolated/native_assets/targets.dart';
 import 'package:hooks/hooks.dart';
 import 'package:hooks_runner/hooks_runner.dart';
 
@@ -18,8 +20,6 @@
     this.onLink,
     this.buildResult = const FakeFlutterNativeAssetsBuilderResult(),
     this.linkResult = const FakeFlutterNativeAssetsBuilderResult(),
-    this.cCompilerConfigResult,
-    this.ndkCCompilerConfigResult,
   });
 
   // TODO(dcharkes): Cleanup this fake https://github.com/flutter/flutter/issues/162061
@@ -28,8 +28,6 @@
   final BuildResult? buildResult;
   final LinkResult? linkResult;
   final List<String> packagesWithNativeAssetsResult;
-  final CCompilerConfig? cCompilerConfigResult;
-  final CCompilerConfig? ndkCCompilerConfigResult;
 
   var buildInvocations = 0;
   var linkInvocations = 0;
@@ -96,11 +94,19 @@
     return packagesWithNativeAssetsResult;
   }
 
-  @override
-  Future<CCompilerConfig?> get cCompilerConfig async => cCompilerConfigResult;
+  CCompilerConfig? get cCompilerConfigResult => null;
+  CCompilerConfig? get ndkCCompilerConfigResult => null;
 
   @override
-  Future<CCompilerConfig?> get ndkCCompilerConfig async => cCompilerConfigResult;
+  Future<void> precacheCCompilerConfig(CodeAssetTarget target) async {
+    if (target is AndroidAssetTarget) {
+      target.cCompilerConfigSync = ndkCCompilerConfigResult;
+    } else if (target is FlutterTesterAssetTarget) {
+      target.subtarget.cCompilerConfigSync = cCompilerConfigResult;
+    } else {
+      target.cCompilerConfigSync = cCompilerConfigResult;
+    }
+  }
 }
 
 final class FakeFlutterNativeAssetsBuilderResult implements BuildResult, LinkResult {
@@ -112,18 +118,25 @@
 
   factory FakeFlutterNativeAssetsBuilderResult.fromAssets({
     List<CodeAsset> codeAssets = const <CodeAsset>[],
+    List<DataAsset> dataAssets = const <DataAsset>[],
     Map<String, List<CodeAsset>> codeAssetsForLinking = const <String, List<CodeAsset>>{},
+    Map<String, List<DataAsset>> dataAssetsForLinking = const <String, List<DataAsset>>{},
     List<Uri> dependencies = const <Uri>[],
   }) {
     return FakeFlutterNativeAssetsBuilderResult(
       encodedAssets: <EncodedAsset>[
         for (final CodeAsset codeAsset in codeAssets) codeAsset.encode(),
+        for (final DataAsset dataAsset in dataAssets) dataAsset.encode(),
       ],
       encodedAssetsForLinking: <String, List<EncodedAsset>>{
         for (final String linkerName in codeAssetsForLinking.keys)
           linkerName: <EncodedAsset>[
             for (final CodeAsset codeAsset in codeAssetsForLinking[linkerName]!) codeAsset.encode(),
           ],
+        for (final String linkerName in dataAssetsForLinking.keys)
+          linkerName: <EncodedAsset>[
+            for (final DataAsset dataAsset in dataAssetsForLinking[linkerName]!) dataAsset.encode(),
+          ],
       },
       dependencies: dependencies,
     );
diff --git a/packages/flutter_tools/test/general.shard/isolated/ios/native_assets_test.dart b/packages/flutter_tools/test/general.shard/isolated/ios/native_assets_test.dart
index 56997bf..a7b8a1c 100644
--- a/packages/flutter_tools/test/general.shard/isolated/ios/native_assets_test.dart
+++ b/packages/flutter_tools/test/general.shard/isolated/ios/native_assets_test.dart
@@ -14,6 +14,7 @@
 import 'package:flutter_tools/src/build_system/build_system.dart';
 import 'package:flutter_tools/src/build_system/targets/native_assets.dart';
 import 'package:flutter_tools/src/globals.dart' as globals;
+import 'package:flutter_tools/src/isolated/native_assets/dart_hook_result.dart';
 import 'package:flutter_tools/src/isolated/native_assets/native_assets.dart';
 import 'package:hooks/hooks.dart';
 
@@ -184,7 +185,7 @@
           kSdkRoot: '.../iPhone Simulator',
           kIosArchs: 'arm64 x86_64',
         };
-        final DartBuildResult dartBuildResult = await runFlutterSpecificDartBuild(
+        final DartHookResult dartHookResult = await runFlutterSpecificHooks(
           environmentDefines: environmentDefines,
           targetPlatform: TargetPlatform.ios,
           projectUri: projectUri,
@@ -192,7 +193,7 @@
           buildRunner: buildRunner,
         );
         await installCodeAssets(
-          dartBuildResult: dartBuildResult,
+          dartHookResult: dartHookResult,
           environmentDefines: environmentDefines,
           targetPlatform: TargetPlatform.ios,
           projectUri: projectUri,
@@ -202,8 +203,8 @@
         expect(
           (globals.logger as BufferLogger).traceText,
           stringContainsInOrder(<String>[
-            'Building native assets for ios [arm64, x64].',
-            'Building native assets for ios [arm64, x64] done.',
+            'Building native assets for ios_arm64, ios_x64.',
+            'Building native assets for ios_arm64, ios_x64 done.',
           ]),
         );
         expect(environment.buildDir.childFile(InstallCodeAssets.nativeAssetsFilename), exists);
diff --git a/packages/flutter_tools/test/general.shard/isolated/linux/native_assets_test.dart b/packages/flutter_tools/test/general.shard/isolated/linux/native_assets_test.dart
index 0bfb294..e40011e 100644
--- a/packages/flutter_tools/test/general.shard/isolated/linux/native_assets_test.dart
+++ b/packages/flutter_tools/test/general.shard/isolated/linux/native_assets_test.dart
@@ -6,20 +6,17 @@
 import 'package:file/file.dart';
 import 'package:file/memory.dart';
 import 'package:flutter_tools/src/artifacts.dart';
-import 'package:flutter_tools/src/base/common.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
 import 'package:flutter_tools/src/base/logger.dart';
 import 'package:flutter_tools/src/base/platform.dart';
 import 'package:flutter_tools/src/build_info.dart';
 import 'package:flutter_tools/src/build_system/build_system.dart';
-import 'package:flutter_tools/src/dart/package_map.dart';
 import 'package:flutter_tools/src/globals.dart' as globals;
+import 'package:flutter_tools/src/isolated/native_assets/linux/native_assets.dart';
 import 'package:flutter_tools/src/isolated/native_assets/native_assets.dart';
-import 'package:package_config/package_config_types.dart';
 
 import '../../../src/common.dart';
 import '../../../src/context.dart';
-import '../../../src/package_config.dart';
 import '../fake_native_assets_build_runner.dart';
 
 void main() {
@@ -29,7 +26,6 @@
   late FileSystem fileSystem;
   late BufferLogger logger;
   late Uri projectUri;
-  late String runPackageName;
 
   setUp(() {
     processManager = FakeProcessManager.empty();
@@ -46,7 +42,6 @@
     );
     environment.buildDir.createSync(recursive: true);
     projectUri = environment.projectDir.uri;
-    runPackageName = environment.projectDir.basename;
   });
 
   testUsingContext(
@@ -56,7 +51,7 @@
       final File packageConfig = environment.projectDir.childFile('.dart_tool/package_config.json');
       await packageConfig.create(recursive: true);
 
-      await runFlutterSpecificDartBuild(
+      await runFlutterSpecificHooks(
         environmentDefines: <String, String>{kBuildMode: BuildMode.debug.cliName},
         targetPlatform: TargetPlatform.linux_x64,
         projectUri: projectUri,
@@ -74,7 +69,7 @@
   // randomization causing issues with what processes are invoked.
   // Exercise the parsing of the process output in this separate test.
   testUsingContext(
-    'NativeAssetsBuildRunnerImpl.cCompilerConfig',
+    'cCompilerConfigLinux',
     overrides: <Type, Generator>{
       ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
         const FakeCommand(
@@ -97,35 +92,10 @@
       await fileSystem.file('/some/path/to/llvm-ar').create();
       await fileSystem.file('/some/path/to/ld.lld').create();
 
-      final File packageConfigFile = writePackageConfigFiles(
-        directory: fileSystem.directory(projectUri),
-        mainLibName: 'my_app',
-      );
-      final PackageConfig packageConfig = await loadPackageConfigWithLogging(
-        packageConfigFile,
-        logger: environment.logger,
-      );
-      final File pubspecFile = fileSystem.file(projectUri.resolve('pubspec.yaml'));
-      await pubspecFile.writeAsString('''
-name: my_app
-''');
-      final FlutterNativeAssetsBuildRunner runner = FlutterNativeAssetsBuildRunnerImpl(
-        packageConfigFile.path,
-        packageConfig,
-        fileSystem,
-        logger,
-        runPackageName,
-        includeDevDependencies: false,
-        pubspecFile.path,
-      );
-      final CCompilerConfig result = (await runner.cCompilerConfig)!;
+      final CCompilerConfig result = await cCompilerConfigLinux();
       expect(result.compiler, Uri.file('/some/path/to/clang'));
     },
   );
 }
 
-class _BuildRunnerWithoutClang extends FakeFlutterNativeAssetsBuildRunner {
-  @override
-  Future<CCompilerConfig> get cCompilerConfig async =>
-      throwToolExit('Failed to find clang++ on the PATH.');
-}
+class _BuildRunnerWithoutClang extends FakeFlutterNativeAssetsBuildRunner {}
diff --git a/packages/flutter_tools/test/general.shard/isolated/macos/native_assets_test.dart b/packages/flutter_tools/test/general.shard/isolated/macos/native_assets_test.dart
index 2717cb7..4e73889 100644
--- a/packages/flutter_tools/test/general.shard/isolated/macos/native_assets_test.dart
+++ b/packages/flutter_tools/test/general.shard/isolated/macos/native_assets_test.dart
@@ -12,15 +12,15 @@
 import 'package:flutter_tools/src/build_info.dart';
 import 'package:flutter_tools/src/build_system/build_system.dart';
 import 'package:flutter_tools/src/build_system/targets/native_assets.dart';
-import 'package:flutter_tools/src/dart/package_map.dart';
 import 'package:flutter_tools/src/globals.dart' as globals;
+import 'package:flutter_tools/src/isolated/native_assets/dart_hook_result.dart';
+import 'package:flutter_tools/src/isolated/native_assets/macos/native_assets_host.dart'
+    show cCompilerConfigMacOS;
 import 'package:flutter_tools/src/isolated/native_assets/native_assets.dart';
 import 'package:hooks/hooks.dart';
-import 'package:package_config/package_config_types.dart';
 
 import '../../../src/common.dart';
 import '../../../src/context.dart';
-import '../../../src/package_config.dart';
 import '../fake_native_assets_build_runner.dart';
 
 void main() {
@@ -30,7 +30,6 @@
   late FileSystem fileSystem;
   late BufferLogger logger;
   late Uri projectUri;
-  late String runPackageName;
 
   setUp(() {
     processManager = FakeProcessManager.empty();
@@ -47,7 +46,6 @@
     );
     environment.buildDir.createSync(recursive: true);
     projectUri = environment.projectDir.uri;
-    runPackageName = environment.projectDir.basename;
   });
 
   for (final flutterTester in <bool>[false, true]) {
@@ -291,7 +289,7 @@
           final TargetPlatform targetPlatform = flutterTester
               ? TargetPlatform.tester
               : TargetPlatform.darwin;
-          final DartBuildResult dartBuildResult = await runFlutterSpecificDartBuild(
+          final DartHookResult dartHookResult = await runFlutterSpecificHooks(
             environmentDefines: environmentDefines,
             targetPlatform: targetPlatform,
             projectUri: projectUri,
@@ -305,7 +303,7 @@
               : nonFlutterTesterAssetUri;
 
           await installCodeAssets(
-            dartBuildResult: dartBuildResult,
+            dartHookResult: dartHookResult,
             environmentDefines: environmentDefines,
             targetPlatform: targetPlatform,
             projectUri: projectUri,
@@ -313,13 +311,13 @@
             nativeAssetsFileUri: nativeAssetsFileUri,
           );
           final expectedArchsBeingBuilt = flutterTester
-              ? (isArm64 ? 'arm64' : 'x64')
-              : '[arm64, x64]';
+              ? (isArm64 ? 'macos_arm64' : 'macos_x64')
+              : 'macos_arm64, macos_x64';
           expect(
             (globals.logger as BufferLogger).traceText,
             stringContainsInOrder(<String>[
-              'Building native assets for macos $expectedArchsBeingBuilt.',
-              'Building native assets for macos $expectedArchsBeingBuilt done.',
+              'Building native assets for $expectedArchsBeingBuilt.',
+              'Building native assets for $expectedArchsBeingBuilt done.',
             ]),
           );
           final String nativeAssetsFileContent = await fileSystem
@@ -379,28 +377,7 @@
         return;
       }
 
-      final File packageConfigFile = writePackageConfigFiles(
-        directory: fileSystem.directory(projectUri),
-        mainLibName: 'my_app',
-      );
-      final PackageConfig packageConfig = await loadPackageConfigWithLogging(
-        packageConfigFile,
-        logger: environment.logger,
-      );
-      final File pubspecFile = fileSystem.file(projectUri.resolve('pubspec.yaml'));
-      await pubspecFile.writeAsString('''
-name: my_app
-''');
-      final FlutterNativeAssetsBuildRunner runner = FlutterNativeAssetsBuildRunnerImpl(
-        packageConfigFile.path,
-        packageConfig,
-        fileSystem,
-        logger,
-        runPackageName,
-        includeDevDependencies: false,
-        pubspecFile.path,
-      );
-      final CCompilerConfig result = (await runner.cCompilerConfig)!;
+      final CCompilerConfig result = await cCompilerConfigMacOS();
       expect(
         result.compiler,
         Uri.file(
diff --git a/packages/flutter_tools/test/general.shard/isolated/native_assets_test.dart b/packages/flutter_tools/test/general.shard/isolated/native_assets_test.dart
index cef107d..fa6019c 100644
--- a/packages/flutter_tools/test/general.shard/isolated/native_assets_test.dart
+++ b/packages/flutter_tools/test/general.shard/isolated/native_assets_test.dart
@@ -13,6 +13,7 @@
 import 'package:flutter_tools/src/build_system/build_system.dart';
 import 'package:flutter_tools/src/build_system/targets/native_assets.dart';
 import 'package:flutter_tools/src/features.dart';
+import 'package:flutter_tools/src/isolated/native_assets/dart_hook_result.dart';
 import 'package:flutter_tools/src/isolated/native_assets/native_assets.dart';
 
 import '../../src/common.dart';
@@ -66,7 +67,7 @@
         makeCodeAsset('free', LookupInExecutable()),
         makeCodeAsset('draw', DynamicLoadingSystem(Uri.file('/usr/lib/skia.so'))),
       ];
-      final DartBuildResult dartBuildResult = await runFlutterSpecificDartBuild(
+      final DartHookResult dartHookResult = await runFlutterSpecificHooks(
         environmentDefines: environmentDefines,
         targetPlatform: TargetPlatform.linux_x64,
         projectUri: projectUri,
@@ -78,7 +79,7 @@
         ),
       );
       await installCodeAssets(
-        dartBuildResult: dartBuildResult,
+        dartHookResult: dartHookResult,
         environmentDefines: environmentDefines,
         targetPlatform: TargetPlatform.windows_x64,
         projectUri: projectUri,
@@ -101,7 +102,7 @@
       await packageConfig.parent.create();
       await packageConfig.create();
       expect(
-        () => runFlutterSpecificDartBuild(
+        () => runFlutterSpecificHooks(
           environmentDefines: <String, String>{kBuildMode: BuildMode.debug.cliName},
           targetPlatform: TargetPlatform.windows_x64,
           projectUri: projectUri,
@@ -110,11 +111,7 @@
             packagesWithNativeAssetsResult: <String>['bar'],
           ),
         ),
-        throwsToolExit(
-          message:
-              'Package(s) bar require the native assets feature to be enabled. '
-              'Enable using `flutter config --enable-native-assets`.',
-        ),
+        throwsToolExit(message: 'Enable code assets using `flutter config --enable-native-assets`'),
       );
     },
   );
@@ -131,7 +128,7 @@
       await packageConfig.create();
 
       final environmentDefines = <String, String>{kBuildMode: BuildMode.debug.cliName};
-      final DartBuildResult dartBuildResult = await runFlutterSpecificDartBuild(
+      final DartHookResult dartHookResult = await runFlutterSpecificHooks(
         environmentDefines: environmentDefines,
         targetPlatform: TargetPlatform.windows_x64,
         projectUri: projectUri,
@@ -141,7 +138,7 @@
         ),
       );
       await installCodeAssets(
-        dartBuildResult: dartBuildResult,
+        dartHookResult: dartHookResult,
         environmentDefines: environmentDefines,
         targetPlatform: TargetPlatform.windows_x64,
         projectUri: projectUri,
@@ -170,7 +167,7 @@
       await packageConfig.parent.create();
       await packageConfig.create();
       expect(
-        () => runFlutterSpecificDartBuild(
+        () => runFlutterSpecificHooks(
           environmentDefines: <String, String>{kBuildMode: BuildMode.debug.cliName},
           targetPlatform: TargetPlatform.linux_x64,
           projectUri: projectUri,
@@ -203,7 +200,7 @@
       CodeAsset makeCodeAsset(String name, Uri file, LinkMode linkMode) =>
           CodeAsset(package: 'bar', name: name, linkMode: linkMode, file: file);
 
-      final DartBuildResult result = await runFlutterSpecificDartBuild(
+      final DartHookResult result = await runFlutterSpecificHooks(
         environmentDefines: <String, String>{
           // Release mode means the dart build has linking enabled.
           kBuildMode: BuildMode.release.cliName,
diff --git a/packages/flutter_tools/test/general.shard/isolated/resident_runner_test.dart b/packages/flutter_tools/test/general.shard/isolated/resident_runner_test.dart
index fae2242..52bcdc1 100644
--- a/packages/flutter_tools/test/general.shard/isolated/resident_runner_test.dart
+++ b/packages/flutter_tools/test/general.shard/isolated/resident_runner_test.dart
@@ -68,6 +68,7 @@
           fakeFlutterVersion: FakeFlutterVersion(),
         ),
         nativeAssetsYamlFile: 'foo.yaml',
+        logger: globals.logger,
       );
 
       final int result = await residentRunner.run();
diff --git a/packages/flutter_tools/test/general.shard/isolated/windows/native_assets_test.dart b/packages/flutter_tools/test/general.shard/isolated/windows/native_assets_test.dart
index c9e4b2a..6028313 100644
--- a/packages/flutter_tools/test/general.shard/isolated/windows/native_assets_test.dart
+++ b/packages/flutter_tools/test/general.shard/isolated/windows/native_assets_test.dart
@@ -13,10 +13,11 @@
 import 'package:flutter_tools/src/build_info.dart';
 import 'package:flutter_tools/src/build_system/build_system.dart';
 import 'package:flutter_tools/src/build_system/targets/native_assets.dart';
-import 'package:flutter_tools/src/dart/package_map.dart';
 import 'package:flutter_tools/src/globals.dart' as globals;
+import 'package:flutter_tools/src/isolated/native_assets/dart_hook_result.dart';
 import 'package:flutter_tools/src/isolated/native_assets/native_assets.dart';
-import 'package:package_config/package_config_types.dart';
+import 'package:flutter_tools/src/isolated/native_assets/windows/native_assets.dart'
+    show cCompilerConfigWindows;
 
 import '../../../src/common.dart';
 import '../../../src/context.dart';
@@ -30,7 +31,6 @@
   late FileSystem fileSystem;
   late BufferLogger logger;
   late Uri projectUri;
-  late String runPackageName;
 
   setUp(() {
     processManager = FakeProcessManager.empty();
@@ -47,7 +47,6 @@
     );
     environment.buildDir.createSync(recursive: true);
     projectUri = environment.projectDir.uri;
-    runPackageName = environment.projectDir.basename;
   });
 
   for (final flutterTester in <bool>[false, true]) {
@@ -96,7 +95,7 @@
           final TargetPlatform targetPlatform = flutterTester
               ? TargetPlatform.tester
               : TargetPlatform.windows_x64;
-          final DartBuildResult dartBuildResult = await runFlutterSpecificDartBuild(
+          final DartHookResult dartHookResult = await runFlutterSpecificHooks(
             environmentDefines: environmentDefines,
             targetPlatform: targetPlatform,
             projectUri: projectUri,
@@ -110,7 +109,7 @@
                 )
               : nonFlutterTesterAssetUri;
           await installCodeAssets(
-            dartBuildResult: dartBuildResult,
+            dartHookResult: dartHookResult,
             environmentDefines: environmentDefines,
             targetPlatform: targetPlatform,
             projectUri: projectUri,
@@ -122,8 +121,8 @@
           expect(
             (globals.logger as BufferLogger).traceText,
             stringContainsInOrder(<String>[
-              'Building native assets for $expectedOS $expectedArch.',
-              'Building native assets for $expectedOS $expectedArch done.',
+              'Building native assets for ${expectedOS}_$expectedArch.',
+              'Building native assets for ${expectedOS}_$expectedArch done.',
             ]),
           );
           expect(
@@ -237,28 +236,7 @@
       );
       await msvcBinDir.create(recursive: true);
 
-      final File packageConfigFile = writePackageConfigFiles(
-        directory: fileSystem.directory(projectUri),
-        mainLibName: 'my_app',
-      );
-      final PackageConfig packageConfig = await loadPackageConfigWithLogging(
-        packageConfigFile,
-        logger: environment.logger,
-      );
-      final File pubspecFile = fileSystem.file(projectUri.resolve('pubspec.yaml'));
-      await pubspecFile.writeAsString('''
-name: my_app
-''');
-      final FlutterNativeAssetsBuildRunner runner = FlutterNativeAssetsBuildRunnerImpl(
-        packageConfigFile.path,
-        packageConfig,
-        fileSystem,
-        logger,
-        runPackageName,
-        includeDevDependencies: false,
-        pubspecFile.path,
-      );
-      final CCompilerConfig result = (await runner.cCompilerConfig)!;
+      final CCompilerConfig result = (await cCompilerConfigWindows())!;
       expect(result.compiler.toFilePath(), msvcBinDir.childFile('cl.exe').uri.toFilePath());
       expect(result.archiver.toFilePath(), msvcBinDir.childFile('lib.exe').uri.toFilePath());
       expect(result.linker.toFilePath(), msvcBinDir.childFile('link.exe').uri.toFilePath());
diff --git a/packages/flutter_tools/test/integration.shard/isolated/dart_data_asset_test.dart b/packages/flutter_tools/test/integration.shard/isolated/dart_data_asset_test.dart
new file mode 100644
index 0000000..51c0fc4
--- /dev/null
+++ b/packages/flutter_tools/test/integration.shard/isolated/dart_data_asset_test.dart
@@ -0,0 +1,422 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This test exercises the embedding of the native assets mapping in dill files.
+// An initial dill file is created by `flutter assemble` and used for running
+// the application. This dill must contain the mapping.
+// When doing hot reload, this mapping must stay in place.
+// When doing a hot restart, a new dill file is pushed. This dill file must also
+// contain the native assets mapping.
+// When doing a hot reload, this mapping must stay in place.
+
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:file/file.dart';
+import 'package:yaml_edit/yaml_edit.dart' show YamlEditor;
+
+import '../../src/common.dart';
+import '../test_utils.dart' show ProcessResultMatcher, fileSystem, flutterBin, platform;
+import '../transition_test_utils.dart';
+import 'native_assets_test_utils.dart';
+
+final String hostOs = platform.operatingSystem;
+const packageName = 'data_asset_example';
+const packageNameDependency = 'data_asset_dependency';
+
+void main() {
+  if (!platform.isMacOS && !platform.isLinux && !platform.isWindows) {
+    return;
+  }
+  setUpAll(() async {
+    processManager
+      ..runSync(<String>[flutterBin, 'config', '--enable-native-assets'])
+      ..runSync(<String>[flutterBin, 'config', '--enable-dart-data-assets']);
+  });
+
+  late Directory tempDirectory;
+  late Directory root;
+  late Directory rootDependency;
+
+  setUp(() async {
+    // Do not reuse project structure to be able to make local changes
+    tempDirectory = fileSystem.directory(
+      fileSystem.systemTempDirectory.createTempSync().resolveSymbolicLinksSync(),
+    );
+    root = createAppWithName(packageName, tempDirectory);
+    await createDataAssetApp(packageName, root);
+
+    rootDependency = createAppWithName(packageNameDependency, tempDirectory);
+  });
+
+  tearDown(() {
+    tryToDelete(tempDirectory);
+  });
+
+  group('dart data assets', () {
+    // NOTE: flutter-tester doesn't support profile/release mode.
+    // NOTE: flutter web doesn't allow cpaturing print()s in profile/release
+    // NOTE: flutter web doens't allow adding assets on hot-restart
+    final devices = <String>[hostOs, 'chrome', 'flutter-tester'];
+    final modes = <String>['debug', 'release'];
+
+    for (final mode in modes) {
+      for (final device in devices) {
+        final isFlutterTester = device == 'flutter-tester';
+        final isWeb = device == 'chrome';
+        final isDebug = mode == 'debug';
+
+        // This test relies on running the flutter app and capturing `print()`s
+        // the app prints to determine if the test succeeded.
+        // `flutter run --profile/release` on the web doesn't support capturing
+        // prints
+        // -> See https://github.com/flutter/flutter/issues/159668
+        if (isWeb && !isDebug) {
+          continue;
+        }
+
+        // Flutter tester only supports debug mode.
+        if (isFlutterTester && !isDebug) {
+          continue;
+        }
+
+        testWithoutContext('flutter run on $device --$mode', () async {
+          final performRestart = isDebug;
+          final performReload = isDebug;
+
+          final assets = <String, String>{'id1': 'content1', 'id2': 'content2'};
+          writeAssets(assets, root);
+          writeHookLibrary(root, assets, available: <String>['id1']);
+          writeHelperLibrary(root, 'version1', assets.keys.toList());
+
+          final ProcessTestResult result = await runFlutter(
+            <String>['run', '-v', '-d', device, '--$mode'],
+            root.path,
+            <Transition>[
+              Barrier.contains('Launching lib${Platform.pathSeparator}main.dart on'),
+              Multiple.contains(
+                <Pattern>[
+                  // The flutter tool will print it's ready to accept keys (e.g.
+                  // q=quit, ...)
+                  // (This can be racy with app already running and printing)
+                  'Flutter run key command',
+
+                  // Once the app runs it will print whether it found assets.
+                  'VERSION: version1',
+                  'FOUND "packages/data_asset_example/id1": "content1".',
+                  'NOT-FOUND "packages/data_asset_example/id2".',
+                ],
+                handler: (_) {
+                  if (!performRestart) {
+                    return 'q';
+                  }
+                  // Now we trigger a hot-restart with new assets & new
+                  // application code, we make the build hook now emit also the
+                  // `id2` data asset.
+                  writeAssets(assets, root);
+                  writeHookLibrary(root, assets, available: <String>['id1', 'id2']);
+                  writeHelperLibrary(root, 'afterRestart', assets.keys.toList());
+                  return 'R';
+                },
+              ),
+              if (performRestart)
+                Multiple.contains(
+                  <Pattern>[
+                    // Once the app runs it will print whether it found assets.
+                    // We expect it to having found the new `id2` now.
+                    'VERSION: afterRestart',
+                    'FOUND "packages/data_asset_example/id1": "content1".',
+
+                    // Flutter web doesn't support new assets on hot-restart atm
+                    // -> See https://github.com/flutter/flutter/issues/137265
+                    if (isWeb)
+                      'NOT-FOUND "packages/data_asset_example/id2".'
+                    else
+                      'FOUND "packages/data_asset_example/id2": "content2".',
+                    if (isWeb) 'Successful hot restart' else 'Hot restart performed',
+                  ],
+                  handler: (_) {
+                    if (!performReload) {
+                      return 'q';
+                    }
+                    // Now we trigger a hot-reload with new assets & new
+                    // application code, we make the build hook now emit also the
+                    // `id3` data asset (but not `id4`).
+                    assets['id3'] = 'content3';
+                    assets['id4'] = 'content4';
+                    writeAssets(assets, root);
+                    writeHookLibrary(root, assets, available: <String>['id1', 'id2', 'id3']);
+                    writeHelperLibrary(root, 'afterReload', assets.keys.toList());
+                    return 'r';
+                  },
+                ),
+              if (performReload)
+                Multiple.contains(
+                  <Pattern>[
+                    // Once the app runs it will print whether it found assets.
+                    'VERSION: afterReload',
+                    'FOUND "packages/data_asset_example/id1": "content1".',
+                    // Flutter web doesn't support new assets on hot-reload atm
+                    // -> See https://github.com/flutter/flutter/issues/137265
+                    if (isWeb) ...<Pattern>[
+                      'NOT-FOUND "packages/data_asset_example/id2".',
+                      'NOT-FOUND "packages/data_asset_example/id3".',
+                    ] else ...<Pattern>[
+                      'FOUND "packages/data_asset_example/id2": "content2".',
+                      'FOUND "packages/data_asset_example/id3": "content3".',
+                    ],
+                    'NOT-FOUND "packages/data_asset_example/id4".',
+                    if (isWeb) 'Successful hot reload' else 'Hot reload performed',
+                  ],
+                  handler: (_) {
+                    return 'q'; // quit
+                  },
+                ),
+              Barrier.contains('Application finished.'),
+            ],
+          );
+          if (result.exitCode != 0) {
+            throw Exception(
+              'flutter run failed: ${result.exitCode}\n${result.stderr}\n${result.stdout}',
+            );
+          }
+        });
+      }
+    }
+
+    for (final target in <String>[hostOs, 'web']) {
+      testWithoutContext('flutter build $target', () async {
+        final assets = <String, String>{'id1': 'content1', 'id2': 'content2'};
+        final available = <String>['id1'];
+        writeAssets(assets, root);
+        writeHookLibrary(root, assets, available: available);
+        writeHelperLibrary(root, 'version1', assets.keys.toList());
+
+        final ProcessTestResult result = await runFlutter(
+          <String>['build', '-v', target],
+          root.path,
+          <Transition>[Barrier.contains('Built build${Platform.pathSeparator}$target')],
+        );
+        if (result.exitCode != 0) {
+          throw Exception(
+            'flutter build failed: ${result.exitCode}\n${result.stderr}\n${result.stdout}',
+          );
+        }
+        final Directory buildTargetDir = root.childDirectory('build').childDirectory(target);
+
+        final List<File> manifestFiles = buildTargetDir
+            .listSync(recursive: true)
+            .whereType<File>()
+            .where((File file) => file.path.endsWith('AssetManifest.json'))
+            .toList();
+
+        if (manifestFiles.isEmpty) {
+          throw Exception('Expected a `AssetManifest.json` to be avilable in the $buildTargetDir.');
+        }
+        for (final manifestFile in manifestFiles) {
+          final manifest = json.decode(manifestFile.readAsStringSync()) as Map<String, Object?>;
+          for (final id in available) {
+            final key = 'packages/$packageName/$id';
+            final entry = manifest[key]! as List<Object?>;
+            expect(entry, equals(<String>[key]));
+
+            final File file = manifestFile.parent.childFile(key);
+            expect(file.readAsStringSync(), assets[id]);
+          }
+        }
+      });
+    }
+
+    for (final target in <String>[hostOs, 'web']) {
+      testWithoutContext('flutter build $target with conflicting assets', () async {
+        final assets = <String, String>{'id1.txt': 'content1', 'id2.txt': 'content2'};
+        final available = <String>['id1.txt'];
+        writeAssets(assets, root);
+        writeAssets(assets, rootDependency);
+        writeHookLibrary(root, assets, available: available);
+        writeHookLibrary(rootDependency, assets, available: available);
+        writeHelperLibrary(root, 'version1', assets.keys.toList());
+
+        await modifyPubspec(root, (YamlEditor editor) {
+          editor.update(
+            <String>['dependencies', packageNameDependency],
+            <String, String>{'path': '../$packageNameDependency'},
+          );
+        });
+
+        await modifyPubspec(rootDependency, (YamlEditor editor) {
+          editor
+            ..update(<String>['flutter', 'assets'], <String>[assets.keys.first])
+            ..update(<String>['dependencies'], <String, String>{'native_assets_cli': '^0.17.0'});
+        });
+
+        final ProcessTestResult result = await runFlutter(
+          <String>['build', '-v', target],
+          root.path,
+          <Transition>[
+            Barrier.contains(
+              'Conflicting assets: The asset "asset: packages/data_asset_dependency/id1.txt" was declared in the pubspec and the hook',
+            ),
+          ],
+        );
+        expect(result.exitCode, isNonZero);
+      });
+    }
+  });
+}
+
+Future<void> modifyPubspec(Directory dir, void Function(YamlEditor editor) modify) async {
+  final File pubspecFile = dir.childFile('pubspec.yaml');
+  final String content = await pubspecFile.readAsString();
+  final yamlEditor = YamlEditor(content);
+  modify(yamlEditor);
+  pubspecFile.writeAsStringSync(yamlEditor.toString());
+}
+
+Future<void> createDataAssetApp(String packageName, Directory root) async {
+  await modifyPubspec(
+    root,
+    (YamlEditor editor) =>
+        editor.update(<String>['dependencies'], <String, String>{'native_assets_cli': '^0.17.0'}),
+  );
+
+  final File pubspecFile = root.childFile('pubspec.yaml');
+  await pinDependencies(pubspecFile);
+
+  final File mainFile = root.childDirectory('lib').childFile('main.dart');
+  writeFile(mainFile, '''
+        import 'dart:async';
+
+        import 'package:flutter/material.dart';
+
+        import 'helper.dart';
+
+        void main() {
+          runApp(const MyApp());
+        }
+
+        class MyApp extends StatelessWidget {
+          const MyApp({super.key});
+
+          @override
+          Widget build(BuildContext context) {
+            bool first = true;
+            Timer.periodic(const Duration(seconds: 1), (_) async {
+              // Delay to give the `flutter run` command time to connect and
+              // setup `print()` capturing logic (especially on web it won't be
+              // able to intercept prints until it has connected to DevTools).
+              if (first) {
+                await Future.delayed(const Duration(seconds: 5));
+              }
+              dumpAssets();
+            });
+
+            return MaterialApp(
+              title: 'Flutter Demo',
+              home: Scaffold(
+                body: Text('Hello world'),
+              ),
+            );
+          }
+        }
+    ''');
+
+  expect(
+    await processManager.run(<String>[flutterBin, 'pub', 'get'], workingDirectory: root.path),
+    const ProcessResultMatcher(),
+  );
+}
+
+Directory createAppWithName(String packageName, Directory tempDirectory) {
+  final ProcessResult result = processManager.runSync(<String>[
+    flutterBin,
+    'create',
+    '--no-pub',
+    packageName,
+  ], workingDirectory: tempDirectory.path);
+  expect(result, const ProcessResultMatcher());
+  final Directory packageDirectory = tempDirectory.childDirectory(packageName);
+
+  expect(
+    processManager.runSync(<String>[
+      flutterBin,
+      'pub',
+      'get',
+    ], workingDirectory: packageDirectory.path),
+    const ProcessResultMatcher(),
+  );
+  return packageDirectory;
+}
+
+void writeHookLibrary(
+  Directory root,
+  Map<String, String> dataAssets, {
+  required List<String> available,
+}) {
+  final File hookFile = root.childDirectory('hook').childFile('build.dart');
+  available = <String>[for (final String id in available) '"$id"'];
+  writeFile(hookFile, '''
+import 'package:native_assets_cli/data_assets.dart';
+
+void main(List<String> args) async {
+  await build(args, (BuildInput input, BuildOutputBuilder output) async {
+    if (input.config.buildAssetTypes.contains('data_assets/data')) {
+      for (final id in $available) {
+        output.assets.data.add(
+          DataAsset(
+            package: input.packageName,
+            name: id,
+            file: input.packageRoot.resolve(id),
+          ),
+        );
+      }
+    }
+  });
+}
+''');
+}
+
+void writeAssets(Map<String, String> dataAssets, Directory root) {
+  dataAssets.forEach((String id, String content) {
+    writeFile(root.childFile(id), content);
+  });
+}
+
+void writeHelperLibrary(Directory root, String version, List<String> assetIds) {
+  assetIds = <String>[for (final String id in assetIds) '"packages/$packageName/$id"'];
+  final File helperFile = root.childDirectory('lib').childFile('helper.dart');
+  writeFile(helperFile, '''
+      import 'package:flutter/services.dart' show rootBundle;
+
+      // Only run the code once, but after hot-restart & hot-reload we want to
+      // run it again.
+      bool $version = false;
+      void dumpAssets() async {
+        if ($version) return;
+        $version = true;
+
+        final found = <String, String>{};
+        final notFound = <String>[];
+        for (final String assetId in $assetIds) {
+          try {
+            found[assetId] = await rootBundle.loadString(assetId);
+          } catch (e, s) {
+            print('EXCEPTION \$e');
+            notFound.add(assetId);
+          }
+        }
+        print('VERSION: $version');
+        for (final MapEntry(:key, :value) in found.entries) {
+          print('FOUND "\$key": "\$value".');
+        }
+        for (final id in notFound) {
+          print('NOT-FOUND "\$id".');
+        }
+      }
+      ''');
+}
+
+void writeFile(File file, String content) => file
+  ..createSync(recursive: true)
+  ..writeAsStringSync(content);
diff --git a/packages/flutter_tools/test/integration.shard/transition_test_utils.dart b/packages/flutter_tools/test/integration.shard/transition_test_utils.dart
index 3045e3b..c84a5a7 100644
--- a/packages/flutter_tools/test/integration.shard/transition_test_utils.dart
+++ b/packages/flutter_tools/test/integration.shard/transition_test_utils.dart
@@ -235,8 +235,8 @@
   }
 
   String stamp() => '[${(clock.elapsed.inMilliseconds / 1000.0).toStringAsFixed(1).padLeft(5)}s]';
-  void processStdout(String line) {
-    final log = LogLine('stdout', stamp(), line);
+
+  void logLine(LogLine log, String line) {
     if (logging) {
       logs.add(log);
     }
@@ -280,12 +280,14 @@
     }
   }
 
-  void processStderr(String line) {
+  void processStdout(String line) {
     final log = LogLine('stdout', stamp(), line);
-    logs.add(log);
-    if (streamingLogs) {
-      log.printClearly();
-    }
+    logLine(log, line);
+  }
+
+  void processStderr(String line) {
+    final log = LogLine('stderr', stamp(), line);
+    logLine(log, line);
   }
 
   if (debug) {
diff --git a/packages/flutter_tools/test/src/fakes.dart b/packages/flutter_tools/test/src/fakes.dart
index 97d587b..b6053e7 100644
--- a/packages/flutter_tools/test/src/fakes.dart
+++ b/packages/flutter_tools/test/src/fakes.dart
@@ -500,6 +500,7 @@
     this.areCustomDevicesEnabled = false,
     this.isCliAnimationEnabled = true,
     this.isNativeAssetsEnabled = false,
+    this.isDartDataAssetsEnabled = false,
     this.isSwiftPackageManagerEnabled = false,
     this.isOmitLegacyVersionFileEnabled = false,
   });
@@ -535,6 +536,9 @@
   final bool isNativeAssetsEnabled;
 
   @override
+  final bool isDartDataAssetsEnabled;
+
+  @override
   final bool isSwiftPackageManagerEnabled;
 
   @override
@@ -570,6 +574,7 @@
     flutterCustomDevicesFeature,
     cliAnimation,
     nativeAssets,
+    dartDataAssets,
     swiftPackageManager,
     omitLegacyVersionFile,
   ];
diff --git a/packages/flutter_tools/test/web.shard/chrome_test.dart b/packages/flutter_tools/test/web.shard/chrome_test.dart
index c1a5a87..6249b6c 100644
--- a/packages/flutter_tools/test/web.shard/chrome_test.dart
+++ b/packages/flutter_tools/test/web.shard/chrome_test.dart
@@ -30,6 +30,7 @@
   '--disable-default-apps',
   '--disable-translate',
   '--disable-search-engine-choice-screen',
+  '--no-sandbox',
 ];
 
 const kCodeCache = <String>['Cache', 'Code Cache', 'GPUCache'];
@@ -537,7 +538,6 @@
           ...kChromeArgs,
           '--headless',
           '--disable-gpu',
-          '--no-sandbox',
           '--window-size=2400,1800',
           'example_url',
         ],
@@ -623,7 +623,6 @@
       ...kChromeArgs,
       '--headless',
       '--disable-gpu',
-      '--no-sandbox',
       '--window-size=2400,1800',
       'example_url',
     ];
@@ -657,7 +656,6 @@
       ...kChromeArgs,
       '--headless',
       '--disable-gpu',
-      '--no-sandbox',
       '--window-size=2400,1800',
       'example_url',
     ];
@@ -695,7 +693,6 @@
             ...kChromeArgs,
             '--headless',
             '--disable-gpu',
-            '--no-sandbox',
             '--window-size=2400,1800',
             'example_url',
           ],