İçeriğe geç / Skip to content / Zum Inhalt

Flutter: Adding Images - Assets and Image.asset

Ahmet Balaman

Last updated:

8 min read

FlutterAssetsImagepubspec.yamlResources
Flutter: Adding Images - Assets and Image.asset

To add images to your Flutter project, you first place them in a folder and then declare them as assets in the pubspec.yaml file. It sounds simple, but one of the most frequent "why doesn't this work?" questions I get in lessons comes from small mistakes in exactly these two steps. This post covers the steps, resolution support, SVG and network images, and how to track down those mistakes.

Step 1: Create an Image Folder

Create an image folder in the root directory of your Flutter project (at the same level as the lib folder). If you're seeing the project structure for the first time, I explained what each folder is for in the getting started with Flutter guide:

my_flutter_app/
  ├── lib/
  ├── assets/
  │   └── images/
  │       ├── logo.png
  │       ├── profile.jpg
  │       └── banner.png
  ├── pubspec.yaml
  └── ...

Note: The folder is usually named assets, images or assets/images. You can choose any name, but in a team the layout everyone expects is assets/images/.

Step 2: Configure pubspec.yaml

Open pubspec.yaml and add the assets declaration under the flutter: section; the full syntax for this section is described in Flutter's "Adding assets and images" guide:

Adding a Single Image

flutter:
  uses-material-design: true

  assets:
    - assets/images/logo.png

Adding a Whole Folder

flutter:
  uses-material-design: true

  assets:
    - assets/images/

If the folder path ends with /, all files in that folder are added as assets. Subfolders are not included in this declaration; the only exception is resolution folders like 2.0x and 3.0x, which we'll look at below.

Including Subfolders

flutter:
  assets:
    - assets/images/
    - assets/images/icons/
    - assets/images/backgrounds/

Example pubspec.yaml

name: my_app
description: My Flutter application.

environment:
  sdk: ^3.9.0

dependencies:
  flutter:
    sdk: flutter

flutter:
  uses-material-design: true

  assets:
    - assets/images/
    - assets/icons/

You don't need to write the sdk line under environment by hand; flutter create fills it in based on your installed Dart version. The ^3.9.0 here corresponds to the Dart version that ships with Flutter 3.35.

Step 3: Use the Images

The Image.asset Widget

Image.asset is one of the constructors listed in the Image widget's API documentation, and like the others it's an ordinary widget; you can place it inside a Row, Column or Container in the widget tree:

Image.asset('assets/images/logo.png')

Sizing

Image.asset(
  'assets/images/logo.png',
  width: 200,
  height: 200,
)

Fitting with BoxFit

Image.asset(
  'assets/images/banner.png',
  width: double.infinity,
  height: 200,
  fit: BoxFit.cover, // Cover the area
)

BoxFit values:

  • cover: Covers the whole area and crops what overflows
  • contain: The whole image is visible; empty space may remain
  • fill: Fills the area; the aspect ratio may be distorted
  • fitWidth: Fits to the width
  • fitHeight: Fits to the height
  • none: Shows the image at its original size
  • scaleDown: Behaves like contain but never scales the image up

Practical Examples

class LogoWidget extends StatelessWidget {
  const LogoWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Image.asset(
        'assets/images/logo.png',
        width: 150,
        height: 150,
      ),
    );
  }
}

Example 2: Profile Picture

const CircleAvatar(
  radius: 50,
  backgroundImage: AssetImage('assets/images/profile.jpg'),
)

CircleAvatar expects an ImageProvider, not a widget, which is why AssetImage is used here instead of Image.asset.

Example 3: Banner Image

SizedBox(
  width: double.infinity,
  height: 200,
  child: Image.asset(
    'assets/images/banner.png',
    fit: BoxFit.cover,
  ),
)

When all you need is a size, using SizedBox instead of Container is both lighter and makes your intent clearer.

Example 4: Icon Image

Row(
  children: [
    Image.asset(
      'assets/images/icons/star.png',
      width: 24,
      height: 24,
    ),
    const SizedBox(width: 8),
    const Text('My Favorites'),
  ],
)

SizedBox, which provides the gap between the icon and the text, is the most practical tool for small rows like this.

Example 5: Background Image

Container(
  decoration: const BoxDecoration(
    image: DecorationImage(
      image: AssetImage('assets/images/background.jpg'),
      fit: BoxFit.cover,
    ),
  ),
  child: const Center(
    child: Text(
      'Welcome',
      style: TextStyle(
        color: Colors.white,
        fontSize: 32,
        fontWeight: FontWeight.bold,
      ),
    ),
  ),
)

