Flutter 3.19 finally bundles assets per flavor, and nobody uses it
Since Flutter 3.19, pubspec.yaml can exclude out-of-flavor assets from the bundle. Lighter builds with zero code changes, and two limits to know.
One white-label app, five brands, five flavors. And inside every shipped APK: the logos, splash screens and illustrations of the four other brands.

This is the kind of thing I find by decompiling an APK during an audit. Nobody had done anything wrong: until recently, Flutter simply offered nothing for this.
The problem
pubspec.yaml declared all the assets, all five brand folders included. The flavor system (Android product flavors + Xcode schemes) differentiated code, config, app icon. But pubspec's assets: section knew nothing about flavors: everything went into every bundle.
Concrete consequences: megabytes of dead weight per build, and brand B's assets readable by anyone decompiling brand A's APK. For white-label work where the clients compete with each other, that matters more than size.
The native solution since Flutter 3.19
Since Flutter 3.19, an asset entry can declare a flavors subfield:
flutter:
assets:
- assets/common/
- path: assets/brand_a/
flavors:
- brand_a
- path: assets/brand_b/
flavors:
- brand_b
At build time:
flutter build apk --flavor brand_a
Only assets/common/ and assets/brand_a/ end up in the bundle. The other brands' assets do not exist in the APK. Zero Dart changes: asset paths stay the same, the packaging does the filtering.
On the audited app the win was immediate: several MB per APK and no more asset leakage between brands.
The limits to accept
It is keyed on --flavor, and nothing else. No condition on --dart-define, no variant per environment (dev/staging/prod) if your environments are not flavors, no per-platform filter. If your asset separation relies on anything other than the build flavor, this mechanism will not help you.
No Web, no Linux. The flavor concept does not exist on those targets: the flavors clause is ignored there and every asset ships. A multi-platform app relying on this filtering for cross-brand confidentiality needs to know that.
Two known traps. Asset paths built dynamically at runtime ('assets/$brand/logo.png') compile fine and then fail at load time if the asset was filtered out: filtering happens at build time, your code must mirror the same invariant. And flutter_gen generates its accessors from the full asset list, unaware of flavors: accessors for excluded assets compile but point at nothing.
Before 3.19: the workarounds you can now delete
Pre-build copy scripts moving the right folder into assets/active/, CI steps rewriting pubspec.yaml, third-party flavor management packages. All of it fragile machinery that breaks on the first flutter pub get run without the script. If you still have some, this is your chance to simplify.
Takeaways
- Since Flutter 3.19,
path:+flavors:in the assets section exclude everything outside the current flavor from the bundle. - Double win: build size and asset confidentiality between variants.
- Limits: driven only by
--flavor, and inoperative on Web and Linux.
Still have an asset-copy script in your CI? It probably predates 3.19.