Skip to content

[web] Add font fallback service#185314

Merged
auto-submit[bot] merged 4 commits into
flutter:masterfrom
harryterkelsen:font-fallback-support
Apr 28, 2026
Merged

[web] Add font fallback service#185314
auto-submit[bot] merged 4 commits into
flutter:masterfrom
harryterkelsen:font-fallback-support

Conversation

@harryterkelsen
Copy link
Copy Markdown
Contributor

This change introduces a centralized FallbackFontService to resolve infinite download loops through resilient failure tracking while significantly boosting text rendering performance by shifting missing character discovery directly to the underlying graphics engine.

By replacing expensive string-based character scanning with a "source of truth" query to Skia for unresolved codepoints during layout, this update eliminates redundant processing and layout thrashing during paragraph building. The new service acts as a stateful coordinator that manages font downloads independently of the UI cycle, implementing a robust retry-and-recovery logic that marks failed assets as permanently unavailable to break infinite re-layout loops.

Fixes #184449

Pre-launch Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

If this change needs to override an active code freeze, provide a comment explaining why. The code freeze workflow can be overridden by code reviewers. See pinned issues for any active code freezes with guidance.

Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the gemini-code-assist bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.

@github-actions github-actions Bot added a: text input Entering text in a text field or keyboard related problems engine flutter/engine related. See also e: labels. platform-web Web applications specifically labels Apr 20, 2026
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the FallbackFontService to provide a more resilient font fallback system for Flutter Web, utilizing Skia-driven discovery and a greedy selection algorithm with built-in retry logic. The review feedback identifies potential null-safety issues when accessing the font fallback manager and registry in the HTML renderer and recommends using for-in loops instead of forEach for side effects to comply with Effective Dart style guidelines.

/// Adds a list of missing code points to be processed.
void addMissingCodePoints(List<int> codePoints) {
var added = false;
final FontFallbackManager manager = renderer.fontCollection.fontFallbackManager!;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The use of the non-null assertion operator (!) on renderer.fontCollection.fontFallbackManager is risky. While this service is primarily intended for the CanvasKit and Skwasm renderers, it is a global singleton. In environments where the HTML renderer is active, fontFallbackManager is null (as seen in WebFontCollection), which will cause a runtime exception here. A defensive null check should be added to ensure the service fails gracefully or remains inactive when the manager is unavailable. This pattern should also be applied to other occurrences in this file (lines 122, 260, 364, 483, 552).

Suggested change
final FontFallbackManager manager = renderer.fontCollection.fontFallbackManager!;
final FontFallbackManager? manager = renderer.fontCollection.fontFallbackManager;
if (manager == null) {
return;
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no HTML renderer. This code will only ever be hit from the Skwasm or CanvasKit renderers.

Comment on lines +476 to +479
final bool success = await renderer.fontCollection.fallbackFontRegistry!.loadFallbackFont(
font.name,
bytes,
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Similar to the fontFallbackManager access, using ! on fallbackFontRegistry is unsafe if the HTML renderer is active. Adding a null check here ensures robustness across different rendering backends.

          final FallbackFontRegistry? registry = renderer.fontCollection.fallbackFontRegistry;
          if (registry == null) {
            break;
          }
          final bool success = await registry.loadFallbackFont(
            font.name,
            bytes,
          );

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no HTML renderer. This code will only ever be hit from the Skwasm or CanvasKit renderers.

}

// Fire and Forget: Start downloads for the selected fonts.
newFonts.forEach(_startDownloadTask);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

According to Effective Dart (which the Flutter style guide follows), it is preferred to use a for-in loop instead of forEach for side effects, especially when dealing with asynchronous tasks, even if they are not awaited. This improves readability and is more idiomatic.

    for (final NotoFont font in newFonts) {
      _startDownloadTask(font);
    }
References
  1. Effective Dart: Style recommends using for-in loops over forEach for side effects. (link)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get an analyzer warning if I switch from using forEach to for-in. I can't find the forEach suggestion in the Effective Dart: Style document. Hallucination?

@harryterkelsen harryterkelsen added the CICD Run CI/CD label Apr 20, 2026
@github-actions github-actions Bot removed the CICD Run CI/CD label Apr 20, 2026
@harryterkelsen harryterkelsen added the CICD Run CI/CD label Apr 20, 2026
@harryterkelsen harryterkelsen requested a review from mdebbar April 21, 2026 21:34
mdebbar
mdebbar previously approved these changes Apr 27, 2026
Copy link
Copy Markdown
Contributor

@mdebbar mdebbar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks for the comprehensive tests!

@harryterkelsen harryterkelsen added the autosubmit Merge PR when tree becomes green via auto submit App label Apr 27, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Apr 27, 2026
@auto-submit
Copy link
Copy Markdown
Contributor

auto-submit Bot commented Apr 27, 2026

autosubmit label was removed for flutter/flutter/185314, because The base commit of the PR is older than 7 days and can not be merged. Please merge the latest changes from the main into this branch and resubmit the PR.

@harryterkelsen harryterkelsen force-pushed the font-fallback-support branch from ca5cc93 to 58efc1d Compare April 27, 2026 22:20
@github-actions github-actions Bot removed the CICD Run CI/CD label Apr 27, 2026
@harryterkelsen harryterkelsen added the CICD Run CI/CD label Apr 27, 2026
@github-actions github-actions Bot removed the CICD Run CI/CD label Apr 27, 2026
@harryterkelsen harryterkelsen added the CICD Run CI/CD label Apr 27, 2026
@harryterkelsen harryterkelsen requested a review from mdebbar April 28, 2026 16:05
@harryterkelsen harryterkelsen added the autosubmit Merge PR when tree becomes green via auto submit App label Apr 28, 2026
@auto-submit auto-submit Bot added this pull request to the merge queue Apr 28, 2026
Merged via the queue into flutter:master with commit 6ce6e7a Apr 28, 2026
199 of 200 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Apr 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: text input Entering text in a text field or keyboard related problems CICD Run CI/CD engine flutter/engine related. See also e: labels. platform-web Web applications specifically

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[web] Font fallback download failure causes infinite loop.

2 participants