skip to content

In a Flutter pubspec.yaml font family, what do weight and style entries describe, and which file renders when a TextStyle requests an undeclared weight?

level: middleimportance: should knowfreq 40%

answer

  1. labels, not transformations
  2. 100 to 900 in hundreds
  3. closest face wins
  4. the file's own metadata
  5. synthetic bold looks wrong

basics

~20 s

Weight and style entries describe which face each file is — a weight from 100 to 900 and normal or italic — and cannot change it. A TextStyle gets the closest bundled face; with no close one, Flutter may fake bold or italic.

solid answer

~50 s

Each `asset` in a family can carry `weight` (100-900 in steps of 100) and `style` (`normal` or `italic`); they describe the file, they never make it heavier or slanted. At runtime a `TextStyle`'s `fontWeight` and `fontStyle` select the closest face in the family: with Light 300, Regular 400 and Bold 700 bundled, `FontWeight.w600` and `w800` both render the Bold file. If no suitable face exists, the docs warn the engine may simulate bold or italic, which looks noticeably worse than a real file. One subtlety from the 3.47 engine source: `FontManifest.json` records the declared values, but the engine registers files by family and matches on the weight and style embedded in each file — so a file with wrong internal metadata is not fixed by its pubspec entry. An italic-only family stays italic even with `FontStyle.normal`.

code

yaml · 11 lines
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
        - asset: fonts/Scribbly-Italic.ttf
          style: italic

go deeper

for a junior

Know that weight and style label files in the family and that a TextStyle's fontWeight picks among them.

for a middle

Explain closest-face matching with a worked example, the 100-900 rule for the pubspec key, and why synthesised bold or italic is a design defect.

for a senior

Diagnose a face that never appears: the engine trusts the file's internal metadata, so you inspect the file rather than the YAML.

for a principal

Weigh how many faces to ship against download size and design fidelity, and when a single variable file replaces a stack of static ones.

## What the descriptors are A font **family** in `pubspec.yaml` is a list of files, and each file is one **face**: a combination of weight (stroke thickness) and style (upright or italic). The optional keys on each `asset` entry describe that face: - **`weight`** — an integer from 100 to 900 in steps of 100, matching `FontWeight.w100` to `FontWeight.w900`. The `flutter` tool rejects anything else, such as 650, with `Invalid value … for font -> weight.` - **`style`** — `normal` or `italic`, matching `FontStyle`. Other values are rejected the same way. - An entry with neither key is the family's regular upright face. The keys are **labels, not transformations**. Declaring `weight: 900` on a bold file does not thicken its strokes, and declaring `style: normal` on an italic file does not straighten it: the glyph outlines are whatever the designer drew. ## How a face is chosen at runtime A `TextStyle` asks for a family, a `fontWeight` and a `fontStyle`. The engine gathers the faces registered for that family and picks the closest match, CSS-style: the right style first, then the nearest weight. Take the children's book app's handwriting family: | Requested | Faces bundled | Face rendered | |---|---|---| | `FontWeight.w400` | 300, 400, 700 | Regular (400) | | `FontWeight.w600` | 300, 400, 700 | Bold (700) — nearest | | `FontWeight.w800` | 300, 400, 700 | Bold (700) — nothing heavier | | `FontWeight.w400`, `FontStyle.italic` | no italic face | Regular, possibly with a simulated slant | When the gap is large — a bold request against a family with only a regular file, or italic with no italic file — the Flutter docs warn that the engine attempts to **simulate** the face. The result is visibly worse than a real file: smeared strokes for fake bold, a mechanical lean for fake italic. The fix is to bundle the face you use, not to rely on synthesis. ## What the engine actually reads The docs tell you to declare `weight` and `style`, and you should: the tool validates them and they document the family. But in the Flutter 3.47 engine the native loader reads only `family` and `asset` from `FontManifest.json` — a source comment notes weights and styles are not handled — and each registered face reports the style **stored inside the font file** when the engine matches. Two practical consequences: 1. A file whose internal metadata is wrong — a "Bold" file that declares itself weight 400 internally, common with hand-made display faces — will not be picked for `w700` even though the pubspec says 700. 2. The name of the file never matters. `Scribbly-Bold.ttf` is bold because of its contents, not its name. When a face refuses to be selected, inspect the file's metadata with a font tool, fix or replace the file, or as a stop-gap give it its own family (`ScribblyBold`) and select it by name. ## Italic-only and single-file families If a family contains only an italic file, that file is the family's regular face too, and `FontStyle.normal` still renders italic glyphs. The reverse also holds: a family with one upright file cannot produce a true italic. Decide per face whether you need it, and ship a file for each face the design uses. ## Weights beyond the hundreds Since Flutter 3.41, `FontWeight` accepts any value from 1 to 1000 (for example `FontWeight(550)`), and `FontWeight.value` replaces the deprecated `FontWeight.index`. For a family of static files such a value still selects the closest face; only a **variable font** can render the in-between weight exactly. ## Checklist 1. Bundle a file for every weight and style the design uses. 2. Label each file with the `weight` and `style` it really has. 3. Check new faces on a device: a face that never appears usually has wrong internal metadata. 4. Avoid requesting weights far from any bundled face. In an interview, the strongest answer ties the three layers together: the YAML labels each file, the engine chooses among the files a family actually contains, and synthesis is a last resort that designers will notice. Candidates who say the label changes the rendering, or that Flutter reads weights from file names, have not looked at what the build produces.

  • The Bold file never renders although pubspec.yaml declares it weight: 700. What do you check?
    In Flutter 3.47 the engine matches on the weight and style stored inside each font file, not on the pubspec label. Inspect the file's internal weight with a font tool; hand-made faces often claim 400. Fix or replace the file, or as a stop-gap declare it as its own family and select that family by name.
  • Why can't weight: 900 on the Bold file make headings heavier?
    The key only describes which face the file is. The outlines inside the file decide how thick the strokes are, so the text looks exactly as bold as the designer drew it. Heavier headings need a real Black file or a variable font with a wider weight axis.

saying these in an interview costs you the question

  • Flutter reads a font's weight from its file name, such as -Bold.
  • Setting weight: 900 on a bold file makes the text render heavier.
  • Requesting a weight that no file declares throws an error.
  • FontStyle.normal turns an italic-only font's glyphs upright.
  • Static font files are interpolated to produce in-between weights.