app_typography_gen 0.1.0
app_typography_gen: ^0.1.0 copied to clipboard
Generate a typed Text widget, style enum, and text styles for Flutter from a single well-structured YAML typography config.
app_typography_gen #
Generate a typed Text wrapper, a style enum, and the matching text styles for
Flutter from one YAML file.
A text style is a font family plus a size, line height, letter spacing and
weight. Apps usually hand-write a TextStyle for each one and a widget to apply
it. You list the styles once in YAML, grouped by font family, and the generator
produces:
- an
enumof every style (e.g.titleRobotoBold), - the
TextStyles themselves, - a
Textwrapper that applies them.
You name each style yourself, so the generated names match whatever your design system already calls them.
The core is plain Dart with no flutter or build dependency, so it's easy to
test on its own. build_runner and an optional CLI sit on top of it.
Why #
A design system's text styles rarely line up with Flutter's built-in
TextTheme, so on most projects we ended up hand-writing custom TextStyles -
and a pile of boilerplate with them. This generator takes that off your plate.
It's inspired by flutter_gen.
Quickstart (build_runner) #
-
Add the dependency:
dev_dependencies: app_typography_gen: ^0.1.0 build_runner: ^2.4.0 -
Create
lib/app.typography.yaml(see the schema below). -
Generate:
dart run build_runner build --delete-conflicting-outputs # Flutter projects: flutter pub run build_runner build --delete-conflicting-outputsThis turns
lib/app.typography.yamlintolib/app.typography.gen.dart. -
Use it:
import 'app.typography.gen.dart'; AppTypography.titleRobotoBold('Hello'); AppTypography.bodyRobotoRegular('World', color: Colors.black54);
Commit the generated file to source control. Don't edit it by hand; regenerate it when the config changes.
The generated widget #
Every constructor renders a Text with the resolved style, disables OS text
scaling (TextScaler.noScaling), and exposes the fields you usually reach for:
AppTypography.titleRobotoBold(
'Heading',
textAlign: TextAlign.center, // default TextAlign.start
color: ColorName.brand, // falls back to output.default_color
maxLines: 2,
overflow: TextOverflow.ellipsis,
decoration: TextDecoration.underline,
fontStyle: FontStyle.italic,
fontWeight: FontWeight.w600, // overrides the style's weight
height: 1.2,
);
Schema reference #
The config lives under a top-level typography: key, either in a standalone
*.typography.yaml file (used by the Builder) or nested inside pubspec.yaml
(used by the CLI).
typography:
output:
widget: AppTypography # generated widget class name
enum: AppTypographyStyle # generated enum name
call_site: factory # factory | enum (default: factory)
style_access: text_theme_extension # text_theme_extension | static_class | widget
default_color: ColorName.gray900 # a Dart expression, copied as-is
imports: # imports the generated file needs
- package:my_app/gen/colors.gen.dart
- package:my_app/gen/fonts.gen.dart
families: # font_family is shared by the styles below
Roboto:
font_family: FontFamily.roboto # a Dart expression, copied as-is
styles:
titleRobotoBold: # the key is the generated style name
size: 72
line_height: 88 # optional; emitted as height: 88 / 72
tracking: -0.8 # optional; becomes letterSpacing
weight: 700 # optional; 100..900 -> FontWeight.w700
bodyRoboto:
size: 16 # size is the only required field
line_height: 24
RobotoSlab:
font_family: FontFamily.robotoSlab
styles:
headingRobotoSlab:
size: 56
line_height: 68
weight: 600
Keys #
| Key | Required | Meaning |
|---|---|---|
output.widget |
yes | Generated widget class name. |
output.enum |
yes | Generated enum name. |
output.call_site |
no (factory) |
factory -> one named constructor per style; enum -> one constructor taking a style: argument. |
output.style_access |
no (text_theme_extension) |
Where the styles live: text_theme_extension -> getters on a TextTheme extension (needs a BuildContext); static_class -> static const fields on a separate class; widget -> static const fields on the widget class, so AppTypography.titleRobotoBold is the TextStyle (requires call_site: enum). |
output.default_color |
yes | Dart expression used as the widget's default color. Copied through unchanged. |
output.imports |
no ([]) |
Imports the generated file needs (for default_color, font_family, etc.). Copied through unchanged. |
families.<Name>.font_family |
yes | Dart expression for fontFamily:, shared by the family's styles. Copied through unchanged. |
families.<Name>.styles.<name> |
yes | A style. The key is the generated name and must be a valid Dart identifier, unique across all families. |
...styles.<name>.size |
yes | Font size (number). |
...styles.<name>.line_height |
no | Line height in pixels; emitted as height: line_height / size. |
...styles.<name>.tracking |
no | Letter spacing. |
...styles.<name>.weight |
no | FontWeight number, 100..900 in steps of 100. |
...styles.<name>.font_family |
no | Overrides the family's font_family for this one style. |
Emission rules #
- The style key is emitted as the name, exactly as written.
- Only the fields you set end up in the
TextStyle. A style with just asizeproducesTextStyle(fontFamily: ..., fontSize: ...)and nothing else. line_heightis in pixels and emitted as the ratioheight: <line_height> / <size>.trackingbecomesletterSpacing;weightbecomesfontWeight: FontWeight.w<n>.font_family,default_color, andimportsare copied straight through. The generator never makes up package paths.
Call-site styles #
factory (default): a named constructor per style.
AppTypography.titleRobotoBold('x');
enum: one constructor plus the style enum.
AppTypography('x', style: AppTypographyStyle.titleRobotoBold);
Getting the raw TextStyle #
text_theme_extension exposes the styles as TextTheme getters
(context.textTheme.titleRobotoBold) and static_class as
AppTypographyStyles.titleRobotoBold. With style_access: widget (which needs
call_site: enum) the styles are static const fields on the widget class
itself:
final style = AppTypography.titleRobotoBold; // a TextStyle
const AppTypography('x', style: AppTypographyStyle.titleRobotoBold); // the widget
Programmatic use #
import 'package:app_typography_gen/app_typography_gen.dart';
final dartSource = generateTypography(yamlString);
Additional information #
- Issues: https://github.com/HotShots-Labs/app_typography_gen/issues
- See
example/for a complete, runnable Flutter app: the input YAML, the generated file, and a screen using the widget.