How To Organize Your Flutter App Assets, Styles, Colors, Images

Flutter category image
Reading Time: 5 minutes

A large app contains many resources in different places. Learn how to organize your Flutter app assets, styles, colors, images.

In this article, we are looking at resource management in Flutter apps. A resource in this context is, for example, a style, an image, a constant, a utility, or a color that will be used within your app. I will show you ways how to organize them inside your app so that it will still be manageable despite a large codebase. The advantage is that you don’t have redundant code parts and if you want to change anything you will only have to look at one place in your source code. Let’s get started right away with some ideas on how to organize your Flutter app assets, styles, colors, images!

The examples will mostly use colors, but this is just to demonstrate the ideas. You can use it for anything like constants, utilities, resources, and so on.

Idea 1: Global declarations

The most basic approach. Just put everything into a single file as a global definition and access it from anywhere. This will get messy very soon, so consider splitting it up into several files. Also, the descriptions might get long to distinguish between a background color for a scaffold and a background color for a button for example. Good tooling is essential in this case (proper code navigation with editors like VS Code)!

Idea 2: Single class per resource type

This idea is still pretty simple. We have one class containing all colors, one containing all text styles, and so on. So if you want to access a color in code, you just do AppColors.background. The naming convention is always: type of resource dot description.

  • What type of resource do I need? I need a color → AppColors class
  • What should the resource be used for? It will be the background of a scaffold → background property

This is straightforward and easy to understand but will lead to many color definitions and similar descriptions in a single file. You’ll also need to import one file per resource which could lead to a longer import list. I’d choose this approach only for smaller apps.

The example makes use of private classes and static references to create the desired relations.

Idea 3: Single class per resource type with grouping

The next idea introduces another hierarchy level but works like the previous idea in general. However, you will need to call AppColors.scaffold.background to get the color from the previous example. This approach adds a widget level to the hierarchy so that you can cluster your colors per widget. If you don’t like the widget hierarchy idea, try it with a view, use case, or state hierarchy instead. You still have multiple files to import to access all resources but the organization is more structured than before.

Idea 4: Grouped resource hierarchy structure

Finally, we can also combine all of our resources into one single structure to simplify access even more. This reduces necessary import statements and defines a consistent access logic that can be used anywhere in the app. An example call will now look like this: Resources.colors.scaffold.background.

Tip: How to reduce import statements

You can use the export keyword to combine multiple imports into a single one. Be aware that this might be confusing to some developers if used incorrectly because it is more difficult to see where the actual code is located. Here is an example of how to do it:

Working with assets and fonts

An asset (or resource) is a file that is included in the app binary and loaded at runtime. Popular examples are images and JSON files. Assets and also fonts are stored in a custom project folder and are referenced in pubspec.yaml, so that the app knows where to load the data from. The pubspec.yaml contains examples of how to include them. For further details, check the official documentation for assets and fonts.

In this section, I want to show approaches to prevent hard-coded strings in multiple locations in your app code. A simple typo can create an exception at runtime that you might miss. So, here are two approaches to prevent such behavior.

Create a class for your assets and fonts

In the previous sections, I provided some example ideas that you can use here, too. Anything is better than using strings throughout the app. If you need to change anything you have exactly one place where to do that. A little example:

Dart
class Assets {
    static String splashImage = "assets/splash.png";
    static String saveIcon = "assets/save.png";
}

To ensure that your files exist, you can write a unit test. Here is a small example:

Dart
import 'dart:io';

void main(){
    test("Verify all used images exist", () async {
        expect(await File("assets/splash.jpg").exists(), isTrue,
	    reason: "no image for splash");
	expect(await File("assets/save.jpg").exists(), isTrue,
	    reason: "no image for save");
    });
}

If that is too much of a hassle, check the next tip.

Use the flutter_gen package to create the wrapper classes for you

The flutter_gen package contains a build runner that scans your pubspec.yaml for assets and automatically creates wrapper classes. You can use these wrappers to access the assets in your widgets. The big advantage is that the package will skip files that aren’t found on the hard disk. So, if you have a typo in your asset definition, there will be no class for that asset. You’ll immediately notice it when you want to reference it in the code.

Source code

You can find the source code on GitHub.

Conclusion

With these tips, it should be easy for you to organize your app. Choose one of these ideas or combine them to suit your specific needs. It always depends on multiple factors what the right approach is. There is no general rule for every use case.

Related articles