With Flutter's google_fonts package, how are fonts fetched and cached at runtime, and when should a release build bundle the font files instead?
answer
- three places, in order
- assets, then disk, then network
- hash-checked download
- allowRuntimeFetching defaults to true
- pendingFonts against the swap
basics
~20 sgoogle_fonts looks for each face in the app's assets, then a device cache, then downloads it over HTTP and caches it. Ship the files as assets and set allowRuntimeFetching to false when text must render offline and without a font swap.
solid answer
~40 s`GoogleFonts.lato()` returns a `TextStyle` whose face `google_fonts` loads through `FontLoader`, trying three sources per family and variant: a matching file in the app's assets (named like `Lato-Bold.ttf`, listed under `assets:`, not `fonts:`), a copy cached in the application support directory, and finally an HTTP download that is checked against a SHA-256 hash and length before it is cached. `GoogleFonts.config.allowRuntimeFetching` defaults to `true`; set to `false`, a missing asset throws an exception naming the file. Until a download finishes the text renders in a fallback font, so the UI visibly swaps; `GoogleFonts.pendingFonts()` with a `FutureBuilder` hides that. Fetching suits prototyping and trims download size; a release that must work offline — a children's book read on a plane — bundles the files and disables fetching. Version 9.0.0 adds `GoogleFontsLite` for tree-shaking.
code
dart · 18 linesimport 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart';
void main() {
// Bundled files under assets/google_fonts/ only; never download in production.
GoogleFonts.config.allowRuntimeFetching = false;
LicenseRegistry.addLicense(() async* {
final String license = await rootBundle.loadString('assets/google_fonts/OFL.txt');
yield LicenseEntryWithLineBreaks(<String>['google_fonts'], license);
});
runApp(
MaterialApp(
theme: ThemeData(textTheme: GoogleFonts.latoTextTheme()),
home: const Scaffold(body: Center(child: Text('Chapter One'))),
),
);
}go deeper
Know that google_fonts can download fonts at runtime and that bundled files work offline.
Walk through the assets, device-cache and network order, and why the first launch swaps fonts.
Make release builds deterministic: bundle the faces, disable fetching, register licences and verify the files are matched by name.
Weigh download size against offline correctness and a runtime third-party request, and set one policy for every app in the portfolio.
## Two ways to get a typeface into a Flutter app A font declared in `pubspec.yaml` under `flutter: fonts:` is **bundled**: the files are in the app package, the engine registers the family at startup, and text renders in it from the first frame. The `google_fonts` package offers a different route: call a method named after a family on fonts.google.com and get a `TextStyle`, while the package takes care of finding the file. Understanding where it finds the file is the interview. ```dart Text('Chapter One', style: GoogleFonts.lato(fontWeight: FontWeight.w700)) ``` ## The load order, per family and variant Each call names a family and a **variant** (weight plus style). The package picks the closest variant the family offers, starts loading it through `FontLoader`, and returns a style pointing at a generated family name, with the plain family name as its fallback. Loading tries three sources in order: 1. **App assets.** It reads the asset manifest and looks for a file whose name ends with the API file name, `<Family>-<Variant>`, such as `Lato-Bold.ttf` or `Lato-Italic.ttf`, in `.ttf` or `.otf` (web also accepts `.woff2` and `.woff`). These files go under `assets:`, **not** `fonts:`, and must keep their original names. 2. **Device cache.** On mobile and desktop it checks the application support directory for a previously downloaded copy with the expected hash. 3. **Network.** If `GoogleFonts.config.allowRuntimeFetching` is `true` — the default — it downloads the file, rejects it unless its **SHA-256 hash and length** match the values generated into the package, and saves it to the cache for next time. If fetching is disabled and no asset matches, the load throws an exception whose message begins `GoogleFonts.config.allowRuntimeFetching is false but font … was not found in the application assets`. ## What the user sees while a font downloads The returned style is usable immediately, so the first frames render in the fallback font and **swap** when the download completes — a visible jump that also shifts line breaks. `GoogleFonts.pendingFonts()` returns a future that completes when the requested fonts have loaded. Create it once, in `initState`, and pass it to a `FutureBuilder`; creating the future inside `build` restarts it on every rebuild. Bundled files avoid the swap entirely because the asset source is checked first. ## Fetch or bundle | Concern | Runtime fetching | Bundled files | |---|---|---| | App download size | smaller | grows by every face shipped | | First launch offline | fallback font until online | correct from the first frame | | Font swap on first use | yes, unless you wait on `pendingFonts()` | no | | Network permission | needed; on macOS the `com.apple.security.network.client` entitlement | none | | Third-party request at runtime | yes, to Google's font servers | none | The README's own guidance is the useful rule: fetching is ideal during development, where you can swap families with hot reload, and can be used in production to save size; once the fonts are settled, bundle them. A children's book app, read on a plane with no signal, is the clear bundling case. ## Bundling correctly - Download only the weights and styles you use; the README maps `FontWeight.w100` … `w900` to names from `Thin` to `Black`. - Keep the file names unchanged; matching is by name. - List the folder under `assets:` in `pubspec.yaml`. - Set `GoogleFonts.config.allowRuntimeFetching = false` early in `main`, so a missing file fails loudly in testing instead of silently downloading in production. - Register the font's licence, such as `OFL.txt`, with `LicenseRegistry.addLicense` so it appears on the licences page. ## Version notes for google_fonts 9.0.0 Version 9.0.0 requires Flutter 3.47 and Dart 3.13 and moves to the standalone `material_ui` and `cupertino_ui` packages. It adds `GoogleFontsLite`, imported from `package:google_fonts/google_fonts_lite.dart`, which exposes only dynamic lookups such as `GoogleFontsLite.getFont('Lato')`. Importing only the lite entry point lets the compiler **tree-shake** the generated per-family methods, which the full `google_fonts.dart` import keeps. The old `Config` class is deprecated in favour of `GoogleFontsConfig`. ## Testing code that uses google_fonts Widget tests have no network, so a test that renders `GoogleFonts.lato()` with fetching enabled logs a load failure. The package's example directory shows the supported set-ups; the simplest is the same policy as a release build — ship the files as assets and turn runtime fetching off — so tests and production resolve fonts the same way.
- You bundled Lato under assets/google_fonts/ but the app still downloads it. What is wrong?The package matches assets by file name ending in `<Family>-<Variant>`, such as `Lato-Bold.ttf`. A renamed file, a missing `assets:` entry, or a variant you requested but did not ship all miss the asset and fall through to the network. Setting `allowRuntimeFetching = false` turns the miss into an exception that names the file.
- How do you stop the visible font swap on a screen that uses google_fonts?Bundle the files so they load from assets first, or wait for loading: create `GoogleFonts.pendingFonts([...])` once in `initState` and gate the text behind a `FutureBuilder`. Creating the future inside `build` restarts the wait on every rebuild.
saying these in an interview costs you the question
- google_fonts always downloads fonts, even when the files are bundled.
- Bundled google_fonts files must be declared under flutter: fonts:.
- allowRuntimeFetching defaults to false, so releases never hit the network.
- Renaming the downloaded files is harmless because fonts are matched by family.
- Fetched fonts are used without any integrity check on the downloaded bytes.