Skip to content
Kenny Mochizuki Escalona edited this page Aug 13, 2026 · 1 revision

Fonts

How to set up, customize, and preload fonts in layrz_ui apps. By default, layrz_ui uses Open Sans from Google Fonts.


Overview

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: Font Identity

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)
}

Font Sources

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

Default Font

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: Abstract Interface

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 in TextStyle.fontFamily

LayrzGoogleFontsHandler: Google Fonts Implementation

The built-in handler that uses the google_fonts package to fetch and resolve fonts.

Constructor

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.

Supported Sources

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

Preloading Fonts

Preload fonts before calling runApp() to avoid visual flash:

Basic: Preload Open Sans

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());
}

With Custom Font Handler

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(),
    );
  }
}

With Custom HTTP Fetcher (for URI fonts)

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));
}

Using Fonts in Typography

Default: Open Sans

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)
)

Custom Fonts

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(),
)

Local Fonts

If a font is already in your pubspec.yaml:

flutter:
  fonts:
    - family: MyCustomFont
      fonts:
        - asset: assets/fonts/MyCustomFont.ttf

Use it in layrz_ui:

LayrzThemeData.light(
  bodyFont: LayrzFont(
    source: LayrzFontSource.local,
    name: 'MyCustomFont',
  ),
)

Font Handler Contract

When you pass a fontHandler to LayrzThemeData.light():

  1. Preload happens before the app runs (in main()), so typography is ready for the first paint
  2. Resolution happens at theme construction time, when LayrzTextTheme.defaults() builds the text style scale
  3. Fallback — if a font cannot be resolved, the handler's fallbacks list is tried in order

Example: Custom Font Handler

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;
  }
}

Google Fonts Coupling Note

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.


Graceful Degradation

If font preload fails (e.g., no internet), the app does not crash. Instead:

  1. Text renders in the fallback system font immediately
  2. Once the app is running, the font loads in the background
  3. 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());
}

Example: Full Setup with Custom Font

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

Clone this wiki locally