Custom Fonts in Flutter: Bundling, Weights, and Families Done Right
Declare font families in pubspec.yaml, map weights and styles correctly, ship fonts from a package, load them at runtime with FontLoader, and pick google_fonts when it fits.
Published on • November 5, 2026
AI Assistant

A custom font is the cheapest design upgrade you can ship, and Flutter makes it a pubspec.yaml edit rather than a platform integration project. What it will not do for you is guess: Flutter never infers a weight or a style from a file name, and getting the declaration wrong silently produces synthesized, fake-looking text.
This post walks through declaring families and weights, exporting fonts from a package, loading fonts at runtime with FontLoader, licensing and glyph coverage, and when the google_fonts package is the better tool.
Declare the family in pubspec.yaml
The Use a custom font cookbook starts with the file formats. Flutter supports OpenType collections (.ttc), TrueType (.ttf) and OpenType (.otf); it does not support .woff or .woff2 on desktop platforms. Drop the files into a fonts/ folder at your project root, then declare them:
flutter:
fonts:
- family: Raleway
fonts:
- asset: fonts/Raleway-Regular.ttf
- asset: fonts/Raleway-Italic.ttf
style: italic
- family: RobotoMono
fonts:
- asset: fonts/RobotoMono-Regular.ttf
- asset: fonts/RobotoMono-Bold.ttf
weight: 700
The family value becomes the fontFamily you pass to TextStyle. Each asset is a path relative to pubspec.yaml, and every file listed here is bundled into the app’s asset bundle at build time, so fonts cost binary size on every install.
Weights and styles: declare, don’t guess
The weight property is an integer multiple of 100 between 100 and 900, matching the FontWeight constants, and style is either normal or italic. The same cookbook is blunt about the alternative: if you point weight: 700 at a file that isn’t actually bold, Flutter tries to simulate the look, and the result differs noticeably from the real font.
Text(
'Invoices',
style: TextStyle(
fontFamily: 'RobotoMono',
fontWeight: FontWeight.w700, // matches RobotoMono-Bold.ttf
fontSize: 20,
),
)
Two constraints follow from the way the mapping works. You cannot use weight to re-label a file — asking for FontWeight.w900 from a family whose boldest file is declared 700 still renders that same file. And setting fontFamily on the theme applies a family app-wide:
MaterialApp(
theme: ThemeData(fontFamily: 'Raleway'),
...
)
For variable fonts, the typography guide points at FontVariation for continuous axes and FontFeature for OpenType feature tags — both only work when the font was designed to support them.
Fonts shipped inside a package
To reuse a typeface across several apps, put the files inside the package’s lib folder (for example lib/fonts/) and prefix the asset path with packages/<package_name> in the app’s pubspec, as the Export fonts from a package recipe shows:
flutter:
fonts:
- family: Raleway
fonts:
- asset: packages/awesome_package/fonts/Raleway-Regular.ttf
- asset: packages/awesome_package/fonts/Raleway-Italic.ttf
style: italic
When a TextStyle is built with a package argument, Flutter prefixes every family in the style — including entries in fontFamilyFallback — with packages/package_name/, so the lookup resolves into the package’s own assets.
Loading fonts at runtime with FontLoader
Bundled declarations cover static assets, but fonts downloaded from your CDN, decrypted at runtime, or stored in a database need FontLoader. It is a builder: create it with the family name, register each font file as Future<ByteData>, then load().
import 'package:flutter/services.dart';
Future<void> loadBrandFont() async {
final loader = FontLoader('BrandSans')
..addFont(rootBundle.load('assets/fonts/BrandSans-Regular.ttf'))
..addFont(rootBundle.load('assets/fonts/BrandSans-Bold.ttf'));
await loader.load(); // family is available to the whole app
}
addFont takes a future, so it works naturally with data fetched over the network; load() pushes the family into the Flutter engine, after which TextStyle(fontFamily: 'BrandSans') resolves normally. Call it before the first frame that needs the typeface.
Licensing and glyph coverage
Bundling a font means shipping its license too. Fonts from fonts.google.com ship license files such as OFL.txt, and the google_fonts documentation shows how to surface them through LicenseRegistry so they appear in your app’s licenses screen:
void main() {
LicenseRegistry.addLicense(() async* {
final String license = await rootBundle.loadString('fonts/OFL.txt');
yield LicenseEntryWithLineBreaks(<String>['Raleway'], license);
});
runApp(const MyApp());
}
Coverage matters as much as licensing. The cookbook asks you to research how many languages a family supports before committing — a display font with Latin-only glyphs will silently degrade in Thai or Japanese strings. When that happens, TextStyle.fontFamilyFallback gives an ordered list of families to try next: fontFamily is checked first, then each fallback in order, then the default platform font, and finally a box is drawn if nothing matches.
Bundled fonts or google_fonts?
google_fonts (version 9.0.0, published September 2026) takes the opposite approach: it fetches font files from fonts.google.com over HTTP at runtime and caches them on the device. Matching files found in your assets are prioritized over HTTP, so you can bundle later without changing code — which is the recommended path for offline-first apps.
Two details matter in production. Importing google_fonts_lite.dart instead of google_fonts.dart lets the compiler tree-shake the generated font methods for a smaller build, and GoogleFonts.pendingFonts() combined with a FutureBuilder avoids the flash of fallback text while a font loads. Also remember that HTTP fetching needs network permissions on some platforms — macOS requires com.apple.security.network.client in the entitlements file.
Use google_fonts for prototyping and wide family coverage; use explicit fonts: declarations when the app must render identically on first launch with no network.
Takeaway: Declare every weight and style you actually ship in pubspec.yaml so Flutter never has to synthesize glyphs, load late-arriving fonts with FontLoader, register font licenses with LicenseRegistry, and reach for google_fonts only when runtime fetching and its caching model fit your offline requirements.