In Flutter, how do you bundle a custom font family in pubspec.yaml and apply it app-wide and to a single Text widget?
answer
- a list under the flutter: key
- family, fonts, asset
- paths relative to pubspec.yaml
- one theme constructor argument
- a per-widget style override
basics
~20 sCopy the font files into the project, list them in pubspec.yaml under flutter: fonts: as a family with asset entries, then pass that family name as fontFamily to ThemeData for the whole app or to a TextStyle for one widget.
solid answer
~40 sPut the `.ttf`/`.otf` files in a folder such as `fonts/`, then declare them in `pubspec.yaml` under `flutter:` → `fonts:` as a `family` with a `fonts` list of `asset` entries, adding `weight` or `style` on the files that are not the regular upright face. The `flutter` tool validates that block, bundles the files and lists them in `FontManifest.json`; they do not also go under `assets:`. Code refers to the font only by its family name: `ThemeData(fontFamily: 'Scribbly')` applies it to the theme's text themes so most Material text picks it up, and `TextStyle(fontFamily: 'Scribbly')` on a `Text` overrides one widget. A family name that matches nothing is not an error — the text silently renders in a fallback font — so check new fonts on a device.
code
yaml · 10 linesflutter:
uses-material-design: true
fonts:
- family: Scribbly
fonts:
- asset: fonts/Scribbly-Light.ttf
weight: 300
- asset: fonts/Scribbly-Regular.ttf
- asset: fonts/Scribbly-Bold.ttf
weight: 700go deeper
Recall the exact pubspec shape — flutter:, fonts:, family, fonts, asset — and the two places a family name is used: ThemeData for the app, TextStyle for one widget.
Explain what the build does with the block: validation, bundling, FontManifest.json, registration by family name, and why a wrong name falls back silently instead of failing.
Show you guard against the silent fallback: a checklist or visual review for new faces, one family per brand voice, and no file names or package prefixes scattered through widget code.
Frame the font as a product dependency: licence terms for bundling, which faces ship at all, and whether a single brand family belongs in a shared package every app depends on.
## Typefaces, font files and family names A **typeface** is a design — say the hand-lettered face a children's book app uses for its stories. A **font file** is one concrete member of it: Light, Regular, Bold. Flutter does not install your custom faces into the operating system. It ships the files inside the app's **asset bundle** and registers them with the engine under a **family name** that you choose. Every piece of Dart code then refers to that family name, never to a file path. ## Declaring the family in pubspec.yaml Fonts are declared under the top-level `flutter:` key of `pubspec.yaml`, in a `fonts:` list with one entry per family: ```yaml flutter: fonts: - family: Scribbly fonts: - asset: fonts/Scribbly-Light.ttf weight: 300 - asset: fonts/Scribbly-Regular.ttf - asset: fonts/Scribbly-Bold.ttf weight: 700 ``` - **`family`** — the name Dart code uses. It need not match the file name or the name stored inside the font. - **`fonts`** — the files that belong to the family. - **`asset`** — the path to one file, **relative to `pubspec.yaml`**. - **`weight`** and **`style`** — optional descriptors for the files that are not the regular upright face. `weight` accepts 100 to 900 in steps of 100; `style` accepts `normal` or `italic`. The `flutter` tool validates this block on every build. An unknown key produces an error such as `Unexpected child "…" found under "fonts".`, and a weight such as 650 is rejected. The tool then copies the files into the bundle and writes a generated `FontManifest.json`, which the engine reads at startup to register each family. Font files therefore do **not** need repeating under `assets:`. Flutter supports `.ttf`, `.otf` and `.ttc` files. `.woff` and `.woff2` are not supported on every platform, so keep web-only formats out of a mobile app. ## Applying it to the whole app ```dart MaterialApp( theme: ThemeData(fontFamily: 'Scribbly'), home: const StoryShelf(), ) ``` The `ThemeData` factory applies the family to its `textTheme` and `primaryTextTheme` through `TextTheme.apply`, so Material components that take their text style from the theme — app bar titles, buttons, list tiles, body text — render in it. Two details follow from how that works: 1. `fontFamily` is only a **constructor argument**. `ThemeData` keeps no `fontFamily` field and `copyWith` has no such parameter; to change the family of an existing theme you change its text themes. 2. A widget that passes its own `TextStyle` with a different `fontFamily` keeps it; the theme only fills in what the widget leaves unset. ## Applying it to one widget ```dart const Text('Once upon a time', style: TextStyle(fontFamily: 'Scribbly')) ``` `Text` merges this style over the ambient default text style, so only the fields you set change: size and colour still come from the theme. Use this for one-off elements — a title page, a speech bubble — and the theme for the app's default voice. ## Mistakes interviewers listen for 1. Declaring fonts under `dependencies:` or only under `assets:`. Neither registers a family. 2. Passing a file name such as `Scribbly-Bold.ttf` as `fontFamily`. The family name is the only key. 3. Mis-indenting the YAML so `fonts:` no longer sits under `flutter:`. 4. Assuming a typo fails loudly. The engine finds no family, falls back through `fontFamilyFallback` and then the platform default, and draws the text anyway — the only symptom is the wrong look. 5. Adding a `weight` of 900 to a file that is really bold and expecting heavier text. The key describes a file; it does not transform it. ## Checking that it worked Because a wrong name never fails, verify a new family deliberately. Run the app after `flutter pub get` picks up the edited `pubspec.yaml` and compare a sample screen with the design: handwriting faces are easy to tell apart from the platform default, but a geometric sans can pass for Roboto at a glance. A quick trick is to render one word in each declared weight side by side on a debug screen; a weight that looks identical to its neighbour usually points at a missing or mislabelled file. ## Where a font can come from | Source | Declared where | Selected in code with | |---|---|---| | Files in the app | the app's `flutter: fonts:` | `TextStyle(fontFamily: 'Scribbly')` | | A package that declares the family | the package's own `pubspec.yaml` | `TextStyle(fontFamily: 'Scribbly', package: 'story_fonts')` | | The `google_fonts` package | nothing, or files under `assets:` | `GoogleFonts.lato()` and similar | The first row is the default for a product's brand face: the font is available from the first frame, works offline and costs only its file size.
- Do the font files also have to be listed under assets: in pubspec.yaml?No. Files named in `flutter: fonts:` are bundled by the `flutter` tool and recorded in `FontManifest.json`, which the engine reads to register the family. Listing them under `assets:` as well only makes them loadable as raw bytes. The exception is the `google_fonts` package, which looks for its files under `assets:` by file name instead of through a declared family.
- What happens when a TextStyle's fontFamily matches no declared family?Nothing fails. The engine finds no registered family, tries `fontFamilyFallback` in order, then the platform's default font, and draws the text. Registered family names are looked up with ASCII case folded, but any other mismatch — a missing `package:` argument, a typo — just produces the wrong typeface, so verify new fonts visually on a device.
- Why does ThemeData.copyWith(fontFamily: 'Scribbly') not compile?`fontFamily` exists only as an argument of the `ThemeData` factory, which applies it to `textTheme` and `primaryTextTheme` and then discards it; the class has no `fontFamily` field, so `copyWith` has nothing to copy. To change an existing theme's family, apply it to the text themes, for example `theme.textTheme.apply(fontFamily: 'Scribbly')`, and pass the result to `copyWith`.
saying these in an interview costs you the question
- Custom fonts are declared under dependencies: in pubspec.yaml.
- Every font file must also be listed under assets: or it will not load.
- fontFamily takes the file name, such as Scribbly-Bold.ttf.
- A misspelled fontFamily throws an exception when the text is laid out.
- ThemeData.copyWith(fontFamily:) swaps the app font on an existing theme.