render/key_face library

Shared pieces of the ThemeSpec key renderers (M3 Expressive, NeoPOP and every later family): the ThemedKeyFace base with bundled-font labels, press/state-layer helpers, the long-press fill and PressTranslate (a label that follows the press by repainting, never rebuilding).

The seam itself (KeyFaceRenderer, KeyFaceSpec, KeyFaceMotion) is M2's lib/themes/key_face.dart; CalcKeyButton only talks to it.

Classes

KeyCellSizes
The key-face sizes the keypad lays out now, published by each key on layout. Theme prefetch prepares renderers for them (key-shape morphs), so no commit or transition frame builds one (design doc "60 fps budget: no Morph construction in commit frames").
KeyFaceMotion
A key's animation channels, as a KeyFaceRenderer reads them (the calculator's KeyPressMotion implements it).
KeyFaceMotionDetail
Extra channels a KeyFaceMotion may also provide (the calculator's KeyPressMotion does). Renderers must still work without them (test fakes, previews): check with motion is KeyFaceMotionDetail.
KeyFaceRenderer
Draws key faces for a theme: the seam M3's ThemeSpec renderers plug into (CalcThemeData.keyFace), so the keypad widget never names a concrete painter.
KeyFaceSpec
What a KeyFaceRenderer draws one key with.
KeyFocusRingPainter
Paints the keyboard focus ring just outside the key face (motion brief §4.1: it appears instantly, no animation).
KeyLabel
A one-line key label drawn exactly in style.
KeyStyle
Colours and label weight for one group of keys.
LerpableKeyFace
A key face that can morph toward another face of the same renderer during a theme transition (motion brief §6.4: one tree, FlatMorph shapes). Renderers that implement it get their shapes interpolated at the transition's shape progress; others switch at the midpoint.
PlainKeyFace
The plain theme's key faces: PlainKeyPainter.
PlainKeyPainter
Paints the plain-theme key face: a rounded-rect fill (and optional outline) with the hover and pressed state layers, plus the long-press radial fill (motion brief §2.2.4, §4.1, §4.4).
PressTranslate
Paints child offset by rest + travel × p where p is the clamped press depth (see effectivePress); repaints on press without rebuilding or relayout. NeoPOP moves the label with the face.
RenderPressTranslate
Render object of PressTranslate.
ThemedKeyFace
Base for ThemeSpec-driven key renderers: labels use the theme's bundled variable font with its weight on the wght axis (Flutter does not map fontWeight onto variable axes reliably), tabular figures, and an optional slashed zero.

Functions

bundledLabelStyle(TextStyle style, {bool slashedZero = false}) → TextStyle
style with the variable-font axes and features of its bundled family (unchanged for the platform font).
effectivePress(double p, {required bool reduceMotion}) → double
The press depth a painter should draw: p itself (springs may overshoot outside 0, 1), or under Reduce Motion the discrete pressed artwork (motion brief §2.2.5) — 1 for any press, 0 at rest.
paintHoldFill(Canvas canvas, Rect rect, KeyFaceMotion motion, KeyFaceSpec spec, {required Path clip}) → void
Paints the long-press progress (motion brief §4.4) clipped to clip (in the canvas' current space).
stateLayerColor(Color fill, Color label) → Color
The state-layer colour of a key with fill and label: black when the label is lighter than the face, white when it is darker. The layer moves the face away from its label, so the pressed, hover and long-press states can only raise label contrast — a label-coloured layer (the M3 default) would pull a label resting at 4.5:1 down to ≈ 3.7:1 while the key is held (spec §5.4; Accessible lock 7:1).
stateLayerOpacity(KeyFaceMotion motion, KeyFaceSpec spec) → double
State-layer opacity: the pressed layer replaces hover (M3 state layers). The pressed tint is snapped on in frame 1 by KeyPressMotion, which is the M3E frame-1 cue.
withStateLayer(Color fill, Color label, double opacity) → Color
fill with its contrast-safe state layer (stateLayerColor) at opacity composited on.