In Flutter 3.47, how do you control a variable font's weight and other axes, and what did Flutter 3.41 change about FontWeight?
answer
- one file, many faces
- four-character axis tags
- TextStyle.fontVariations
- FontWeight now drives wght
- FontVariation.weight overrides it
basics
~10 sDeclare the variable file once in pubspec, then set axes with TextStyle.fontVariations such as FontVariation.width(90). Since Flutter 3.41, fontWeight also sets the wght axis and accepts any value from 1 to 1000.
solid answer
~40 sA variable font is one file whose outlines vary along design axes identified by four-character tags — `wght`, `wdth`, `slnt`, `ital`, `opsz`. You declare it once in `pubspec.yaml` and set axes through `TextStyle.fontVariations`, a list of `FontVariation(axis, value)` or the named constructors `FontVariation.weight`, `.width`, `.slant`, `.italic` and `.opticalSize`. Since Flutter 3.41, `fontWeight` also sets the `wght` axis, `FontWeight` accepts any value from 1 to 1000 (`FontWeight(550)`), and `FontWeight.index` is deprecated for `value`; an explicit `FontVariation.weight` overrides the `FontWeight`. Apps that relied on `fontWeight` not touching a variable font may render heavier after upgrading — `FontWeight.normal` restores the default. `TextStyle.lerp` interpolates `fontVariations`, fastest when both styles list the same axes in the same order.
code
dart · 23 linesimport 'package:flutter/material.dart';
class ReadAlongWord extends StatelessWidget {
const ReadAlongWord({super.key, required this.word, required this.emphasis});
final String word;
final double emphasis; // 0.0 = resting, 1.0 = being read aloud
@override
Widget build(BuildContext context) {
return Text(
word,
style: TextStyle(
fontFamily: 'ScribblyVariable',
fontSize: 26,
fontVariations: <FontVariation>[
FontVariation.weight(400 + 300 * emphasis),
const FontVariation.width(100),
],
),
);
}
}go deeper
Know that a variable font is one file with adjustable axes, set through TextStyle.fontVariations.
Explain the axis tags and constructors, the 3.41 change to FontWeight, and fontVariations versus fontFeatures.
Handle the upgrade regression where text turns heavier, and design axis animations that interpolate cheaply.
Judge when one variable file should replace a set of static faces across a product, weighing size, animation needs and design control.
## Static files versus a variable font A **static** font file holds one face: Regular, or Bold, or Light. A **variable font** holds a whole range of faces in one file. Its outlines are defined along **design axes**, and the renderer computes the face for any position on each axis. The children's book app's handwriting face can ship as a single variable file with a weight axis from 300 to 700 instead of three static files — smaller in total, and able to render any weight in between. Axes are identified by four-character **tags** from the OpenType registry: | Tag | Axis | `FontVariation` constructor | Range enforced by the constructor | |---|---|---|---| | `wght` | weight | `FontVariation.weight` | 1 to 1000 | | `wdth` | width | `FontVariation.width` | 0 or more, 100 is normal | | `slnt` | slant | `FontVariation.slant` | greater than -90 and less than 90 degrees | | `ital` | italic | `FontVariation.italic` | 0.0 to 1.0 | | `opsz` | optical size | `FontVariation.opticalSize` | greater than 0 | A font can also define custom axes; the generic `FontVariation('GRAD', 50)` takes any four-character tag. Which axes a file supports, and their ranges, is up to the font; the renderer typically clamps a value to the range the font defines. ## Declaring the variable file In `pubspec.yaml` a variable font is an ordinary family with a single `asset` entry, because one file covers every weight on its axis. Giving it its own family name, such as `ScribblyVariable`, keeps it from mixing with static files of the same design. A family that contains both a static Bold file and the variable file is harder to reason about: face matching picks a file first, and only then does the weight axis apply, so which file wins for a given `fontWeight` is not obvious from the code. Check which axes the file really has before designing around one. A face with only `wght` ignores width and slant settings, and many handwriting faces ship no italic axis at all. ## Declaring and using it The file is declared once, like any other font, and selected by family name. Axis values then go in `TextStyle.fontVariations`: ```dart const TextStyle( fontFamily: 'ScribblyVariable', fontWeight: FontWeight(550), fontVariations: <FontVariation>[FontVariation.width(90)], ) ``` `fontVariations` is for **continuous** parameters. Its sibling `fontFeatures` selects **discrete** OpenType features, such as tabular figures or a slashed zero; the two are easy to confuse in an interview. ## What changed in Flutter 3.41 Before 3.41, `fontWeight` only chose the nearest file within a family. A variable font is a single file, so `fontWeight` picked it and nothing more: the text stayed at the file's default weight unless you also set `FontVariation('wght', …)`. Flutter 3.41 changed that: - `fontWeight` now **also sets the `wght` axis** of fonts that have one; the engine adds the equivalent of `FontVariation('wght', value)` unless the style's `fontVariations` already sets `wght`. - `FontWeight` accepts **any integer from 1 to 1000**, not just `w100` … `w900`. - `FontWeight.index` is **deprecated**; use `FontWeight.value`. - An explicit `FontVariation.weight` in `fontVariations` **overrides** the `FontWeight`. The API docs recommend declaring weight through `FontWeight` and keeping `FontVariation.weight` for special cases. Migration note: an app that set `fontWeight: FontWeight.bold` on a variable font and expected no effect now renders bold text. Set `fontWeight` to the intended value, or `FontWeight.normal` to restore the font's default weight. ## Animating axes Because a variable font renders any position on an axis, it can animate smoothly — for example thickening the word being read aloud in a read-along feature. `TextStyle.lerp` interpolates `fontVariations` axis by axis: 1. It pairs variations by axis tag; an axis missing on one side is interpolated against its absence. 2. It is fastest when both styles list the same axes **in the same order**. 3. `FontWeight.lerp` rounds and clamps to 100-900, so for weights outside that band animate `FontVariation.weight` instead. `FontVariation.opticalSize` has a quieter use in animation: pinning the optical size stops a font that adapts its outlines to text size from visibly shifting while the size animates. ## When to choose a variable font - **Many weights or widths in use** — one variable file often beats several static ones on size. - **Animated weight or width** — only a variable font animates without jumping between files. - **One or two faces only** — static files are simpler and usually smaller.
- After upgrading past Flutter 3.41, headings in a variable font look heavier. Why?Since 3.41 `fontWeight` also sets the variable font's `wght` axis. Before, a `FontWeight.bold` on a single variable file changed nothing, so the text stayed at the file's default weight. Now it really renders bold. Set the intended `fontWeight`, or `FontWeight.normal` to get the default weight back.
- What is the difference between fontVariations and fontFeatures on a Flutter TextStyle?`fontVariations` sets continuous design-axis positions of a variable font, such as weight 550 or width 90, and can be interpolated by `TextStyle.lerp`. `fontFeatures` switches discrete OpenType features on or off, such as tabular figures or a slashed zero, and is not interpolated.
saying these in an interview costs you the question
- In Flutter 3.47 fontWeight has no effect on a variable font's weight axis.
- FontWeight only accepts the nine constants w100 through w900.
- A variable font needs one pubspec asset entry per weight it can render.
- fontFeatures is how you set a variable font's weight or width.
- FontWeight.index is the current way to read a weight's numeric value.