Back to all posts

Dynamically Load Custom Fonts in Flutter

Custom fonts can greatly enhance the visual appeal of your Flutter application. While Google Fonts offers a vast selection, sometimes you may need a specific font not available there. This article will guide you through the process of dynamically loading custom fonts in Flutter.

Why do we need Custom Fonts?

Google Fonts offer a wide variety of fonts, so why do we need custom fonts? Despite the extensive collection available through Google Fonts, there are times when using a custom font is essential to make a strong impression on the user.

To achieve this, you can easily add the custom font to your assets and use it. However, what if you need to use numerous custom fonts? Adding all these fonts directly to your app would increase the app bundle size, which is not a best practice.

To avoid this, we need to load custom fonts dynamically. This approach serves our purpose without increasing the app bundle size.

Now, how do we load custom fonts dynamically?

Load Custom Fonts Dynamically

For that we will use the FontLoader class provided by Flutter.

First, we need to download the Font!

Future<File> downloadFont() async {
  final appDir = await getApplicationDocumentsDirectory();
  final fontFile = File('${appDir.path}/Custom-FontName');
  final response = await http.get(Uri.parse('https://yourhostedservice/Custom-FontName.ttf'));
  await fontFile.writeAsBytes(response.bodyBytes);
  return fontFile;
}

Note: If you want to store the fonts in cache instead of data, you can use getTemporaryDirectory() and save the font there. The cache directory is suitable for temporary files that can be re-created or re-downloaded as needed. Be aware that files in the cache directory can be cleared by the system when the device is low on storage or when the user manually clears the app cache.

Now we will use FontLoader class to load the Font.

Future<void> loadFont() async {
  final fontFile = await downloadFont();
  final fontLoader = FontLoader('Custom FontName');
  fontLoader.addFont(getFontLoaderBytes(fontFile));
  await fontLoader.load();
}

The method getFontLoaderBytes is designed to read the bytes from a given file and return them as a ByteData object, which can be useful for various purposes, such as loading custom fonts in a Flutter application.

Here is the method:

Future<ByteData> getFontLoaderBytes(File file) async {
  final bytes = await file.readAsBytes();
  return ByteData.view(bytes.buffer);
}

Now, here is the example for how to use it:

Text(
    "Hello Flutter!",
    style: TextStyle(
        fontFamily: 'Custom FontName', //your font name that you give in Font Loader
        fontWeight: FontWeight.bold
    )
)

It's as simple as that!

However, ensure that you load the font before navigating to the screen where you intend to use it to prevent any visual glitches. Additionally, don't forget to implement robust error handling.

Wait, there's more! If we proceed this way, the font will be downloaded every time the app is opened. How can we avoid this redundancy?

Avoid Redundancy

To prevent redundant downloads, we need to store the font file locally. If a request for the same font is made again, we first check whether the font file already exists. If it does, we simply return the existing file.

Here is the Complete Code:

Download

Future<File> downloadFont({
    required String url,  // URL where the font is hosted
    required String fontName,
  }) async {
    try {
      final appDir = await getApplicationDocumentsDirectory(); // Use the path_provider package
      final fontFile = File('${appDir.path}/$fontName');
      if (await fontFile.exists()) {
        return fontFile;
      }

      final response = await http.get(Uri.parse(url));
      await fontFile.writeAsBytes(response.bodyBytes);
      return fontFile;
    } catch (e) {
      throw Exception('Failed to download the Font $e');
    }
  }

Load Font

Future<void> loadFont(
      {required String url, required String fontName}) async {
    try {
      final fontFile = await downloadFont(url: url, fontName: fontName);
      final fontLoader = FontLoader(fontName);
      fontLoader.addFont(getFontLoaderBytes(fontFile));
      await fontLoader.load();
    } catch (e) {
      throw Exception("Failed to load font: $e");
    }
  }

Get Font Loader Bytes

Future<ByteData> getFontLoaderBytes(File file) async {
    try {
      final bytes = await file.readAsBytes();
      return ByteData.view(bytes.buffer);
    } catch (e) {
      throw Exception("Failed to get font loader bytes: $e");
    }
  }

And to reiterate, always load the font before using it!

Conclusion

Dynamically loading custom fonts in Flutter helps you get the perfect look without increasing your app size. Follow the steps to download, store, and use custom fonts efficiently. Enjoy creating a visually appealing and smooth app experience!