If you need to place several elements freely on top of the image, Stack and Positioned are a more flexible option.

Images for Multiple Screen Densities

You can use images in different resolutions for different screen densities:

logo.png, 2.0x/logo.png and 3.0x/logo.png are matched against the device pixel ratio

Folder Structure

assets/
  └── images/
      ├── logo.png          (1x, main file)
      ├── 1.5x/
      │   └── logo.png      (1.5x)
      ├── 2.0x/
      │   └── logo.png      (2x)
      ├── 3.0x/
      │   └── logo.png      (3x)
      └── 4.0x/
          └── logo.png      (4x)

The file in each folder has the same name; only the pixel dimensions differ. For example, if logo.png is 100x100 pixels, 2.0x/logo.png should be 200x200 and 3.0x/logo.png 300x300 pixels.

pubspec.yaml

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

Note: Flutter automatically bundles the variants in the 1.5x, 2.0x, 3.0x and 4.0x folders together with the main file; you don't need to list them separately.

Usage

Image.asset('assets/images/logo.png')

Flutter automatically picks the variant closest to the device's devicePixelRatio. In code, you always write the path of the main file. If you want the same image to fly and grow between two pages, just wrap Image.asset in a Hero; you'll find the details in the Hero animations post.

Image Naming Tips

Don't put the resolution in the file name; names like logo_low.png and logo_high.png bypass Flutter's automatic selection and force you to decide in code which file to use when. Use the 2.0x and 3.0x folders above for resolution instead.

These file naming rules make life easier:

onboarding_step1.png   // lowercase, words separated by underscores
icon_cart_24.png       // size at the end for icons used at different sizes
bg_login.jpg           // prefix by type (bg_, icon_, img_)

Avoid spaces, non-ASCII characters and capital letters. Some file systems are case-sensitive; a Logo.PNG that works on your machine may not be found in another environment.

Keep Paths in One Place

When image paths are repeated as strings across your code, a typo only shows up at runtime. Keeping the paths as constants in a single place reduces that risk:

abstract final class AppImages {
  static const logo = 'assets/images/logo.png';
  static const profile = 'assets/images/profile.jpg';
  static const banner = 'assets/images/banner.png';
}

// Usage
Image.asset(AppImages.logo)

abstract final class is Dart 3 syntax: the class can't be instantiated and can't be extended from outside, so it's clear it exists only to group constants.

Loading and Error States

Fading In (frameBuilder)

Image.asset(
  'assets/images/logo.png',
  frameBuilder: (context, child, frame, wasSynchronouslyLoaded) {
    if (wasSynchronouslyLoaded) {
      return child;
    }
    return AnimatedOpacity(
      opacity: frame == null ? 0 : 1,
      duration: const Duration(milliseconds: 300),
      curve: Curves.easeOut,
      child: child,
    );
  },
)

frame is null until the first frame has been decoded; this example keeps the image invisible until then and fades it in smoothly afterwards.

Error State

Image.asset(
  'assets/images/logo.png',
  errorBuilder: (context, error, stackTrace) {
    return Container(
      color: Colors.grey.shade300,
      child: const Center(
        child: Icon(Icons.error, color: Colors.red),
      ),
    );
  },
)

errorBuilder shows a widget of your choice instead of the red error screen when the file can't be found or decoded. For assets, this is usually a sign of a configuration mistake; before hiding the error, check the "Common Mistakes" section below.

Image.network vs. Image.asset

Image.asset (Local Image)

Image.asset('assets/images/logo.png')
  • Bundled with the app
  • Doesn't need an internet connection
  • Loads quickly
  • Increases the app size

Image.network (Image from the Internet)

Image.network('https://example.com/image.png')
  • Downloaded from the internet at runtime
  • Needs an internet connection
  • May be delayed depending on connection speed
  • Doesn't increase the app size
  • Flutter keeps downloaded images only in memory (ImageCache); they're downloaded again after the app is closed. For caching on disk, use the cached_network_image package below.

Smooth Loading with FadeInImage

To show a local placeholder while an image loads from the network:

FadeInImage.assetNetwork(
  placeholder: 'assets/images/placeholder.png',
  image: 'https://example.com/image.png',
  fit: BoxFit.cover,
)

You can get the same result with ImageProviders:

