Skip to content

Assets in Flutter Apps

Assets are the files your app ships alongside its code: images, fonts, JSON files, animations, shaders. Flutter packs them into the app when it builds, and your Dart code reads them at runtime. Because they ship inside the app binary, they’re also one of the things a Shorebird patch can’t change, so it pays to know which of your files are assets.

Flutter bundles only the files you list under flutter: assets: in pubspec.yaml (Adding assets and images):

flutter:
  assets:
    - assets/logo.png
    - assets/images/
    - assets/config.json

A directory entry includes only the files directly inside it, not its subdirectories; add each subdirectory as its own entry. Since Flutter 3.41, an entry can also be limited to some platforms with path: and platforms:.

Load them by the same path:

// An image, in a widget's build method
const Image(image: AssetImage('assets/logo.png'));

// Text, outside a widget
final json = await rootBundle.loadString('assets/config.json');

Assets from a package dependency take its name: AssetImage('icons/heart.png', package: 'my_icons'), or the path packages/my_icons/icons/heart.png.

For images, put higher-density versions in 2.0x/, 3.0x/ (and so on) folders next to the main file, and declare only the main file. Flutter bundles the variants and picks the closest one for the device’s pixel ratio: a device with a ratio of 2.7 gets 3.0x/logo.png.

Fonts are declared under flutter: fonts: and bundled the same way. Flutter also tree shakes icon fonts in release builds: MaterialIcons-Regular.otf keeps only the glyphs your Dart code uses. Using an icon you didn’t use before therefore changes the font file in the build. --no-tree-shake-icons keeps every glyph, at the cost of a larger app.

Fragment shaders listed under flutter: shaders: are compiled when you build and bundled with the assets, then loaded with FragmentProgram.fromAsset.

At build time, Flutter places your assets in the app’s asset bundle:

  • Android: assets/flutter_assets/ inside the APK or app bundle.
  • iOS and macOS: flutter_assets/ inside App.framework.
  • Windows and Linux: data/flutter_assets/ next to the executable.

Each bundle also carries an AssetManifest listing what’s in it. Files that belong to the native app rather than to Flutter, such as the launcher icon and launch screen (android/app/src/main/res, ios/Runner/Assets.xcassets), are built into Android resources or the iOS asset catalog instead. See How Flutter builds your app for the rest of the build output.

A Shorebird patch replaces your compiled Dart code only. Every file above, from flutter_assets to Android resources and the iOS asset catalog, stays as the store release installed it. So:

  • Adding or changing an asset needs a new store release. A patch that refers to an image the release doesn’t contain fails to load it on users’ devices.
  • Removing an asset is safe if no code still uses it: the release keeps the file, and the patch just stops using it.
  • Using a new icon from an icon font changes the tree-shaken font. The release’s font doesn’t have the glyph, so the icon renders incorrectly in the patched app.

shorebird patch compares the new build’s assets with the release’s and stops to ask before publishing when assets were added or changed:

[WARN] Your app contains asset changes, which will not be included in the patch.

What each listed file means, and when it’s safe to continue, is covered in Your app contains asset changes warning when creating a patch.

Keeping content changeable between releases

Section titled “Keeping content changeable between releases”

Some content changes more often than your store releases. A few patterns keep it updatable:

  • Put text and configuration in Dart code. Strings generated by Flutter’s internationalization tooling (gen-l10n) compile into Dart code, so translation fixes can ship in a patch. So can constants and --dart-define values.
  • Draw simple graphics in code. A CustomPainter, a gradient or a widget composition is Dart code; an SVG or PNG file is an asset.
  • Load content that changes often from the network, with Image.network or a caching package, instead of bundling it. The same applies to Lottie or Rive animations, which load from a URL as well as from an asset (see Flutter Animations).
  • Add assets in a release ahead of the code that uses them, when you know a feature is coming.
  • Keep every icon glyph if your patches often start using new Material icons. --no-tree-shake-icons is a Flutter flag, so pass it after --, to the release and to every patch: shorebird release android -- --no-tree-shake-icons.