-
Notifications
You must be signed in to change notification settings - Fork 0
Fonts
How to set up, customize, and preload fonts in layrz_ui apps. By default, layrz_ui uses Open Sans from Google Fonts.
The fonts module provides:
-
LayrzFont— an immutable representation of a font with its source and identity -
LayrzFontSource— an enum specifying where font bytes come from (Google Fonts, local, or custom URI) -
LayrzFontHandler— an abstract interface for resolving fonts and preloading bytes -
LayrzGoogleFontsHandler— a concrete implementation using Google Fonts
This design allows apps to preload fonts before the first paint, avoiding visual flash and layout shift.
LayrzFont represents a font resource:
class LayrzFont {
final LayrzFontSource source; // Where the bytes come from
final String name; // Font family name
final String? uri; // URL to fetch from (only for LayrzFontSource.uri)
}| Source | Example | Usage |
|---|---|---|
LayrzFontSource.google |
LayrzFont(source: LayrzFontSource.google, name: 'Open Sans') |
Fetch from Google Fonts at runtime |
LayrzFontSource.local |
LayrzFont(source: LayrzFontSource.local, name: 'CustomFont') |
Already registered in pubspec.yaml fonts: section |
LayrzFontSource.uri |
LayrzFont(source: LayrzFontSource.uri, name: 'MyFont', uri: 'https://...') |
Download raw font file from a URL |
const LayrzFont kLayrzFont = LayrzFont(
source: LayrzFontSource.google,
name: 'Open Sans',
);
const List<String> kLayrzFontFallbacks = ['Ubuntu', 'Roboto'];The default layrz_ui font is Open Sans. If it cannot be resolved, the system falls back to Ubuntu, then Roboto, then system fonts.
LayrzFontHandler defines the contract for font resolution and preloading:
abstract class LayrzFontHandler {
/// Preload font bytes before first paint.
Future<void> preload(LayrzFont font);
/// Resolve a font into a concrete family name for TextStyle.fontFamily.
String resolveFamily(LayrzFont font);
/// Fallback font families if the primary font cannot be resolved.
List<String> get fallbacks;
}Two methods:
-
preload(font)— Downloads or loads font bytes and registers them with the engine -
resolveFamily(font)— Returns the font family name to use inTextStyle.fontFamily
The built-in handler that uses the google_fonts package to fetch and resolve fonts.
const LayrzGoogleFontsHandler({
this.fetcher, // Optional callback to download URI fonts
});
// The optional fetcher callback:
// Future<ByteData> Function(String uri)?The fetcher callback is required only if you need to preload fonts from custom URIs (not Google Fonts). It must download raw font bytes from a URL and return a ByteData.
| Source | Behavior |
|---|---|
LayrzFontSource.google |
Fetches from Google Fonts; if unavailable, falls back to Open Sans |
LayrzFontSource.local |
Assumes the font is already registered in pubspec.yaml; no preload needed |
LayrzFontSource.uri |
Downloads from the provided URI using the fetcher callback; throws if no fetcher is provided |
Preload fonts before calling runApp() to avoid visual flash:
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
const handler = LayrzGoogleFontsHandler();
try {
await handler.preload(kLayrzFont);
} catch (e) {
debugPrint('Font preload failed (likely offline): $e');
}
runApp(const MyApp());
}Pass the same handler to LayrzThemeData.light() so typography uses the preloaded font:
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return LayrzApp(
theme: LayrzThemeData.light(
fontHandler: const LayrzGoogleFontsHandler(),
),
home: const HomePage(),
);
}
}If you need to download fonts from a custom URL:
import 'package:http/http.dart' as http;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// Create a handler with a custom fetcher
final handler = LayrzGoogleFontsHandler(
fetcher: (uri) async {
final response = await http.get(Uri.parse(uri));
if (response.statusCode == 200) {
return ByteData.view(response.bodyBytes.buffer);
}
throw Exception('Failed to fetch font from $uri');
},
);
// Preload a custom font
try {
await handler.preload(LayrzFont(
source: LayrzFontSource.uri,
name: 'CustomFont',
uri: 'https://example.com/fonts/CustomFont.ttf',
));
} catch (e) {
debugPrint('Font preload failed: $e');
}
runApp(MyApp(fontHandler: handler));
}By default, LayrzThemeData.light() uses Open Sans for both body and title text:
LayrzThemeData.light(
titleFont: kLayrzFont, // Open Sans (default)
bodyFont: kLayrzFont, // Open Sans (default)
)Specify different fonts for titles and body text:
LayrzThemeData.light(
titleFont: LayrzFont(
source: LayrzFontSource.google,
name: 'Poppins',
),
bodyFont: LayrzFont(
source: LayrzFontSource.google,
name: 'Inter',
),
fontHandler: const LayrzGoogleFontsHandler(),
)If a font is already in your pubspec.yaml:
flutter:
fonts:
- family: MyCustomFont
fonts:
- asset: assets/fonts/MyCustomFont.ttfUse it in layrz_ui:
LayrzThemeData.light(
bodyFont: LayrzFont(
source: LayrzFontSource.local,
name: 'MyCustomFont',
),
)When you pass a fontHandler to LayrzThemeData.light():
-
Preload happens before the app runs (in
main()), so typography is ready for the first paint -
Resolution happens at theme construction time, when
LayrzTextTheme.defaults()builds the text style scale -
Fallback — if a font cannot be resolved, the handler's
fallbackslist is tried in order
If you need custom resolution logic, implement LayrzFontHandler:
class MyFontHandler extends LayrzFontHandler {
@override
List<String> get fallbacks => ['Roboto', 'Arial'];
@override
Future<void> preload(LayrzFont font) async {
// Load font bytes from your custom source
}
@override
String resolveFamily(LayrzFont font) {
// Return the font family name for TextStyle.fontFamily
return font.name;
}
}layrz_ui depends on google_fonts to fetch Google Fonts at runtime. The google_fonts package imports Material in its source, but layrz_ui never uses Material-requiring APIs from it.
See decision D3 for the rationale.
If font preload fails (e.g., no internet), the app does not crash. Instead:
- Text renders in the fallback system font immediately
- Once the app is running, the font loads in the background
- Text reflows when the font becomes available
This is the standard Flutter behavior and is acceptable for layrz_ui.
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
const handler = LayrzGoogleFontsHandler();
try {
await handler.preload(kLayrzFont);
} catch (e) {
debugPrint('Font preload failed: $e — app will open with fallback fonts');
}
runApp(const MyApp());
}import 'package:flutter/widgets.dart';
import 'package:layrz_ui/layrz_ui.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final handler = LayrzGoogleFontsHandler();
// Preload Poppins for titles
try {
await handler.preload(LayrzFont(
source: LayrzFontSource.google,
name: 'Poppins',
));
} catch (e) {
debugPrint('Title font preload failed: $e');
}
runApp(MyApp(fontHandler: handler));
}
class MyApp extends StatelessWidget {
const MyApp({required this.fontHandler, super.key});
final LayrzGoogleFontsHandler fontHandler;
@override
Widget build(BuildContext context) {
return LayrzApp(
title: 'My App',
theme: LayrzThemeData.light(
titleFont: LayrzFont(
source: LayrzFontSource.google,
name: 'Poppins',
),
bodyFont: kLayrzFont, // Open Sans
fontHandler: fontHandler,
),
home: const HomePage(),
);
}
}Last updated: 2026-08-13
Related pages: Getting-Started, Theming, Design-Tokens
Made with ❤️ by Golden M, Inc.