FadeInImage(
  placeholder: const AssetImage('assets/images/placeholder.png'),
  image: const NetworkImage('https://example.com/image.png'),
  fit: BoxFit.cover,
)

Because the placeholder is an asset, it appears instantly; when the network image arrives, it replaces the placeholder with a smooth transition.

SVG Images

Flutter's Image widget can't draw SVG; for that you need the flutter_svg package. Add it from the terminal:

flutter pub add flutter_svg

This command writes the package's current version into the dependencies section of pubspec.yaml for you.

Usage

import 'package:flutter_svg/flutter_svg.dart';

SvgPicture.asset(
  'assets/images/logo.svg',
  width: 100,
  height: 100,
  semanticsLabel: 'App logo',
)

The SVG file must also be declared as an asset in pubspec.yaml. To change its color, use colorFilter: const ColorFilter.mode(Colors.orange, BlendMode.srcIn).

Cached Network Image

The cached_network_image package caches network images on disk:

flutter pub add cached_network_image

Usage

import 'package:cached_network_image/cached_network_image.dart';

CachedNetworkImage(
  imageUrl: 'https://example.com/image.png',
  placeholder: (context, url) => const CircularProgressIndicator(),
  errorWidget: (context, url, error) => const Icon(Icons.error),
)

Once an image has been downloaded, it's stored on the device and shown on later launches without going to the network.

Common Mistakes

1. pubspec.yaml Indentation

# ❌ WRONG
flutter:
uses-material-design: true
assets:
  - assets/images/

# ✅ RIGHT
flutter:
  uses-material-design: true
  assets:
    - assets/images/

Indentation carries meaning in YAML. The assets: line must be indented two spaces under flutter:; don't use tab characters.

2. Wrong Path

// ❌ WRONG
Image.asset('images/logo.png')  // 'assets/' is missing

// ✅ RIGHT
Image.asset('assets/images/logo.png')

The path in code must start exactly like the path in pubspec.yaml.

3. File Name Case

// If the file is named logo.PNG:
Image.asset('assets/images/logo.png') // Not found in some environments

Your file system may be case-sensitive; match the path in code to the file name letter for letter.

4. Forgetting to Declare a Subfolder

Declaring assets/images/ doesn't cover the files inside assets/images/icons/. Add every subfolder as its own line.

5. The Hot Reload Problem

For pubspec.yaml changes:

  • Hot reload isn't enough
  • Do a hot restart (the 🔄 button)
  • If you still see "Unable to load asset", stop the app completely and start it again

Summary

Adding images in Flutter:

  1. Create a folder: assets/images/
  2. Add images: PNG, JPG, WebP; for SVG, also flutter_svg
  3. Declare them in pubspec.yaml:
    flutter:
      assets:
        - assets/images/
  4. Hot restart
  5. Use them:
    Image.asset('assets/images/logo.png')

Recommendations:

  • Create 2.0x and 3.0x folders for multiple resolutions
  • Use meaningful, lowercase file names
  • Collect image paths in a single constants class
  • Use flutter_svg for SVG images
  • Use cached_network_image to cache network images on disk

Performance Tips:

  • Don't add images much larger than the size they'll be displayed at
  • WebP usually gives smaller file sizes than PNG and JPG
  • If you show a large image in a small area, pass cacheWidth or cacheHeight so it's decoded at a smaller size in memory: Image.asset('assets/images/banner.png', cacheWidth: 600)
  • Use ListView.builder for long lists of images; only the items visible on screen are built
  • Delete unused images from the project

Images are an important part of a Flutter app's visual identity. Used at the right size, in the right place and in the right format, they make your app look both fast and polished. The app's own icon, however, is not an asset but a platform resource; I cover it separately in the creating an app icon post.

Frequently Asked Questions

I added it to pubspec.yaml but still get "Unable to load asset", why?

There are three usual suspects: the assets: line must be indented two spaces under flutter:, the path in code must be the full path such as assets/images/logo.png, and you must hot restart after changing pubspec. Also remember that subfolders have to be listed separately.

Can I put images inside the lib folder instead of assets?

Technically yes, and you still have to declare them in pubspec.yaml. But lib is for code; keeping images under assets/ at the project root keeps things tidy and is the layout your teammates will expect.

Images are making my app too large, what should I do?

Use WebP instead of PNG, shrink images to the size they'll actually be displayed at and delete unused files. For large images that are rarely shown, loading them from the network with cached_network_image instead of bundling them also cuts the size significantly.

Comments